/*************************************************************************
ALGLIB 4.07.0 (source code generated 2025-12-29)
Copyright (c) Sergey Bochkanov (ALGLIB project).

>>> SOURCE LICENSE >>>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation (www.fsf.org); either version 2 of the 
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

A copy of the GNU General Public License is available at
http://www.fsf.org/licensing/licenses
>>> END OF LICENSE >>>
*************************************************************************/
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "stdafx.h"
#include "optimization.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

/////////////////////////////////////////////////////////////////////////
//
// THIS SECTION CONTAINS IMPLEMENTATION OF C++ INTERFACE
//
/////////////////////////////////////////////////////////////////////////
namespace alglib
{


#if defined(AE_COMPILE_OPTGUARDAPI) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
This structure is used to store  OptGuard  report,  i.e.  report  on   the
properties of the nonlinear function being optimized with ALGLIB.

After you tell your optimizer to activate OptGuard  this technology starts
to silently monitor function values and gradients/Jacobians  being  passed
all around during your optimization session. Depending on specific set  of
checks enabled OptGuard may perform additional function evaluations  (say,
about 3*N evaluations if you want to check analytic gradient for errors).

Upon discovering that something strange happens  (function  values  and/or
gradient components change too sharply and/or unexpectedly) OptGuard  sets
one of the "suspicion  flags" (without interrupting optimization session).
After optimization is done, you can examine OptGuard report.

Following report fields can be set:
* nonc0suspected
* nonc1suspected
* badgradsuspected


=== WHAT CAN BE DETECTED WITH OptGuard INTEGRITY CHECKER =================

Following  types  of  errors  in your target function (constraints) can be
caught:
a) discontinuous functions ("non-C0" part of the report)
b) functions with discontinuous derivative ("non-C1" part of the report)
c) errors in the analytic gradient provided by user

These types of errors result in optimizer  stopping  well  before reaching
solution (most often - right after encountering discontinuity).

Type A errors are usually  coding  errors  during  implementation  of  the
target function. Most "normal" problems involve continuous functions,  and
anyway you can't reliably optimize discontinuous function.

Type B errors are either coding errors or (in case code itself is correct)
evidence of the fact  that  your  problem  is  an  "incorrect"  one.  Most
optimizers (except for ones provided by MINNS subpackage) do  not  support
nonsmooth problems.

Type C errors are coding errors which often prevent optimizer from  making
even one step  or result in optimizing stopping  too  early,  as  soon  as
actual descent direction becomes too different from one suggested by user-
supplied gradient.


=== WHAT IS REPORTED =====================================================

Following set of report fields deals with discontinuous  target functions,
ones not belonging to C0 continuity class:

* nonc0suspected - is a flag which is set upon discovering some indication
  of the discontinuity. If this flag is false, the rest of "non-C0" fields
  should be ignored
* nonc0fidx - is an index of the function (0 for  target  function,  1  or
  higher for nonlinear constraints) which is suspected of being "non-C0"
* nonc0lipshitzc - a Lipchitz constant for a function which was  suspected
  of being non-continuous.
* nonc0test0positive -  set  to  indicate  specific  test  which  detected
  continuity violation (test #0)

Following set of report fields deals with discontinuous gradient/Jacobian,
i.e. with functions violating C1 continuity:

* nonc1suspected - is a flag which is set upon discovering some indication
  of the discontinuity. If this flag is false, the rest of "non-C1" fields
  should be ignored
* nonc1fidx - is an index of the function (0 for  target  function,  1  or
  higher for nonlinear constraints) which is suspected of being "non-C1"
* nonc1lipshitzc - a Lipchitz constant for a function gradient  which  was
  suspected of being non-smooth.
* nonc1test0positive -  set  to  indicate  specific  test  which  detected
  continuity violation (test #0)
* nonc1test1positive -  set  to  indicate  specific  test  which  detected
  continuity violation (test #1)

Following set of report fields deals with errors in the gradient:
* badgradsuspected - is a flad which is set upon discovering an  error  in
  the analytic gradient supplied by user
* badgradfidx - index  of   the  function  with bad gradient (0 for target
  function, 1 or higher for nonlinear constraints)
* badgradvidx - index of the variable
* badgradxbase - location where Jacobian is tested
* following  matrices  store  user-supplied  Jacobian  and  its  numerical
  differentiation version (which is assumed to be  free  from  the  coding
  errors), both of them computed near the initial point:
  * badgraduser, an array[K,N], analytic Jacobian supplied by user
  * badgradnum,  an array[K,N], numeric  Jacobian computed by ALGLIB
  Here K is a total number of  nonlinear  functions  (target  +  nonlinear
  constraints), N is a variable number.
  The  element  of  badgraduser[] with index [badgradfidx,badgradvidx]  is
  assumed to be wrong.

More detailed error log can  be  obtained  from  optimizer  by  explicitly
requesting reports for tests C0.0, C1.0, C1.1.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
_optguardreport_owner::_optguardreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_optguardreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::optguardreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::optguardreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::optguardreport));
    alglib_impl::_optguardreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_optguardreport_owner::_optguardreport_owner(alglib_impl::optguardreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_optguardreport_owner::_optguardreport_owner(const _optguardreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_optguardreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: optguardreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::optguardreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::optguardreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::optguardreport));
    alglib_impl::_optguardreport_init_copy(p_struct, const_cast<alglib_impl::optguardreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_optguardreport_owner& _optguardreport_owner::operator=(const _optguardreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: optguardreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: optguardreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: optguardreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_optguardreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::optguardreport));
    alglib_impl::_optguardreport_init_copy(p_struct, const_cast<alglib_impl::optguardreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_optguardreport_owner::~_optguardreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_optguardreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::optguardreport* _optguardreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::optguardreport* _optguardreport_owner::c_ptr() const
{
    return p_struct;
}
optguardreport::optguardreport() : _optguardreport_owner() ,nonc0suspected(p_struct->nonc0suspected),nonc0test0positive(p_struct->nonc0test0positive),nonc0fidx(p_struct->nonc0fidx),nonc0lipschitzc(p_struct->nonc0lipschitzc),nonc1suspected(p_struct->nonc1suspected),nonc1test0positive(p_struct->nonc1test0positive),nonc1test1positive(p_struct->nonc1test1positive),nonc1fidx(p_struct->nonc1fidx),nonc1lipschitzc(p_struct->nonc1lipschitzc),badgradsuspected(p_struct->badgradsuspected),badgradfidx(p_struct->badgradfidx),badgradvidx(p_struct->badgradvidx),badgradxbase(&p_struct->badgradxbase),badgraduser(&p_struct->badgraduser),badgradnum(&p_struct->badgradnum)
{
}

optguardreport::optguardreport(alglib_impl::optguardreport *attach_to):_optguardreport_owner(attach_to) ,nonc0suspected(p_struct->nonc0suspected),nonc0test0positive(p_struct->nonc0test0positive),nonc0fidx(p_struct->nonc0fidx),nonc0lipschitzc(p_struct->nonc0lipschitzc),nonc1suspected(p_struct->nonc1suspected),nonc1test0positive(p_struct->nonc1test0positive),nonc1test1positive(p_struct->nonc1test1positive),nonc1fidx(p_struct->nonc1fidx),nonc1lipschitzc(p_struct->nonc1lipschitzc),badgradsuspected(p_struct->badgradsuspected),badgradfidx(p_struct->badgradfidx),badgradvidx(p_struct->badgradvidx),badgradxbase(&p_struct->badgradxbase),badgraduser(&p_struct->badgraduser),badgradnum(&p_struct->badgradnum)
{
}

optguardreport::optguardreport(const optguardreport &rhs):_optguardreport_owner(rhs) ,nonc0suspected(p_struct->nonc0suspected),nonc0test0positive(p_struct->nonc0test0positive),nonc0fidx(p_struct->nonc0fidx),nonc0lipschitzc(p_struct->nonc0lipschitzc),nonc1suspected(p_struct->nonc1suspected),nonc1test0positive(p_struct->nonc1test0positive),nonc1test1positive(p_struct->nonc1test1positive),nonc1fidx(p_struct->nonc1fidx),nonc1lipschitzc(p_struct->nonc1lipschitzc),badgradsuspected(p_struct->badgradsuspected),badgradfidx(p_struct->badgradfidx),badgradvidx(p_struct->badgradvidx),badgradxbase(&p_struct->badgradxbase),badgraduser(&p_struct->badgraduser),badgradnum(&p_struct->badgradnum)
{
}

optguardreport& optguardreport::operator=(const optguardreport &rhs)
{
    if( this==&rhs )
        return *this;
    _optguardreport_owner::operator=(rhs);
    return *this;
}

optguardreport::~optguardreport()
{
}




/*************************************************************************
This  structure  is  used  for  detailed   reporting  about  suspected  C0
continuity violation.

=== WHAT IS TESTED =======================================================

C0 test  studies  function  values (not gradient!)  obtained  during  line
searches and monitors estimate of the Lipschitz  constant.  Sudden  spikes
usually indicate that discontinuity was detected.


=== WHAT IS REPORTED =====================================================

Actually, report retrieval function returns TWO report structures:

* one for most suspicious point found so far (one with highest  change  in
  the function value), so called "strongest" report
* another one for most detailed line search (more function  evaluations  =
  easier to understand what's going on) which triggered  test #0 criteria,
  so called "longest" report

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* fidx - is an index of the function (0 for  target  function, 1 or higher
  for nonlinear constraints) which is suspected of being "non-C1"
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], f[] - arrays of length CNT which store step lengths and  function
  values at these points; f[i] is evaluated in x0+stp[i]*d.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
* inneriter, outeriter - inner and outer iteration indexes (can be -1 if no
  iteration information was specified)

You can plot function values stored in stp[]  and  f[]  arrays  and  study
behavior of your function by your own eyes, just  to  be  sure  that  test
correctly reported C1 violation.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
_optguardnonc0report_owner::_optguardnonc0report_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_optguardnonc0report_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::optguardnonc0report*)alglib_impl::ae_malloc(sizeof(alglib_impl::optguardnonc0report), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::optguardnonc0report));
    alglib_impl::_optguardnonc0report_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_optguardnonc0report_owner::_optguardnonc0report_owner(alglib_impl::optguardnonc0report *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_optguardnonc0report_owner::_optguardnonc0report_owner(const _optguardnonc0report_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_optguardnonc0report_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: optguardnonc0report copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::optguardnonc0report*)alglib_impl::ae_malloc(sizeof(alglib_impl::optguardnonc0report), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::optguardnonc0report));
    alglib_impl::_optguardnonc0report_init_copy(p_struct, const_cast<alglib_impl::optguardnonc0report*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_optguardnonc0report_owner& _optguardnonc0report_owner::operator=(const _optguardnonc0report_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: optguardnonc0report assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: optguardnonc0report assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: optguardnonc0report assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_optguardnonc0report_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::optguardnonc0report));
    alglib_impl::_optguardnonc0report_init_copy(p_struct, const_cast<alglib_impl::optguardnonc0report*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_optguardnonc0report_owner::~_optguardnonc0report_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_optguardnonc0report_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::optguardnonc0report* _optguardnonc0report_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::optguardnonc0report* _optguardnonc0report_owner::c_ptr() const
{
    return p_struct;
}
optguardnonc0report::optguardnonc0report() : _optguardnonc0report_owner() ,positive(p_struct->positive),fidx(p_struct->fidx),x0(&p_struct->x0),d(&p_struct->d),n(p_struct->n),stp(&p_struct->stp),f(&p_struct->f),cnt(p_struct->cnt),stpidxa(p_struct->stpidxa),stpidxb(p_struct->stpidxb),inneriter(p_struct->inneriter),outeriter(p_struct->outeriter)
{
}

optguardnonc0report::optguardnonc0report(alglib_impl::optguardnonc0report *attach_to):_optguardnonc0report_owner(attach_to) ,positive(p_struct->positive),fidx(p_struct->fidx),x0(&p_struct->x0),d(&p_struct->d),n(p_struct->n),stp(&p_struct->stp),f(&p_struct->f),cnt(p_struct->cnt),stpidxa(p_struct->stpidxa),stpidxb(p_struct->stpidxb),inneriter(p_struct->inneriter),outeriter(p_struct->outeriter)
{
}

optguardnonc0report::optguardnonc0report(const optguardnonc0report &rhs):_optguardnonc0report_owner(rhs) ,positive(p_struct->positive),fidx(p_struct->fidx),x0(&p_struct->x0),d(&p_struct->d),n(p_struct->n),stp(&p_struct->stp),f(&p_struct->f),cnt(p_struct->cnt),stpidxa(p_struct->stpidxa),stpidxb(p_struct->stpidxb),inneriter(p_struct->inneriter),outeriter(p_struct->outeriter)
{
}

optguardnonc0report& optguardnonc0report::operator=(const optguardnonc0report &rhs)
{
    if( this==&rhs )
        return *this;
    _optguardnonc0report_owner::operator=(rhs);
    return *this;
}

optguardnonc0report::~optguardnonc0report()
{
}




/*************************************************************************
This  structure  is  used  for  detailed   reporting  about  suspected  C1
continuity violation as flagged by C1 test #0 (OptGuard  has several tests
for C1 continuity, this report is used by #0).

=== WHAT IS TESTED =======================================================

C1 test #0 studies function values (not gradient!)  obtained  during  line
searches and monitors behavior of directional  derivative  estimate.  This
test is less powerful than test #1, but it does  not  depend  on  gradient
values  and  thus  it  is  more  robust  against  artifacts  introduced by
numerical differentiation.


=== WHAT IS REPORTED =====================================================

Actually, report retrieval function returns TWO report structures:

* one for most suspicious point found so far (one with highest  change  in
  the directional derivative), so called "strongest" report
* another one for most detailed line search (more function  evaluations  =
  easier to understand what's going on) which triggered  test #0 criteria,
  so called "longest" report

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* fidx - is an index of the function (0 for  target  function, 1 or higher
  for nonlinear constraints) which is suspected of being "non-C1"
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], f[] - arrays of length CNT which store step lengths and  function
  values at these points; f[i] is evaluated in x0+stp[i]*d.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
* inneriter, outeriter - inner and outer iteration indexes (can be -1 if no
  iteration information was specified)

You can plot function values stored in stp[]  and  f[]  arrays  and  study
behavior of your function by your own eyes, just  to  be  sure  that  test
correctly reported C1 violation.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
_optguardnonc1test0report_owner::_optguardnonc1test0report_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_optguardnonc1test0report_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::optguardnonc1test0report*)alglib_impl::ae_malloc(sizeof(alglib_impl::optguardnonc1test0report), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::optguardnonc1test0report));
    alglib_impl::_optguardnonc1test0report_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_optguardnonc1test0report_owner::_optguardnonc1test0report_owner(alglib_impl::optguardnonc1test0report *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_optguardnonc1test0report_owner::_optguardnonc1test0report_owner(const _optguardnonc1test0report_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_optguardnonc1test0report_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: optguardnonc1test0report copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::optguardnonc1test0report*)alglib_impl::ae_malloc(sizeof(alglib_impl::optguardnonc1test0report), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::optguardnonc1test0report));
    alglib_impl::_optguardnonc1test0report_init_copy(p_struct, const_cast<alglib_impl::optguardnonc1test0report*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_optguardnonc1test0report_owner& _optguardnonc1test0report_owner::operator=(const _optguardnonc1test0report_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: optguardnonc1test0report assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: optguardnonc1test0report assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: optguardnonc1test0report assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_optguardnonc1test0report_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::optguardnonc1test0report));
    alglib_impl::_optguardnonc1test0report_init_copy(p_struct, const_cast<alglib_impl::optguardnonc1test0report*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_optguardnonc1test0report_owner::~_optguardnonc1test0report_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_optguardnonc1test0report_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::optguardnonc1test0report* _optguardnonc1test0report_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::optguardnonc1test0report* _optguardnonc1test0report_owner::c_ptr() const
{
    return p_struct;
}
optguardnonc1test0report::optguardnonc1test0report() : _optguardnonc1test0report_owner() ,positive(p_struct->positive),fidx(p_struct->fidx),x0(&p_struct->x0),d(&p_struct->d),n(p_struct->n),stp(&p_struct->stp),f(&p_struct->f),cnt(p_struct->cnt),stpidxa(p_struct->stpidxa),stpidxb(p_struct->stpidxb),inneriter(p_struct->inneriter),outeriter(p_struct->outeriter)
{
}

optguardnonc1test0report::optguardnonc1test0report(alglib_impl::optguardnonc1test0report *attach_to):_optguardnonc1test0report_owner(attach_to) ,positive(p_struct->positive),fidx(p_struct->fidx),x0(&p_struct->x0),d(&p_struct->d),n(p_struct->n),stp(&p_struct->stp),f(&p_struct->f),cnt(p_struct->cnt),stpidxa(p_struct->stpidxa),stpidxb(p_struct->stpidxb),inneriter(p_struct->inneriter),outeriter(p_struct->outeriter)
{
}

optguardnonc1test0report::optguardnonc1test0report(const optguardnonc1test0report &rhs):_optguardnonc1test0report_owner(rhs) ,positive(p_struct->positive),fidx(p_struct->fidx),x0(&p_struct->x0),d(&p_struct->d),n(p_struct->n),stp(&p_struct->stp),f(&p_struct->f),cnt(p_struct->cnt),stpidxa(p_struct->stpidxa),stpidxb(p_struct->stpidxb),inneriter(p_struct->inneriter),outeriter(p_struct->outeriter)
{
}

optguardnonc1test0report& optguardnonc1test0report::operator=(const optguardnonc1test0report &rhs)
{
    if( this==&rhs )
        return *this;
    _optguardnonc1test0report_owner::operator=(rhs);
    return *this;
}

optguardnonc1test0report::~optguardnonc1test0report()
{
}




/*************************************************************************
This  structure  is  used  for  detailed   reporting  about  suspected  C1
continuity violation as flagged by C1 test #1 (OptGuard  has several tests
for C1 continuity, this report is used by #1).

=== WHAT IS TESTED =======================================================

C1 test #1 studies individual  components  of  the  gradient  as  recorded
during line searches. Upon discovering discontinuity in the gradient  this
test records specific component which was suspected (or  one  with  highest
indication of discontinuity if multiple components are suspected).

When precise analytic gradient is provided this test is more powerful than
test #0  which  works  with  function  values  and  ignores  user-provided
gradient.  However,  test  #0  becomes  more   powerful   when   numerical
differentiation is employed (in such cases test #1 detects  higher  levels
of numerical noise and becomes too conservative).

This test also tells specific components of the gradient which violate  C1
continuity, which makes it more informative than #0, which just tells that
continuity is violated.


=== WHAT IS REPORTED =====================================================

Actually, report retrieval function returns TWO report structures:

* one for most suspicious point found so far (one with highest  change  in
  the directional derivative), so called "strongest" report
* another one for most detailed line search (more function  evaluations  =
  easier to understand what's going on) which triggered  test #1 criteria,
  so called "longest" report

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* fidx - is an index of the function (0 for  target  function, 1 or higher
  for nonlinear constraints) which is suspected of being "non-C1"
* vidx - is an index of the variable in [0,N) with nonsmooth derivative
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], g[] - arrays of length CNT which store step lengths and  gradient
  values at these points; g[i] is evaluated in  x0+stp[i]*d  and  contains
  vidx-th component of the gradient.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
* inneriter, outeriter - inner and outer iteration indexes (can be -1 if  no
  iteration information was specified)

You can plot function values stored in stp[]  and  g[]  arrays  and  study
behavior of your function by your own eyes, just  to  be  sure  that  test
correctly reported C1 violation.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
_optguardnonc1test1report_owner::_optguardnonc1test1report_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_optguardnonc1test1report_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::optguardnonc1test1report*)alglib_impl::ae_malloc(sizeof(alglib_impl::optguardnonc1test1report), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::optguardnonc1test1report));
    alglib_impl::_optguardnonc1test1report_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_optguardnonc1test1report_owner::_optguardnonc1test1report_owner(alglib_impl::optguardnonc1test1report *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_optguardnonc1test1report_owner::_optguardnonc1test1report_owner(const _optguardnonc1test1report_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_optguardnonc1test1report_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: optguardnonc1test1report copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::optguardnonc1test1report*)alglib_impl::ae_malloc(sizeof(alglib_impl::optguardnonc1test1report), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::optguardnonc1test1report));
    alglib_impl::_optguardnonc1test1report_init_copy(p_struct, const_cast<alglib_impl::optguardnonc1test1report*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_optguardnonc1test1report_owner& _optguardnonc1test1report_owner::operator=(const _optguardnonc1test1report_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: optguardnonc1test1report assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: optguardnonc1test1report assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: optguardnonc1test1report assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_optguardnonc1test1report_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::optguardnonc1test1report));
    alglib_impl::_optguardnonc1test1report_init_copy(p_struct, const_cast<alglib_impl::optguardnonc1test1report*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_optguardnonc1test1report_owner::~_optguardnonc1test1report_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_optguardnonc1test1report_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::optguardnonc1test1report* _optguardnonc1test1report_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::optguardnonc1test1report* _optguardnonc1test1report_owner::c_ptr() const
{
    return p_struct;
}
optguardnonc1test1report::optguardnonc1test1report() : _optguardnonc1test1report_owner() ,positive(p_struct->positive),fidx(p_struct->fidx),vidx(p_struct->vidx),x0(&p_struct->x0),d(&p_struct->d),n(p_struct->n),stp(&p_struct->stp),g(&p_struct->g),cnt(p_struct->cnt),stpidxa(p_struct->stpidxa),stpidxb(p_struct->stpidxb),inneriter(p_struct->inneriter),outeriter(p_struct->outeriter)
{
}

optguardnonc1test1report::optguardnonc1test1report(alglib_impl::optguardnonc1test1report *attach_to):_optguardnonc1test1report_owner(attach_to) ,positive(p_struct->positive),fidx(p_struct->fidx),vidx(p_struct->vidx),x0(&p_struct->x0),d(&p_struct->d),n(p_struct->n),stp(&p_struct->stp),g(&p_struct->g),cnt(p_struct->cnt),stpidxa(p_struct->stpidxa),stpidxb(p_struct->stpidxb),inneriter(p_struct->inneriter),outeriter(p_struct->outeriter)
{
}

optguardnonc1test1report::optguardnonc1test1report(const optguardnonc1test1report &rhs):_optguardnonc1test1report_owner(rhs) ,positive(p_struct->positive),fidx(p_struct->fidx),vidx(p_struct->vidx),x0(&p_struct->x0),d(&p_struct->d),n(p_struct->n),stp(&p_struct->stp),g(&p_struct->g),cnt(p_struct->cnt),stpidxa(p_struct->stpidxa),stpidxb(p_struct->stpidxb),inneriter(p_struct->inneriter),outeriter(p_struct->outeriter)
{
}

optguardnonc1test1report& optguardnonc1test1report::operator=(const optguardnonc1test1report &rhs)
{
    if( this==&rhs )
        return *this;
    _optguardnonc1test1report_owner::operator=(rhs);
    return *this;
}

optguardnonc1test1report::~optguardnonc1test1report()
{
}
#endif

#if defined(AE_COMPILE_OPTS) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
This function serializes data structure to string.

Important properties of s_out:
* it contains alphanumeric characters, dots, underscores, minus signs
* these symbols are grouped into words, which are separated by spaces
  and Windows-style (CR+LF) newlines
* although  serializer  uses  spaces and CR+LF as separators, you can 
  replace any separator character by arbitrary combination of spaces,
  tabs, Windows or Unix newlines. It allows flexible reformatting  of
  the  string in case you want to include it into a text or XML file. 
  But you should not insert separators into the middle of the "words"
  nor should you change the case of letters.
* s_out can be freely moved between 32-bit and 64-bit systems, little
  and big endian machines, and so on. You can serialize structure  on
  32-bit machine and unserialize it on 64-bit one (or vice versa), or
  serialize  it  on  SPARC  and  unserialize  on  x86.  You  can also 
  serialize it in C++ version of ALGLIB and unserialize it in C# one, 
  and vice versa.
*************************************************************************/
void lptestproblemserialize(const lptestproblem &obj, std::string &s_out)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state state;
    alglib_impl::ae_serializer serializer;
    alglib_impl::ae_int_t ssize;

    alglib_impl::ae_state_init(&state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&state, &_break_jump);
    alglib_impl::ae_serializer_init(&serializer);
    alglib_impl::ae_serializer_alloc_start(&serializer);
    alglib_impl::lptestproblemalloc(&serializer, obj.c_ptr(), &state);
    ssize = alglib_impl::ae_serializer_get_alloc_size(&serializer);
    s_out.clear();
    s_out.reserve((size_t)(ssize+1));
    alglib_impl::ae_serializer_sstart_str(&serializer, &s_out);
    alglib_impl::lptestproblemserialize(&serializer, obj.c_ptr(), &state);
    alglib_impl::ae_serializer_stop(&serializer, &state);
    alglib_impl::ae_assert( s_out.length()<=(size_t)ssize, "ALGLIB: serialization integrity error", &state);
    alglib_impl::ae_serializer_clear(&serializer);
    alglib_impl::ae_state_clear(&state);
}
/*************************************************************************
This function serializes data structure to C++ stream.

Data stream generated by this function is same as  string  representation
generated  by  string  version  of  serializer - alphanumeric characters,
dots, underscores, minus signs, which are grouped into words separated by
spaces and CR+LF.

We recommend you to read comments on string version of serializer to find
out more about serialization of AlGLIB objects.
*************************************************************************/
void lptestproblemserialize(const lptestproblem &obj, std::ostream &s_out)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state state;
    alglib_impl::ae_serializer serializer;

    alglib_impl::ae_state_init(&state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&state, &_break_jump);
    alglib_impl::ae_serializer_init(&serializer);
    alglib_impl::ae_serializer_alloc_start(&serializer);
    alglib_impl::lptestproblemalloc(&serializer, obj.c_ptr(), &state);
    alglib_impl::ae_serializer_get_alloc_size(&serializer); // not actually needed, but we have to ask
    alglib_impl::ae_serializer_sstart_stream(&serializer, &s_out);
    alglib_impl::lptestproblemserialize(&serializer, obj.c_ptr(), &state);
    alglib_impl::ae_serializer_stop(&serializer, &state);
    alglib_impl::ae_serializer_clear(&serializer);
    alglib_impl::ae_state_clear(&state);
}
/*************************************************************************
This function unserializes data structure from string.
*************************************************************************/
void lptestproblemunserialize(const std::string &s_in, lptestproblem &obj)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state state;
    alglib_impl::ae_serializer serializer;

    alglib_impl::ae_state_init(&state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&state, &_break_jump);
    alglib_impl::ae_serializer_init(&serializer);
    alglib_impl::ae_serializer_ustart_str(&serializer, &s_in);
    alglib_impl::lptestproblemunserialize(&serializer, obj.c_ptr(), &state);
    alglib_impl::ae_serializer_stop(&serializer, &state);
    alglib_impl::ae_serializer_clear(&serializer);
    alglib_impl::ae_state_clear(&state);
}
/*************************************************************************
This function unserializes data structure from stream.
*************************************************************************/
void lptestproblemunserialize(const std::istream &s_in, lptestproblem &obj)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state state;
    alglib_impl::ae_serializer serializer;

    alglib_impl::ae_state_init(&state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&state, &_break_jump);
    alglib_impl::ae_serializer_init(&serializer);
    alglib_impl::ae_serializer_ustart_stream(&serializer, &s_in);
    alglib_impl::lptestproblemunserialize(&serializer, obj.c_ptr(), &state);
    alglib_impl::ae_serializer_stop(&serializer, &state);
    alglib_impl::ae_serializer_clear(&serializer);
    alglib_impl::ae_state_clear(&state);
}

/*************************************************************************
Initialize test LP problem.

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemcreate(const ae_int_t n, const bool hasknowntarget, const double targetf, lptestproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::lptestproblemcreate(n, hasknowntarget, targetf, p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Query test problem info

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
bool lptestproblemhasknowntarget(lptestproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::lptestproblemhasknowntarget(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}

/*************************************************************************
Query test problem info

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
double lptestproblemgettargetf(lptestproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    double result = alglib_impl::lptestproblemgettargetf(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return double(result);
}

/*************************************************************************
Query test problem info

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
ae_int_t lptestproblemgetn(lptestproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::lptestproblemgetn(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
Query test problem info

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
ae_int_t lptestproblemgetm(lptestproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::lptestproblemgetm(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
Set scale for test LP problem

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemsetscale(lptestproblem &p, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::lptestproblemsetscale(p.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Set cost for test LP problem

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemsetcost(lptestproblem &p, const real_1d_array &c, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::lptestproblemsetcost(p.c_ptr(), c.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Set box constraints for test LP problem

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemsetbc(lptestproblem &p, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::lptestproblemsetbc(p.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Set box constraints for test LP problem

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemsetlc2(lptestproblem &p, const sparsematrix &a, const real_1d_array &al, const real_1d_array &au, const ae_int_t m, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::lptestproblemsetlc2(p.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), m, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}



/*************************************************************************
Initialize QPX problem.

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemcreate(const ae_int_t n, qpxproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemcreate(n, p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Returns objective type: True for zero/linear/constant.

Present version does not return False.

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
bool qpxproblemisquadraticobjective(qpxproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::qpxproblemisquadraticobjective(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}

/*************************************************************************
Get variables count

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t qpxproblemgetn(qpxproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::qpxproblemgetn(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
Get linear constraints count

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t qpxproblemgetmlc(qpxproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::qpxproblemgetmlc(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
Get quadratic constraints count

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t qpxproblemgetmqc(qpxproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::qpxproblemgetmqc(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
Get conic constraints count

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t qpxproblemgetmcc(qpxproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::qpxproblemgetmcc(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
Get total constraints count

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t qpxproblemgettotalconstraints(qpxproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::qpxproblemgettotalconstraints(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}



/*************************************************************************
Set initial point

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemsetinitialpoint(qpxproblem &p, const real_1d_array &x0, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemsetinitialpoint(p.c_ptr(), x0.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Get initial point

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetinitialpoint(qpxproblem &p, real_1d_array &x0, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemgetinitialpoint(p.c_ptr(), x0.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Get initial point presence

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
bool qpxproblemhasinitialpoint(qpxproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::qpxproblemhasinitialpoint(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}



/*************************************************************************
Set scale

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemsetscale(qpxproblem &p, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemsetscale(p.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Get scale

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetscale(qpxproblem &p, real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemgetscale(p.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Get scale presence

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
bool qpxproblemhasscale(qpxproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::qpxproblemhasscale(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}



/*************************************************************************
Set origin

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemsetorigin(qpxproblem &p, const real_1d_array &xorigin, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemsetorigin(p.c_ptr(), xorigin.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Get origin

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetorigin(qpxproblem &p, real_1d_array &xorigin, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemgetorigin(p.c_ptr(), xorigin.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Get origin presence

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
bool qpxproblemhasorigin(qpxproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::qpxproblemhasorigin(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}



/*************************************************************************
Set linear term

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemsetlinearterm(qpxproblem &p, const real_1d_array &c, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemsetlinearterm(p.c_ptr(), c.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Get linear term

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetlinearterm(qpxproblem &p, real_1d_array &c, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemgetlinearterm(p.c_ptr(), c.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}



/*************************************************************************
Set quadratic term; Q can be in any sparse matrix format.

Only one triangle (lower or upper) is referenced by this function.

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemsetquadraticterm(qpxproblem &p, const sparsematrix &q, const bool isupper, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemsetquadraticterm(p.c_ptr(), q.c_ptr(), isupper, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Get quadratic term, returns zero matrix if no quadratic term is present

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetquadraticterm(qpxproblem &p, sparsematrix &q, bool &isupper, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemgetquadraticterm(p.c_ptr(), q.c_ptr(), &isupper, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Returns False if no quadratic term was specified, or quadratic term is
numerically zero.

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
bool qpxproblemhasquadraticterm(qpxproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::qpxproblemhasquadraticterm(p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}



/*************************************************************************
Set box constraints

  -- ALGLIB --
     Copyright 20.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemsetbc(qpxproblem &p, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemsetbc(p.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Get box constraints

  -- ALGLIB --
     Copyright 20.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetbc(qpxproblem &p, real_1d_array &bndl, real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemgetbc(p.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}



/*************************************************************************
Set linear constraints

  -- ALGLIB --
     Copyright 20.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemsetlc2(qpxproblem &p, const sparsematrix &a, const real_1d_array &al, const real_1d_array &au, const ae_int_t m, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemsetlc2(p.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), m, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Get linear constraints

  -- ALGLIB --
     Copyright 20.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetlc2(qpxproblem &p, sparsematrix &a, real_1d_array &al, real_1d_array &au, ae_int_t &m, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemgetlc2(p.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), &m, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}



/*************************************************************************
Append two-sided quadratic constraint, same format as minqpaddqc2()

  -- ALGLIB --
     Copyright 19.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemaddqc2(qpxproblem &p, const sparsematrix &q, const bool isupper, const real_1d_array &b, const double cl, const double cu, const bool applyorigin, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemaddqc2(p.c_ptr(), q.c_ptr(), isupper, b.c_ptr(), cl, cu, applyorigin, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Get IDX-th two-sided quadratic constraint, same format as minqpaddqc2(),
except for the fact that it always returns isUpper=False, even if the
original matrix was an upper triangular one.

NOTE: this function is not optimized for big matrices. Whilst still having
      O(max(N,Nonzeros)) running time, it may  be  somewhat  slow  due  to
      dynamic structures being used internally.


  -- ALGLIB --
     Copyright 19.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetqc2i(qpxproblem &p, const ae_int_t idx, sparsematrix &q, bool &isupper, real_1d_array &b, double &cl, double &cu, bool &applyorigin, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::qpxproblemgetqc2i(p.c_ptr(), idx, q.c_ptr(), &isupper, b.c_ptr(), &cl, &cu, &applyorigin, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************
A general QP problem (a linear/quadratic target subject to a mix  of  box,
linear, quadratic and conic constraints).

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
_qpxproblem_owner::_qpxproblem_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_qpxproblem_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::qpxproblem*)alglib_impl::ae_malloc(sizeof(alglib_impl::qpxproblem), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::qpxproblem));
    alglib_impl::_qpxproblem_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_qpxproblem_owner::_qpxproblem_owner(alglib_impl::qpxproblem *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_qpxproblem_owner::_qpxproblem_owner(const _qpxproblem_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_qpxproblem_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: qpxproblem copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::qpxproblem*)alglib_impl::ae_malloc(sizeof(alglib_impl::qpxproblem), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::qpxproblem));
    alglib_impl::_qpxproblem_init_copy(p_struct, const_cast<alglib_impl::qpxproblem*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_qpxproblem_owner& _qpxproblem_owner::operator=(const _qpxproblem_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: qpxproblem assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: qpxproblem assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: qpxproblem assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_qpxproblem_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::qpxproblem));
    alglib_impl::_qpxproblem_init_copy(p_struct, const_cast<alglib_impl::qpxproblem*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_qpxproblem_owner::~_qpxproblem_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_qpxproblem_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::qpxproblem* _qpxproblem_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::qpxproblem* _qpxproblem_owner::c_ptr() const
{
    return p_struct;
}
qpxproblem::qpxproblem() : _qpxproblem_owner() 
{
}

qpxproblem::qpxproblem(alglib_impl::qpxproblem *attach_to):_qpxproblem_owner(attach_to) 
{
}

qpxproblem::qpxproblem(const qpxproblem &rhs):_qpxproblem_owner(rhs) 
{
}

qpxproblem& qpxproblem::operator=(const qpxproblem &rhs)
{
    if( this==&rhs )
        return *this;
    _qpxproblem_owner::operator=(rhs);
    return *this;
}

qpxproblem::~qpxproblem()
{
}




/*************************************************************************
This is a test problem class  intended  for  internal  performance  tests.
Never use it directly in your projects.
*************************************************************************/
_lptestproblem_owner::_lptestproblem_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_lptestproblem_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::lptestproblem*)alglib_impl::ae_malloc(sizeof(alglib_impl::lptestproblem), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::lptestproblem));
    alglib_impl::_lptestproblem_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_lptestproblem_owner::_lptestproblem_owner(alglib_impl::lptestproblem *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_lptestproblem_owner::_lptestproblem_owner(const _lptestproblem_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_lptestproblem_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: lptestproblem copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::lptestproblem*)alglib_impl::ae_malloc(sizeof(alglib_impl::lptestproblem), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::lptestproblem));
    alglib_impl::_lptestproblem_init_copy(p_struct, const_cast<alglib_impl::lptestproblem*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_lptestproblem_owner& _lptestproblem_owner::operator=(const _lptestproblem_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: lptestproblem assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: lptestproblem assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: lptestproblem assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_lptestproblem_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::lptestproblem));
    alglib_impl::_lptestproblem_init_copy(p_struct, const_cast<alglib_impl::lptestproblem*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_lptestproblem_owner::~_lptestproblem_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_lptestproblem_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::lptestproblem* _lptestproblem_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::lptestproblem* _lptestproblem_owner::c_ptr() const
{
    return p_struct;
}
lptestproblem::lptestproblem() : _lptestproblem_owner() 
{
}

lptestproblem::lptestproblem(alglib_impl::lptestproblem *attach_to):_lptestproblem_owner(attach_to) 
{
}

lptestproblem::lptestproblem(const lptestproblem &rhs):_lptestproblem_owner(rhs) 
{
}

lptestproblem& lptestproblem::operator=(const lptestproblem &rhs)
{
    if( this==&rhs )
        return *this;
    _lptestproblem_owner::operator=(rhs);
    return *this;
}

lptestproblem::~lptestproblem()
{
}
#endif

#if defined(AE_COMPILE_OPTSERV) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_MINLBFGS) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
        LIMITED MEMORY BFGS METHOD FOR LARGE SCALE OPTIMIZATION

DESCRIPTION:
The subroutine minimizes function F(x) of N arguments by  using  a  quasi-
Newton method (LBFGS scheme) which is optimized to use  a  minimum  amount
of memory.
The subroutine generates the approximation of an inverse Hessian matrix by
using information about the last M steps of the algorithm  (instead of N).
It lessens a required amount of memory from a value  of  order  N^2  to  a
value of order 2*N*M.


REQUIREMENTS:
Algorithm will request following information during its operation:
* function value F and its gradient G (simultaneously) at given point X


USAGE:
1. User initializes algorithm state with MinLBFGSCreate() call
2. User tunes solver parameters with MinLBFGSSetCond() MinLBFGSSetStpMax()
   and other functions
3. User calls MinLBFGSOptimize() function which takes algorithm  state and
   pointer (delegate, etc.) to callback function which calculates F/G.
4. User calls MinLBFGSResults() to get solution
5. Optionally user may call MinLBFGSRestartFrom() to solve another problem
   with same N/M but another starting point and/or another function.
   MinLBFGSRestartFrom() allows to reuse already initialized structure.

INPUT PARAMETERS:
    N       -   problem dimension. N>0
    M       -   number of corrections in the BFGS scheme of Hessian
                approximation update. Recommended value:  3<=M<=7. The smaller
                value causes worse convergence, the bigger will  not  cause  a
                considerably better convergence, but will cause a fall in  the
                performance. M<=N.
    X       -   initial solution approximation, array[0..N-1].


OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

IMPORTANT: the   LBFGS  optimizer  supports  parallel  parallel  numerical
           differentiation  ('callback    parallelism').   This   feature,
           which  is  present  in  commercial   ALGLIB   editions  greatly
           accelerates  optimization  with  numerical  differentiation  of
           an expensive target functions.

           Callback parallelism is usually  beneficial  when  computing  a
           numerical gradient requires  more  than  several  milliseconds.

           See ALGLIB Reference Manual, 'Working with commercial version'
           section,  and  comments  on  minlbfgsoptimize() function for
           more information.

NOTES:
1. you may tune stopping conditions with MinLBFGSSetCond() function
2. if target function contains exp() or other fast growing functions,  and
   optimization algorithm makes too large steps which leads  to  overflow,
   use MinLBFGSSetStpMax() function to bound algorithm's  steps.  However,
   L-BFGS rarely needs such a tuning.


  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgscreate(const ae_int_t n, const ae_int_t m, const real_1d_array &x, minlbfgsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgscreate(n, m, x.c_ptr(), state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
        LIMITED MEMORY BFGS METHOD FOR LARGE SCALE OPTIMIZATION

DESCRIPTION:
The subroutine minimizes function F(x) of N arguments by  using  a  quasi-
Newton method (LBFGS scheme) which is optimized to use  a  minimum  amount
of memory.
The subroutine generates the approximation of an inverse Hessian matrix by
using information about the last M steps of the algorithm  (instead of N).
It lessens a required amount of memory from a value  of  order  N^2  to  a
value of order 2*N*M.


REQUIREMENTS:
Algorithm will request following information during its operation:
* function value F and its gradient G (simultaneously) at given point X


USAGE:
1. User initializes algorithm state with MinLBFGSCreate() call
2. User tunes solver parameters with MinLBFGSSetCond() MinLBFGSSetStpMax()
   and other functions
3. User calls MinLBFGSOptimize() function which takes algorithm  state and
   pointer (delegate, etc.) to callback function which calculates F/G.
4. User calls MinLBFGSResults() to get solution
5. Optionally user may call MinLBFGSRestartFrom() to solve another problem
   with same N/M but another starting point and/or another function.
   MinLBFGSRestartFrom() allows to reuse already initialized structure.

INPUT PARAMETERS:
    N       -   problem dimension. N>0
    M       -   number of corrections in the BFGS scheme of Hessian
                approximation update. Recommended value:  3<=M<=7. The smaller
                value causes worse convergence, the bigger will  not  cause  a
                considerably better convergence, but will cause a fall in  the
                performance. M<=N.
    X       -   initial solution approximation, array[0..N-1].


OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

IMPORTANT: the   LBFGS  optimizer  supports  parallel  parallel  numerical
           differentiation  ('callback    parallelism').   This   feature,
           which  is  present  in  commercial   ALGLIB   editions  greatly
           accelerates  optimization  with  numerical  differentiation  of
           an expensive target functions.

           Callback parallelism is usually  beneficial  when  computing  a
           numerical gradient requires  more  than  several  milliseconds.

           See ALGLIB Reference Manual, 'Working with commercial version'
           section,  and  comments  on  minlbfgsoptimize() function for
           more information.

NOTES:
1. you may tune stopping conditions with MinLBFGSSetCond() function
2. if target function contains exp() or other fast growing functions,  and
   optimization algorithm makes too large steps which leads  to  overflow,
   use MinLBFGSSetStpMax() function to bound algorithm's  steps.  However,
   L-BFGS rarely needs such a tuning.


  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minlbfgscreate(const ae_int_t m, const real_1d_array &x, minlbfgsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgscreate(n, m, x.c_ptr(), state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
The subroutine is finite difference variant of MinLBFGSCreate().  It  uses
finite differences in order to differentiate target function.

Description below contains information which is specific to  this function
only. We recommend to read comments on MinLBFGSCreate() in  order  to  get
more information about creation of LBFGS optimizer.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    M       -   number of corrections in the BFGS scheme of Hessian
                approximation update. Recommended value:  3<=M<=7. The smaller
                value causes worse convergence, the bigger will  not  cause  a
                considerably better convergence, but will cause a fall in  the
                performance. M<=N.
    X       -   starting point, array[0..N-1].
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state


IMPORTANT: the   LBFGS  optimizer  supports  parallel  parallel  numerical
           differentiation  ('callback    parallelism').   This   feature,
           which  is  present  in  commercial   ALGLIB   editions  greatly
           accelerates  optimization  with  numerical  differentiation  of
           an expensive target functions.

           Callback parallelism is usually  beneficial  when  computing  a
           numerical gradient requires  more  than  several  milliseconds.

           See ALGLIB Reference Manual, 'Working with commercial version'
           section,  and  comments  on  minlbfgsoptimize() function for
           more information.

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinLBFGSSetScale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large truncation  errors, while too small
   step will result in too large numerical  errors.  1.0E-6  can  be  good
   value to start with.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is   less  robust  and  precise.  LBFGS  needs  exact  gradient values.
   Imprecise gradient may slow  down  convergence,  especially  on  highly
   nonlinear problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
void minlbfgscreatef(const ae_int_t n, const ae_int_t m, const real_1d_array &x, const double diffstep, minlbfgsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgscreatef(n, m, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
The subroutine is finite difference variant of MinLBFGSCreate().  It  uses
finite differences in order to differentiate target function.

Description below contains information which is specific to  this function
only. We recommend to read comments on MinLBFGSCreate() in  order  to  get
more information about creation of LBFGS optimizer.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    M       -   number of corrections in the BFGS scheme of Hessian
                approximation update. Recommended value:  3<=M<=7. The smaller
                value causes worse convergence, the bigger will  not  cause  a
                considerably better convergence, but will cause a fall in  the
                performance. M<=N.
    X       -   starting point, array[0..N-1].
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state


IMPORTANT: the   LBFGS  optimizer  supports  parallel  parallel  numerical
           differentiation  ('callback    parallelism').   This   feature,
           which  is  present  in  commercial   ALGLIB   editions  greatly
           accelerates  optimization  with  numerical  differentiation  of
           an expensive target functions.

           Callback parallelism is usually  beneficial  when  computing  a
           numerical gradient requires  more  than  several  milliseconds.

           See ALGLIB Reference Manual, 'Working with commercial version'
           section,  and  comments  on  minlbfgsoptimize() function for
           more information.

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinLBFGSSetScale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large truncation  errors, while too small
   step will result in too large numerical  errors.  1.0E-6  can  be  good
   value to start with.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is   less  robust  and  precise.  LBFGS  needs  exact  gradient values.
   Imprecise gradient may slow  down  convergence,  especially  on  highly
   nonlinear problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minlbfgscreatef(const ae_int_t m, const real_1d_array &x, const double diffstep, minlbfgsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgscreatef(n, m, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets stopping conditions for L-BFGS optimization algorithm.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsG    -   >=0
                The  subroutine  finishes  its  work   if   the  condition
                |v|<EpsG is satisfied, where:
                * |.| means Euclidian norm
                * v - scaled gradient vector, v[i]=g[i]*s[i]
                * g - gradient
                * s - scaling coefficients set by MinLBFGSSetScale()
    EpsF    -   >=0
                The  subroutine  finishes  its work if on k+1-th iteration
                the  condition  |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1}
                is satisfied.
    EpsX    -   >=0
                The subroutine finishes its work if  on  k+1-th  iteration
                the condition |v|<=EpsX is fulfilled, where:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - ste pvector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinLBFGSSetScale()
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsG=0, EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to
automatic stopping criterion selection (small EpsX).

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetcond(minlbfgsstate &state, const double epsg, const double epsf, const double epsx, const ae_int_t maxits, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgssetcond(state.c_ptr(), epsg, epsf, epsx, maxits, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinLBFGSOptimize().


  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetxrep(minlbfgsstate &state, const bool needxrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgssetxrep(state.c_ptr(), needxrep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets maximum step length

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    StpMax  -   maximum step length, >=0. Set StpMax to 0.0 (default),  if
                you don't want to limit step length.

Use this subroutine when you optimize target function which contains exp()
or  other  fast  growing  functions,  and optimization algorithm makes too
large  steps  which  leads  to overflow. This function allows us to reject
steps  that  are  too  large  (and  therefore  expose  us  to the possible
overflow) without actually calculating function value at the x+stp*d.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetstpmax(minlbfgsstate &state, const double stpmax, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgssetstpmax(state.c_ptr(), stpmax, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets scaling coefficients for LBFGS optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Scaling is also used by finite difference variant of the optimizer  - step
along I-th axis is equal to DiffStep*S[I].

In  most  optimizers  (and  in  the  LBFGS  too)  scaling is NOT a form of
preconditioning. It just  affects  stopping  conditions.  You  should  set
preconditioner  by  separate  call  to  one  of  the  MinLBFGSSetPrec...()
functions.

There  is  special  preconditioning  mode, however,  which  uses   scaling
coefficients to form diagonal preconditioning matrix. You  can  turn  this
mode on, if you want.   But  you should understand that scaling is not the
same thing as preconditioning - these are two different, although  related
forms of tuning solver.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetscale(minlbfgsstate &state, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgssetscale(state.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification  of  the  preconditioner:  default  preconditioner    (simple
scaling, same for all elements of X) is used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTE:  you  can  change  preconditioner  "on  the  fly",  during algorithm
iterations.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetprecdefault(minlbfgsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgssetprecdefault(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification of the preconditioner: Cholesky factorization of  approximate
Hessian is used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    P       -   triangular preconditioner, Cholesky factorization of
                the approximate Hessian. array[0..N-1,0..N-1],
                (if larger, only leading N elements are used).
    IsUpper -   whether upper or lower triangle of P is given
                (other triangle is not referenced)

After call to this function preconditioner is changed to P  (P  is  copied
into the internal buffer).

NOTE:  you  can  change  preconditioner  "on  the  fly",  during algorithm
iterations.

NOTE 2:  P  should  be nonsingular. Exception will be thrown otherwise.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetpreccholesky(minlbfgsstate &state, const real_2d_array &p, const bool isupper, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgssetpreccholesky(state.c_ptr(), p.c_ptr(), isupper, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification  of  the  preconditioner:  diagonal of approximate Hessian is
used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    D       -   diagonal of the approximate Hessian, array[0..N-1],
                (if larger, only leading N elements are used).

NOTE:  you  can  change  preconditioner  "on  the  fly",  during algorithm
iterations.

NOTE 2: D[i] should be positive. Exception will be thrown otherwise.

NOTE 3: you should pass diagonal of approximate Hessian - NOT ITS INVERSE.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetprecdiag(minlbfgsstate &state, const real_1d_array &d, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgssetprecdiag(state.c_ptr(), d.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification of the preconditioner: scale-based diagonal preconditioning.

This preconditioning mode can be useful when you  don't  have  approximate
diagonal of Hessian, but you know that your  variables  are  badly  scaled
(for  example,  one  variable is in [1,10], and another in [1000,100000]),
and most part of the ill-conditioning comes from different scales of vars.

In this case simple  scale-based  preconditioner,  with H[i] = 1/(s[i]^2),
can greatly improve convergence.

IMPRTANT: you should set scale of your variables  with  MinLBFGSSetScale()
call  (before  or after MinLBFGSSetPrecScale() call). Without knowledge of
the scale of your variables scale-based preconditioner will be  just  unit
matrix.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetprecscale(minlbfgsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgssetprecscale(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function provides reverse communication interface
Reverse communication interface is not documented or recommended to use.
See below for functions which provide better documented API
*************************************************************************/
bool minlbfgsiteration(minlbfgsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::minlbfgsiteration(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}


void minlbfgsoptimize(minlbfgsstate &state,
    void (*func)(const real_1d_array &x, double &func, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    alglib_impl::minlbfgsstate &optimizer = *(state.c_ptr());
    alglib_impl::rcommv2_request request(
        optimizer.requesttype,
        optimizer.querysize, optimizer.queryfuncs, optimizer.queryvars, optimizer.querydim, optimizer.queryformulasize,
        optimizer.querydata.ptr.p_double, optimizer.replyfi.ptr.p_double, optimizer.replydj.ptr.p_double, optimizer.replysj, ptr,
        "minlbfgs");
    alglib_impl::rcommv2_callbacks callbacks;
    alglib_impl::rcommv2_buffers buffers(&state.c_ptr()->tmpx1, &state.c_ptr()->tmpc1, &state.c_ptr()->tmpf1, &state.c_ptr()->tmpg1, &state.c_ptr()->tmpj1, &state.c_ptr()->tmps1);
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(func!=NULL, "ALGLIB: error in 'minlbfgsoptimize()' (func is NULL)", &_alglib_env_state);
callbacks.func = func;

    alglib_impl::minlbfgssetprotocolv2(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minlbfgsiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
        if( optimizer.requesttype==3 )
        {
            const ae_int_t njobs = request.size*request.vars+request.size;
            for(alglib_impl::ae_int_t job_idx=0; job_idx<njobs; job_idx++)
                alglib_impl::process_v2request_3phase0(request, job_idx, callbacks, buffers);
            alglib_impl::process_v2request_3phase1(request);
            request.request = 0;
            continue;
        }
        if( optimizer.requesttype==5 )
        {
            const ae_int_t njobs = request.size*request.vars+request.size;
            for(alglib_impl::ae_int_t job_idx=0; job_idx<njobs; job_idx++)
                alglib_impl::process_v2request_5phase0(request, job_idx, callbacks, buffers);
            alglib_impl::process_v2request_5phase1(request);
            request.request = 0;
            continue;
        }
        if( optimizer.requesttype==-1 )
        {
            memmove(&buffers.tmpX[0], optimizer.reportx.ptr.p_double, optimizer.queryvars*sizeof(double));
            if( rep!=NULL )
                rep(buffers.tmpX, optimizer.reportf, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
lbl_no_callback:
    alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minlbfgsoptimize' (some derivatives were not provided?)", &_alglib_env_state);
}


void minlbfgsoptimize(minlbfgsstate &state,
    void (*grad)(const real_1d_array &x, double &func, real_1d_array &grad, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    alglib_impl::minlbfgsstate &optimizer = *(state.c_ptr());
    alglib_impl::rcommv2_request request(
        optimizer.requesttype,
        optimizer.querysize, optimizer.queryfuncs, optimizer.queryvars, optimizer.querydim, optimizer.queryformulasize,
        optimizer.querydata.ptr.p_double, optimizer.replyfi.ptr.p_double, optimizer.replydj.ptr.p_double, optimizer.replysj, ptr,
        "minlbfgs");
    alglib_impl::rcommv2_callbacks callbacks;
    alglib_impl::rcommv2_buffers buffers(&state.c_ptr()->tmpx1, &state.c_ptr()->tmpc1, &state.c_ptr()->tmpf1, &state.c_ptr()->tmpg1, &state.c_ptr()->tmpj1, &state.c_ptr()->tmps1);
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(grad!=NULL, "ALGLIB: error in 'minlbfgsoptimize()' (grad is NULL)", &_alglib_env_state);
callbacks.grad = grad;

    alglib_impl::minlbfgssetprotocolv2(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minlbfgsiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
        if( optimizer.requesttype==2 )
        { 
            for(alglib_impl::ae_int_t qidx=0; qidx<optimizer.querysize; qidx++)
                alglib_impl::process_v2request_2(request, qidx, callbacks, buffers);
            optimizer.requesttype = 0;
            continue;
        }
        if( optimizer.requesttype==-1 )
        {
            memmove(&buffers.tmpX[0], optimizer.reportx.ptr.p_double, optimizer.queryvars*sizeof(double));
            if( rep!=NULL )
                rep(buffers.tmpX, optimizer.reportf, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
lbl_no_callback:
    alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minlbfgsoptimize' (some derivatives were not provided?)", &_alglib_env_state);
}



/*************************************************************************
This  function  activates/deactivates verification  of  the  user-supplied
analytic gradient.

Upon  activation  of  this  option  OptGuard  integrity  checker  performs
numerical differentiation of your target function  at  the  initial  point
(note: future versions may also perform check  at  the  final  point)  and
compares numerical gradient with analytic one provided by you.

If difference is too large, an error flag is set and optimization  session
continues. After optimization session is over, you can retrieve the report
which  stores  both  gradients  and  specific  components  highlighted  as
suspicious by the OptGuard.

The primary OptGuard report can be retrieved with minlbfgsoptguardresults().

IMPORTANT: gradient check is a high-overhead option which  will  cost  you
           about 3*N additional function evaluations. In many cases it may
           cost as much as the rest of the optimization session.

           YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO
           CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY.

NOTE: unlike previous incarnation of the gradient checking code,  OptGuard
      does NOT interrupt optimization even if it discovers bad gradient.

INPUT PARAMETERS:
    State       -   structure used to store algorithm state
    TestStep    -   verification step used for numerical differentiation:
                    * TestStep=0 turns verification off
                    * TestStep>0 activates verification
                    You should carefully choose TestStep. Value  which  is
                    too large (so large that  function  behavior  is  non-
                    cubic at this scale) will lead  to  false  alarms. Too
                    short step will result in rounding  errors  dominating
                    numerical derivative.

                    You may use different step for different parameters by
                    means of setting scale with minlbfgssetscale().

=== EXPLANATION ==========================================================

In order to verify gradient algorithm performs following steps:
  * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i],
    where X[i] is i-th component of the initial point and S[i] is a  scale
    of i-th parameter
  * F(X) is evaluated at these trial points
  * we perform one more evaluation in the middle point of the interval
  * we  build  cubic  model using function values and derivatives at trial
    points and we compare its prediction with actual value in  the  middle
    point

  -- ALGLIB --
     Copyright 15.06.2014 by Bochkanov Sergey
*************************************************************************/
void minlbfgsoptguardgradient(minlbfgsstate &state, const double teststep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgsoptguardgradient(state.c_ptr(), teststep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0)
b) nonsmooth     target function (non-C1)

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.

      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.

      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.

      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.

=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minlbfgsoptguardsmoothness(minlbfgsstate &state, const ae_int_t level, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgsoptguardsmoothness(state.c_ptr(), level, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0)
b) nonsmooth     target function (non-C1)

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.

      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.

      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.

      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.

=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minlbfgsoptguardsmoothness(minlbfgsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t level;

    level = 1;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgsoptguardsmoothness(state.c_ptr(), level, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
Results of OptGuard integrity check, should be called  after  optimization
session is over.

=== PRIMARY REPORT =======================================================

OptGuard performs several checks which are intended to catch common errors
in the implementation of nonlinear function/gradient:
* incorrect analytic gradient
* discontinuous (non-C0) target functions (constraints)
* nonsmooth     (non-C1) target functions (constraints)

Each of these checks is activated with appropriate function:
* minlbfgsoptguardgradient() for gradient verification
* minlbfgsoptguardsmoothness() for C0/C1 checks

Following flags are set when these errors are suspected:
* rep.badgradsuspected, and additionally:
  * rep.badgradvidx for specific variable (gradient element) suspected
  * rep.badgradxbase, a point where gradient is tested
  * rep.badgraduser, user-provided gradient  (stored  as  2D  matrix  with
    single row in order to make  report  structure  compatible  with  more
    complex optimizers like MinNLC or MinLM)
  * rep.badgradnum,   reference    gradient    obtained    via   numerical
    differentiation (stored as  2D matrix with single row in order to make
    report structure compatible with more complex optimizers  like  MinNLC
    or MinLM)
* rep.nonc0suspected
* rep.nonc1suspected

=== ADDITIONAL REPORTS/LOGS ==============================================

Several different tests are performed to catch C0/C1 errors, you can  find
out specific test signaled error by looking to:
* rep.nonc0test0positive, for non-C0 test #0
* rep.nonc1test0positive, for non-C1 test #0
* rep.nonc1test1positive, for non-C1 test #1

Additional information (including line search logs)  can  be  obtained  by
means of:
* minlbfgsoptguardnonc1test0results()
* minlbfgsoptguardnonc1test1results()
which return detailed error reports, specific points where discontinuities
were found, and so on.

==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    rep     -   generic OptGuard report;  more  detailed  reports  can  be
                retrieved with other functions.

NOTE: false negatives (nonsmooth problems are not identified as  nonsmooth
      ones) are possible although unlikely.

      The reason  is  that  you  need  to  make several evaluations around
      nonsmoothness  in  order  to  accumulate  enough  information  about
      function curvature. Say, if you start right from the nonsmooth point,
      optimizer simply won't get enough data to understand what  is  going
      wrong before it terminates due to abrupt changes in the  derivative.
      It is also  possible  that  "unlucky"  step  will  move  us  to  the
      termination too quickly.

      Our current approach is to have less than 0.1%  false  negatives  in
      our test examples  (measured  with  multiple  restarts  from  random
      points), and to have exactly 0% false positives.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minlbfgsoptguardresults(minlbfgsstate &state, optguardreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgsoptguardresults(state.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #0

Nonsmoothness (non-C1) test #0 studies  function  values  (not  gradient!)
obtained during line searches and monitors  behavior  of  the  directional
derivative estimate.

This test is less powerful than test #1, but it does  not  depend  on  the
gradient values and thus it is more robust against artifacts introduced by
numerical differentiation.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], f[] - arrays of length CNT which store step lengths and  function
  values at these points; f[i] is evaluated in x0+stp[i]*d.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.

==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #0 "strong" report
    lngrep  -   C1 test #0 "long" report

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minlbfgsoptguardnonc1test0results(const minlbfgsstate &state, optguardnonc1test0report &strrep, optguardnonc1test0report &lngrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgsoptguardnonc1test0results(state.c_ptr(), strrep.c_ptr(), lngrep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #1

Nonsmoothness (non-C1)  test  #1  studies  individual  components  of  the
gradient computed during line search.

When precise analytic gradient is provided this test is more powerful than
test #0  which  works  with  function  values  and  ignores  user-provided
gradient.  However,  test  #0  becomes  more   powerful   when   numerical
differentiation is employed (in such cases test #1 detects  higher  levels
of numerical noise and becomes too conservative).

This test also tells specific components of the gradient which violate  C1
continuity, which makes it more informative than #0, which just tells that
continuity is violated.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* vidx - is an index of the variable in [0,N) with nonsmooth derivative
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], g[] - arrays of length CNT which store step lengths and  gradient
  values at these points; g[i] is evaluated in  x0+stp[i]*d  and  contains
  vidx-th component of the gradient.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.

==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #1 "strong" report
    lngrep  -   C1 test #1 "long" report

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minlbfgsoptguardnonc1test1results(minlbfgsstate &state, optguardnonc1test1report &strrep, optguardnonc1test1report &lngrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgsoptguardnonc1test1results(state.c_ptr(), strrep.c_ptr(), lngrep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
L-BFGS algorithm results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report:
                * Rep.TerminationType completetion code:
                    * -8    internal integrity control  detected  infinite
                            or NAN values in  function/gradient.  Abnormal
                            termination signalled.
                    * -2    rounding errors prevent further improvement.
                            X contains best point found.
                    * -1    incorrect parameters were specified
                    *  1    relative function improvement is no more than
                            EpsF.
                    *  2    relative step is no more than EpsX.
                    *  4    gradient norm is no more than EpsG
                    *  5    MaxIts steps was taken
                    *  7    stopping conditions are too stringent,
                            further improvement is impossible
                    *  8    terminated by user who called minlbfgsrequesttermination().
                            X contains point which was "current accepted" when
                            termination request was submitted.
                * Rep.IterationsCount contains iterations count
                * NFEV countains number of function calculations

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgsresults(const minlbfgsstate &state, real_1d_array &x, minlbfgsreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgsresults(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
L-BFGS algorithm results

Buffered implementation of MinLBFGSResults which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 20.08.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgsresultsbuf(const minlbfgsstate &state, real_1d_array &x, minlbfgsreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgsresultsbuf(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  subroutine restarts LBFGS algorithm from new point. All optimization
parameters are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure used to store algorithm state
    X       -   new starting point.

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgsrestartfrom(minlbfgsstate &state, const real_1d_array &x, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgsrestartfrom(state.c_ptr(), x.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.

NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void minlbfgsrequesttermination(minlbfgsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgsrequesttermination(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************

*************************************************************************/
_minlbfgsstate_owner::_minlbfgsstate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minlbfgsstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minlbfgsstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minlbfgsstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minlbfgsstate));
    alglib_impl::_minlbfgsstate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minlbfgsstate_owner::_minlbfgsstate_owner(alglib_impl::minlbfgsstate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minlbfgsstate_owner::_minlbfgsstate_owner(const _minlbfgsstate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minlbfgsstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minlbfgsstate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minlbfgsstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minlbfgsstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minlbfgsstate));
    alglib_impl::_minlbfgsstate_init_copy(p_struct, const_cast<alglib_impl::minlbfgsstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minlbfgsstate_owner& _minlbfgsstate_owner::operator=(const _minlbfgsstate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minlbfgsstate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minlbfgsstate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minlbfgsstate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minlbfgsstate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minlbfgsstate));
    alglib_impl::_minlbfgsstate_init_copy(p_struct, const_cast<alglib_impl::minlbfgsstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minlbfgsstate_owner::~_minlbfgsstate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minlbfgsstate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minlbfgsstate* _minlbfgsstate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minlbfgsstate* _minlbfgsstate_owner::c_ptr() const
{
    return p_struct;
}
minlbfgsstate::minlbfgsstate() : _minlbfgsstate_owner() 
{
}

minlbfgsstate::minlbfgsstate(alglib_impl::minlbfgsstate *attach_to):_minlbfgsstate_owner(attach_to) 
{
}

minlbfgsstate::minlbfgsstate(const minlbfgsstate &rhs):_minlbfgsstate_owner(rhs) 
{
}

minlbfgsstate& minlbfgsstate::operator=(const minlbfgsstate &rhs)
{
    if( this==&rhs )
        return *this;
    _minlbfgsstate_owner::operator=(rhs);
    return *this;
}

minlbfgsstate::~minlbfgsstate()
{
}




/*************************************************************************
This structure stores optimization report:
* IterationsCount           total number of inner iterations
* NFEV                      number of gradient evaluations
* TerminationType           termination type (see below)

TERMINATION CODES

TerminationType field contains completion code, which can be:
  -8    internal integrity control detected  infinite  or  NAN  values  in
        function/gradient. Abnormal termination signalled.
   1    relative function improvement is no more than EpsF.
   2    relative step is no more than EpsX.
   4    gradient norm is no more than EpsG
   5    MaxIts steps was taken
   7    stopping conditions are too stringent,
        further improvement is impossible,
        X contains best point found so far.
   8    terminated    by  user  who  called  minlbfgsrequesttermination().
        X contains point which was   "current accepted"  when  termination
        request was submitted.

Other fields of this structure are not documented and should not be used!
*************************************************************************/
_minlbfgsreport_owner::_minlbfgsreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minlbfgsreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minlbfgsreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minlbfgsreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minlbfgsreport));
    alglib_impl::_minlbfgsreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minlbfgsreport_owner::_minlbfgsreport_owner(alglib_impl::minlbfgsreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minlbfgsreport_owner::_minlbfgsreport_owner(const _minlbfgsreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minlbfgsreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minlbfgsreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minlbfgsreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minlbfgsreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minlbfgsreport));
    alglib_impl::_minlbfgsreport_init_copy(p_struct, const_cast<alglib_impl::minlbfgsreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minlbfgsreport_owner& _minlbfgsreport_owner::operator=(const _minlbfgsreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minlbfgsreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minlbfgsreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minlbfgsreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minlbfgsreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minlbfgsreport));
    alglib_impl::_minlbfgsreport_init_copy(p_struct, const_cast<alglib_impl::minlbfgsreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minlbfgsreport_owner::~_minlbfgsreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minlbfgsreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minlbfgsreport* _minlbfgsreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minlbfgsreport* _minlbfgsreport_owner::c_ptr() const
{
    return p_struct;
}
minlbfgsreport::minlbfgsreport() : _minlbfgsreport_owner() ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype)
{
}

minlbfgsreport::minlbfgsreport(alglib_impl::minlbfgsreport *attach_to):_minlbfgsreport_owner(attach_to) ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype)
{
}

minlbfgsreport::minlbfgsreport(const minlbfgsreport &rhs):_minlbfgsreport_owner(rhs) ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype)
{
}

minlbfgsreport& minlbfgsreport::operator=(const minlbfgsreport &rhs)
{
    if( this==&rhs )
        return *this;
    _minlbfgsreport_owner::operator=(rhs);
    return *this;
}

minlbfgsreport::~minlbfgsreport()
{
}
#endif

#if defined(AE_COMPILE_CQMODELS) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_LPQPSERV) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_SNNLS) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_SACTIVESETS) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_QQPSOLVER) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_QPDENSEAULSOLVER) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_VIPMSOLVER) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_IPM2SOLVER) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_ECQPSOLVER) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_GIPM) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_GQPIPM) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_LPQPPRESOLVE) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_MINQP) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
                    CONSTRAINED QUADRATIC PROGRAMMING

The subroutine creates QP optimizer. After initial creation,  it  contains
default optimization problem with zero quadratic and linear terms  and  no
constraints.

In order to actually solve something you should:

specify objective:
* set linear term with minqpsetlinearterm()
* set quadratic term with minqpsetquadraticterm() or
  minqpsetquadratictermsparse()

specify constraints:
* set variable bounds with minqpsetbc() or minqpsetbcall()
* specify linear constraint matrix with one of the following functions:
  * modern API:
    * minqpsetlc2()       for sparse two-sided constraints AL <= A*x <= AU
    * minqpsetlc2dense()  for dense  two-sided constraints AL <= A*x <= AU
    * minqpsetlc2mixed()  for mixed  two-sided constraints AL <= A*x <= AU
    * minqpaddlc2dense()  to add one dense row to the dense constraint submatrix
    * minqpaddlc2()       to add one sparse row to the sparse constraint submatrix
    * minqpaddlc2sparsefromdense() to add one sparse row (passed as a dense array) to the sparse constraint submatrix
  * legacy API:
    * minqpsetlc()        for dense one-sided equality/inequality constraints
    * minqpsetlcsparse()  for sparse one-sided equality/inequality constraints
    * minqpsetlcmixed()   for mixed dense/sparse one-sided equality/inequality constraints
* add two-sided quadratic constraint(s) of the form CL <= b'x+0.5*x'Qx <= CU
  with one of the following functions:
  * minqpaddqc2()         for a quadratic constraint given by a sparse
                          matrix structure; has O(max(N,NNZ)) memory and
                          running time requirements.
  * minqpaddqc2dense()    for a quadratic constraint given by a dense
                          matrix; has O(N^2) memory and running time requirements.
  * minqpaddqc2list()     for a sparse quadratic constraint given by
                          a list of non-zero  entries;  has  O(NNZ) memory
                          and O(NNZ*logNNZ)  running   time  requirements,
                          ideal for constraints  with  much  less  than  N
                          nonzero elements.
* add second order cone constraints with:
  * minqpaddsoccprimitive()     for a primitive second order cone constraint
  * minqpaddsoccorthogonal()    for an axis-orthogonal second order cone constraint
* add power cone constraints with:
  * minqpaddpowccprimitive()    for a primitive power cone constraint
  * minqpaddpowccorthogonal()   for an axis-orthogonal power cone constraint

configure and run QP solver:
* choose appropriate QP solver and set it  and  its stopping  criteria  by
  means of minqpsetalgo??????() function
* call minqpoptimize() to run the solver and  minqpresults()  to  get  the
  solution vector and additional information.

Following solvers are recommended for  convex  and  semidefinite  problems
with box and linear constraints:
* QuickQP for small  dense  problems  with  box-only  constraints  (or  no
  constraints at all)
* DENSE-IPM-QP for  convex  or  semidefinite  problems  with   medium  (up
  to several thousands) variable count, dense/sparse  quadratic  term  and
  any number  (up  to  many  thousands)  of  dense/sparse  general  linear
  constraints
* SPARSE-IPM-QP for convex  or  semidefinite  problems  with   large (many
  thousands) variable count, sparse quadratic term AND linear constraints.
* SPARSE-ECQP for convex having only  linear  equality  constraints.  This
  specialized solver can be orders of magnitude faster than IPM.

If your problem happens to be nonconvex or has nonlinear constraints, then
you can use:
* DENSE-GENIPM or SPARSE-GENIPM  solver  which  supports  convex/nonconvex
  QP   problems  with  box,  linear,  quadratic  equality/inequality   and
  conic constraints.
* HYBRID-GENIPM solver which is optimized  for  well-conditioned  problems
  with large dense quadratic term/constraints or for problems  with  large
  sparse quadratic terms having prohibitively dense Cholesky factors.  The
  solver uses limited-memory SR1 quadratic model to accelerate Newton step.
* QuickQP for small dense nonconvex problems with box-only constraints

INPUT PARAMETERS:
    N       -   problem size

OUTPUT PARAMETERS:
    State   -   optimizer with zero quadratic/linear terms
                and no constraints

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpcreate(const ae_int_t n, minqpstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpcreate(n, state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets linear term for QP solver.

By default, linear term is zero.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    B       -   linear term, array[N].

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetlinearterm(minqpstate &state, const real_1d_array &b, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetlinearterm(state.c_ptr(), b.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  sets  dense  quadratic  term  for  QP solver. By  default,
quadratic term is zero.

IMPORTANT:

This solver minimizes following  function:
    f(x) = 0.5*x'*A*x + b'*x.
Note that quadratic term has 0.5 before it. So if  you  want  to  minimize
    f(x) = x^2 + x
you should rewrite your problem as follows:
    f(x) = 0.5*(2*x^2) + x
and your matrix A will be equal to [[2.0]], not to [[1.0]]

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    A       -   matrix, array[N,N]
    IsUpper -   storage type:
                * if True, symmetric matrix  A  is  given  by  its  upper
                  triangle, and the lower triangle isn't used
                * if False, symmetric matrix  A  is  given  by  its lower
                  triangle, and the upper triangle isn't used

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetquadraticterm(minqpstate &state, const real_2d_array &a, const bool isupper, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetquadraticterm(state.c_ptr(), a.c_ptr(), isupper, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  sets  sparse  quadratic  term  for  QP solver. By default,
quadratic  term  is  zero.  This  function  overrides  previous  calls  to
minqpsetquadraticterm() or minqpsetquadratictermsparse().

NOTE: dense solvers like DENSE-AUL-QP or DENSE-IPM-QP  will  convert  this
      matrix to dense storage anyway.

IMPORTANT:

This solver minimizes following  function:
    f(x) = 0.5*x'*A*x + b'*x.
Note that quadratic term has 0.5 before it. So if  you  want  to  minimize
    f(x) = x^2 + x
you should rewrite your problem as follows:
    f(x) = 0.5*(2*x^2) + x
and your matrix A will be equal to [[2.0]], not to [[1.0]]

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    A       -   matrix, array[N,N]
    IsUpper -   (optional) storage type:
                * if True, symmetric matrix  A  is  given  by  its  upper
                  triangle, and the lower triangle isn't used
                * if False, symmetric matrix  A  is  given  by  its lower
                  triangle, and the upper triangle isn't used
                * if not given, both lower and upper  triangles  must  be
                  filled.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetquadratictermsparse(minqpstate &state, const sparsematrix &a, const bool isupper, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetquadratictermsparse(state.c_ptr(), a.c_ptr(), isupper, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets starting point for QP solver. It is useful to have good
initial approximation to the solution, because it will increase  speed  of
convergence and identification of active constraints.

NOTE: interior point solvers ignore initial point provided by user.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    X       -   starting point, array[N].

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetstartingpoint(minqpstate &state, const real_1d_array &x, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetstartingpoint(state.c_ptr(), x.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function sets origin for QP solver. By default, following QP program
is solved:

    min(0.5*x'*A*x+b'*x)

This function allows to solve a different problem:

    min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin))

Specification of non-zero origin  affects  function  being  minimized  and
quadratic/conic constraints, but not box and linear constraints which  are
still calculated without origin.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    XOrigin -   origin, array[N].

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetorigin(minqpstate &state, const real_1d_array &xorigin, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetorigin(state.c_ptr(), xorigin.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets scaling coefficients.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison  with  tolerances)  and  as
preconditioner.

Scale of the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the
   function

If you do not know how to choose scales of your variables, you can:
* read www.alglib.net/optimization/scaling.php article
* use minqpsetscaleautodiag(), which calculates scale  using  diagonal  of
  the  quadratic  term:  S  is  set to 1/sqrt(diag(A)), which works well
  sometimes.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetscale(minqpstate &state, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetscale(state.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets automatic evaluation of variable scaling.

IMPORTANT: this function works only for  matrices  with positive  diagonal
           elements! Zero or negative elements will  result  in  -9  error
           code  being  returned.  Specify  scale  vector  manually   with
           minqpsetscale() in such cases.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison  with  tolerances)  and  as
preconditioner.

The  best  way  to  set  scaling  is  to manually specify variable scales.
However, sometimes you just need quick-and-dirty solution  -  either  when
you perform fast prototyping, or when you know your problem well  and  you
are 100% sure that this quick solution is robust enough in your case.

One such solution is to evaluate scale of I-th variable as 1/Sqrt(A[i,i]),
where A[i,i] is an I-th diagonal element of the quadratic term.

Such approach works well sometimes, but you have to be careful here.

INPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 26.12.2017 by Bochkanov Sergey
*************************************************************************/
void minqpsetscaleautodiag(minqpstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetscaleautodiag(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function tells QP solver to use DENSE-AUL algorithm and sets stopping
criteria for the algorithm.

This  algorithm  is  intended  for  non-convex problems with moderate  (up
to several thousands) variable count and arbitrary number  of  constraints
which are either (a) effectively convexified under constraints or (b) have
unique solution even with nonconvex target.

IMPORTANT: when DENSE-IPM solver is applicable, its performance is usually
           much better than that of DENSE-AUL.
           We recommend  you to use DENSE-AUL only when other solvers  can
           not be used.

ALGORITHM FEATURES:

* supports  box  and  dense/sparse  general   linear   equality/inequality
  constraints
* convergence is theoretically proved for positive-definite  (convex)   QP
  problems. Semidefinite and non-convex problems can be solved as long  as
  they  are   bounded  from  below  under  constraints,  although  without
  theoretical guarantees.

ALGORITHM OUTLINE:

* this  algorithm   is   an   augmented   Lagrangian   method  with  dense
  preconditioner (hence  its  name).
* it performs several outer iterations in order to refine  values  of  the
  Lagrange multipliers. Single outer  iteration  is  a  solution  of  some
  unconstrained optimization problem: first  it  performs  dense  Cholesky
  factorization of the Hessian in order to build preconditioner  (adaptive
  regularization is applied to enforce positive  definiteness),  and  then
  it uses L-BFGS optimizer to solve optimization problem.
* typically you need about 5-10 outer iterations to converge to solution

ALGORITHM LIMITATIONS:

* because dense Cholesky driver is used, this algorithm has O(N^2)  memory
  requirements and O(OuterIterations*N^3) minimum running time.  From  the
  practical  point  of  view,  it  limits  its  applicability  by  several
  thousands of variables.
  From  the  other  side,  variables  count  is  the most limiting factor,
  and dependence on constraint count is  much  more  lower. Assuming  that
  constraint matrix is sparse, it may handle tens of thousands  of general
  linear constraints.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsX    -   >=0, stopping criteria for inner optimizer.
                Inner  iterations  are  stopped  when  step  length  (with
                variable scaling being applied) is less than EpsX.
                See  minqpsetscale()  for  more  information  on  variable
                scaling.
    Rho     -   penalty coefficient, Rho>0:
                * large enough  that  algorithm  converges  with   desired
                  precision.
                * not TOO large to prevent ill-conditioning
                * recommended values are 100, 1000 or 10000
    ItsCnt  -   number of outer iterations:
                * recommended values: 10-15 (although  in  most  cases  it
                  converges within 5 iterations, you may need a  few  more
                  to be sure).
                * ItsCnt=0 means that small number of outer iterations  is
                  automatically chosen (10 iterations in current version).
                * ItsCnt=1 means that AUL algorithm performs just as usual
                  penalty method.
                * ItsCnt>1 means that  AUL  algorithm  performs  specified
                  number of outer iterations

IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT!

NOTE: Passing  EpsX=0  will  lead  to  automatic  step  length  selection
      (specific step length chosen may change in the future  versions  of
      ALGLIB, so it is better to specify step length explicitly).

  -- ALGLIB --
     Copyright 20.08.2016 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgodenseaul(minqpstate &state, const double epsx, const double rho, const ae_int_t itscnt, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetalgodenseaul(state.c_ptr(), epsx, rho, itscnt, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function tells QP solver to  use  DENSE-IPM  QP  algorithm  and  sets
stopping criteria for the algorithm.

This  algorithm  is  intended for convex and semidefinite QP (but not QCQP
or conic) problems with moderate (up to several thousands) variable  count
and arbitrary number of linear constraints. Quadratic and conic constraints
are supported by another solver (DENSE-GENIPM).

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). It can also use  a  performance  backend
           (e.g.  Intel  MKL  or  another  platform-specific  library)  to
           accelerate dense factorization.

           Dense Cholesky factorization is a well  studied  and  optimized
           algorithm. ALGLIB  includes  a  good  implementation;  however,
           using a hardware vendor-provided  performance  library  usually
           results in a better performance.

           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.

IMPORTANT: this  algorithm  is  likely  to  fail  on  nonconvex  problems,
           furthermore, sometimes it fails without a notice. If you try to
           run DENSE-IPM on a problem  with  indefinite  matrix (a  matrix
           having  at least one negative eigenvalue) then depending on the
           circumstances it may either (a) stall at some  arbitrary point,
           or (b) throw an exception due to the failure  of  the  Cholesky
           decomposition.

           Use GENIPM algorithm if  your  problem  is  nonconvex  or has a
           potential of becoming  nonconvex.  The GENIPM solver  can  also
           handle problems with quadratic and conic constraints.

ALGORITHM FEATURES:

* supports  box  and  dense/sparse  general   linear   equality/inequality
  constraints

ALGORITHM OUTLINE:

* this  algorithm  is  our implementation  of  interior  point  method  as
  formulated by  R.J.Vanderbei, with minor modifications to the  algorithm
  (damped Newton directions are extensively used)
* like all interior point methods, this algorithm  tends  to  converge  in
  roughly same number of iterations (between 15 and 50) independently from
  the problem dimensionality

ALGORITHM LIMITATIONS:

* because a dense Cholesky driver is used, for  N-dimensional problem with
  M dense constaints this algorithm has O(N^2+N*M) memory requirements and
  O(N^3+M*N^2) running time.
  Having sparse constraints with Z nonzeros per row  relaxes  storage  and
  running time down to O(N^2+M*Z) and O(N^3+M*Z^2)
  From the practical  point  of  view,  it  limits  its  applicability  by
  several thousands of variables.
  From  the  other  side,  variables  count  is  the most limiting factor,
  and dependence on constraint count is  much  more  lower. Assuming  that
  the constraint matrix is sparse, it may  handle  tens  of  thousands  of
  general linear constraints.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Eps     -   >=0, stopping criteria. The algorithm stops  when   primal
                and dual infeasiblities as well as complementarity gap are
                less than Eps.

IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT!

NOTE: Passing EpsX=0 will lead to automatic selection of small epsilon.

===== TRACING IPM SOLVER =================================================

IPM solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'IPM'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'IPM.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'IPM'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("IPM,PREC.F6", "path/to/trace.log")
>

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgodenseipm(minqpstate &state, const double eps, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetalgodenseipm(state.c_ptr(), eps, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function tells QP solver to  use  SPARSE-IPM  QP algorithm  and  sets
stopping criteria for the algorithm.

This  algorithm  is  intended for convex and semidefinite QP (but not QCQP
or conic) problems with large  variable  and  constraint  count and sparse
quadratic term and sparse linear constraints. It was successfully used for
problems with millions of variables and constraints. Quadratic  and  conic
constraints are supported by another solver (SPARSE-GENIPM).

It is possible to have some limited set of dense linear constraints - they
will be handled separately  by  the  dense  BLAS  -  but  the  more  dense
constraints you have, the more time solver needs.

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). It can also use  a  performance  backend
           (e.g. Intel PARDISO or another  platform-specific  library)  to
           accelerate sparse factorization.

           Specific speed-up due to parallelism  and  performance  backend
           usage heavily depends on the sparsity pattern of quadratic term
           and constraints. For some problem  types  performance  backends
           provide great speed-up. For other  ones,  ALGLIB's  own  sparse
           factorization code is the preferred option.

           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.

IMPORTANT: internally this solver performs large  and  sparse  (N+M)x(N+M)
           triangular factorization. So it expects both quadratic term and
           constraints to be highly sparse. However, its  running  time is
           influenced by BOTH fill factor and sparsity pattern.

           Generally we expect that no more than few nonzero  elements per
           row are present. However different sparsity patterns may result
           in completely different running  times  even  given  same  fill
           factor.

           In many cases this algorithm outperforms DENSE-IPM by order  of
           magnitude. However, in some cases you may  get  better  results
           with DENSE-IPM even when solving sparse task.

IMPORTANT: this algorithm won't work for nonconvex problems. If you try to
           run SPARSE-IPM  on a problem with indefinite quadratic term  (a
           matrix having at least one negative eigenvalue) then  depending
           on the circumstances it may either (a) stall  at some arbitrary
           point, or (b) throw an exception due  to  the  failure  of  the
           Cholesky decomposition.

           Use GENIPM algorithm if  your  problem  is  nonconvex  or has a
           potential of becoming  nonconvex.  The  GENIPM  solver can also
           handle problems with quadratic and conic constraints.

ALGORITHM FEATURES:

* supports  box  and  dense/sparse  general   linear   equality/inequality
  constraints
* specializes on large-scale sparse problems

ALGORITHM OUTLINE:

* this  algorithm  is  our implementation  of  interior  point  method  as
  formulated by  R.J.Vanderbei, with minor modifications to the  algorithm
  (damped Newton directions are extensively used)
* like all interior point methods, this algorithm  tends  to  converge  in
  roughly same number of iterations (between 15 and 50) independently from
  the problem dimensionality

ALGORITHM LIMITATIONS:

* this algorithm may handle moderate number  of dense constraints, usually
  no more than a thousand of dense ones without losing its efficiency.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Eps     -   >=0, stopping criteria. The algorithm stops  when   primal
                and dual infeasiblities as well as complementarity gap are
                less than Eps.

IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT!

NOTE: Passing EpsX=0 will lead to automatic selection of small epsilon.

===== TRACING IPM SOLVER =================================================

IPM solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'IPM'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'IPM.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'IPM'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("IPM,PREC.F6", "path/to/trace.log")
>

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgosparseipm(minqpstate &state, const double eps, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetalgosparseipm(state.c_ptr(), eps, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function tells QP solver to  use  DENSE-GENIPM  QP algorithm and sets
stopping criteria for the algorithm.

This  algorithm  is  intended  for convex/nonconvex box/linearly/conically
constrained QP problems with moderate (up to several thousands)  variables
count and arbitrary number  of  constraints.  Use  SPARSE-GENIPM  if  your
problem is sparse.

The algorithm is a generalization of DENSE-IPM solver, capable of handling
more general constraints as well as nonconvexity of  the  target.  In  the
latter case, a local solution is found.

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). It can also use  a  performance  backend
           (e.g.  Intel  MKL  or  another  platform-specific  library)  to
           accelerate dense factorization.

           Dense Cholesky factorization is a well  studied  and  optimized
           algorithm.  ALGLIB  includes  a  good  implementation; however,
           using a hardware vendor-provided  performance  library  usually
           results in a better performance.

           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.

ALGORITHM FEATURES:

* supports box, linear equality/inequality and conic constraints
* for convex problems returns the global (and the only) solution
* can handle non-convex  problem  (only  a  locally  optimal  solution  is
  returned in this case)

ALGORITHM LIMITATIONS:

* because a dense Cholesky driver is used, for  N-dimensional problem with
  M dense constaints this algorithm has O(N^2+N*M) memory requirements and
  O(N^3+M*N^2) running time.
  Having sparse constraints with Z nonzeros per row  relaxes  storage  and
  running time down to O(N^2+M*Z) and O(N^3+M*Z^2)
  From the practical  point  of  view,  it  limits  its  applicability  by
  several thousands of variables.
  From  the  other  side,  variables  count  is  the most limiting factor,
  and dependence on constraint count is  much  more  lower. Assuming  that
  the constraint matrix is sparse, it may  handle  tens  of  thousands  of
  general linear constraints.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Eps     -   >=0, stopping criteria. The algorithm stops  when   primal
                and dual infeasiblities as well as complementarity gap are
                less than Eps.

IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT!

NOTE: Passing EpsX=0 will lead to automatic selection of small epsilon.

===== TRACING GENIPM SOLVER ==============================================

GENIPM solver supports advanced tracing capabilities. You can log algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'GENIPM'      - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'GENIPM.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'GENIPM'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("GENIPM,PREC.F6", "path/to/trace.log")
>

  -- ALGLIB --
     Copyright 01.05.2024 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgodensegenipm(minqpstate &state, const double eps, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetalgodensegenipm(state.c_ptr(), eps, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function tells QP solver to  use SPARSE-GENIPM  QP algorithm and sets
stopping criteria for the algorithm.

This   algorithm  is intended for convex/nonconvex box/linearly/conically/
quadratically constrained QP  problems  with  sparse  quadratic  term  and
constraints. It can handle millions of variables and constraints, assuming
that the problem is sufficiently sparse. If your problem is small (several
thousands vars at most) and dense, consider using DENSE-GENIPM  as  a more
efficient alternative.

The  algorithm  is  a  generalization of the SPARSE-IPM solver, capable of
handling more general constraints as well as nonconvexity of  the  target.
In the latter case, a local solution is found.

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). Specific  speed-up  due  to  parallelism
           heavily depends on the sparsity pattern of quadratic  term  and
           constraints.

           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism support.

           This function does not use performance backends  to  accelerate
           sparse factorization because external  libraries  typically  do
           not  provide   fine  control over regularization, pivoting  and
           sparse orderings.

IMPORTANT: internally this solver performs large  and  sparse  (N+M)x(N+M)
           triangular factorization. So it expects both quadratic term and
           constraints to be highly sparse. However, its  running  time is
           influenced by BOTH fill factor and sparsity pattern.

           Generally we expect that no more than few nonzero  elements per
           row are present. However different sparsity patterns may result
           in completely different running  times  even  given  same  fill
           factor.

           In many cases this algorithm outperforms DENSE-IPM by order  of
           magnitude. However, in some cases you may  get  better  results
           with DENSE-IPM even when solving sparse task.

ALGORITHM FEATURES:


* supports box, linear equality/inequality constraints
* for convex problems returns the global (and the only) solution
* can handle non-convex  problem  (only  a  locally  optimal  solution  is
  returned in this case)
* specializes on large-scale sparse problems

ALGORITHM LIMITATIONS:

* this algorithm may handle moderate number  of dense constraints, usually
  no more than a thousand of dense ones without losing its efficiency.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Eps     -   >=0, stopping criteria. The algorithm stops  when   primal
                and dual infeasiblities as well as complementarity gap are
                less than Eps.

IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT!

NOTE: Passing EpsX=0 will lead to automatic selection of small epsilon.

===== TRACING GENIPM SOLVER ==============================================

GENIPM solver supports advanced tracing capabilities. You can log algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'GENIPM'      - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'GENIPM.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'IPM'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("GENIPM,PREC.F6", "path/to/trace.log")
>

  -- ALGLIB --
     Copyright 01.05.2024 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgosparsegenipm(minqpstate &state, const double eps, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetalgosparsegenipm(state.c_ptr(), eps, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function tells QP solver to  use HYBRID-GENIPM  QP algorithm and sets
stopping criteria for the algorithm.

This  algorithm is intended for finding medium-accuracy solutions (say, no
more than 5 accurate digits) to large-scale convex/nonconvex box/linearly/
conically/quadratically constrained  QP  problems  with  objective  and/or
quadratic constraints being  well-conditioned  but  having   prohibitively
dense Cholesky factors. This includes both  dense  problems  and  problems
with sparse terms having too dense factorizations.

Being well conditioned allows us to use low rank LBFGS/LSR1 approximations
to the objective (quadratic constraints) and use sparse linear algebra for
the rest of the problem,  thus  achieving  significant  speed-up  on  many
types of problems.

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). Specific  speed-up  due  to  parallelism
           heavily depends on the sparsity pattern of quadratic  term  and
           constraints.

           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism support.

           This function does not use performance backends  to  accelerate
           sparse factorization because external  libraries  typically  do
           not  provide   fine  control over regularization, pivoting  and
           sparse orderings.

IMPORTANT: performance of this function depends on both  sparsity  pattern
           of the problem and on its conditioning properties.  The running
           time for a dense QP/QCQP problem grows with variables count  as
           O(N^2) while DENSE-GENIPM running time grows as O(N^3).

           On the other hand, DENSE/SPARSE-GENIPM running time shows  only
           moderate dependence on the condition number, while HYBRID-GENIPM
           running time often explodes with  condition  number larger than
           10000.

           In practice, on a dense QCQP problem HYBRID algorithm starts to
           singificantly outperform DENSE-GENIPM for N>=1000.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

    Eps     -   >=0, stopping criteria. The algorithm stops  when   primal
                and dual infeasiblities as well as complementarity gap are
                less than Eps.

                This   solver  is  optimized for  finding  medium-accuracy
                solutions, with eps being between 1E-3 and 1E-5.

    MemLen  -   >=0, memory length for the limited memory SR1 model.  Zero
                value means automatic selection of some  small  predefined
                value  which  may  change  in  future  versions of ALGLIB.
                Optimal value is problem-dependent, with  8...32  being  a
                good initial point for the experimentation.  Values  above
                N are silently truncated down to N.

    MaxOffdiag- >=0, quadratic terms with more than MaxOffdiag offdiagonal
                elements are handled via quasi-Newton approximation. Terms
                with no more than MaxOffdiag elements are handled exactly,
                including strictly diagonal terms that are always  handled
                exactly (because they have the minimum number off-diagonal
                elements possible - zero). Symmetric terms appearing above
                and below diagonal are counted as one  term,  not  two.  A
                single quasi-Newton model is built, which approximates all
                eligible quadratic terms together.

                The following values are possible:
                * 0 - approximate every quadratic term having at least one
                  off-diagonal element with a quasi-Newton model.
                * 0<MaxOffdiag<N*(N-1)/2 - eligible  quadratic terms (ones
                  that are too dense) are approximated, sufficiently sparse
                  ones are handled precisely
                * MaxOffdiag>=N*(N-1)/2 - no quadratic  term  is  eligible
                  for quasi-Newton approximation, algorithm performance is
                  roughly similar to  that  of  SPARSE-GENIPM,  with  some
                  additional small overhead for quasi-Newton code checks.

  -- ALGLIB --
     Copyright 01.05.2024 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgohybridgenipm(minqpstate &state, const double eps, const ae_int_t memlen, const ae_int_t maxoffdiag, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetalgohybridgenipm(state.c_ptr(), eps, memlen, maxoffdiag, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function tells QP solver to use an ECQP algorithm.

This  algorithm  is  intended  for sparse convex problems with only linear
equality constraints. It can handle millions of variables and constraints,
assuming that the problem is sufficiently  sparse.  However,  it  can  NOT
deal with nonlinear equality constraints or inequality constraints of  any
type (including box ones), nor it can deal with nonconvex problems.

When applicable, it outperforms SPARSE-IPM by  tens  of  times.  It  is  a
regularized direct linear algebra solver that performs several  rounds  of
iterative  refinement  in  order to improve a solution. Thus, due  to  its
direct nature, it does not need stopping criteria and performs much faster
than interior point methods.

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). It can also use  a  performance  backend
           (e.g. Intel PARDISO or another  platform-specific  library)  to
           accelerate sparse factorization.

           Specific speed-up due to parallelism  and  performance  backend
           usage heavily depends on the sparsity pattern of quadratic term
           and constraints. For some problem  types  performance  backends
           provide great speed-up. For other  ones,  ALGLIB's  own  sparse
           factorization code is the preferred option.

           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.

IMPORTANT: internally this solver performs large  and  sparse  (N+M)x(N+M)
           triangular factorization. So it expects both quadratic term and
           constraints to be highly sparse. However, its  running  time is
           influenced by BOTH fill factor and sparsity pattern.

           Generally we expect that no more than few nonzero  elements per
           row are present. However different sparsity patterns may result
           in completely different running  times  even  given  same  fill
           factor.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Eps     -   >=0, stopping criteria. The algorithm stops  when   primal
                and dual infeasiblities are less than Eps.

IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT!

NOTE: Passing EpsX=0 will lead to automatic selection of small epsilon.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgosparseecqp(minqpstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetalgosparseecqp(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function tells solver to use QuickQP  algorithm:  special  extra-fast
algorithm for problems with box-only constrants. It may  solve  non-convex
problems as long as they are bounded from below under constraints.

ALGORITHM FEATURES:
* several times faster than DENSE-IPM when running on box-only problem
* utilizes accelerated methods for activation of constraints.
* supports dense and sparse QP problems
* supports ONLY box constraints; general linear constraints are NOT
  supported by this solver
* can solve all types of problems  (convex,  semidefinite,  nonconvex)  as
  long as they are bounded from below under constraints.
  Say, it is possible to solve "min{-x^2} subject to -1<=x<=+1".
  In convex/semidefinite case global minimum  is  returned,  in  nonconvex
  case - algorithm returns one of the local minimums.

ALGORITHM OUTLINE:

* algorithm  performs  two kinds of iterations: constrained CG  iterations
  and constrained Newton iterations
* initially it performs small number of constrained CG  iterations,  which
  can efficiently activate/deactivate multiple constraints
* after CG phase algorithm tries to calculate Cholesky  decomposition  and
  to perform several constrained Newton steps. If  Cholesky  decomposition
  failed (matrix is indefinite even under constraints),  we  perform  more
  CG iterations until we converge to such set of constraints  that  system
  matrix becomes  positive  definite.  Constrained  Newton  steps  greatly
  increase convergence speed and precision.
* algorithm interleaves CG and Newton iterations which  allows  to  handle
  indefinite matrices (CG phase) and quickly converge after final  set  of
  constraints is found (Newton phase). Combination of CG and Newton phases
  is called "outer iteration".
* it is possible to turn off Newton  phase  (beneficial  for  semidefinite
  problems - Cholesky decomposition will fail too often)

ALGORITHM LIMITATIONS:

* algorithm does not support general  linear  constraints;  only  box ones
  are supported
* Cholesky decomposition for sparse problems  is  performed  with  Skyline
  Cholesky solver, which is intended for low-profile matrices. No profile-
  reducing reordering of variables is performed in this version of ALGLIB.
* problems with near-zero negative eigenvalues (or exacty zero  ones)  may
  experience about 2-3x performance penalty. The reason is  that  Cholesky
  decomposition can not be performed until we identify directions of  zero
  and negative curvature and activate corresponding boundary constraints -
  but we need a lot of trial and errors because these directions  are hard
  to notice in the matrix spectrum.
  In this case you may turn off Newton phase of algorithm.
  Large negative eigenvalues  are  not  an  issue,  so  highly  non-convex
  problems can be solved very efficiently.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsG    -   >=0
                The  subroutine  finishes  its  work   if   the  condition
                |v|<EpsG is satisfied, where:
                * |.| means Euclidian norm
                * v - scaled constrained gradient vector, v[i]=g[i]*s[i]
                * g - gradient
                * s - scaling coefficients set by MinQPSetScale()
    EpsF    -   >=0
                The  subroutine  finishes its work if exploratory steepest
                descent  step  on  k+1-th iteration  satisfies   following
                condition:  |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1}
    EpsX    -   >=0
                The  subroutine  finishes its work if exploratory steepest
                descent  step  on  k+1-th iteration  satisfies   following
                condition:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - step vector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinQPSetScale()
    MaxOuterIts-maximum number of OUTER iterations.  One  outer  iteration
                includes some amount of CG iterations (from 5 to  ~N)  and
                one or several (usually small amount) Newton steps.  Thus,
                one outer iteration has high cost, but can greatly  reduce
                funcation value.
                Use 0 if you do not want to limit number of outer iterations.
    UseNewton-  use Newton phase or not:
                * Newton phase improves performance of  positive  definite
                  dense problems (about 2 times improvement can be observed)
                * can result in some performance penalty  on  semidefinite
                  or slightly negative definite  problems  -  each  Newton
                  phase will bring no improvement (Cholesky failure),  but
                  still will require computational time.
                * if you doubt, you can turn off this  phase  -  optimizer
                  will retain its most of its high speed.

IT IS VERY IMPORTANT TO CALL MinQPSetScale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS STOPPING CRITERIA ARE SCALE-DEPENDENT!

Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead
to automatic stopping criterion selection (presently it is  small    step
length, but it may change in the future versions of ALGLIB).

  -- ALGLIB --
     Copyright 22.05.2014 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgoquickqp(minqpstate &state, const double epsg, const double epsf, const double epsx, const ae_int_t maxouterits, const bool usenewton, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetalgoquickqp(state.c_ptr(), epsg, epsf, epsx, maxouterits, usenewton, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets box constraints for QP solver

Box constraints are inactive by default (after  initial  creation).  After
being  set,  they are  preserved until explicitly overwritten with another
minqpsetbc()  or  minqpsetbcall()  call,  or  partially  overwritten  with
minqpsetbci() call.

Following types of constraints are supported:

    DESCRIPTION         CONSTRAINT              HOW TO SPECIFY
    fixed variable      x[i]=Bnd[i]             BndL[i]=BndU[i]
    lower bound         BndL[i]<=x[i]           BndU[i]=+INF
    upper bound         x[i]<=BndU[i]           BndL[i]=-INF
    range               BndL[i]<=x[i]<=BndU[i]  ...
    free variable       -                       BndL[I]=-INF, BndU[I]+INF

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF (latter is recommended because
                it will allow solver to use better algorithm).
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF (latter is recommended because
                it will allow solver to use better algorithm).

NOTE: infinite values can be specified by means of Double.PositiveInfinity
      and  Double.NegativeInfinity  (in  C#)  and  alglib::fp_posinf   and
      alglib::fp_neginf (in C++).

NOTE: you may replace infinities by very small/very large values,  but  it
      is not recommended because large numbers may introduce large numerical
      errors in the algorithm.

NOTE: if constraints for all variables are same you may use minqpsetbcall()
      which allows to specify constraints without using arrays.

NOTE: BndL>BndU will result in QP problem being recognized as infeasible.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetbc(minqpstate &state, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetbc(state.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets box constraints for QP solver (all variables  at  once,
same constraints for all variables)

Box constraints are inactive by default (after  initial  creation).  After
being  set,  they are  preserved until explicitly overwritten with another
minqpsetbc()  or  minqpsetbcall()  call,  or  partially  overwritten  with
minqpsetbci() call.

Following types of constraints are supported:

    DESCRIPTION         CONSTRAINT              HOW TO SPECIFY
    fixed variable      x[i]=Bnd                BndL=BndU
    lower bound         BndL<=x[i]              BndU=+INF
    upper bound         x[i]<=BndU              BndL=-INF
    range               BndL<=x[i]<=BndU        ...
    free variable       -                       BndL=-INF, BndU+INF

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bound, same for all variables
    BndU    -   upper bound, same for all variables

NOTE: infinite values can be specified by means of Double.PositiveInfinity
      and  Double.NegativeInfinity  (in  C#)  and  alglib::fp_posinf   and
      alglib::fp_neginf (in C++).

NOTE: you may replace infinities by very small/very large values,  but  it
      is not recommended because large numbers may introduce large numerical
      errors in the algorithm.

NOTE: BndL>BndU will result in QP problem being recognized as infeasible.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetbcall(minqpstate &state, const double bndl, const double bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetbcall(state.c_ptr(), bndl, bndu, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets box constraints for I-th variable (other variables are
not modified).

Following types of constraints are supported:

    DESCRIPTION         CONSTRAINT              HOW TO SPECIFY
    fixed variable      x[i]=Bnd                BndL=BndU
    lower bound         BndL<=x[i]              BndU=+INF
    upper bound         x[i]<=BndU              BndL=-INF
    range               BndL<=x[i]<=BndU        ...
    free variable       -                       BndL=-INF, BndU+INF

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bound
    BndU    -   upper bound

NOTE: infinite values can be specified by means of Double.PositiveInfinity
      and  Double.NegativeInfinity  (in  C#)  and  alglib::fp_posinf   and
      alglib::fp_neginf (in C++).

NOTE: you may replace infinities by very small/very large values,  but  it
      is not recommended because large numbers may introduce large numerical
      errors in the algorithm.

NOTE: BndL>BndU will result in QP problem being recognized as infeasible.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetbci(minqpstate &state, const ae_int_t i, const double bndl, const double bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetbci(state.c_ptr(), i, bndl, bndu, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets dense linear constraints for QP optimizer.

This  function  overrides  results  of  previous  calls  to  minqpsetlc(),
minqpsetlcsparse() and minqpsetlcmixed().  After  call  to  this  function
all non-box constraints are dropped, and you have only  those  constraints
which were specified in the present call.

If you want  to  specify  mixed  (with  dense  and  sparse  terms)  linear
constraints, you should call minqpsetlcmixed().

INPUT PARAMETERS:
    State   -   structure previously allocated with MinQPCreate call.
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

NOTE 1: linear (non-bound) constraints are satisfied only approximately  -
        there always exists some violation due  to  numerical  errors  and
        algorithmic limitations.

  -- ALGLIB --
     Copyright 19.06.2012 by Bochkanov Sergey
*************************************************************************/
void minqpsetlc(minqpstate &state, const real_2d_array &c, const integer_1d_array &ct, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetlc(state.c_ptr(), c.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets dense linear constraints for QP optimizer.

This  function  overrides  results  of  previous  calls  to  minqpsetlc(),
minqpsetlcsparse() and minqpsetlcmixed().  After  call  to  this  function
all non-box constraints are dropped, and you have only  those  constraints
which were specified in the present call.

If you want  to  specify  mixed  (with  dense  and  sparse  terms)  linear
constraints, you should call minqpsetlcmixed().

INPUT PARAMETERS:
    State   -   structure previously allocated with MinQPCreate call.
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

NOTE 1: linear (non-bound) constraints are satisfied only approximately  -
        there always exists some violation due  to  numerical  errors  and
        algorithmic limitations.

  -- ALGLIB --
     Copyright 19.06.2012 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minqpsetlc(minqpstate &state, const real_2d_array &c, const integer_1d_array &ct, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t k;
    if( (c.rows()!=ct.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'minqpsetlc': looks like one of arguments has wrong size");
    k = c.rows();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetlc(state.c_ptr(), c.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets sparse linear constraints for QP optimizer.

This  function  overrides  results  of  previous  calls  to  minqpsetlc(),
minqpsetlcsparse() and minqpsetlcmixed().  After  call  to  this  function
all non-box constraints are dropped, and you have only  those  constraints
which were specified in the present call.

If you want  to  specify  mixed  (with  dense  and  sparse  terms)  linear
constraints, you should call minqpsetlcmixed().

INPUT PARAMETERS:
    State   -   structure previously allocated with MinQPCreate call.
    C       -   linear  constraints,  sparse  matrix  with  dimensions  at
                least [K,N+1]. If matrix has  larger  size,  only  leading
                Kx(N+1) rectangle is used.
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0

NOTE 1: linear (non-bound) constraints are satisfied only approximately  -
        there always exists some violation due  to  numerical  errors  and
        algorithmic limitations.

  -- ALGLIB --
     Copyright 22.08.2016 by Bochkanov Sergey
*************************************************************************/
void minqpsetlcsparse(minqpstate &state, const sparsematrix &c, const integer_1d_array &ct, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetlcsparse(state.c_ptr(), c.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets mixed linear constraints, which include a set of  dense
rows, and a set of sparse rows.

This  function  overrides  results  of  previous  calls  to  minqpsetlc(),
minqpsetlcsparse() and minqpsetlcmixed().

This function may be useful if constraint matrix includes large number  of
both types of rows - dense and sparse. If you have just a few sparse rows,
you  may  represent  them  in  dense  format  without losing  performance.
Similarly, if you have just a few dense rows, you may store them in sparse
format with almost same performance.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinQPCreate call.
    SparseC -   linear constraints, sparse  matrix with dimensions EXACTLY
                EQUAL TO [SparseK,N+1].  Each  row  of  C  represents  one
                constraint, either equality or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    SparseCT-   type of sparse constraints, array[K]:
                * if SparseCT[i]>0, then I-th constraint is SparseC[i,*]*x >= SparseC[i,n+1]
                * if SparseCT[i]=0, then I-th constraint is SparseC[i,*]*x  = SparseC[i,n+1]
                * if SparseCT[i]<0, then I-th constraint is SparseC[i,*]*x <= SparseC[i,n+1]
    SparseK -   number of sparse equality/inequality constraints, K>=0
    DenseC  -   dense linear constraints, array[K,N+1].
                Each row of DenseC represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of DenseC (including right part) must be finite.
    DenseCT -   type of constraints, array[K]:
                * if DenseCT[i]>0, then I-th constraint is DenseC[i,*]*x >= DenseC[i,n+1]
                * if DenseCT[i]=0, then I-th constraint is DenseC[i,*]*x  = DenseC[i,n+1]
                * if DenseCT[i]<0, then I-th constraint is DenseC[i,*]*x <= DenseC[i,n+1]
    DenseK  -   number of equality/inequality constraints, DenseK>=0

NOTE 1: linear (non-box) constraints  are  satisfied only approximately  -
        there always exists some violation due  to  numerical  errors  and
        algorithmic limitations.

NOTE 2: due to backward compatibility reasons SparseC can be  larger  than
        [SparseK,N+1]. In this case only leading  [SparseK,N+1]  submatrix
        will be  used.  However,  the  rest  of  ALGLIB  has  more  strict
        requirements on the input size, so we recommend you to pass sparse
        term whose size exactly matches algorithm expectations.

  -- ALGLIB --
     Copyright 22.08.2016 by Bochkanov Sergey
*************************************************************************/
void minqpsetlcmixed(minqpstate &state, const sparsematrix &sparsec, const integer_1d_array &sparsect, const ae_int_t sparsek, const real_2d_array &densec, const integer_1d_array &densect, const ae_int_t densek, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetlcmixed(state.c_ptr(), sparsec.c_ptr(), sparsect.c_ptr(), sparsek, densec.c_ptr(), densect.c_ptr(), densek, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function provides legacy API for specification of mixed  dense/sparse
linear constraints.

New conventions used by ALGLIB since release  3.16.0  state  that  set  of
sparse constraints comes first,  followed  by  set  of  dense  ones.  This
convention is essential when you talk about things like order of  Lagrange
multipliers.

However, legacy API accepted mixed  constraints  in  reverse  order.  This
function is here to simplify situation with code relying on legacy API. It
simply accepts constraints in one order (old) and passes them to new  API,
now in correct order.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minqpsetlcmixedlegacy(minqpstate &state, const real_2d_array &densec, const integer_1d_array &densect, const ae_int_t densek, const sparsematrix &sparsec, const integer_1d_array &sparsect, const ae_int_t sparsek, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetlcmixedlegacy(state.c_ptr(), densec.c_ptr(), densect.c_ptr(), densek, sparsec.c_ptr(), sparsect.c_ptr(), sparsek, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU with dense
constraint matrix A.

NOTE: knowing  that  constraint  matrix  is  dense  helps  some QP solvers
      (especially modern IPM method) to utilize efficient  dense  Level  3
      BLAS for dense parts of the problem. If your problem has both  dense
      and sparse constraints, you  can  use  minqpsetlc2mixed()  function,
      which will result in dense algebra being applied to dense terms, and
      sparse sparse linear algebra applied to sparse terms.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minqpsetlc2dense(minqpstate &state, const real_2d_array &a, const real_1d_array &al, const real_1d_array &au, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetlc2dense(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU with dense
constraint matrix A.

NOTE: knowing  that  constraint  matrix  is  dense  helps  some QP solvers
      (especially modern IPM method) to utilize efficient  dense  Level  3
      BLAS for dense parts of the problem. If your problem has both  dense
      and sparse constraints, you  can  use  minqpsetlc2mixed()  function,
      which will result in dense algebra being applied to dense terms, and
      sparse sparse linear algebra applied to sparse terms.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minqpsetlc2dense(minqpstate &state, const real_2d_array &a, const real_1d_array &al, const real_1d_array &au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t k;
    if( (a.rows()!=al.length()) || (a.rows()!=au.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'minqpsetlc2dense': looks like one of arguments has wrong size");
    k = a.rows();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetlc2dense(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
sparse constraining matrix A. Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    A       -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minqpsetlc2(minqpstate &state, const sparsematrix &a, const real_1d_array &al, const real_1d_array &au, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetlc2(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
mixed constraining matrix A including sparse part (first SparseK rows) and
dense part (last DenseK rows). Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

This function may be useful if constraint matrix includes large number  of
both types of rows - dense and sparse. If you have just a few sparse rows,
you  may  represent  them  in  dense  format  without losing  performance.
Similarly, if you have just a few dense rows, you may store them in sparse
format with almost same performance.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    SparseA -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    SparseK -   number of sparse constraints, SparseK>=0
    DenseA  -   linear constraints, array[K,N], set of dense constraints.
                Each row of A represents one general linear constraint.
    DenseK  -   number of dense constraints, DenseK>=0
    AL, AU  -   lower and upper bounds, array[SparseK+DenseK], with former
                SparseK elements corresponding to sparse constraints,  and
                latter DenseK elements corresponding to dense constraints;
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minqpsetlc2mixed(minqpstate &state, const sparsematrix &sparsea, const ae_int_t ksparse, const real_2d_array &densea, const ae_int_t kdense, const real_1d_array &al, const real_1d_array &au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpsetlc2mixed(state.c_ptr(), sparsea.c_ptr(), ksparse, densea.c_ptr(), kdense, al.c_ptr(), au.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
matrix of currently present dense constraints.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    A       -   linear constraint coefficient, array[N], right side is NOT
                included.
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint Ai*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint Ai*x<=AU
                * AU=+INF  => one-sided constraint AL<=Ai*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minqpaddlc2dense(minqpstate &state, const real_1d_array &a, const double al, const double au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpaddlc2dense(state.c_ptr(), a.c_ptr(), al, au, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present sparse constraints.

Constraint is passed in compressed format: as list of non-zero entries  of
coefficient vector A. Such approach is more efficient than  dense  storage
for highly sparse constraint vectors.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    IdxA    -   array[NNZ], indexes of non-zero elements of A:
                * can be unsorted
                * can include duplicate indexes (corresponding entries  of
                  ValA[] will be summed)
    ValA    -   array[NNZ], values of non-zero elements of A
    NNZ     -   number of non-zero coefficients in A
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minqpaddlc2(minqpstate &state, const integer_1d_array &idxa, const real_1d_array &vala, const ae_int_t nnz, const double al, const double au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpaddlc2(state.c_ptr(), idxa.c_ptr(), vala.c_ptr(), nnz, al, au, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present sparse constraints.

Constraint vector A is  passed  as  a  dense  array  which  is  internally
sparsified by this function.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    DA      -   array[N], constraint vector
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minqpaddlc2sparsefromdense(minqpstate &state, const real_1d_array &da, const double al, const double au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpaddlc2sparsefromdense(state.c_ptr(), da.c_ptr(), al, au, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function clears the list of quadratic constraints. Other  constraints
are not modified.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.

  -- ALGLIB --
     Copyright 19.06.2024 by Bochkanov Sergey
*************************************************************************/
void minqpclearqc(minqpstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpclearqc(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends a two-sided quadratic constraint of the form

    CL <= b'x + 0.5*x'*Q*x <= CU

or (depending on the ApplyOrigin parameter)

    CL <= b'(x-origin) + 0.5*(x-origin)'*Q*(x-origin) <= CU

to the set of currently present constraints. The linear term is  given  by
a dense array, the quadratic term is given by a sparse array.

Here CL can be finite or -INF (absense of constraint), CU can be finite or
+INF (absense of constraint), CL<=CU, with  CL=CU  denoting   an  equality
constraint. Q is an arbitrary (including indefinite) symmetric matrix.

The  function  has  O(max(N,NNZ)) memory and time requirements  because  a
dense array is used to store linear term and because  most  sparse  matrix
storage formats supported by ALGLIB need at least O(N) memory even for  an
empty quadratic constraint matrix.

Use minqpaddqc2list() if you have to add many constraints with  much  less
than N nonzero elements.

IMPORTANT: ALGLIB  supports  arbitrary  quadratic  constraints,  including
           nonconvex ones. However, only convex constraints (combined with
           the convex objective) result in guaranteed convergence  to  the
           global minimizer. In all other cases, only local convergence to
           a local minimum is guaranteed.

           A convex constraint is a  constraint  of  the  following  form:
           b'*(x-origin) + 0.5(x-origin)'*Q*(x-origin) <=CU, with Q  being
           a semidefinite matrix. All other modifications  are  nonconvex:
           * -x0^2<=1 is nonconvex
           *  x0^2>=1 is nonconvex (despite Q=1 being positive definite)
           *  x0^2 =1 is nonconvex

           The latter case is notable because it effectively converts a QP
           problem into a mixed integer QP program. Smooth interior  point
           solver can not efficiently handle such programs, converging  to
           a randomly chosen x0 (either +1 or -1) and  keeping  its  value
           fixed during the optimization.

           It is also  notable  that  larger  equality  constraints  (e.g.
           x0^2+x1^2=1) are much less difficult  to  handle  because  they
           form large connected regions within the parameters space.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    Q       -   symmetric matrix Q in a sparse matrix storage format:
                * if IsUpper=True, then the upper triangle  is  given, and
                  the lower triangle is ignored
                * if IsUpper=False, then the lower triangle is  given, and
                  the upper triangle is ignored
                * any sparse matrix storage format present in ALGLIB is
                  supported
                * the matrix must be exactly NxN
    IsUpper -   whether upper or lower triangle of Q is used
    B       -   array[N], linear term
    CL, CU  -   lower and upper bounds:
                * CL can be finite or -INF (absence of a bound)
                * CU can be finite or +INF (absence of a bound)
                * CL<=CU, with CL=CU meaning an equality constraint
                * CL=-INF, CU=+INF => constraint is ignored
    ApplyOrigin-whether origin (as specified by minqpsetorigin) is applied
                to the constraint or not. If no origin was specified, this
                parameter has no effect.

RESULT:
    constraint index, starting from 0

  -- ALGLIB --
     Copyright 19.07.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddqc2(minqpstate &state, const sparsematrix &q, const bool isupper, const real_1d_array &b, const double cl, const double cu, const bool applyorigin, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::minqpaddqc2(state.c_ptr(), q.c_ptr(), isupper, b.c_ptr(), cl, cu, applyorigin, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
This function appends a two-sided quadratic constraint of the form

    CL <= b'x + 0.5*x'*Q*x <= CU

or (depending on the ApplyOrigin parameter)

    CL <= b'(x-origin) + 0.5*(x-origin)'*Q*(x-origin) <= CU

to the set of currently present constraints.  Both  linear  and  quadratic
terms are given as lists of non-zero entries.

Here CL can be finite or -INF (absense of constraint), CU can be finite or
+INF (absense of constraint), CL<=CU, with  CL=CU  denoting   an  equality
constraint. Q is an arbitrary (including indefinite) symmetric matrix.

The function needs O(NNZ) memory for temporaries and  O(NNZ*logNNZ)  time,
where NNZ is a  total  number  of  non-zeros  in  both  lists.  For  small
constraints it can be orders of magnitude faster than  minqpaddqc2()  with
its  O(max(N,NNZ)) temporary memory or minqpaddqc2dense() with its  O(N^2)
temporaries. Thus, it is recommended if you have many small constraints.

NOTE: in  the  end,  all  quadratic  constraints  are  stored  in the same
      memory-efficient compressed format. However, you have to allocate an
      NxN  temporary  dense  matrix  when  you  pass  a  constraint  using
      minqpaddqc2dense(). Similarly, data structures used as a part of the
      API  provided  by   minqpaddqc2()   have   O(N)   temporary   memory
      requirements.

IMPORTANT: ALGLIB  supports  arbitrary  quadratic  constraints,  including
           nonconvex ones. However, only convex constraints (combined with
           the convex objective) result in guaranteed convergence  to  the
           global minimizer. In all other cases, only local convergence to
           a local minimum is guaranteed.

           A convex constraint is a  constraint  of  the  following  form:
           b'*(x-origin) + 0.5(x-origin)'*Q*(x-origin) <=CU, with Q  being
           a semidefinite matrix. All other modifications  are  nonconvex:
           * -x0^2<=1 is nonconvex
           *  x0^2>=1 is nonconvex (despite Q=1 being positive definite)
           *  x0^2 =1 is nonconvex

           The latter case is notable because it effectively converts a QP
           problem into a mixed integer QP program. Smooth interior  point
           solver can not efficiently handle such programs, converging  to
           a randomly chosen x0 (either +1 or -1) and  keeping  its  value
           fixed during the optimization.

           It is also  notable  that  larger  equality  constraints  (e.g.
           x0^2+x1^2=1) are much less difficult  to  handle  because  they
           form large connected regions within the parameters space.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    QRIdx   -   array[QNNZ], stores row indexes of QNNZ  nonzero  elements
                of a symmetric matrix Q
    QCIdx   -   array[QNNZ], stores col indexes of QNNZ  nonzero  elements
                of a symmetric matrix Q
    QVals   -   array[QNNZ], stores values of QNNZ  nonzero  elements of a
                symmetric matrix Q
    QNNZ    -   number of non-zero elements in Q, QNNZ>=0
    IsUpper -   whether upper or lower triangle of Q is used:
                * if IsUpper=True, then only elements with QRIdx[I]<=QCIdx[I]
                  are used and the rest is ignored
                * if IsUpper=False, then only elements with QRIdx[I]>=QCIdx[I]
                  are used and the rest is ignored
    BIdx    -   array[BNNZ], indexes of BNNZ nonzero elements of a linear term
    BVals   -   array[BNNZ], values of BNNZ nonzero elements of a linear term
    BNNZ    -   number of nonzero elements in B, BNNZ>=0
    CL, CU  -   lower and upper bounds:
                * CL can be finite or -INF (absence of a bound)
                * CU can be finite or +INF (absence of a bound)
                * CL<=CU, with CL=CU meaning an equality constraint
                * CL=-INF, CU=+INF => constraint is ignored
    ApplyOrigin-whether origin (as specified by minqpsetorigin) is applied
                to the constraint or not. If no origin was specified, this
                parameter has no effect.

RESULT:
    constraint index, starting from 0

  -- ALGLIB --
     Copyright 19.07.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddqc2list(minqpstate &state, const integer_1d_array &qridx, const integer_1d_array &qcidx, const real_1d_array &qvals, const ae_int_t qnnz, const bool isupper, const integer_1d_array &bidx, const real_1d_array &bvals, const ae_int_t bnnz, const double cl, const double cu, const bool applyorigin, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::minqpaddqc2list(state.c_ptr(), qridx.c_ptr(), qcidx.c_ptr(), qvals.c_ptr(), qnnz, isupper, bidx.c_ptr(), bvals.c_ptr(), bnnz, cl, cu, applyorigin, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
This function appends a two-sided quadratic constraint of the form

    CL <= b'x + 0.5*x'*Q*x <= CU

or (depending on the ApplyOrigin parameter)

    CL <= b'(x-origin) + 0.5*(x-origin)'*Q*(x-origin) <= CU

to the set of currently present constraints. The linear and quadratic terms
are given by dense arrays.

Here CL can be finite or -INF (absense of constraint), CU can be finite or
+INF (absense of constraint), CL<=CU, with  CL=CU  denoting   an  equality
constraint. Q is an arbitrary (including indefinite) symmetric matrix.

This function trades convenience of using dense arrays for the efficiency.
Because dense NxN storage is used, merely calling this function has O(N^2)
complexity, no matter how sparse the Q is.

Use minqpaddqc2() or minqpaddqc2list() if you have sparse  Q  and/or  many
constraints to handle.

IMPORTANT: ALGLIB  supports  arbitrary  quadratic  constraints,  including
           nonconvex ones. However, only convex constraints (combined with
           the convex objective) result in guaranteed convergence  to  the
           global minimizer. In all other cases, only local convergence to
           a local minimum is guaranteed.

           A convex constraint is a  constraint  of  the  following  form:
           b'*(x-origin) + 0.5(x-origin)'*Q*(x-origin) <=CU, with Q  being
           a semidefinite matrix. All other modifications  are  nonconvex:
           * -x0^2<=1 is nonconvex
           *  x0^2>=1 is nonconvex (despite Q=1 being positive definite)
           *  x0^2 =1 is nonconvex

           The latter case is notable because it effectively converts a QP
           problem into a mixed integer QP program. Smooth interior  point
           solver can not efficiently handle such programs, converging  to
           a randomly chosen x0 (either +1 or -1) and  keeping  its  value
           fixed during the optimization.

           It is also  notable  that  larger  equality  constraints  (e.g.
           x0^2+x1^2=1) are much less difficult  to  handle  because  they
           form large connected regions within the parameters space.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    Q       -   array[N,N], symmetric matrix Q:
                * if IsUpper=True, then the upper triangle  is  given, and
                  the lower triangle is ignored
                * if IsUpper=False, then the lower triangle is  given, and
                  the upper triangle is ignored
                * if more than N rows/cols are present, only leading N
                  elements are used
    IsUpper -   whether upper or lower triangle of Q is used
    B       -   array[N], linear term
    CL, CU  -   lower and upper bounds:
                * CL can be finite or -INF (absence of a bound)
                * CU can be finite or +INF (absence of a bound)
                * CL<=CU, with CL=CU meaning an equality constraint
                * CL=-INF, CU=+INF => constraint is ignored
    ApplyOrigin-whether origin (as specified by minqpsetorigin) is applied
                to the constraint or not. If no origin was specified, this
                parameter has no effect.

RESULT:
    constraint index, starting from 0

  -- ALGLIB --
     Copyright 19.06.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddqc2dense(minqpstate &state, const real_2d_array &q, const bool isupper, const real_1d_array &b, const double cl, const double cu, const bool applyorigin, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::minqpaddqc2dense(state.c_ptr(), q.c_ptr(), isupper, b.c_ptr(), cl, cu, applyorigin, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
This function clears the list of conic constraints. Other  constraints are
not modified.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.

  -- ALGLIB --
     Copyright 19.06.2024 by Bochkanov Sergey
*************************************************************************/
void minqpclearcc(minqpstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpclearcc(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends a primitive second-order  conic  constraint  of  the
form

        (                                                  )
    sqrt(x[range0]^2 + x[range0+1]^2 + ... + x[range1-1]^2 ) <= x[axisidx]
        (                                                  )

with 'primitive' meaning that there are no per-variable  scales  and  that
variables under the square root have sequential indexes. More general form
of conic constraints can be specified with minqpaddsoccorthogonal().

Alternatively, if ApplyOrigin parameter  is  True,  x[i]  is  replaced  by
x[i]-origin[i] (applies to all variables).

Unlike  many  other  conic  solvers,  ALGLIB  allows  conic constraints to
overlap, i.e. it allows  a  variable  to  be  a  part  of  multiple  conic
constraints.

NOTE: second-order conic constraints are always  convex,  so  having  them
      preserves convexity of the QP problem.

NOTE: A starting point that is strictly feasible with respect to both  box
      and conic constraints greatly helps the solver to power up; however,
      it will work even without such a point,  albeit  at  somewhat  lower
      performance.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    Range0,
    Range1  -   0<=range0<=range1<=N, variable range for the LHS;
                * squared variables x[range0]...x[range1-1] are summed up
                  under the square root.
                * range0=range1 means that the constraint is interpreted
                  as x[AxisIdx]>=0.
    AxisIdx -   RHS variable index:
                * 0<=AxisIdx<N
                * either AxisIdx<range0 or AxisAdx>=Range1.

RESULT:
    constraint index in a conic constraints list, starting from 0

  -- ALGLIB --
     Copyright 09.09.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddsoccprimitive(minqpstate &state, const ae_int_t range0, const ae_int_t range1, const ae_int_t axisidx, const bool applyorigin, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::minqpaddsoccprimitive(state.c_ptr(), range0, range1, axisidx, applyorigin, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
This function appends an axis-orthogonal second-order  conic constraint of
the form

         ( k-2 (                     )^2          )
    sqrt ( SUM ( a[i]*x[idx[i]]+c[i] )  + theta^2 ) <= a[k-1]*x[idx[k-1]]+c[k-1]
         ( i=0 (                     )            )

Alternatively, if ApplyOrigin parameter  is  True,  x[i]  is  replaced  by
x[i]-origin[i] (applies to all variables).

Unlike many other conic solvers, ALGLIB provides a flexible conic API that
allows a[] to have zero elements at arbitrary positions (e.g.,  |x|<=const
can be handled just as easy as |x|<=y). Furthermore, ALGLIB  allows  conic
constraints to overlap, i.e. it allows a variable to be a part of multiple
conic constraints, or to appear multiple times in the same constraint.

NOTE: second-order conic constraints are always  convex,  so  having  them
      preserves convexity of the QP problem.

NOTE: A starting point that is strictly feasible with respect to both  box
      and conic constraints greatly helps the solver to power up; however,
      it will work even without such a point,  albeit  at  somewhat  lower
      performance.

INPUT PARAMETERS:
    State   -   structure previously allocated  with  minqpcreate()  call.
    Idx     -   array[K] (or larger, only leading  K  elements  are  used)
                storing variable indexes. Indexes can be  unsorted  and/or
                non-distinct.
    A       -   array[K]  (or larger, only leading  K  elements are used),
                variable multipliers. Can contain zero values.
    C       -   array[K] (or larger, only leading  K  elements  are used),
                variable shifts.
    K       -   cone dimensionality, K>=1. It is possible to have K>N.
    Theta   -   additional constant term, can be zero

RESULT:
    constraint index in a conic constraints list, starting from 0

  -- ALGLIB --
     Copyright 09.09.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddsoccorthogonal(minqpstate &state, const integer_1d_array &idx, const real_1d_array &a, const real_1d_array &c, const ae_int_t k, const double theta, const bool applyorigin, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::minqpaddsoccorthogonal(state.c_ptr(), idx.c_ptr(), a.c_ptr(), c.c_ptr(), k, theta, applyorigin, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
This function appends a primitive power cone constraint of the form

        (                                                  )
    sqrt(x[range0]^2 + x[range0+1]^2 + ... + x[range1-1]^2 ) <= x[axisidx]^alpha
        (                                                  )

or, written in another form,

    (    (                                                  ))^(1/alpha)
    (sqrt(x[range0]^2 + x[range0+1]^2 + ... + x[range1-1]^2 ))           <= x[axisidx]
    (    (                                                  ))

where

    0<alpha<=1

with 'primitive' meaning that there are no per-variable  scales  and  that
variables under the square root have sequential indexes. More general form
of power cone constraints can be specified with minqpaddpowccorthogonal().

Alternatively, if ApplyOrigin parameter  is  True,  x[i]  is  replaced  by
x[i]-origin[i] (applies to all variables).

Unlike many other conic solvers, ALGLIB provides a flexible conic API that
allows alpha to be any positive value less than or equal to 1 (e.g.  it is
possible to formulate |x|<z^0.33 without using  slack vars that are  fixed
at 1.0). Furthermore, ALGLIB allows conic constraints to overlap, i.e.  it
allows a variable to be a part of multiple conic constraints.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    Range0,
    Range1  -   0<=range0<=range1<=N, variable range for the LHS;
                * squared variables x[range0]...x[range1-1] are summed up
                  under the square root.
                * range0=range1 means that the constraint is interpreted
                  as x[AxisIdx]>=0.
    AxisIdx -   RHS variable index:
                * 0<=AxisIdx<N
                * either AxisIdx<range0 or AxisAdx>=Range1.
    Alpha   -   power parameter, 0<alpha<=1

RESULT:
    constraint index in a conic constraints list, starting from 0

NOTE: power cone constraints are always  convex,  so having them preserves
      convexity of the QP problem.

NOTE: A starting point that is strictly feasible with respect to both  box
      and conic constraints greatly helps the solver to power up; however,
      it will work even without such a point,  albeit  at  somewhat  lower
      performance.

NOTE: a power cone with alpha<1 is sensitive to numerical errors near  the
      origin.  Suppose,  for  example,  that  we  have a constraint of the
      form |y|<=x^alpha with alpha=0.25 and that x is zero at the solution
      Furthermore, suppose that we perturbed x by as little  as  eps=1E-8.
      Because of alpha=0.25 the constraint is  perturbed  by  as  much  as
      eps^(1/4)=0.01!

      Such great sensitivity is  explained  by  the  non-differentiability
      of x^alpha for alpha<1 near x=0.  It  does  not  prevent  the  conic
      solver  from  converging  to   the precise solution, it merely makes
      constraints  extremely  sensitive  to  small   errors,   e.g.   ones
      introduced during the presolve/postsolve, like discussed below.

      The conic solver sometimes has to insert slack variables, most often
      because of constraints referring the same right-hand  side  variable
      twice.   For   example,  for  sqrt(x0^2+x1^2)<=(y0-1)^0.5*(y0+1)^0.5
      it will automatically add t=y0+1 (a linear equality constraint)  and
      will rewrite the  constraint  as  sqrt(x0^2+x1^2)<=(y0-1)^0.5*t^0.5.
      The solver will have no  difficulty  solving  the  problem,  even if
      the optimal t is close to zero. However, the equality y0=t-1 will be
      satisfied only approximately, and even a tiny error will be  greatly
      magnified when evaluating the constraint violation.

      Thus,   one  has  to  be  very  careful  when  evaluating constraint
      violation errors  for  power  cones.  Having  high  error  does  not
      necessarily mean that the solver has failed.

  -- ALGLIB --
     Copyright 19.11.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddpowccprimitive(minqpstate &state, const ae_int_t range0, const ae_int_t range1, const ae_int_t axisidx, const double alpha, const bool applyorigin, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::minqpaddpowccprimitive(state.c_ptr(), range0, range1, axisidx, alpha, applyorigin, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
This function appends an axis-orthogonal power cone constraint of the form

         (          k-kp-1      )     k-1
    sqrt ( theta^2 + SUM y[i]^2 ) <=  MUL y[i]^alpha[i]
         (           i=0        )    i=k-kp

where

    y[i] = a[i]*x[idx[i]]+c[i], y[i]>=0

    0<alpha[i]<1, with SUM(alpha[i])<=1

    1<=kp<=k, with kp=k meaning that we have MUL(y[i]^alpha[i])>=|theta|

Alternatively, if ApplyOrigin parameter  is  True,  x[i]  is  replaced  by
x[i]-origin[i] (applies to all variables).

Unlike many other conic solvers, ALGLIB provides a flexible conic API that
allows alpha[] to sum up to any positive value less than  or  equal  to  1
(e.g. it is possible to formulate |x|<z^0.33 without  using  slack  vars).
Furthermore, ALGLIB allows conic constraints to overlap, i.e. it allows  a
variable to be a part of multiple conic constraints, or to appear multiple
times in the same constraint.

INPUT PARAMETERS:
    State   -   structure previously allocated  with  minqpcreate()  call.
    Idx     -   array[K] (or larger, only leading  K  elements  are  used)
                storing variable indexes. Indexes can be  unsorted  and/or
                non-distinct.
    A       -   array[K]  (or larger, only leading  K  elements are used),
                variable multipliers. Can contain zero values.
    C       -   array[K] (or larger, only leading  K  elements  are used),
                variable shifts.
    K       -   cone dimensionality, K>=1. It is possible to have K>N.
    Theta   -   additional constant term, can be zero
    AlphaV  -   array[KPow], power coefficients:
                * 0<AlphaV[i]<=1
                * 0<SUM(AlphaV[])<=1
    KPow    -   1<=KPow<=K, with KPow=K being correctly handled.

RESULT:
    constraint index in a conic constraints list, starting from 0

NOTE: power cone constraints are always  convex,  so having them preserves
      convexity of the QP problem.

NOTE: A starting point that is strictly feasible with respect to both  box
      and conic constraints greatly helps the solver to power up; however,
      it will work even without such a point,  albeit  at  somewhat  lower
      performance.

NOTE: a power cone with alpha<1 is sensitive to numerical errors near  the
      origin.  Suppose,  for  example,  that  we  have a constraint of the
      form |y|<=x^alpha with alpha=0.25 and that x is zero at the solution
      Furthermore, suppose that we perturbed x by as little  as  eps=1E-8.
      Because of alpha=0.25 the constraint is  perturbed  by  as  much  as
      eps^(1/4)=0.01!

      Such great sensitivity is  explained  by  the  non-differentiability
      of x^alpha for alpha<1 near x=0.  It  does  not  prevent  the  conic
      solver  from  converging  to   the precise solution, it merely makes
      constraints  extremely  sensitive  to  small   errors,   e.g.   ones
      introduced during the presolve/postsolve, like discussed below.

      The conic solver sometimes has to insert slack variables, most often
      because of constraints referring the same right-hand  side  variable
      twice.   For   example,  for  sqrt(x0^2+x1^2)<=(y0-1)^0.5*(y0+1)^0.5
      it will automatically add t=y0+1 (a linear equality constraint)  and
      will rewrite the  constraint  as  sqrt(x0^2+x1^2)<=(y0-1)^0.5*t^0.5.
      The solver will have no  difficulty  solving  the  problem,  even if
      the optimal t is close to zero. However, the equality y0=t-1 will be
      satisfied only approximately, and even a tiny error will be  greatly
      magnified when evaluating the constraint violation.

      Thus,   one  has  to  be  very  careful  when  evaluating constraint
      violation errors  for  power  cones.  Having  high  error  does  not
      necessarily mean that the solver has failed.

  -- ALGLIB --
     Copyright 09.09.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddpowccorthogonal(minqpstate &state, const integer_1d_array &idx, const real_1d_array &a, const real_1d_array &c, const ae_int_t k, const double theta, const real_1d_array &alphav, const ae_int_t kpow, const bool applyorigin, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_int_t result = alglib_impl::minqpaddpowccorthogonal(state.c_ptr(), idx.c_ptr(), a.c_ptr(), c.c_ptr(), k, theta, alphav.c_ptr(), kpow, applyorigin, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return ae_int_t(result);
}

/*************************************************************************
This function solves quadratic programming problem.

Prior to calling this function you should choose solver by means of one of
the following functions:

* minqpsetalgoquickqp()     - for QuickQP solver
* minqpsetalgodenseaul()    - for Dense-AUL-QP solver
* minqpsetalgodenseipm()    - for convex Dense-IPM-QP solver
* minqpsetalgosparseipm()   - for convex Sparse-IPM-QP solver
* minqpsetalgodensegenipm() - for convex/nonconvex Dense-IPM-QP solver with conic constraints
* minqpsetalgosparsegenipm()- for convex/nonconvex Sparse-IPM-QP solver with conic constraints

These functions also allow you to control stopping criteria of the solver.
If you did not set solver,  MinQP  subpackage  will  automatically  select
solver for your problem and will run it with default stopping criteria.

However, it is better to set explicitly solver and its stopping criteria.

INPUT PARAMETERS:
    State   -   algorithm state

You should use MinQPResults() function to access results after calls
to this function.

  -- ALGLIB --
     Copyright 2011-2024 by Bochkanov Sergey.
     Special thanks to Elvira Illarionova  for  important  suggestions  on
     the linearly constrained QP algorithm.
*************************************************************************/
void minqpoptimize(minqpstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpoptimize(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
QP solver results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution (on failure - the best point found
                so far).
    Rep     -   optimization report, contains:
                * completion code in Rep.TerminationType (positive  values
                  denote some kind of success, negative - failures)
                * Lagrange multipliers - for QP solvers which support them
                * other statistics
                See comments on minqpreport structure for more information

Following completion codes are returned in Rep.TerminationType:
* -9    failure of the automatic scale evaluation:  one  of  the  diagonal
        elements of the quadratic term is non-positive.  Specify  variable
        scales manually!
* -5    inappropriate solver was used:
        * QuickQP solver for problem with general linear constraints
        * QuickQP/DENSE-AUL/DENSE-IPM/SPARSE-IPM for a problem with
          quadratic/conic constraints
* -4    the function is unbounded from below even under constraints,
        no meaningful minimum can be found.
* -3    inconsistent constraints (or, maybe, feasible point is too hard to
        find).
* -2    IPM solver has difficulty finding primal/dual feasible point.
        It is likely that the problem is either infeasible or unbounded,
        but it is difficult to determine exact reason for termination.
        X contains best point found so far.
*  >0   success
*  7    stopping conditions are too stringent,
        further improvement is impossible,
        X contains best point found so far.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpresults(const minqpstate &state, real_1d_array &x, minqpreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpresults(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
QP results

Buffered implementation of MinQPResults() which uses pre-allocated  buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpresultsbuf(const minqpstate &state, real_1d_array &x, minqpreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpresultsbuf(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Export current QP problem stored in the solver into  QPXProblem  instance.
This  instance  can  be  serialized  into   ALGLIB-specific   format   and
unserialized from several widely acknowledged formats.

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    P       -   QPXProblem instance storing current objective and
                constraints.

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void minqpexport(minqpstate &state, qpxproblem &p, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpexport(state.c_ptr(), p.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Imports QP problem, as defined by QPXProblem instance, creating a QP solver
with objective/constraints/scales/origin set to that stored in the instance.

INPUT PARAMETERS:
    P       -   QPXProblem instance storing current objective and
                constraints.

OUTPUT PARAMETERS:
    State   -   newly created solver

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void minqpimport(qpxproblem &p, minqpstate &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minqpimport(p.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************
This object stores nonlinear optimizer state.
You should use functions provided by MinQP subpackage to work with this
object
*************************************************************************/
_minqpstate_owner::_minqpstate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minqpstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minqpstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minqpstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minqpstate));
    alglib_impl::_minqpstate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minqpstate_owner::_minqpstate_owner(alglib_impl::minqpstate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minqpstate_owner::_minqpstate_owner(const _minqpstate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minqpstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minqpstate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minqpstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minqpstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minqpstate));
    alglib_impl::_minqpstate_init_copy(p_struct, const_cast<alglib_impl::minqpstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minqpstate_owner& _minqpstate_owner::operator=(const _minqpstate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minqpstate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minqpstate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minqpstate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minqpstate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minqpstate));
    alglib_impl::_minqpstate_init_copy(p_struct, const_cast<alglib_impl::minqpstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minqpstate_owner::~_minqpstate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minqpstate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minqpstate* _minqpstate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minqpstate* _minqpstate_owner::c_ptr() const
{
    return p_struct;
}
minqpstate::minqpstate() : _minqpstate_owner() 
{
}

minqpstate::minqpstate(alglib_impl::minqpstate *attach_to):_minqpstate_owner(attach_to) 
{
}

minqpstate::minqpstate(const minqpstate &rhs):_minqpstate_owner(rhs) 
{
}

minqpstate& minqpstate::operator=(const minqpstate &rhs)
{
    if( this==&rhs )
        return *this;
    _minqpstate_owner::operator=(rhs);
    return *this;
}

minqpstate::~minqpstate()
{
}




/*************************************************************************
This structure stores optimization report:
* InnerIterationsCount      number of inner iterations
* OuterIterationsCount      number of outer iterations
* NCholesky                 number of Cholesky decomposition
* NMV                       number of matrix-vector products
                            (only products calculated as part of iterative
                            process are counted)
* TerminationType           completion code (see below)
* F                         for positive terminationtype stores quadratic
                            model value at the solution
* LagBC                     Lagrange multipliers for box constraints,
                            array[N]
* LagLC                     Lagrange multipliers for linear constraints,
                            array[MSparse+MDense]
* LagQC                     Lagrange multipliers for quadratic constraints

=== COMPLETION CODES =====================================================

Completion codes:
* -9    failure of the automatic scale evaluation:  one  of  the  diagonal
        elements of the quadratic term is non-positive.  Specify  variable
        scales manually!
* -5    inappropriate solver was used:
        * QuickQP solver for a problem with general linear constraints (dense/sparse)
        * QuickQP/DENSE-AUL/DENSE-IPM/SPARSE-IPM for a problem with
          quadratic/conic constraints
        * ECQP for a problem with inequality or nonlinear equality constraints
* -4    the problem is highly likely to be unbounded; either one of the solvers
        found an unconstrained direction of negative curvature, or objective
        simply decreased for too much (more than 1E50).
* -3    inconsistent constraints (or, maybe, feasible point is
        too hard to find). If you are sure that constraints are feasible,
        try to restart optimizer with better initial approximation.
* -2    IPM solver has difficulty finding primal/dual feasible point.
        It is likely that the problem is either infeasible or unbounded,
        but it is difficult to determine exact reason for termination.
        X contains best point found so far.
*  1..4 successful completion
*  5    MaxIts steps was taken
*  7    stopping conditions are too stringent,
        further improvement is impossible,
        X contains best point found so far.

=== LAGRANGE MULTIPLIERS =================================================

Some  optimizers  report  values of  Lagrange  multipliers  on  successful
completion (positive completion code):
* dense and sparse IPM/GENIPM return very precise Lagrange  multipliers as
  determined during solution process.
* DENSE-AUL-QP returns approximate Lagrange multipliers  (which  are  very
  close to "true"  Lagrange  multipliers  except  for  overconstrained  or
  degenerate problems)

Three arrays of multipliers are returned:
* LagBC is array[N] which is loaded with multipliers from box constraints;
  LagBC[i]>0 means that I-th constraint is at the  upper bound, LagBC[I]<0
  means that I-th constraint is at the lower bound, LagBC[I]=0 means  that
  I-th box constraint is inactive.
* LagLC is array[MSparse+MDense] which is  loaded  with  multipliers  from
  general  linear  constraints  (former  MSparse  elements  corresponds to
  sparse part of the constraint matrix, latter MDense are  for  the  dense
  constraints, as was specified by user).
  LagLC[i]>0 means that I-th constraint at  the  upper  bound,  LagLC[i]<0
  means that I-th constraint is at the lower bound, LagLC[i]=0 means  that
  I-th linear constraint is inactive.
* LagQC is array[MQC]  which stores multipliers for quadratic constraints.
  LagQC[i]>0 means that I-th constraint at  the  upper  bound,  LagQC[i]<0
  means that I-th constraint is at the lower bound, LagQC[i]=0 means  that
  I-th linear constraint is inactive.

On failure (or when optimizer does not support Lagrange multipliers) these
arrays are zero-filled.

It is expected that at solution the dual feasibility condition holds:

    C+H*(Xs-X0) + SUM(Ei*LagBC[i],i=0..n-1) + SUM(Ai*LagLC[i],i=0..m-1) + ... ~ 0

where
* C is a linear term
* H is a quadratic term
* Xs is a solution, and X0 is an origin term (zero by default)
* Ei is a vector with 1.0 at position I and 0 in other positions
* Ai is an I-th row of linear constraint matrix

NOTE: methods  from  IPM  family  may  also  return  meaningful   Lagrange
      multipliers  on  completion   with   code   -2   (infeasibility   or
      unboundedness  detected).
*************************************************************************/
_minqpreport_owner::_minqpreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minqpreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minqpreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minqpreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minqpreport));
    alglib_impl::_minqpreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minqpreport_owner::_minqpreport_owner(alglib_impl::minqpreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minqpreport_owner::_minqpreport_owner(const _minqpreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minqpreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minqpreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minqpreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minqpreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minqpreport));
    alglib_impl::_minqpreport_init_copy(p_struct, const_cast<alglib_impl::minqpreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minqpreport_owner& _minqpreport_owner::operator=(const _minqpreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minqpreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minqpreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minqpreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minqpreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minqpreport));
    alglib_impl::_minqpreport_init_copy(p_struct, const_cast<alglib_impl::minqpreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minqpreport_owner::~_minqpreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minqpreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minqpreport* _minqpreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minqpreport* _minqpreport_owner::c_ptr() const
{
    return p_struct;
}
minqpreport::minqpreport() : _minqpreport_owner() ,inneriterationscount(p_struct->inneriterationscount),outeriterationscount(p_struct->outeriterationscount),nmv(p_struct->nmv),ncholesky(p_struct->ncholesky),terminationtype(p_struct->terminationtype),f(p_struct->f),lagbc(&p_struct->lagbc),laglc(&p_struct->laglc),lagqc(&p_struct->lagqc)
{
}

minqpreport::minqpreport(alglib_impl::minqpreport *attach_to):_minqpreport_owner(attach_to) ,inneriterationscount(p_struct->inneriterationscount),outeriterationscount(p_struct->outeriterationscount),nmv(p_struct->nmv),ncholesky(p_struct->ncholesky),terminationtype(p_struct->terminationtype),f(p_struct->f),lagbc(&p_struct->lagbc),laglc(&p_struct->laglc),lagqc(&p_struct->lagqc)
{
}

minqpreport::minqpreport(const minqpreport &rhs):_minqpreport_owner(rhs) ,inneriterationscount(p_struct->inneriterationscount),outeriterationscount(p_struct->outeriterationscount),nmv(p_struct->nmv),ncholesky(p_struct->ncholesky),terminationtype(p_struct->terminationtype),f(p_struct->f),lagbc(&p_struct->lagbc),laglc(&p_struct->laglc),lagqc(&p_struct->lagqc)
{
}

minqpreport& minqpreport::operator=(const minqpreport &rhs)
{
    if( this==&rhs )
        return *this;
    _minqpreport_owner::operator=(rhs);
    return *this;
}

minqpreport::~minqpreport()
{
}
#endif

#if defined(AE_COMPILE_NLCFSQP) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_MINLM) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
                IMPROVED LEVENBERG-MARQUARDT METHOD FOR
                 NON-LINEAR LEAST SQUARES OPTIMIZATION

DESCRIPTION:
This function is used to find minimum of function which is represented  as
sum of squares:
    F(x) = f[0]^2(x[0],...,x[n-1]) + ... + f[m-1]^2(x[0],...,x[n-1])
using value of function vector f[] and Jacobian of f[].


REQUIREMENTS:
This algorithm will request following information during its operation:

* function vector f[] at given point X
* function vector f[] and Jacobian of f[] (simultaneously) at given point

There are several overloaded versions of  MinLMOptimize()  function  which
correspond  to  different LM-like optimization algorithms provided by this
unit. You should choose version which accepts fvec()  and jac() callbacks.
First  one  is used to calculate f[] at given point, second one calculates
f[] and Jacobian df[i]/dx[j].

You can try to initialize MinLMState structure with VJ  function and  then
use incorrect version  of  MinLMOptimize()  (for  example,  version  which
works  with  general  form function and does not provide Jacobian), but it
will  lead  to  exception  being  thrown  after first attempt to calculate
Jacobian.


USAGE:
1. User initializes algorithm state with MinLMCreateVJ() call
2. User tunes solver parameters with MinLMSetCond(),  MinLMSetStpMax() and
   other functions
3. User calls MinLMOptimize() function which  takes algorithm  state   and
   callback functions.
4. User calls MinLMResults() to get solution
5. Optionally, user may call MinLMRestartFrom() to solve  another  problem
   with same N/M but another starting point and/or another function.
   MinLMRestartFrom() allows to reuse already initialized structure.


INPUT PARAMETERS:
    N       -   dimension, N>1
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    M       -   number of functions f[i]
    X       -   initial solution, array[0..N-1]

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTES:
1. you may tune stopping conditions with MinLMSetCond() function
2. if target function contains exp() or other fast growing functions,  and
   optimization algorithm makes too large steps which leads  to  overflow,
   use MinLMSetStpMax() function to bound algorithm's steps.

  -- ALGLIB --
     Copyright 30.03.2009 by Bochkanov Sergey
*************************************************************************/
void minlmcreatevj(const ae_int_t n, const ae_int_t m, const real_1d_array &x, minlmstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmcreatevj(n, m, x.c_ptr(), state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
                IMPROVED LEVENBERG-MARQUARDT METHOD FOR
                 NON-LINEAR LEAST SQUARES OPTIMIZATION

DESCRIPTION:
This function is used to find minimum of function which is represented  as
sum of squares:
    F(x) = f[0]^2(x[0],...,x[n-1]) + ... + f[m-1]^2(x[0],...,x[n-1])
using value of function vector f[] and Jacobian of f[].


REQUIREMENTS:
This algorithm will request following information during its operation:

* function vector f[] at given point X
* function vector f[] and Jacobian of f[] (simultaneously) at given point

There are several overloaded versions of  MinLMOptimize()  function  which
correspond  to  different LM-like optimization algorithms provided by this
unit. You should choose version which accepts fvec()  and jac() callbacks.
First  one  is used to calculate f[] at given point, second one calculates
f[] and Jacobian df[i]/dx[j].

You can try to initialize MinLMState structure with VJ  function and  then
use incorrect version  of  MinLMOptimize()  (for  example,  version  which
works  with  general  form function and does not provide Jacobian), but it
will  lead  to  exception  being  thrown  after first attempt to calculate
Jacobian.


USAGE:
1. User initializes algorithm state with MinLMCreateVJ() call
2. User tunes solver parameters with MinLMSetCond(),  MinLMSetStpMax() and
   other functions
3. User calls MinLMOptimize() function which  takes algorithm  state   and
   callback functions.
4. User calls MinLMResults() to get solution
5. Optionally, user may call MinLMRestartFrom() to solve  another  problem
   with same N/M but another starting point and/or another function.
   MinLMRestartFrom() allows to reuse already initialized structure.


INPUT PARAMETERS:
    N       -   dimension, N>1
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    M       -   number of functions f[i]
    X       -   initial solution, array[0..N-1]

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTES:
1. you may tune stopping conditions with MinLMSetCond() function
2. if target function contains exp() or other fast growing functions,  and
   optimization algorithm makes too large steps which leads  to  overflow,
   use MinLMSetStpMax() function to bound algorithm's steps.

  -- ALGLIB --
     Copyright 30.03.2009 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minlmcreatevj(const ae_int_t m, const real_1d_array &x, minlmstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmcreatevj(n, m, x.c_ptr(), state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
                IMPROVED LEVENBERG-MARQUARDT METHOD FOR
                 NON-LINEAR LEAST SQUARES OPTIMIZATION

DESCRIPTION:
This function is used to find minimum of function which is represented  as
sum of squares:
    F(x) = f[0]^2(x[0],...,x[n-1]) + ... + f[m-1]^2(x[0],...,x[n-1])
using value of function vector f[] only. Finite differences  are  used  to
calculate Jacobian.


REQUIREMENTS:
This algorithm will request following information during its operation:
* function vector f[] at given point X

There are several overloaded versions of  MinLMOptimize()  function  which
correspond  to  different LM-like optimization algorithms provided by this
unit. You should choose version which accepts fvec() callback.

You can try to initialize MinLMState structure with VJ  function and  then
use incorrect version  of  MinLMOptimize()  (for  example,  version  which
works with general form function and does not accept function vector), but
it will  lead  to  exception being thrown after first attempt to calculate
Jacobian.


USAGE:
1. User initializes algorithm state with MinLMCreateV() call
2. User tunes solver parameters with MinLMSetCond(),  MinLMSetStpMax() and
   other functions
3. User calls MinLMOptimize() function which  takes algorithm  state   and
   callback functions.
4. User calls MinLMResults() to get solution
5. Optionally, user may call MinLMRestartFrom() to solve  another  problem
   with same N/M but another starting point and/or another function.
   MinLMRestartFrom() allows to reuse already initialized structure.


INPUT PARAMETERS:
    N       -   dimension, N>1
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    M       -   number of functions f[i]
    X       -   initial solution, array[0..N-1]
    DiffStep-   differentiation step, >0. By  default,  symmetric  3-point
                formula which provides good accuracy is used.  It  can  be
                changed to a faster but  less  precise  2-point  one  with
                minlmsetnumdiff() function.

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

See also MinLMIteration, MinLMResults.

NOTES:
1. you may tune stopping conditions with MinLMSetCond() function
2. if target function contains exp() or other fast growing functions,  and
   optimization algorithm makes too large steps which leads  to  overflow,
   use MinLMSetStpMax() function to bound algorithm's steps.

  -- ALGLIB --
     Copyright 30.03.2009 by Bochkanov Sergey
*************************************************************************/
void minlmcreatev(const ae_int_t n, const ae_int_t m, const real_1d_array &x, const double diffstep, minlmstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmcreatev(n, m, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
                IMPROVED LEVENBERG-MARQUARDT METHOD FOR
                 NON-LINEAR LEAST SQUARES OPTIMIZATION

DESCRIPTION:
This function is used to find minimum of function which is represented  as
sum of squares:
    F(x) = f[0]^2(x[0],...,x[n-1]) + ... + f[m-1]^2(x[0],...,x[n-1])
using value of function vector f[] only. Finite differences  are  used  to
calculate Jacobian.


REQUIREMENTS:
This algorithm will request following information during its operation:
* function vector f[] at given point X

There are several overloaded versions of  MinLMOptimize()  function  which
correspond  to  different LM-like optimization algorithms provided by this
unit. You should choose version which accepts fvec() callback.

You can try to initialize MinLMState structure with VJ  function and  then
use incorrect version  of  MinLMOptimize()  (for  example,  version  which
works with general form function and does not accept function vector), but
it will  lead  to  exception being thrown after first attempt to calculate
Jacobian.


USAGE:
1. User initializes algorithm state with MinLMCreateV() call
2. User tunes solver parameters with MinLMSetCond(),  MinLMSetStpMax() and
   other functions
3. User calls MinLMOptimize() function which  takes algorithm  state   and
   callback functions.
4. User calls MinLMResults() to get solution
5. Optionally, user may call MinLMRestartFrom() to solve  another  problem
   with same N/M but another starting point and/or another function.
   MinLMRestartFrom() allows to reuse already initialized structure.


INPUT PARAMETERS:
    N       -   dimension, N>1
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    M       -   number of functions f[i]
    X       -   initial solution, array[0..N-1]
    DiffStep-   differentiation step, >0. By  default,  symmetric  3-point
                formula which provides good accuracy is used.  It  can  be
                changed to a faster but  less  precise  2-point  one  with
                minlmsetnumdiff() function.

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

See also MinLMIteration, MinLMResults.

NOTES:
1. you may tune stopping conditions with MinLMSetCond() function
2. if target function contains exp() or other fast growing functions,  and
   optimization algorithm makes too large steps which leads  to  overflow,
   use MinLMSetStpMax() function to bound algorithm's steps.

  -- ALGLIB --
     Copyright 30.03.2009 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minlmcreatev(const ae_int_t m, const real_1d_array &x, const double diffstep, minlmstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmcreatev(n, m, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets stopping conditions for Levenberg-Marquardt optimization
algorithm.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsX    -   >=0
                The subroutine finishes its work if  on  k+1-th  iteration
                the condition |v|<=EpsX is fulfilled, where:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - ste pvector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinLMSetScale()
                Recommended values: 1E-9 ... 1E-12.
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations   is    unlimited.   Only   Levenberg-Marquardt
                iterations  are  counted  (L-BFGS/CG  iterations  are  NOT
                counted because their cost is very low compared to that of
                LM).

Passing  EpsX=0  and  MaxIts=0  (simultaneously)  will  lead  to automatic
stopping criterion selection (small EpsX).

NOTE: it is not recommended to set large EpsX (say, 0.001). Because LM  is
      a second-order method, it performs very precise steps anyway.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlmsetcond(minlmstate &state, const double epsx, const ae_int_t maxits, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmsetcond(state.c_ptr(), epsx, maxits, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinLMOptimize(). Both Levenberg-Marquardt and internal  L-BFGS
iterations are reported.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlmsetxrep(minlmstate &state, const bool needxrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmsetxrep(state.c_ptr(), needxrep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets maximum step length

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    StpMax  -   maximum step length, >=0. Set StpMax to 0.0,  if you don't
                want to limit step length.

Use this subroutine when you optimize target function which contains exp()
or  other  fast  growing  functions,  and optimization algorithm makes too
large  steps  which  leads  to overflow. This function allows us to reject
steps  that  are  too  large  (and  therefore  expose  us  to the possible
overflow) without actually calculating function value at the x+stp*d.

NOTE: non-zero StpMax leads to moderate  performance  degradation  because
intermediate  step  of  preconditioned L-BFGS optimization is incompatible
with limits on step size.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlmsetstpmax(minlmstate &state, const double stpmax, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmsetstpmax(state.c_ptr(), stpmax, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets scaling coefficients for LM optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Generally, scale is NOT considered to be a form of preconditioner.  But LM
optimizer is unique in that it uses scaling matrix both  in  the  stopping
condition tests and as Marquardt damping factor.

Proper scaling is very important for the algorithm performance. It is less
important for the quality of results, but still has some influence (it  is
easier  to  converge  when  variables  are  properly  scaled, so premature
stopping is possible when very badly scalled variables are  combined  with
relaxed stopping conditions).

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minlmsetscale(minlmstate &state, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmsetscale(state.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets boundary constraints for LM optimizer

Boundary constraints are inactive by default (after initial creation).
They are preserved until explicitly turned off with another SetBC() call.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF (latter is recommended because
                it will allow solver to use better algorithm).
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF (latter is recommended because
                it will allow solver to use better algorithm).

NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

NOTE 2: this solver has following useful properties:
* bound constraints are always satisfied exactly
* function is evaluated only INSIDE area specified by bound constraints
  or at its boundary

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minlmsetbc(minlmstate &state, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmsetbc(state.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets general linear constraints for LM optimizer

Linear constraints are inactive by default (after initial creation).  They
are preserved until explicitly turned off with another minlmsetlc() call.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

IMPORTANT: if you have linear constraints, it is strongly  recommended  to
           set scale of variables with minlmsetscale(). QP solver which is
           used to calculate linearly constrained steps heavily relies  on
           good scaling of input problems.

IMPORTANT: solvers created with minlmcreatefgh()  do  not  support  linear
           constraints.

NOTE: linear  (non-bound)  constraints are satisfied only approximately  -
      there  always  exists some violation due  to  numerical  errors  and
      algorithmic limitations.

NOTE: general linear constraints  add  significant  overhead  to  solution
      process. Although solver performs roughly same amount of  iterations
      (when compared  with  similar  box-only  constrained  problem), each
      iteration   now    involves  solution  of  linearly  constrained  QP
      subproblem, which requires ~3-5 times more Cholesky  decompositions.
      Thus, if you can reformulate your problem in such way  this  it  has
      only box constraints, it may be beneficial to do so.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minlmsetlc(minlmstate &state, const real_2d_array &c, const integer_1d_array &ct, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmsetlc(state.c_ptr(), c.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets general linear constraints for LM optimizer

Linear constraints are inactive by default (after initial creation).  They
are preserved until explicitly turned off with another minlmsetlc() call.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

IMPORTANT: if you have linear constraints, it is strongly  recommended  to
           set scale of variables with minlmsetscale(). QP solver which is
           used to calculate linearly constrained steps heavily relies  on
           good scaling of input problems.

IMPORTANT: solvers created with minlmcreatefgh()  do  not  support  linear
           constraints.

NOTE: linear  (non-bound)  constraints are satisfied only approximately  -
      there  always  exists some violation due  to  numerical  errors  and
      algorithmic limitations.

NOTE: general linear constraints  add  significant  overhead  to  solution
      process. Although solver performs roughly same amount of  iterations
      (when compared  with  similar  box-only  constrained  problem), each
      iteration   now    involves  solution  of  linearly  constrained  QP
      subproblem, which requires ~3-5 times more Cholesky  decompositions.
      Thus, if you can reformulate your problem in such way  this  it  has
      only box constraints, it may be beneficial to do so.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minlmsetlc(minlmstate &state, const real_2d_array &c, const integer_1d_array &ct, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t k;
    if( (c.rows()!=ct.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'minlmsetlc': looks like one of arguments has wrong size");
    k = c.rows();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmsetlc(state.c_ptr(), c.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function is used to change acceleration settings

You can choose between three acceleration strategies:
* AccType=0, no acceleration.
* AccType=1, secant updates are used to update quadratic model after  each
  iteration. After fixed number of iterations (or after  model  breakdown)
  we  recalculate  quadratic  model  using  analytic  Jacobian  or  finite
  differences. Number of secant-based iterations depends  on  optimization
  settings: about 3 iterations - when we have analytic Jacobian, up to 2*N
  iterations - when we use finite differences to calculate Jacobian.

AccType=1 is recommended when Jacobian  calculation  cost is prohibitively
high (several Mx1 function vector calculations  followed  by  several  NxN
Cholesky factorizations are faster than calculation of one M*N  Jacobian).
It should also be used when we have no Jacobian, because finite difference
approximation takes too much time to compute.

Table below list  optimization  protocols  (XYZ  protocol  corresponds  to
MinLMCreateXYZ) and acceleration types they support (and use by  default).

ACCELERATION TYPES SUPPORTED BY OPTIMIZATION PROTOCOLS:

protocol    0   1   comment
V           +   +
VJ          +   +
FGH         +

DEFAULT VALUES:

protocol    0   1   comment
V               x   without acceleration it is so slooooooooow
VJ          x
FGH         x

NOTE: this  function should be called before optimization. Attempt to call
it during algorithm iterations may result in unexpected behavior.

NOTE: attempt to call this function with unsupported protocol/acceleration
combination will result in exception being thrown.

  -- ALGLIB --
     Copyright 14.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlmsetacctype(minlmstate &state, const ae_int_t acctype, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmsetacctype(state.c_ptr(), acctype, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function is used  to  activate/deactivate  nonmonotonic  steps.  Such
steps may improve  convergence  on  noisy  problems  or  ones  with  minor
smoothness defects.

In its standard mode, LM solver compares value at the trial   point   f[1]
with the value at the current point f[0]. Only steps that decrease f() are
accepted.

When the nonmonotonic mode is activated, f[1]  is  compared  with  maximum
over several previous  locations:  max(f[0],f[-1],...,f[-CNT]).  We  still
accept only steps that decrease  f(),  however  our  reference  value  has
changed. The net results is that f[1]>f[0] are now allowed.

Nonmonotonic steps can help to handle minor defects in the objective (e.g.
small  noise,  discontinuous  jumps  or  nonsmoothness).  However,  it  is
important  that  the  overall  shape  of  the  problem  is  still  smooth.
It  may  also  help  to  minimize  perfectly  smooth  targets with complex
geometries by allowing to jump through curved valleys.

However, sometimes nonmonotonic steps degrade convergence by  allowing  an
optimizer to wander too far away from the solution, so this feature should
be used only after careful testing.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    Cnt     -   nonmonotonic memory length, Cnt>=0:
                * 0 for traditional monotonic steps
                * 2..3 is recommended for the nonmonotonic optimization

  -- ALGLIB --
     Copyright 07.04.2024 by Bochkanov Sergey
*************************************************************************/
void minlmsetnonmonotonicsteps(minlmstate &state, const ae_int_t cnt, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmsetnonmonotonicsteps(state.c_ptr(), cnt, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets specific finite  difference  formula  to  be  used  for
numerical differentiation.

It works only for optimizers created  with  minlmcreatev()  function;   in
other cases it has no effect.

INPUT PARAMETERS:
    State       -   structure previously allocated with MinLMCreateV() call.
    FormulaType -   formula type:
                    * 3 for a 3-point formula, which is also  known  as  a
                      symmetric difference quotient (the formula  actually
                      uses only two function values per variable:  at  x+h
                      and x-h). A good choice for medium-accuracy  setups,
                      a default option.
                    * 2 for a forward (or backward, depending  on variable
                      bounds)  finite   difference  (f(x+h)-f(x))/h.  This
                      formula has the lowest accuracy. However, it  is  4x
                      faster than the 5-point formula and 2x  faster  than
                      the 3-point one because, in addition to the  central
                      value f(x), it needs only  one  additional  function
                      evaluation per variable.


  -- ALGLIB --
     Copyright 03.12.2024 by Bochkanov Sergey
*************************************************************************/
void minlmsetnumdiff(minlmstate &state, const ae_int_t formulatype, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmsetnumdiff(state.c_ptr(), formulatype, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function provides reverse communication interface
Reverse communication interface is not documented or recommended to use.
See below for functions which provide better documented API
*************************************************************************/
bool minlmiteration(minlmstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::minlmiteration(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}


void minlmoptimize(minlmstate &state,
    void (*fvec)(const real_1d_array &x, real_1d_array &fi, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    alglib_impl::minlmstate &optimizer = *(state.c_ptr());
    alglib_impl::rcommv2_request request(
        optimizer.requesttype,
        optimizer.querysize, optimizer.queryfuncs, optimizer.queryvars, optimizer.querydim, optimizer.queryformulasize,
        optimizer.querydata.ptr.p_double, optimizer.replyfi.ptr.p_double, optimizer.replydj.ptr.p_double, optimizer.replysj, ptr,
        "minlm");
    alglib_impl::rcommv2_callbacks callbacks;
    alglib_impl::rcommv2_buffers buffers(&state.c_ptr()->tmpx1, &state.c_ptr()->tmpc1, &state.c_ptr()->tmpf1, &state.c_ptr()->tmpg1, &state.c_ptr()->tmpj1, &state.c_ptr()->tmps1);
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(fvec!=NULL, "ALGLIB: error in 'minlmoptimize()' (fvec is NULL)", &_alglib_env_state);
callbacks.fvec = fvec;

    alglib_impl::minlmsetprotocolv2(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minlmiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
        if( optimizer.requesttype==3 )
        {
            const ae_int_t njobs = request.size*request.vars+request.size;
            for(alglib_impl::ae_int_t job_idx=0; job_idx<njobs; job_idx++)
                alglib_impl::process_v2request_3phase0(request, job_idx, callbacks, buffers);
            alglib_impl::process_v2request_3phase1(request);
            request.request = 0;
            continue;
        }
        if( optimizer.requesttype==5 )
        {
            const ae_int_t njobs = request.size*request.vars+request.size;
            for(alglib_impl::ae_int_t job_idx=0; job_idx<njobs; job_idx++)
                alglib_impl::process_v2request_5phase0(request, job_idx, callbacks, buffers);
            alglib_impl::process_v2request_5phase1(request);
            request.request = 0;
            continue;
        }if( optimizer.requesttype==4 )
        { 
            for(alglib_impl::ae_int_t qidx=0; qidx<optimizer.querysize; qidx++)
                alglib_impl::process_v2request_4(request, qidx, callbacks, buffers);
            optimizer.requesttype = 0;
            continue;
        }
        if( optimizer.requesttype==-1 )
        {
            memmove(&buffers.tmpX[0], optimizer.reportx.ptr.p_double, optimizer.queryvars*sizeof(double));
            if( rep!=NULL )
                rep(buffers.tmpX, optimizer.reportf, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
lbl_no_callback:
    alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minlmoptimize' (some derivatives were not provided?)", &_alglib_env_state);
}


void minlmoptimize(minlmstate &state,
    void (*fvec)(const real_1d_array &x, real_1d_array &fi, void *ptr),
    void  (*jac)(const real_1d_array &x, real_1d_array &fi, real_2d_array &jac, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    alglib_impl::minlmstate &optimizer = *(state.c_ptr());
    alglib_impl::rcommv2_request request(
        optimizer.requesttype,
        optimizer.querysize, optimizer.queryfuncs, optimizer.queryvars, optimizer.querydim, optimizer.queryformulasize,
        optimizer.querydata.ptr.p_double, optimizer.replyfi.ptr.p_double, optimizer.replydj.ptr.p_double, optimizer.replysj, ptr,
        "minlm");
    alglib_impl::rcommv2_callbacks callbacks;
    alglib_impl::rcommv2_buffers buffers(&state.c_ptr()->tmpx1, &state.c_ptr()->tmpc1, &state.c_ptr()->tmpf1, &state.c_ptr()->tmpg1, &state.c_ptr()->tmpj1, &state.c_ptr()->tmps1);
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(fvec!=NULL, "ALGLIB: error in 'minlmoptimize()' (fvec is NULL)", &_alglib_env_state);
    alglib_impl::ae_assert(jac!=NULL,  "ALGLIB: error in 'minlmoptimize()' (jac is NULL)", &_alglib_env_state);
callbacks.fvec = fvec;
callbacks.jac = jac;

    alglib_impl::minlmsetprotocolv2(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minlmiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
        if( optimizer.requesttype==2 )
        { 
            for(alglib_impl::ae_int_t qidx=0; qidx<optimizer.querysize; qidx++)
                alglib_impl::process_v2request_2(request, qidx, callbacks, buffers);
            optimizer.requesttype = 0;
            continue;
        }if( optimizer.requesttype==4 )
        { 
            for(alglib_impl::ae_int_t qidx=0; qidx<optimizer.querysize; qidx++)
                alglib_impl::process_v2request_4(request, qidx, callbacks, buffers);
            optimizer.requesttype = 0;
            continue;
        }
        if( optimizer.requesttype==-1 )
        {
            memmove(&buffers.tmpX[0], optimizer.reportx.ptr.p_double, optimizer.queryvars*sizeof(double));
            if( rep!=NULL )
                rep(buffers.tmpX, optimizer.reportf, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
lbl_no_callback:
    alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minlmoptimize' (some derivatives were not provided?)", &_alglib_env_state);
}



/*************************************************************************
This  function  activates/deactivates verification  of  the  user-supplied
analytic Jacobian.

Upon  activation  of  this  option  OptGuard  integrity  checker  performs
numerical differentiation of your target function vector  at  the  initial
point (note: future versions may also perform check  at  the final  point)
and compares numerical Jacobian with analytic one provided by you.

If difference is too large, an error flag is set and optimization  session
continues. After optimization session is over, you can retrieve the report
which stores  both  Jacobians,  and  specific  components  highlighted  as
suspicious by the OptGuard.

The OptGuard report can be retrieved with minlmoptguardresults().

IMPORTANT: gradient check is a high-overhead option which  will  cost  you
           about 3*N additional function evaluations. In many cases it may
           cost as much as the rest of the optimization session.

           YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO
           CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY.

NOTE: unlike previous incarnation of the gradient checking code,  OptGuard
      does NOT interrupt optimization even if it discovers bad gradient.

INPUT PARAMETERS:
    State       -   structure used to store algorithm state
    TestStep    -   verification step used for numerical differentiation:
                    * TestStep=0 turns verification off
                    * TestStep>0 activates verification
                    You should carefully choose TestStep. Value  which  is
                    too large (so large that  function  behavior  is  non-
                    cubic at this scale) will lead  to  false  alarms. Too
                    short step will result in rounding  errors  dominating
                    numerical derivative.

                    You may use different step for different parameters by
                    means of setting scale with minlmsetscale().

=== EXPLANATION ==========================================================

In order to verify gradient algorithm performs following steps:
  * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i],
    where X[i] is i-th component of the initial point and S[i] is a  scale
    of i-th parameter
  * F(X) is evaluated at these trial points
  * we perform one more evaluation in the middle point of the interval
  * we  build  cubic  model using function values and derivatives at trial
    points and we compare its prediction with actual value in  the  middle
    point

  -- ALGLIB --
     Copyright 15.06.2014 by Bochkanov Sergey
*************************************************************************/
void minlmoptguardgradient(minlmstate &state, const double teststep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmoptguardgradient(state.c_ptr(), teststep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Results of OptGuard integrity check, should be called  after  optimization
session is over.

OptGuard checks analytic Jacobian  against  reference  value  obtained  by
numerical differentiation with user-specified step.

NOTE: other optimizers perform additional OptGuard checks for things  like
      C0/C1-continuity violations. However, LM optimizer  can  check  only
      for incorrect Jacobian.

      The reason is that unlike line search methods LM optimizer does  not
      perform extensive evaluations along the line. Thus, we simply do not
      have enough data to catch C0/C1-violations.

This check is activated with  minlmoptguardgradient() function.

Following flags are set when these errors are suspected:
* rep.badgradsuspected, and additionally:
  * rep.badgradfidx for specific function (Jacobian row) suspected
  * rep.badgradvidx for specific variable (Jacobian column) suspected
  * rep.badgradxbase, a point where gradient/Jacobian is tested
  * rep.badgraduser, user-provided gradient/Jacobian
  * rep.badgradnum, reference gradient/Jacobian obtained via numerical
    differentiation

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    rep     -   OptGuard report

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minlmoptguardresults(minlmstate &state, optguardreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmoptguardresults(state.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Levenberg-Marquardt algorithm results

NOTE: if you activated OptGuard integrity checking functionality and  want
      to get OptGuard report,  it  can  be  retrieved  with  the  help  of
      minlmoptguardresults() function.

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization  report;  includes  termination   codes   and
                additional information. Termination codes are listed below,
                see comments for this structure for more info.

                Termination code is stored in rep.terminationtype field:
                * -8    optimizer detected NAN/INF values either in the
                        function itself, or in its Jacobian
                * -3    constraints are inconsistent
                *  2    relative step is no more than EpsX.
                *  5    MaxIts steps was taken
                *  7    stopping conditions are too stringent,
                        further improvement is impossible
                *  8    terminated by user who called minlmrequesttermination().
                        X contains point which was "current accepted" when
                        termination request was submitted.

                rep.f contains SUM(f[i]^2) at X

  -- ALGLIB --
     Copyright 10.03.2009 by Bochkanov Sergey
*************************************************************************/
void minlmresults(const minlmstate &state, real_1d_array &x, minlmreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmresults(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Levenberg-Marquardt algorithm results

Buffered implementation of MinLMResults(), which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 10.03.2009 by Bochkanov Sergey
*************************************************************************/
void minlmresultsbuf(const minlmstate &state, real_1d_array &x, minlmreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmresultsbuf(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  subroutine  restarts  LM  algorithm from new point. All optimization
parameters are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure used for reverse communication previously
                allocated with MinLMCreateXXX call.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
void minlmrestartfrom(minlmstate &state, const real_1d_array &x, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmrestartfrom(state.c_ptr(), x.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.

NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void minlmrequesttermination(minlmstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlmrequesttermination(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************
Levenberg-Marquardt optimizer.

This structure should be created using one of the MinLMCreate???()
functions. You should not access its fields directly; use ALGLIB functions
to work with it.
*************************************************************************/
_minlmstate_owner::_minlmstate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minlmstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minlmstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minlmstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minlmstate));
    alglib_impl::_minlmstate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minlmstate_owner::_minlmstate_owner(alglib_impl::minlmstate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minlmstate_owner::_minlmstate_owner(const _minlmstate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minlmstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minlmstate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minlmstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minlmstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minlmstate));
    alglib_impl::_minlmstate_init_copy(p_struct, const_cast<alglib_impl::minlmstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minlmstate_owner& _minlmstate_owner::operator=(const _minlmstate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minlmstate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minlmstate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minlmstate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minlmstate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minlmstate));
    alglib_impl::_minlmstate_init_copy(p_struct, const_cast<alglib_impl::minlmstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minlmstate_owner::~_minlmstate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minlmstate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minlmstate* _minlmstate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minlmstate* _minlmstate_owner::c_ptr() const
{
    return p_struct;
}
minlmstate::minlmstate() : _minlmstate_owner() 
{
}

minlmstate::minlmstate(alglib_impl::minlmstate *attach_to):_minlmstate_owner(attach_to) 
{
}

minlmstate::minlmstate(const minlmstate &rhs):_minlmstate_owner(rhs) 
{
}

minlmstate& minlmstate::operator=(const minlmstate &rhs)
{
    if( this==&rhs )
        return *this;
    _minlmstate_owner::operator=(rhs);
    return *this;
}

minlmstate::~minlmstate()
{
}




/*************************************************************************
Optimization report, filled by MinLMResults() function

FIELDS:
* TerminationType, completetion code:
    * -8    optimizer detected NAN/INF values either in the function itself,
            or in its Jacobian
    * -5    inappropriate solver was used:
            * solver created with minlmcreatefgh() used  on  problem  with
              general linear constraints (set with minlmsetlc() call).
    * -3    constraints are inconsistent
    *  2    relative step is no more than EpsX.
    *  5    MaxIts steps was taken
    *  7    stopping conditions are too stringent,
            further improvement is impossible
    *  8    terminated   by  user  who  called  MinLMRequestTermination().
            X contains point which was "current accepted" when termination
            request was submitted.
* F, objective value, SUM(f[i]^2)
* IterationsCount, contains iterations count
* NFunc, number of function calculations
* NJac, number of Jacobi matrix calculations
* NGrad, number of gradient calculations
* NHess, number of Hessian calculations
* NCholesky, number of Cholesky decomposition calculations
*************************************************************************/
_minlmreport_owner::_minlmreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minlmreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minlmreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minlmreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minlmreport));
    alglib_impl::_minlmreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minlmreport_owner::_minlmreport_owner(alglib_impl::minlmreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minlmreport_owner::_minlmreport_owner(const _minlmreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minlmreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minlmreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minlmreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minlmreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minlmreport));
    alglib_impl::_minlmreport_init_copy(p_struct, const_cast<alglib_impl::minlmreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minlmreport_owner& _minlmreport_owner::operator=(const _minlmreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minlmreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minlmreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minlmreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minlmreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minlmreport));
    alglib_impl::_minlmreport_init_copy(p_struct, const_cast<alglib_impl::minlmreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minlmreport_owner::~_minlmreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minlmreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minlmreport* _minlmreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minlmreport* _minlmreport_owner::c_ptr() const
{
    return p_struct;
}
minlmreport::minlmreport() : _minlmreport_owner() ,iterationscount(p_struct->iterationscount),terminationtype(p_struct->terminationtype),f(p_struct->f),nfunc(p_struct->nfunc),njac(p_struct->njac),ngrad(p_struct->ngrad),nhess(p_struct->nhess),ncholesky(p_struct->ncholesky)
{
}

minlmreport::minlmreport(alglib_impl::minlmreport *attach_to):_minlmreport_owner(attach_to) ,iterationscount(p_struct->iterationscount),terminationtype(p_struct->terminationtype),f(p_struct->f),nfunc(p_struct->nfunc),njac(p_struct->njac),ngrad(p_struct->ngrad),nhess(p_struct->nhess),ncholesky(p_struct->ncholesky)
{
}

minlmreport::minlmreport(const minlmreport &rhs):_minlmreport_owner(rhs) ,iterationscount(p_struct->iterationscount),terminationtype(p_struct->terminationtype),f(p_struct->f),nfunc(p_struct->nfunc),njac(p_struct->njac),ngrad(p_struct->ngrad),nhess(p_struct->nhess),ncholesky(p_struct->ncholesky)
{
}

minlmreport& minlmreport::operator=(const minlmreport &rhs)
{
    if( this==&rhs )
        return *this;
    _minlmreport_owner::operator=(rhs);
    return *this;
}

minlmreport::~minlmreport()
{
}
#endif

#if defined(AE_COMPILE_NLCAUL) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_MINCG) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
        NONLINEAR CONJUGATE GRADIENT METHOD

DESCRIPTION:
The subroutine minimizes function F(x) of N arguments by using one of  the
nonlinear conjugate gradient methods.

These CG methods are globally convergent (even on non-convex functions) as
long as grad(f) is Lipschitz continuous in  a  some  neighborhood  of  the
L = { x : f(x)<=f(x0) }.


REQUIREMENTS:
Algorithm will request following information during its operation:
* function value F and its gradient G (simultaneously) at given point X


USAGE:
1. User initializes algorithm state with MinCGCreate() call
2. User tunes solver parameters with MinCGSetCond(), MinCGSetStpMax() and
   other functions
3. User calls MinCGOptimize() function which takes algorithm  state   and
   pointer (delegate, etc.) to callback function which calculates F/G.
4. User calls MinCGResults() to get solution
5. Optionally, user may call MinCGRestartFrom() to solve another  problem
   with same N but another starting point and/or another function.
   MinCGRestartFrom() allows to reuse already initialized structure.


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[0..N-1].

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 25.03.2010 by Bochkanov Sergey
*************************************************************************/
void mincgcreate(const ae_int_t n, const real_1d_array &x, mincgstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgcreate(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
        NONLINEAR CONJUGATE GRADIENT METHOD

DESCRIPTION:
The subroutine minimizes function F(x) of N arguments by using one of  the
nonlinear conjugate gradient methods.

These CG methods are globally convergent (even on non-convex functions) as
long as grad(f) is Lipschitz continuous in  a  some  neighborhood  of  the
L = { x : f(x)<=f(x0) }.


REQUIREMENTS:
Algorithm will request following information during its operation:
* function value F and its gradient G (simultaneously) at given point X


USAGE:
1. User initializes algorithm state with MinCGCreate() call
2. User tunes solver parameters with MinCGSetCond(), MinCGSetStpMax() and
   other functions
3. User calls MinCGOptimize() function which takes algorithm  state   and
   pointer (delegate, etc.) to callback function which calculates F/G.
4. User calls MinCGResults() to get solution
5. Optionally, user may call MinCGRestartFrom() to solve another  problem
   with same N but another starting point and/or another function.
   MinCGRestartFrom() allows to reuse already initialized structure.


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[0..N-1].

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 25.03.2010 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void mincgcreate(const real_1d_array &x, mincgstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgcreate(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
The subroutine is finite difference variant of MinCGCreate(). It uses
finite differences in order to differentiate target function.

Description below contains information which is specific to this function
only. We recommend to read comments on MinCGCreate() in order to get more
information about creation of CG optimizer.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[0..N-1].
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinCGSetScale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large truncation  errors, while too small
   step will result in too large numerical  errors.  1.0E-6  can  be  good
   value to start with.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is  less  robust  and  precise.  L-BFGS  needs  exact  gradient values.
   Imprecise  gradient may slow down  convergence,  especially  on  highly
   nonlinear problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
void mincgcreatef(const ae_int_t n, const real_1d_array &x, const double diffstep, mincgstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgcreatef(n, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
The subroutine is finite difference variant of MinCGCreate(). It uses
finite differences in order to differentiate target function.

Description below contains information which is specific to this function
only. We recommend to read comments on MinCGCreate() in order to get more
information about creation of CG optimizer.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[0..N-1].
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinCGSetScale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large truncation  errors, while too small
   step will result in too large numerical  errors.  1.0E-6  can  be  good
   value to start with.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is  less  robust  and  precise.  L-BFGS  needs  exact  gradient values.
   Imprecise  gradient may slow down  convergence,  especially  on  highly
   nonlinear problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void mincgcreatef(const real_1d_array &x, const double diffstep, mincgstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgcreatef(n, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets stopping conditions for CG optimization algorithm.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsG    -   >=0
                The  subroutine  finishes  its  work   if   the  condition
                |v|<EpsG is satisfied, where:
                * |.| means Euclidian norm
                * v - scaled gradient vector, v[i]=g[i]*s[i]
                * g - gradient
                * s - scaling coefficients set by MinCGSetScale()
    EpsF    -   >=0
                The  subroutine  finishes  its work if on k+1-th iteration
                the  condition  |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1}
                is satisfied.
    EpsX    -   >=0
                The subroutine finishes its work if  on  k+1-th  iteration
                the condition |v|<=EpsX is fulfilled, where:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - ste pvector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinCGSetScale()
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsG=0, EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to
automatic stopping criterion selection (small EpsX).

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetcond(mincgstate &state, const double epsg, const double epsf, const double epsx, const ae_int_t maxits, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgsetcond(state.c_ptr(), epsg, epsf, epsx, maxits, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets scaling coefficients for CG optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Scaling is also used by finite difference variant of CG optimizer  -  step
along I-th axis is equal to DiffStep*S[I].

In   most   optimizers  (and  in  the  CG  too)  scaling is NOT a form  of
preconditioning. It just  affects  stopping  conditions.  You  should  set
preconditioner by separate call to one of the MinCGSetPrec...() functions.

There  is  special  preconditioning  mode, however,  which  uses   scaling
coefficients to form diagonal preconditioning matrix. You  can  turn  this
mode on, if you want.   But  you should understand that scaling is not the
same thing as preconditioning - these are two different, although  related
forms of tuning solver.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void mincgsetscale(mincgstate &state, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgsetscale(state.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinCGOptimize().

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetxrep(mincgstate &state, const bool needxrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgsetxrep(state.c_ptr(), needxrep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets CG algorithm.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    CGType  -   algorithm type:
                * -1    automatic selection of the best algorithm
                * 0     DY (Dai and Yuan) algorithm
                * 1     Hybrid DY-HS algorithm

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetcgtype(mincgstate &state, const ae_int_t cgtype, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgsetcgtype(state.c_ptr(), cgtype, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets maximum step length

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    StpMax  -   maximum step length, >=0. Set StpMax to 0.0,  if you don't
                want to limit step length.

Use this subroutine when you optimize target function which contains exp()
or  other  fast  growing  functions,  and optimization algorithm makes too
large  steps  which  leads  to overflow. This function allows us to reject
steps  that  are  too  large  (and  therefore  expose  us  to the possible
overflow) without actually calculating function value at the x+stp*d.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetstpmax(mincgstate &state, const double stpmax, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgsetstpmax(state.c_ptr(), stpmax, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function allows to suggest initial step length to the CG algorithm.

Suggested  step  length  is used as starting point for the line search. It
can be useful when you have  badly  scaled  problem,  i.e.  when  ||grad||
(which is used as initial estimate for the first step) is many  orders  of
magnitude different from the desired step.

Line search  may  fail  on  such problems without good estimate of initial
step length. Imagine, for example, problem with ||grad||=10^50 and desired
step equal to 0.1 Line  search function will use 10^50  as  initial  step,
then  it  will  decrease step length by 2 (up to 20 attempts) and will get
10^44, which is still too large.

This function allows us to tell than line search should  be  started  from
some moderate step length, like 1.0, so algorithm will be able  to  detect
desired step length in a several searches.

Default behavior (when no step is suggested) is to use preconditioner,  if
it is available, to generate initial estimate of step length.

This function influences only first iteration of algorithm. It  should  be
called between MinCGCreate/MinCGRestartFrom() call and MinCGOptimize call.
Suggested step is ignored if you have preconditioner.

INPUT PARAMETERS:
    State   -   structure used to store algorithm state.
    Stp     -   initial estimate of the step length.
                Can be zero (no estimate).

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsuggeststep(mincgstate &state, const double stp, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgsuggeststep(state.c_ptr(), stp, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification of the preconditioner: preconditioning is turned off.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTE:  you  can  change  preconditioner  "on  the  fly",  during algorithm
iterations.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetprecdefault(mincgstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgsetprecdefault(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification  of  the  preconditioner:  diagonal of approximate Hessian is
used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    D       -   diagonal of the approximate Hessian, array[0..N-1],
                (if larger, only leading N elements are used).

NOTE:  you  can  change  preconditioner  "on  the  fly",  during algorithm
iterations.

NOTE 2: D[i] should be positive. Exception will be thrown otherwise.

NOTE 3: you should pass diagonal of approximate Hessian - NOT ITS INVERSE.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetprecdiag(mincgstate &state, const real_1d_array &d, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgsetprecdiag(state.c_ptr(), d.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification of the preconditioner: scale-based diagonal preconditioning.

This preconditioning mode can be useful when you  don't  have  approximate
diagonal of Hessian, but you know that your  variables  are  badly  scaled
(for  example,  one  variable is in [1,10], and another in [1000,100000]),
and most part of the ill-conditioning comes from different scales of vars.

In this case simple  scale-based  preconditioner,  with H[i] = 1/(s[i]^2),
can greatly improve convergence.

IMPRTANT: you should set scale of your variables with MinCGSetScale() call
(before or after MinCGSetPrecScale() call). Without knowledge of the scale
of your variables scale-based preconditioner will be just unit matrix.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTE:  you  can  change  preconditioner  "on  the  fly",  during algorithm
iterations.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetprecscale(mincgstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgsetprecscale(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function provides reverse communication interface
Reverse communication interface is not documented or recommended to use.
See below for functions which provide better documented API
*************************************************************************/
bool mincgiteration(mincgstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::mincgiteration(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}


void mincgoptimize(mincgstate &state,
    void (*func)(const real_1d_array &x, double &func, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(func!=NULL, "ALGLIB: error in 'mincgoptimize()' (func is NULL)", &_alglib_env_state);
    alglib_impl::mincgsetprotocolv1(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::mincgiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
                if( state.needf )
                {
                    func(state.x, state.f, ptr);
                    continue;
                }
        if( state.xupdated )
        {
            if( rep!=NULL )
                rep(state.x, state.f, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    lbl_no_callback:
        alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'mincgoptimize' (some derivatives were not provided?)", &_alglib_env_state);
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
}


void mincgoptimize(mincgstate &state,
    void (*grad)(const real_1d_array &x, double &func, real_1d_array &grad, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(grad!=NULL, "ALGLIB: error in 'mincgoptimize()' (grad is NULL)", &_alglib_env_state);
    alglib_impl::mincgsetprotocolv1(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::mincgiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
                if( state.needfg )
                {
                    grad(state.x, state.f, state.g, ptr);
                    continue;
                }
        if( state.xupdated )
        {
            if( rep!=NULL )
                rep(state.x, state.f, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    lbl_no_callback:
        alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'mincgoptimize' (some derivatives were not provided?)", &_alglib_env_state);
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
}



/*************************************************************************
This  function  activates/deactivates verification  of  the  user-supplied
analytic gradient.

Upon  activation  of  this  option  OptGuard  integrity  checker  performs
numerical differentiation of your target function  at  the  initial  point
(note: future versions may also perform check  at  the  final  point)  and
compares numerical gradient with analytic one provided by you.

If difference is too large, an error flag is set and optimization  session
continues. After optimization session is over, you can retrieve the report
which  stores  both  gradients  and  specific  components  highlighted  as
suspicious by the OptGuard.

The primary OptGuard report can be retrieved with mincgoptguardresults().

IMPORTANT: gradient check is a high-overhead option which  will  cost  you
           about 3*N additional function evaluations. In many cases it may
           cost as much as the rest of the optimization session.

           YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO
           CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY.

NOTE: unlike previous incarnation of the gradient checking code,  OptGuard
      does NOT interrupt optimization even if it discovers bad gradient.

INPUT PARAMETERS:
    State       -   structure used to store algorithm state
    TestStep    -   verification step used for numerical differentiation:
                    * TestStep=0 turns verification off
                    * TestStep>0 activates verification
                    You should carefully choose TestStep. Value  which  is
                    too large (so large that  function  behavior  is  non-
                    cubic at this scale) will lead  to  false  alarms. Too
                    short step will result in rounding  errors  dominating
                    numerical derivative.

                    You may use different step for different parameters by
                    means of setting scale with mincgsetscale().

=== EXPLANATION ==========================================================

In order to verify gradient algorithm performs following steps:
  * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i],
    where X[i] is i-th component of the initial point and S[i] is a  scale
    of i-th parameter
  * F(X) is evaluated at these trial points
  * we perform one more evaluation in the middle point of the interval
  * we  build  cubic  model using function values and derivatives at trial
    points and we compare its prediction with actual value in  the  middle
    point

  -- ALGLIB --
     Copyright 15.06.2014 by Bochkanov Sergey
*************************************************************************/
void mincgoptguardgradient(mincgstate &state, const double teststep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgoptguardgradient(state.c_ptr(), teststep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0)
b) nonsmooth     target function (non-C1)

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.

      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.

      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.

      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.

=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void mincgoptguardsmoothness(mincgstate &state, const ae_int_t level, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgoptguardsmoothness(state.c_ptr(), level, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0)
b) nonsmooth     target function (non-C1)

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.

      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.

      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.

      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.

=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void mincgoptguardsmoothness(mincgstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t level;

    level = 1;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgoptguardsmoothness(state.c_ptr(), level, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
Results of OptGuard integrity check, should be called  after  optimization
session is over.

=== PRIMARY REPORT =======================================================

OptGuard performs several checks which are intended to catch common errors
in the implementation of nonlinear function/gradient:
* incorrect analytic gradient
* discontinuous (non-C0) target functions (constraints)
* nonsmooth     (non-C1) target functions (constraints)

Each of these checks is activated with appropriate function:
* mincgoptguardgradient() for gradient verification
* mincgoptguardsmoothness() for C0/C1 checks

Following flags are set when these errors are suspected:
* rep.badgradsuspected, and additionally:
  * rep.badgradvidx for specific variable (gradient element) suspected
  * rep.badgradxbase, a point where gradient is tested
  * rep.badgraduser, user-provided gradient  (stored  as  2D  matrix  with
    single row in order to make  report  structure  compatible  with  more
    complex optimizers like MinNLC or MinLM)
  * rep.badgradnum,   reference    gradient    obtained    via   numerical
    differentiation (stored as  2D matrix with single row in order to make
    report structure compatible with more complex optimizers  like  MinNLC
    or MinLM)
* rep.nonc0suspected
* rep.nonc1suspected

=== ADDITIONAL REPORTS/LOGS ==============================================

Several different tests are performed to catch C0/C1 errors, you can  find
out specific test signaled error by looking to:
* rep.nonc0test0positive, for non-C0 test #0
* rep.nonc1test0positive, for non-C1 test #0
* rep.nonc1test1positive, for non-C1 test #1

Additional information (including line search logs)  can  be  obtained  by
means of:
* mincgoptguardnonc1test0results()
* mincgoptguardnonc1test1results()
which return detailed error reports, specific points where discontinuities
were found, and so on.

==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    rep     -   generic OptGuard report;  more  detailed  reports  can  be
                retrieved with other functions.

NOTE: false negatives (nonsmooth problems are not identified as  nonsmooth
      ones) are possible although unlikely.

      The reason  is  that  you  need  to  make several evaluations around
      nonsmoothness  in  order  to  accumulate  enough  information  about
      function curvature. Say, if you start right from the nonsmooth point,
      optimizer simply won't get enough data to understand what  is  going
      wrong before it terminates due to abrupt changes in the  derivative.
      It is also  possible  that  "unlucky"  step  will  move  us  to  the
      termination too quickly.

      Our current approach is to have less than 0.1%  false  negatives  in
      our test examples  (measured  with  multiple  restarts  from  random
      points), and to have exactly 0% false positives.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void mincgoptguardresults(mincgstate &state, optguardreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgoptguardresults(state.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #0

Nonsmoothness (non-C1) test #0 studies  function  values  (not  gradient!)
obtained during line searches and monitors  behavior  of  the  directional
derivative estimate.

This test is less powerful than test #1, but it does  not  depend  on  the
gradient values and thus it is more robust against artifacts introduced by
numerical differentiation.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], f[] - arrays of length CNT which store step lengths and  function
  values at these points; f[i] is evaluated in x0+stp[i]*d.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.

==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #0 "strong" report
    lngrep  -   C1 test #0 "long" report

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void mincgoptguardnonc1test0results(const mincgstate &state, optguardnonc1test0report &strrep, optguardnonc1test0report &lngrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgoptguardnonc1test0results(state.c_ptr(), strrep.c_ptr(), lngrep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #1

Nonsmoothness (non-C1)  test  #1  studies  individual  components  of  the
gradient computed during line search.

When precise analytic gradient is provided this test is more powerful than
test #0  which  works  with  function  values  and  ignores  user-provided
gradient.  However,  test  #0  becomes  more   powerful   when   numerical
differentiation is employed (in such cases test #1 detects  higher  levels
of numerical noise and becomes too conservative).

This test also tells specific components of the gradient which violate  C1
continuity, which makes it more informative than #0, which just tells that
continuity is violated.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* vidx - is an index of the variable in [0,N) with nonsmooth derivative
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], g[] - arrays of length CNT which store step lengths and  gradient
  values at these points; g[i] is evaluated in  x0+stp[i]*d  and  contains
  vidx-th component of the gradient.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.

==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #1 "strong" report
    lngrep  -   C1 test #1 "long" report

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void mincgoptguardnonc1test1results(mincgstate &state, optguardnonc1test1report &strrep, optguardnonc1test1report &lngrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgoptguardnonc1test1results(state.c_ptr(), strrep.c_ptr(), lngrep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Conjugate gradient results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report:
                * Rep.TerminationType completetion code:
                    * -8    internal integrity control  detected  infinite
                            or NAN values in  function/gradient.  Abnormal
                            termination signalled.
                    * -7    gradient verification failed.
                            See MinCGSetGradientCheck() for more information.
                    *  1    relative function improvement is no more than
                            EpsF.
                    *  2    relative step is no more than EpsX.
                    *  4    gradient norm is no more than EpsG
                    *  5    MaxIts steps was taken
                    *  7    stopping conditions are too stringent,
                            further improvement is impossible,
                            we return best X found so far
                    *  8    terminated by user
                * Rep.IterationsCount contains iterations count
                * NFEV countains number of function calculations

  -- ALGLIB --
     Copyright 20.04.2009 by Bochkanov Sergey
*************************************************************************/
void mincgresults(const mincgstate &state, real_1d_array &x, mincgreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgresults(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Conjugate gradient results

Buffered implementation of MinCGResults(), which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 20.04.2009 by Bochkanov Sergey
*************************************************************************/
void mincgresultsbuf(const mincgstate &state, real_1d_array &x, mincgreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgresultsbuf(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  subroutine  restarts  CG  algorithm from new point. All optimization
parameters are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure used to store algorithm state.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
void mincgrestartfrom(mincgstate &state, const real_1d_array &x, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgrestartfrom(state.c_ptr(), x.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.

NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void mincgrequesttermination(mincgstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mincgrequesttermination(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************
This object stores state of the nonlinear CG optimizer.

You should use ALGLIB functions to work with this object.
*************************************************************************/
_mincgstate_owner::_mincgstate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_mincgstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::mincgstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::mincgstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::mincgstate));
    alglib_impl::_mincgstate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_mincgstate_owner::_mincgstate_owner(alglib_impl::mincgstate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_mincgstate_owner::_mincgstate_owner(const _mincgstate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_mincgstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: mincgstate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::mincgstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::mincgstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::mincgstate));
    alglib_impl::_mincgstate_init_copy(p_struct, const_cast<alglib_impl::mincgstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_mincgstate_owner& _mincgstate_owner::operator=(const _mincgstate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: mincgstate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: mincgstate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: mincgstate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_mincgstate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::mincgstate));
    alglib_impl::_mincgstate_init_copy(p_struct, const_cast<alglib_impl::mincgstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_mincgstate_owner::~_mincgstate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_mincgstate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::mincgstate* _mincgstate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::mincgstate* _mincgstate_owner::c_ptr() const
{
    return p_struct;
}
mincgstate::mincgstate() : _mincgstate_owner() ,needf(p_struct->needf),needfg(p_struct->needfg),xupdated(p_struct->xupdated),f(p_struct->f),g(&p_struct->g),x(&p_struct->x)
{
}

mincgstate::mincgstate(alglib_impl::mincgstate *attach_to):_mincgstate_owner(attach_to) ,needf(p_struct->needf),needfg(p_struct->needfg),xupdated(p_struct->xupdated),f(p_struct->f),g(&p_struct->g),x(&p_struct->x)
{
}

mincgstate::mincgstate(const mincgstate &rhs):_mincgstate_owner(rhs) ,needf(p_struct->needf),needfg(p_struct->needfg),xupdated(p_struct->xupdated),f(p_struct->f),g(&p_struct->g),x(&p_struct->x)
{
}

mincgstate& mincgstate::operator=(const mincgstate &rhs)
{
    if( this==&rhs )
        return *this;
    _mincgstate_owner::operator=(rhs);
    return *this;
}

mincgstate::~mincgstate()
{
}




/*************************************************************************
This structure stores optimization report:
* IterationsCount           total number of inner iterations
* NFEV                      number of gradient evaluations
* TerminationType           termination type (see below)

TERMINATION CODES

TerminationType field contains completion code, which can be:
  -8    internal integrity control detected  infinite  or  NAN  values  in
        function/gradient. Abnormal termination signalled.
   1    relative function improvement is no more than EpsF.
   2    relative step is no more than EpsX.
   4    gradient norm is no more than EpsG
   5    MaxIts steps was taken
   7    stopping conditions are too stringent,
        further improvement is impossible,
        X contains best point found so far.
   8    terminated by user who called mincgrequesttermination(). X contains
        point which was "current accepted" when  termination  request  was
        submitted.

Other fields of this structure are not documented and should not be used!
*************************************************************************/
_mincgreport_owner::_mincgreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_mincgreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::mincgreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::mincgreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::mincgreport));
    alglib_impl::_mincgreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_mincgreport_owner::_mincgreport_owner(alglib_impl::mincgreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_mincgreport_owner::_mincgreport_owner(const _mincgreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_mincgreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: mincgreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::mincgreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::mincgreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::mincgreport));
    alglib_impl::_mincgreport_init_copy(p_struct, const_cast<alglib_impl::mincgreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_mincgreport_owner& _mincgreport_owner::operator=(const _mincgreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: mincgreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: mincgreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: mincgreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_mincgreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::mincgreport));
    alglib_impl::_mincgreport_init_copy(p_struct, const_cast<alglib_impl::mincgreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_mincgreport_owner::~_mincgreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_mincgreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::mincgreport* _mincgreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::mincgreport* _mincgreport_owner::c_ptr() const
{
    return p_struct;
}
mincgreport::mincgreport() : _mincgreport_owner() ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype)
{
}

mincgreport::mincgreport(alglib_impl::mincgreport *attach_to):_mincgreport_owner(attach_to) ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype)
{
}

mincgreport::mincgreport(const mincgreport &rhs):_mincgreport_owner(rhs) ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype)
{
}

mincgreport& mincgreport::operator=(const mincgreport &rhs)
{
    if( this==&rhs )
        return *this;
    _mincgreport_owner::operator=(rhs);
    return *this;
}

mincgreport::~mincgreport()
{
}
#endif

#if defined(AE_COMPILE_DFGENMOD) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_NLCSQP) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_SSGD) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_DIFFEVO) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_MINDF) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
                          GLOBAL OPTIMIZATION
               SUBJECT TO BOX/LINEAR/NONLINEAR CONSTRAINTS

The  subroutine  minimizes  function   F(x)  of N arguments subject to any
combination of:
* bound constraints
* linear inequality constraints
* linear equality constraints
* nonlinear generalized inequality constraints Li<=Ci(x)<=Ui, with one  of
  Li/Ui possibly being infinite

REQUIREMENTS:
* F() and C()  do  NOT  have  to be differentiable, locally Lipschitz   or
  continuous. Most solvers in this subpackage can deal with  nonsmoothness
  or minor discontinuities, although obviously smoother problems  are  the
  most easy ones.
* generally, F() and C() must be computable at any point which is feasible
  subject to box constraints

USAGE:

1. User  initializes  algorithm  state   with   mindfcreate()   call   and
   chooses specific solver to be used. There is some solver which is  used
   by default, with default settings, but  you  should  NOT  rely  on  the
   default choice. It may change in the future releases of ALGLIB  without
   notice, and no one can guarantee that the new solver will be  able   to
   solve your problem with default settings.

2. User adds boundary and/or linear and/or nonlinear constraints by  means
   of calling one of the following functions:
   a) mindfsetbc() for boundary constraints
   b) mindfsetlc2dense() for linear constraints
   c) mindfsetnlc2() for nonlinear constraints
   You may combine (a), (b) and (c) in one optimization problem.

3. User sets scale of the variables with mindfsetscale() function.  It  is
   VERY important to set  variable  scales  because  many  derivative-free
   algorithms refuse to work when variables are badly scaled.
   Scaling  helps  to seed initial  population,  control  convergence  and
   enforce penalties for constraint violation.

4. Finally, user calls mindfoptimize()  function which takes algorithm
   state and pointer (delegate, etc) to callback function which calculates
   F and G.

5. User calls mindfresults() to get a solution


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from  size  of  X
    X       -   starting point, array[N]. Some solvers can utilize  a good
                initial point to seed computations.

                As of ALGLIB 4.04, the initial point is:
                * used by GDEMO

                If the chosen solver does not need initial point, one  can
                supply zeros.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state


IMPORTANT: the  MINDF  optimizer   supports   parallel   model  evaluation
           ('callback  parallelism'). This feature, which  is  present  in
           commercial ALGLIB editions, greatly accelerates algorithms like
           differential evolution which usually issue  batch  requests  to
           user callbacks which can be efficiently parallelized.

           Callback parallelism  is  usually  beneficial  when  the  batch
           evalution requires more than several milliseconds.

           See ALGLIB Reference Manual, 'Working with commercial  version'
           section,  and  comments  on  mindfoptimize()  function for more
           information.

  -- ALGLIB --
     Copyright 24.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfcreate(const ae_int_t n, const real_1d_array &x, mindfstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfcreate(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
                          GLOBAL OPTIMIZATION
               SUBJECT TO BOX/LINEAR/NONLINEAR CONSTRAINTS

The  subroutine  minimizes  function   F(x)  of N arguments subject to any
combination of:
* bound constraints
* linear inequality constraints
* linear equality constraints
* nonlinear generalized inequality constraints Li<=Ci(x)<=Ui, with one  of
  Li/Ui possibly being infinite

REQUIREMENTS:
* F() and C()  do  NOT  have  to be differentiable, locally Lipschitz   or
  continuous. Most solvers in this subpackage can deal with  nonsmoothness
  or minor discontinuities, although obviously smoother problems  are  the
  most easy ones.
* generally, F() and C() must be computable at any point which is feasible
  subject to box constraints

USAGE:

1. User  initializes  algorithm  state   with   mindfcreate()   call   and
   chooses specific solver to be used. There is some solver which is  used
   by default, with default settings, but  you  should  NOT  rely  on  the
   default choice. It may change in the future releases of ALGLIB  without
   notice, and no one can guarantee that the new solver will be  able   to
   solve your problem with default settings.

2. User adds boundary and/or linear and/or nonlinear constraints by  means
   of calling one of the following functions:
   a) mindfsetbc() for boundary constraints
   b) mindfsetlc2dense() for linear constraints
   c) mindfsetnlc2() for nonlinear constraints
   You may combine (a), (b) and (c) in one optimization problem.

3. User sets scale of the variables with mindfsetscale() function.  It  is
   VERY important to set  variable  scales  because  many  derivative-free
   algorithms refuse to work when variables are badly scaled.
   Scaling  helps  to seed initial  population,  control  convergence  and
   enforce penalties for constraint violation.

4. Finally, user calls mindfoptimize()  function which takes algorithm
   state and pointer (delegate, etc) to callback function which calculates
   F and G.

5. User calls mindfresults() to get a solution


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from  size  of  X
    X       -   starting point, array[N]. Some solvers can utilize  a good
                initial point to seed computations.

                As of ALGLIB 4.04, the initial point is:
                * used by GDEMO

                If the chosen solver does not need initial point, one  can
                supply zeros.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state


IMPORTANT: the  MINDF  optimizer   supports   parallel   model  evaluation
           ('callback  parallelism'). This feature, which  is  present  in
           commercial ALGLIB editions, greatly accelerates algorithms like
           differential evolution which usually issue  batch  requests  to
           user callbacks which can be efficiently parallelized.

           Callback parallelism  is  usually  beneficial  when  the  batch
           evalution requires more than several milliseconds.

           See ALGLIB Reference Manual, 'Working with commercial  version'
           section,  and  comments  on  mindfoptimize()  function for more
           information.

  -- ALGLIB --
     Copyright 24.07.2023 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void mindfcreate(const real_1d_array &x, mindfstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfcreate(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets box constraints.

Box constraints are inactive by default (after initial creation).

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF.

  -- ALGLIB --
     Copyright 24.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetbc(mindfstate &state, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetbc(state.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU with dense
constraint matrix A.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinDFcreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetlc2dense(mindfstate &state, const real_2d_array &a, const real_1d_array &al, const real_1d_array &au, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetlc2dense(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU with dense
constraint matrix A.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinDFcreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void mindfsetlc2dense(mindfstate &state, const real_2d_array &a, const real_1d_array &al, const real_1d_array &au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t k;
    if( (a.rows()!=al.length()) || (a.rows()!=au.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'mindfsetlc2dense': looks like one of arguments has wrong size");
    k = a.rows();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetlc2dense(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets two-sided nonlinear constraints.

In fact, this function sets only  NUMBER  of  the  nonlinear  constraints.
Constraints  themselves  (constraint  functions)   are   passed   to   the
MinDFOptimize() method.

This method accepts user-defined vector function F[] where:
* first component of F[] corresponds to the objective
* subsequent NNLC components of F[] correspond to the two-sided  nonlinear
  constraints NL<=C(x)<=NU, where
  * NL[i]=NU[i] => I-th row is an equality constraint Ci(x)=NL
  * NL[i]<NU[i] => I-th tow is a  two-sided constraint NL[i]<=Ci(x)<=NU[i]
  * NL[i]=-INF  => I-th row is an one-sided constraint Ci(x)<=NU[i]
  * NU[i]=+INF  => I-th row is an one-sided constraint NL[i]<=Ci(x)
  * NL[i]=-INF, NU[i]=+INF => constraint is ignored

NOTE: you may combine nonlinear constraints with linear/boundary ones.  If
      your problem has mixed constraints, you  may explicitly specify some
      of them as linear or box ones.
      It helps optimizer to handle them more efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinDFCreate call.
    NL      -   array[NNLC], lower bounds, can contain -INF
    NU      -   array[NNLC], lower bounds, can contain +INF
    NNLC    -   constraints count, NNLC>=0

NOTE 1: nonlinear constraints are satisfied only  approximately!   It   is
        possible   that  algorithm  will  evaluate  function  outside   of
        feasible area!

NOTE 2: the algorithm scales variables according to the scale specified by
        MinDFSetScale() function, so it can handle problems with badly
        scaled variables (as long as we KNOW their scales).

        However,  there  is  no  way  to  automatically  scale   nonlinear
        constraints. Inappropriate scaling  of nonlinear  constraints  may
        ruin convergence. Solving problem with  constraint  "1000*G0(x)=0"
        is NOT the same as solving it with constraint "0.001*G0(x)=0".

        It means that YOU are  the  one who is responsible for the correct
        scaling of the nonlinear constraints. We recommend  you  to  scale
        nonlinear constraints in such  a  way  that  the  derivatives  (if
        constraints are differentiable) have approximately unit  magnitude
        (for problems  with  unit  variable  scales)  or  have  magnitudes
        approximately equal to 1/S[i] (where S is a variable scale set  by
        MinDFSetScale() function).

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetnlc2(mindfstate &state, const real_1d_array &nl, const real_1d_array &nu, const ae_int_t nnlc, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetnlc2(state.c_ptr(), nl.c_ptr(), nu.c_ptr(), nnlc, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets two-sided nonlinear constraints.

In fact, this function sets only  NUMBER  of  the  nonlinear  constraints.
Constraints  themselves  (constraint  functions)   are   passed   to   the
MinDFOptimize() method.

This method accepts user-defined vector function F[] where:
* first component of F[] corresponds to the objective
* subsequent NNLC components of F[] correspond to the two-sided  nonlinear
  constraints NL<=C(x)<=NU, where
  * NL[i]=NU[i] => I-th row is an equality constraint Ci(x)=NL
  * NL[i]<NU[i] => I-th tow is a  two-sided constraint NL[i]<=Ci(x)<=NU[i]
  * NL[i]=-INF  => I-th row is an one-sided constraint Ci(x)<=NU[i]
  * NU[i]=+INF  => I-th row is an one-sided constraint NL[i]<=Ci(x)
  * NL[i]=-INF, NU[i]=+INF => constraint is ignored

NOTE: you may combine nonlinear constraints with linear/boundary ones.  If
      your problem has mixed constraints, you  may explicitly specify some
      of them as linear or box ones.
      It helps optimizer to handle them more efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinDFCreate call.
    NL      -   array[NNLC], lower bounds, can contain -INF
    NU      -   array[NNLC], lower bounds, can contain +INF
    NNLC    -   constraints count, NNLC>=0

NOTE 1: nonlinear constraints are satisfied only  approximately!   It   is
        possible   that  algorithm  will  evaluate  function  outside   of
        feasible area!

NOTE 2: the algorithm scales variables according to the scale specified by
        MinDFSetScale() function, so it can handle problems with badly
        scaled variables (as long as we KNOW their scales).

        However,  there  is  no  way  to  automatically  scale   nonlinear
        constraints. Inappropriate scaling  of nonlinear  constraints  may
        ruin convergence. Solving problem with  constraint  "1000*G0(x)=0"
        is NOT the same as solving it with constraint "0.001*G0(x)=0".

        It means that YOU are  the  one who is responsible for the correct
        scaling of the nonlinear constraints. We recommend  you  to  scale
        nonlinear constraints in such  a  way  that  the  derivatives  (if
        constraints are differentiable) have approximately unit  magnitude
        (for problems  with  unit  variable  scales)  or  have  magnitudes
        approximately equal to 1/S[i] (where S is a variable scale set  by
        MinDFSetScale() function).

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void mindfsetnlc2(mindfstate &state, const real_1d_array &nl, const real_1d_array &nu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t nnlc;
    if( (nl.length()!=nu.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'mindfsetnlc2': looks like one of arguments has wrong size");
    nnlc = nl.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetnlc2(state.c_ptr(), nl.c_ptr(), nu.c_ptr(), nnlc, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets stopping conditions for the optimizer.

This function sets a  combined   stopping  condition:  stopping  when  two
criteria are met simultaneously:
* function   values   has  converged  to  a  neighborhood  whose  size  is
  proportional to epsF
* variable   values   has  converged  to  a  neighborhood  whose  size  is
  proportional to epsX
It is possible to use only one condition by setting another EPS to zero.

Most derivarive-free solvers are heuristics, so the code used to implement
this stopping condition is an heuristic too. Usually 'proportional to EPS'
means that we are somewhere between Eps/10...Eps*10 away from the solution.
However, there are no warranties that the solver  has  actually  converged
to something, although in practice it works well.

The  specific  meaning  of  'converging'  is  algorithm-dependent.  It  is
possible that some future ALGLIB optimizers will  ignore  this  condition,
see comments on specific solvers for more info. This  condition  does  not
work for multi-objective problems.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsF    -   >=0:
                * zero value means no condition for F
                * EpsF>0 means stopping when the solver converged with
                  an error estimate less than EpsF*max(|F|,1)
    EpsX    -   >=0:
                * zero value means no condition for X
                * EpsX>0 means stopping when the solver converged with
                  error in I-th variable less than EpsX*S[i], where S[i]
                  is a variable scale

  -- ALGLIB --
     Copyright 23.04.2024 by Bochkanov Sergey
*************************************************************************/
void mindfsetcondfx(mindfstate &state, const double epsf, const double epsx, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetcondfx(state.c_ptr(), epsf, epsx, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function activates/deactivates internal timers  used  to  track  time
spent in various parts of the solver (mostly, callbacks vs solver itself).

When activated with this function, the following timings are stored in the
mindfreport structure fields:
* total time spend in the optimization
* time spent in the callback
* time spent in the solver itself

See comments on mindfreport structure for more  information  about  timers
and their accuracy.

Timers are an essential part of reports that helps to find out  where  the
most time is spent and how to  optimize  the  code.  E.g.,  noticing  that
significant amount of time is spent  in  numerical  differentiation  makes
obvious that ALGLIB-provided parallel numerical differentiation is needed.

However, time measurements add noticeable  overhead,  about  50-100ns  per
function call. In some applications it results in a significant  slowdown,
that's why this option is inactive  by  default  and  should  be  manually
activated.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    UseTimers-  true or false

NOTE: when tracing is turned on with alglib::trace_file(), some derivative
      free  solvers   may  also  perform  internal,  more  detailed   time
      measurements, which are printed to the log file.

  -- ALGLIB --
     Copyright 23.04.2024 by Bochkanov Sergey
*************************************************************************/
void mindfusetimers(mindfstate &state, const bool usetimers, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfusetimers(state.c_ptr(), usetimers, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets stopping condition for the optimizer: function   values
has converged to a neighborhood whose size is proportional to epsF.

Most derivarive-free solvers are heuristics, so the code used to implement
this stopping condition is an heuristic too. Usually 'proportional to EPS'
means that we are somewhere between Eps/10...Eps*10 away from the solution.
However, there are no warranties that the solver  has  actually  converged
to something, although in practice it works well.

The  specific  meaning  of  'converging'  is  algorithm-dependent.  It  is
possible that some future ALGLIB optimizers will  ignore  this  condition,
see comments on specific solvers for more info. This  condition  does  not
work for multi-objective problems.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsF    -   >=0:
                * zero value means no condition for F
                * EpsF>0 means stopping when the solver converged with
                  an error estimate less than EpsF*max(|F|,1)

  -- ALGLIB --
     Copyright 23.04.2024 by Bochkanov Sergey
*************************************************************************/
void mindfsetcondf(mindfstate &state, const double epsf, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetcondf(state.c_ptr(), epsf, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets variable scales.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison  with  tolerances)  and  to
guide algorithm steps.

The scale of a variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetscale(mindfstate &state, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetscale(state.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinDFOptimize().

NOTE: algorithm passes two parameters to rep() callback  - the best  point
      so far and a function value at the point. For unconstrained problems
      the function value is  non-increasing (the most recent best point is
      always at least not worse than the previous best one).  However,  it
      can increase between iterations when  solving  constrained  problems
      (a  better  point  may  have  higher  objective  value  but  smaller
      constraint violation).

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void mindfsetxrep(mindfstate &state, const bool needxrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetxrep(state.c_ptr(), needxrep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine submits request for termination of a running optimizer. It
should be called from a user-supplied callback when user decides  that  it
is time to "smoothly" terminate optimization process.  As  a  result,  the
optimizer  stops  at  the  point  which  was  "current  accepted" when the
termination request was submitted and returns  error  code  8  (successful
termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on an optimizer which is NOT running will have
      no effect.

NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfrequesttermination(mindfstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfrequesttermination(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets seed used by internal RNG.

By default, a random seed is used, i.e. every time you run the solver,  we
seed its generator with a new value obtained  from  the  system-wide  RNG.
Thus, the solver returns non-deterministic results. You  can  change  such
a behavior by specifying a fixed positive seed value.

INPUT PARAMETERS:
    S           -   optimizer structure
    SeedVal     -   seed value:
                    * positive values are used for  seeding  RNG  with  a
                      fixed  seed,  i.e.  subsequent  runs  on  the  same
                      objective will return the same results
                    * non-positive seed means that a random  seed is used
                      for every run, i.e. subsequent  runs  on  the  same
                      objective will return slightly different results

  -- ALGLIB --
     Copyright 26.04.2024 by Bochkanov Sergey
*************************************************************************/
void mindfsetseed(mindfstate &s, const ae_int_t seedval, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetseed(s.c_ptr(), seedval, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine sets optimization algorithm to the differential  evolution
solver GDEMO (Generalized Differential Evolution Multiobjective)  with  an
automatic parameters selection.

NOTE: a version with manually tuned parameters can be activated by calling
      the mindfsetalgogdemofixed() function.

The primary stopping condition for the solver is to stop after the specified
number of iterations. You can also specify  additional  criteria  to  stop
early:
* stop when subpopulation target values (2N+1 best individuals) are within
  EPS from the best one so far (function values seem to converge)
* stop when both subpopulation target values  AND  subpopulation  variable
  values are within EPS from the best one so far

The first condition is specified with mindfsetcondf(), the second  one  is
activated with mindfsetcondfx().

Both conditions are heuristics which  may fail. Being 'within EPS from the
best value so far'  in  practice  means  that  we  are  somewhere   within
[0.1EPS,10EPS] from the true  solution;  however,  on  difficult  problems
this condition may fire too early.

Imposing an additional requirement that variable values have clustered  too
may prevent us from premature  stopping. However, on multi-extremal and/or
noisy problems too many individuals may be trapped away from the  optimum,
preventing this condition from activation.

ALGORITHM PROPERTIES:

* the solver uses a variant of  the  adaptive  parameter  tuning  strategy
  called 'Success-History  Based  Parameter  Adaptation  for  Differential
  Evolution Ensemble' (SHADE) by Ryoji Tanabe and Alex  Fukunaga.  You  do
  not have to specify crossover probability and differential weight,   the
  solver will automatically choose the most appropriate strategy.

* the solver can handle box, linear,  nonlinear  constraints.  Linear  and
  nonlinear constraints are  handled  by  means  of an L1/L2  penalty. The
  solver does not violate box constraints at any point,  but  may  violate
  linear and nonlinear ones. Penalty coefficient can be changed with   the
  mindfsetgdemopenalty() function.

* the solver heavily depends on variable scales being available (specified
  by means of mindfsetscale() call) and on box constraints with both lower
  and upper bounds being available which are used to determine the  search
  region. It will work without box constraints  and  without  scales,  but
  results are likely to be suboptimal.

* the solver is SIMD-optimized  and  parallelized  (in  commercial  ALGLIB
  editions), with nearly linear scalability of parallel processing.

* this solver is intended for finding solutions  with up to several digits
  of precision at best. Its primary purpose  is  to  find  at  least  some
  solution to an otherwise intractable problem.

IMPORTANT: derivative-free optimization is inherently less robust than the
           smooth nonlinear programming, especially when nonsmoothness and
           discontinuities are present.

           Derivative-free  algorithms  have  less  convergence guarantees
           than their smooth  counterparts.  It  is  considered  a  normal
           (although, obviously, undesirable) situation when a derivative-
           -free algorithm fails to converge with desired precision. Having
           2 digits of accurasy is already a  good  result,  on  difficult
           problems (high numerical noise, discontinuities) you  may  have
           even less than that.

INPUT PARAMETERS:
    State       -   solver
    EpochsCnt   -   iterations count, >0.  Usually   the  algorithm  needs
                    hundreds of iterations to converge.
    PopSize     -   population  size,  >=0.  Zero  value  means  that  the
                    default value (which is 10*N in the  current  version)
                    will be chosen. Good values are  in  5*N...20*N,  with
                    the smaller values being recommended for easy problems
                    and the larger  values  for  difficult  multi-extremal
                    and/or noisy tasks.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetalgogdemo(mindfstate &state, const ae_int_t epochscnt, const ae_int_t popsize, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetalgogdemo(state.c_ptr(), epochscnt, popsize, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine sets optimization algorithm to the differential  evolution
solver GDEMO (Generalized Differential Evolution Multiobjective)  with  an
automatic parameters selection.

NOTE: a version with manually tuned parameters can be activated by calling
      the mindfsetalgogdemofixed() function.

The primary stopping condition for the solver is to stop after the specified
number of iterations. You can also specify  additional  criteria  to  stop
early:
* stop when subpopulation target values (2N+1 best individuals) are within
  EPS from the best one so far (function values seem to converge)
* stop when both subpopulation target values  AND  subpopulation  variable
  values are within EPS from the best one so far

The first condition is specified with mindfsetcondf(), the second  one  is
activated with mindfsetcondfx().

Both conditions are heuristics which  may fail. Being 'within EPS from the
best value so far'  in  practice  means  that  we  are  somewhere   within
[0.1EPS,10EPS] from the true  solution;  however,  on  difficult  problems
this condition may fire too early.

Imposing an additional requirement that variable values have clustered  too
may prevent us from premature  stopping. However, on multi-extremal and/or
noisy problems too many individuals may be trapped away from the  optimum,
preventing this condition from activation.

ALGORITHM PROPERTIES:

* the solver uses a variant of  the  adaptive  parameter  tuning  strategy
  called 'Success-History  Based  Parameter  Adaptation  for  Differential
  Evolution Ensemble' (SHADE) by Ryoji Tanabe and Alex  Fukunaga.  You  do
  not have to specify crossover probability and differential weight,   the
  solver will automatically choose the most appropriate strategy.

* the solver can handle box, linear,  nonlinear  constraints.  Linear  and
  nonlinear constraints are  handled  by  means  of an L1/L2  penalty. The
  solver does not violate box constraints at any point,  but  may  violate
  linear and nonlinear ones. Penalty coefficient can be changed with   the
  mindfsetgdemopenalty() function.

* the solver heavily depends on variable scales being available (specified
  by means of mindfsetscale() call) and on box constraints with both lower
  and upper bounds being available which are used to determine the  search
  region. It will work without box constraints  and  without  scales,  but
  results are likely to be suboptimal.

* the solver is SIMD-optimized  and  parallelized  (in  commercial  ALGLIB
  editions), with nearly linear scalability of parallel processing.

* this solver is intended for finding solutions  with up to several digits
  of precision at best. Its primary purpose  is  to  find  at  least  some
  solution to an otherwise intractable problem.

IMPORTANT: derivative-free optimization is inherently less robust than the
           smooth nonlinear programming, especially when nonsmoothness and
           discontinuities are present.

           Derivative-free  algorithms  have  less  convergence guarantees
           than their smooth  counterparts.  It  is  considered  a  normal
           (although, obviously, undesirable) situation when a derivative-
           -free algorithm fails to converge with desired precision. Having
           2 digits of accurasy is already a  good  result,  on  difficult
           problems (high numerical noise, discontinuities) you  may  have
           even less than that.

INPUT PARAMETERS:
    State       -   solver
    EpochsCnt   -   iterations count, >0.  Usually   the  algorithm  needs
                    hundreds of iterations to converge.
    PopSize     -   population  size,  >=0.  Zero  value  means  that  the
                    default value (which is 10*N in the  current  version)
                    will be chosen. Good values are  in  5*N...20*N,  with
                    the smaller values being recommended for easy problems
                    and the larger  values  for  difficult  multi-extremal
                    and/or noisy tasks.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void mindfsetalgogdemo(mindfstate &state, const ae_int_t epochscnt, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t popsize;

    popsize = 0;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetalgogdemo(state.c_ptr(), epochscnt, popsize, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This subroutine tells GDEMO differential  evolution  optimizer  to  use  a
ROBUST profile (the default option).

The ROBUST profile is intended  to  facilitate  explorative  behavior  and
robust convergence even on difficult multi-extremal problems. It comes  at
the expense of increased running time  even  on  easy  problems. The QUICK
profile can be chosen if your problem is relatively easy to handle and you
prefer speed over robustness. In most cases, the QUICK profile  is  ~2x-3x
faster than the robust one.

This function has effect only on adaptive GDEMO with automatic  parameters
selection.  It  has  no  effect  on  fixed-parameters  GDEMO  or any other
solvers.

IMPORTANT: this function does NOT change the  optimization  algorithm.  If
           want to activate differential evolution solver, you still  have
           to call a proper mindfsetalgo???() function.

INPUT PARAMETERS:
    State       -   solver

  -- ALGLIB --
     Copyright 25.04.2024 by Bochkanov Sergey
*************************************************************************/
void mindfsetgdemoprofilerobust(mindfstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetgdemoprofilerobust(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine tells GDEMO differential  evolution  optimizer  to  use  a
QUICK profile.

The QUICK profile is intended  to  facilitate accelerated  convergence  on
medium-complexity problems at the cost  of  (sometimes)  having  premature
convergence  on  difficult  and/or  multi-extremal  problems.  The  ROBUST
profile can be selected if you favor convergence  warranties  over  speed.
In most cases, the ROBUST profile is ~2x-3x slower than the QUICK one.

This function has effect only on adaptive GDEMO with automatic  parameters
selection.  It  has  no  effect  on  fixed-parameters  GDEMO  or any other
solvers.

IMPORTANT: this function does NOT change the  optimization  algorithm.  If
           you want to activate differential evolution solver,  you  still
           have to call a proper mindfsetalgo???() function.

INPUT PARAMETERS:
    State       -   solver

  -- ALGLIB --
     Copyright 25.04.2024 by Bochkanov Sergey
*************************************************************************/
void mindfsetgdemoprofilequick(mindfstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetgdemoprofilequick(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine tells GDEMO differential  evolution  optimizer  to  handle
linear/nonlinear constraints by an L1/L2 penalty function.

IMPORTANT: this function does NOT change the  optimization  algorithm.  If
           want to activate differential evolution solver, you still  have
           to call a proper mindfsetalgo???() function.

INPUT PARAMETERS:
    State       -   solver
    Rho1, Rho2  -   penalty parameters for constraint violations:
                    * Rho1 is a multiplier for L1 penalty
                    * Rho2 is a multiplier for L2 penalty
                    * Rho1,Rho2>=0
                    * having both of them at zero means that some  default
                      value will be chosen.
                    Ignored for problems with box-only constraints.

                    L1 penalty is usually better at enforcing constraints,
                    but leads to slower convergence than L2 penalty. It is
                    possible to combine both kinds of penalties together.

                    There is a compromise between constraint  satisfaction
                    and  optimality:  high  values  of   Rho   mean   that
                    constraints are satisfied with high accuracy  but that
                    the target may  be  underoptimized  due  to  numerical
                    difficulties.  Small  values  of  Rho  mean  that  the
                    solution may  grossly  violate  constraints.  Choosing
                    good Rho is usually a matter of trial and error.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetgdemopenalty(mindfstate &state, const double rho1, const double rho2, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetgdemopenalty(state.c_ptr(), rho1, rho2, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine sets optimization algorithm to the differential  evolution
solver GDEMO (Generalized Differential Evolution Multiobjective)  with the
manual parameters selection.

Unlike DE with an automatic parameters selection  this  function  requires
user to manually specify  algorithm  parameters. In the general  case  the
full-auto GDEMO is better. However, it has to spend some time finding  out
properties of a problem being solved; furthermore, it is  not  allowed  to
try potentially dangerous values of  parameters  that  lead  to  premature
stopping. Manually tuning the solver to the specific problem at  hand  can
get 2x-3x better running time.

Aside from that, the algorithm is fully equivalent to automatic GDEMO, and
we   recommend  you  reading  comments  on  mindfsetalgogdemo()  for  more
information about algorithm properties and stopping criteria.

INPUT PARAMETERS:
    State       -   solver
    EpochsCnt   -   iterations count, >0.  Usually   the  algorithm  needs
                    hundreds of iterations to converge.
    Strategy    -   specific DE strategy to use:
                    * 0 for DE/rand/1
                    * 1 for DE/best/2
                    * 2 for DE/current-to-best/1
    CrossoverProb-  crossover probability, 0<CrossoverProb<1
    DifferentialWeight- weight, 0<DifferentialWeight<2
    PopSize     -   population  size,  >=0.  Zero  value  means  that  the
                    default value (which is 10*N in the  current  version)
                    will be chosen. Good values are  in  5*N...20*N,  with
                    the smaller values being recommended for easy problems
                    and the larger  values  for  difficult  multi-extremal
                    and/or noisy tasks.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetalgogdemofixed(mindfstate &state, const ae_int_t epochscnt, const ae_int_t strategy, const double crossoverprob, const double differentialweight, const ae_int_t popsize, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfsetalgogdemofixed(state.c_ptr(), epochscnt, strategy, crossoverprob, differentialweight, popsize, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function provides reverse communication interface
Reverse communication interface is not documented or recommended to use.
See below for functions which provide better documented API
*************************************************************************/
bool mindfiteration(mindfstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::mindfiteration(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}


void mindfoptimize(mindfstate &state,
    void (*fvec)(const real_1d_array &x, real_1d_array &fi, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    alglib_impl::mindfstate &optimizer = *(state.c_ptr());
    alglib_impl::rcommv2_request request(
        optimizer.requesttype,
        optimizer.querysize, optimizer.queryfuncs, optimizer.queryvars, optimizer.querydim, optimizer.queryformulasize,
        optimizer.querydata.ptr.p_double, optimizer.replyfi.ptr.p_double, optimizer.replydj.ptr.p_double, optimizer.replysj, ptr,
        "mindf");
    alglib_impl::rcommv2_callbacks callbacks;
    alglib_impl::rcommv2_buffers buffers(&state.c_ptr()->tmpx1, &state.c_ptr()->tmpc1, &state.c_ptr()->tmpf1, &state.c_ptr()->tmpg1, &state.c_ptr()->tmpj1, &state.c_ptr()->tmps1);
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(fvec!=NULL, "ALGLIB: error in 'mindfoptimize()' (fvec is NULL)", &_alglib_env_state);
callbacks.fvec = fvec;

    alglib_impl::mindfsetprotocolv2(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::mindfiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
        if( optimizer.requesttype==3 )
        {
            const ae_int_t njobs = request.size*request.vars+request.size;
            for(alglib_impl::ae_int_t job_idx=0; job_idx<njobs; job_idx++)
                alglib_impl::process_v2request_3phase0(request, job_idx, callbacks, buffers);
            alglib_impl::process_v2request_3phase1(request);
            request.request = 0;
            continue;
        }
        if( optimizer.requesttype==5 )
        {
            const ae_int_t njobs = request.size*request.vars+request.size;
            for(alglib_impl::ae_int_t job_idx=0; job_idx<njobs; job_idx++)
                alglib_impl::process_v2request_5phase0(request, job_idx, callbacks, buffers);
            alglib_impl::process_v2request_5phase1(request);
            request.request = 0;
            continue;
        }if( optimizer.requesttype==4 )
        { 
            for(alglib_impl::ae_int_t qidx=0; qidx<optimizer.querysize; qidx++)
                alglib_impl::process_v2request_4(request, qidx, callbacks, buffers);
            optimizer.requesttype = 0;
            continue;
        }
        if( optimizer.requesttype==-1 )
        {
            memmove(&buffers.tmpX[0], optimizer.reportx.ptr.p_double, optimizer.queryvars*sizeof(double));
            if( rep!=NULL )
                rep(buffers.tmpX, optimizer.reportf, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
lbl_no_callback:
    alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'mindfoptimize' (some derivatives were not provided?)", &_alglib_env_state);
}



/*************************************************************************
MinDF results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report:

                rep.f stores objective value at the solution

                You  should     check  rep.terminationtype  in   order  to
                distinguish successful termination from unsuccessful one:
                * -8   internal integrity control  detected  infinite  or
                       NAN   values   in   function/gradient.    Abnormal
                       termination signalled.
                * -3   box constraints are inconsistent
                * -1   inconsistent parameters were passed:
                       * penalty parameter is zero, but we have nonlinear
                         constraints set by MinDFsetnlc2()
                *  1   successful termination according to a solver-specific
                       set of conditions
                *  8   User requested termination via minnsrequesttermination()

                If you activated timers  with  mindfusetimers(),  you  can
                also find out how much time was spent in various code parts:
                * rep.timetotal    - for a total time in seconds
                * rep.timesolver   - for a time spent in the solver itself
                * rep.timecallback - for a time spent in user callbacks
                See comments on mindfreport structure for more information
                about timers and their accuracy.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfresults(const mindfstate &state, real_1d_array &x, mindfreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfresults(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************

Buffered implementation of MinDFresults() which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfresultsbuf(const mindfstate &state, real_1d_array &x, mindfreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::mindfresultsbuf(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************
This object stores nonlinear optimizer state.
You should use functions provided by MinDF subpackage to work  with  this
object
*************************************************************************/
_mindfstate_owner::_mindfstate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_mindfstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::mindfstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::mindfstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::mindfstate));
    alglib_impl::_mindfstate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_mindfstate_owner::_mindfstate_owner(alglib_impl::mindfstate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_mindfstate_owner::_mindfstate_owner(const _mindfstate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_mindfstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: mindfstate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::mindfstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::mindfstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::mindfstate));
    alglib_impl::_mindfstate_init_copy(p_struct, const_cast<alglib_impl::mindfstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_mindfstate_owner& _mindfstate_owner::operator=(const _mindfstate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: mindfstate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: mindfstate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: mindfstate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_mindfstate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::mindfstate));
    alglib_impl::_mindfstate_init_copy(p_struct, const_cast<alglib_impl::mindfstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_mindfstate_owner::~_mindfstate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_mindfstate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::mindfstate* _mindfstate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::mindfstate* _mindfstate_owner::c_ptr() const
{
    return p_struct;
}
mindfstate::mindfstate() : _mindfstate_owner() 
{
}

mindfstate::mindfstate(alglib_impl::mindfstate *attach_to):_mindfstate_owner(attach_to) 
{
}

mindfstate::mindfstate(const mindfstate &rhs):_mindfstate_owner(rhs) 
{
}

mindfstate& mindfstate::operator=(const mindfstate &rhs)
{
    if( this==&rhs )
        return *this;
    _mindfstate_owner::operator=(rhs);
    return *this;
}

mindfstate::~mindfstate()
{
}




/*************************************************************************
This structure stores optimization report:
* f                         objective value at the solution
* iterationscount           total number of inner iterations
* nfev                      number of gradient evaluations
* terminationtype           termination type (see below)
* bcerr                     maximum violation of box constraints
* lcerr                     maximum violation of linear constraints
* nlcerr                    maximum violation of nonlinear constraints

If timers were activated, the structure also stores running times:
* timesolver                time (in seconds, stored as a floating-point
                            value) spent in the solver itself. Time spent
                            in the user callback is not included.
                            See 'TIMERS' below for more information.
* timecallback              time (in seconds, stored as a floating-point
                            value) spent in the user callback.
                            See 'TIMERS' below for more information.
* timetotal                 total time spent during the optimization,
                            including both the solver and callbacks.
                            See 'TIMERS' below for more information.
In  order  to  activate timers, the caller has to  call  mindfusetimers()
function.

Other fields of this structure are not documented and should not be used!

TERMINATION CODES

TerminationType field contains completion code, which can be:
  -8    internal integrity control detected  infinite  or  NAN  values  in
        function/gradient. Abnormal termination signalled.
  -3    box constraints are inconsistent
  -1    inconsistent parameters were passed:
        * penalty parameter is zero, but we have nonlinear constraints
          set by mindfsetnlc2()
   1    function value has converged within epsf
   2    sampling radius decreased below epsx
   5    MaxIts steps was taken
   7    stopping conditions are too stringent,
        further improvement is impossible,
        X contains best point found so far.
   8    User requested termination via mindfrequesttermination()

TIMERS

Starting from ALGLIB 4.04, many optimizers report time  spent  in  in  the
solver itself and in user  callbacks. The  time  is  reported  in seconds,
using floating-point (i.e. fractional-length intervals can  be  reported).

In  order  to  activate timers, the caller has to  call  mindfusetimers()
function.

The accuracy of   the  reported  value depends on the specific programming
language and OS being used:
* C++, no AE_OS is #defined - the accuracy is that of time() function, i.e.
  one second.
* C++, AE_OS=AE_WINDOWS is #defined - the accuracy is that of GetTickCount(),
  i.e. about 10-20ms
* C++, AE_OS=AE_POSIX is #defined - the accuracy is that of gettimeofday()
* C#, managed core, any OS - the accuracy is that of Environment.TickCount
* C#, HPC core, any OS - the accuracy is that of a corresponding C++ version
* any other language - the accuracy is that of a corresponding C++ version

Whilst modern operating systems provide more accurate timers, these timers
often have significant overhead or backward  compatibility  issues.  Thus,
ALGLIB stick to the most basic and efficient functions, even at  the  cost
of some accuracy being lost.
*************************************************************************/
_mindfreport_owner::_mindfreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_mindfreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::mindfreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::mindfreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::mindfreport));
    alglib_impl::_mindfreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_mindfreport_owner::_mindfreport_owner(alglib_impl::mindfreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_mindfreport_owner::_mindfreport_owner(const _mindfreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_mindfreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: mindfreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::mindfreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::mindfreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::mindfreport));
    alglib_impl::_mindfreport_init_copy(p_struct, const_cast<alglib_impl::mindfreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_mindfreport_owner& _mindfreport_owner::operator=(const _mindfreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: mindfreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: mindfreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: mindfreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_mindfreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::mindfreport));
    alglib_impl::_mindfreport_init_copy(p_struct, const_cast<alglib_impl::mindfreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_mindfreport_owner::~_mindfreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_mindfreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::mindfreport* _mindfreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::mindfreport* _mindfreport_owner::c_ptr() const
{
    return p_struct;
}
mindfreport::mindfreport() : _mindfreport_owner() ,f(p_struct->f),iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),bcerr(p_struct->bcerr),lcerr(p_struct->lcerr),nlcerr(p_struct->nlcerr),terminationtype(p_struct->terminationtype),timetotal(p_struct->timetotal),timesolver(p_struct->timesolver),timecallback(p_struct->timecallback)
{
}

mindfreport::mindfreport(alglib_impl::mindfreport *attach_to):_mindfreport_owner(attach_to) ,f(p_struct->f),iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),bcerr(p_struct->bcerr),lcerr(p_struct->lcerr),nlcerr(p_struct->nlcerr),terminationtype(p_struct->terminationtype),timetotal(p_struct->timetotal),timesolver(p_struct->timesolver),timecallback(p_struct->timecallback)
{
}

mindfreport::mindfreport(const mindfreport &rhs):_mindfreport_owner(rhs) ,f(p_struct->f),iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),bcerr(p_struct->bcerr),lcerr(p_struct->lcerr),nlcerr(p_struct->nlcerr),terminationtype(p_struct->terminationtype),timetotal(p_struct->timetotal),timesolver(p_struct->timesolver),timecallback(p_struct->timecallback)
{
}

mindfreport& mindfreport::operator=(const mindfreport &rhs)
{
    if( this==&rhs )
        return *this;
    _mindfreport_owner::operator=(rhs);
    return *this;
}

mindfreport::~mindfreport()
{
}
#endif

#if defined(AE_COMPILE_REVISEDDUALSIMPLEX) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_LPSOLVERS) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
                            LINEAR PROGRAMMING

The subroutine creates LP  solver.  After  initial  creation  it  contains
default optimization problem with zero cost vector and all variables being
fixed to zero values and no constraints.

In order to actually solve something you should:
* set cost vector with minlpsetcost()
* set variable bounds with minlpsetbc() or minlpsetbcall()
* specify constraint matrix with one of the following functions:
  [*] minlpsetlc()        for dense one-sided constraints
  [*] minlpsetlc2dense()  for dense two-sided constraints
  [*] minlpsetlc2()       for sparse two-sided constraints
  [*] minlpaddlc2dense()  to add one dense row to constraint matrix
  [*] minlpaddlc2()       to add one row to constraint matrix (compressed format)
* call minlpoptimize() to run the solver and  minlpresults()  to  get  the
  solution vector and additional information.

By  default,  LP  solver uses best algorithm available. As of ALGLIB 3.17,
sparse interior point (barrier) solver is used. Future releases of  ALGLIB
may introduce other solvers.

User may choose specific LP algorithm by calling:
* minlpsetalgodss() for revised dual simplex method with DSE  pricing  and
  bounds flipping ratio test (aka long dual step).  Large-scale  sparse LU
  solverwith  Forest-Tomlin update is used internally  as  linear  algebra
  driver.
* minlpsetalgoipm() for sparse interior point method

INPUT PARAMETERS:
    N       -   problem size

OUTPUT PARAMETERS:
    State   -   optimizer in the default state

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpcreate(const ae_int_t n, minlpstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpcreate(n, state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets LP algorithm to revised dual simplex method.

ALGLIB implementation of dual simplex method supports advanced performance
and stability improvements like DSE pricing , bounds flipping  ratio  test
(aka long dual step), Forest-Tomlin update, shifting.

INPUT PARAMETERS:
    State   -   optimizer
    Eps     -   stopping condition, Eps>=0:
                * should be small number about 1E-6 or 1E-7.
                * zero value means that solver automatically selects good
                  value (can be different in different ALGLIB versions)
                * default value is zero
                Algorithm stops when relative error is less than Eps.

===== TRACING DSS SOLVER =================================================

DSS solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'DSS'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'DSS.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'DSS'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("DSS,PREC.F6", "path/to/trace.log")
>

  -- ALGLIB --
     Copyright 08.11.2020 by Bochkanov Sergey
*************************************************************************/
void minlpsetalgodss(minlpstate &state, const double eps, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetalgodss(state.c_ptr(), eps, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets LP algorithm to sparse interior point method.

ALGORITHM INFORMATION:

* this  algorithm  is  our implementation  of  interior  point  method  as
  formulated by  R.J.Vanderbei, with minor modifications to the  algorithm
  (damped Newton directions are extensively used)
* like all interior point methods, this algorithm  tends  to  converge  in
  roughly same number of iterations (between 15 and 50) independently from
  the problem dimensionality

INPUT PARAMETERS:
    State   -   optimizer
    Eps     -   stopping condition, Eps>=0:
                * should be small number about 1E-6 or 1E-8.
                * zero value means that solver automatically selects good
                  value (can be different in different ALGLIB versions)
                * default value is zero
                Algorithm  stops  when  primal  error  AND  dual error AND
                duality gap are less than Eps.

===== TRACING IPM SOLVER =================================================

IPM solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'IPM'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'IPM.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'IPM'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("IPM,PREC.F6", "path/to/trace.log")
>

  -- ALGLIB --
     Copyright 08.11.2020 by Bochkanov Sergey
*************************************************************************/
void minlpsetalgoipm(minlpstate &state, const double eps, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetalgoipm(state.c_ptr(), eps, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets LP algorithm to sparse interior point method.

ALGORITHM INFORMATION:

* this  algorithm  is  our implementation  of  interior  point  method  as
  formulated by  R.J.Vanderbei, with minor modifications to the  algorithm
  (damped Newton directions are extensively used)
* like all interior point methods, this algorithm  tends  to  converge  in
  roughly same number of iterations (between 15 and 50) independently from
  the problem dimensionality

INPUT PARAMETERS:
    State   -   optimizer
    Eps     -   stopping condition, Eps>=0:
                * should be small number about 1E-6 or 1E-8.
                * zero value means that solver automatically selects good
                  value (can be different in different ALGLIB versions)
                * default value is zero
                Algorithm  stops  when  primal  error  AND  dual error AND
                duality gap are less than Eps.

===== TRACING IPM SOLVER =================================================

IPM solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'IPM'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'IPM.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'IPM'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("IPM,PREC.F6", "path/to/trace.log")
>

  -- ALGLIB --
     Copyright 08.11.2020 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minlpsetalgoipm(minlpstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    double eps;

    eps = 0.0;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetalgoipm(state.c_ptr(), eps, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets cost term for LP solver.

By default, cost term is zero.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    C       -   cost term, array[N].

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetcost(minlpstate &state, const real_1d_array &c, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetcost(state.c_ptr(), c.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets scaling coefficients.

ALGLIB optimizers use scaling matrices to test stopping  conditions and as
preconditioner.

Scale of the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the
   function

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetscale(minlpstate &state, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetscale(state.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets box constraints for LP solver (all variables  at  once,
different constraints for different variables).

The default state of constraints is to have all variables fixed  at  zero.
You have to overwrite it by your own constraint vector. Constraint  status
is preserved until constraints are  explicitly  overwritten  with  another
minlpsetbc()  call,   overwritten   with  minlpsetbcall(),  or   partially
overwritten with minlmsetbci() call.

Following types of constraints are supported:

    DESCRIPTION         CONSTRAINT              HOW TO SPECIFY
    fixed variable      x[i]=Bnd[i]             BndL[i]=BndU[i]
    lower bound         BndL[i]<=x[i]           BndU[i]=+INF
    upper bound         x[i]<=BndU[i]           BndL[i]=-INF
    range               BndL[i]<=x[i]<=BndU[i]  ...
    free variable       -                       BndL[I]=-INF, BndU[I]+INF

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
    BndU    -   upper bounds, array[N].

NOTE: infinite values can be specified by means of Double.PositiveInfinity
      and  Double.NegativeInfinity  (in  C#)  and  alglib::fp_posinf   and
      alglib::fp_neginf (in C++).

NOTE: you may replace infinities by very small/very large values,  but  it
      is not recommended because large numbers may introduce large numerical
      errors in the algorithm.

NOTE: if constraints for all variables are same you may use minlpsetbcall()
      which allows to specify constraints without using arrays.

NOTE: BndL>BndU will result in LP problem being recognized as infeasible.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetbc(minlpstate &state, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetbc(state.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets box constraints for LP solver (all variables  at  once,
same constraints for all variables)

The default state of constraints is to have all variables fixed  at  zero.
You have to overwrite it by your own constraint vector. Constraint  status
is preserved until constraints are  explicitly  overwritten  with  another
minlpsetbc() call or partially overwritten with minlpsetbcall().

Following types of constraints are supported:

    DESCRIPTION         CONSTRAINT              HOW TO SPECIFY
    fixed variable      x[i]=Bnd[i]             BndL[i]=BndU[i]
    lower bound         BndL[i]<=x[i]           BndU[i]=+INF
    upper bound         x[i]<=BndU[i]           BndL[i]=-INF
    range               BndL[i]<=x[i]<=BndU[i]  ...
    free variable       -                       BndL[I]=-INF, BndU[I]+INF

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bound, same for all variables
    BndU    -   upper bound, same for all variables

NOTE: infinite values can be specified by means of Double.PositiveInfinity
      and  Double.NegativeInfinity  (in  C#)  and  alglib::fp_posinf   and
      alglib::fp_neginf (in C++).

NOTE: you may replace infinities by very small/very large values,  but  it
      is not recommended because large numbers may introduce large numerical
      errors in the algorithm.

NOTE: minlpsetbc() can  be  used  to  specify  different  constraints  for
      different variables.

NOTE: BndL>BndU will result in LP problem being recognized as infeasible.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetbcall(minlpstate &state, const double bndl, const double bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetbcall(state.c_ptr(), bndl, bndu, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets box constraints for I-th variable (other variables are
not modified).

The default state of constraints is to have all variables fixed  at  zero.
You have to overwrite it by your own constraint vector.

Following types of constraints are supported:

    DESCRIPTION         CONSTRAINT              HOW TO SPECIFY
    fixed variable      x[i]=Bnd[i]             BndL[i]=BndU[i]
    lower bound         BndL[i]<=x[i]           BndU[i]=+INF
    upper bound         x[i]<=BndU[i]           BndL[i]=-INF
    range               BndL[i]<=x[i]<=BndU[i]  ...
    free variable       -                       BndL[I]=-INF, BndU[I]+INF

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    I       -   variable index, in [0,N)
    BndL    -   lower bound for I-th variable
    BndU    -   upper bound for I-th variable

NOTE: infinite values can be specified by means of Double.PositiveInfinity
      and  Double.NegativeInfinity  (in  C#)  and  alglib::fp_posinf   and
      alglib::fp_neginf (in C++).

NOTE: you may replace infinities by very small/very large values,  but  it
      is not recommended because large numbers may introduce large numerical
      errors in the algorithm.

NOTE: minlpsetbc() can  be  used  to  specify  different  constraints  for
      different variables.

NOTE: BndL>BndU will result in LP problem being recognized as infeasible.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetbci(minlpstate &state, const ae_int_t i, const double bndl, const double bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetbci(state.c_ptr(), i, bndl, bndu, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets one-sided linear constraints A*x ~ AU, where "~" can be
a mix of "<=", "=" and ">=".

IMPORTANT: this function is provided here for compatibility with the  rest
           of ALGLIB optimizers which accept constraints  in  format  like
           this one. Many real-life problems feature two-sided constraints
           like a0 <= a*x <= a1. It is really inefficient to add them as a
           pair of one-sided constraints.

           Use minlpsetlc2dense(), minlpsetlc2(), minlpaddlc2()  (or   its
           sparse version) wherever possible.

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    A       -   linear constraints, array[K,N+1]. Each row of A represents
                one constraint, with first N elements being linear coefficients,
                and last element being right side.
    CT      -   constraint types, array[K]:
                * if CT[i]>0, then I-th constraint is A[i,*]*x >= A[i,n]
                * if CT[i]=0, then I-th constraint is A[i,*]*x  = A[i,n]
                * if CT[i]<0, then I-th constraint is A[i,*]*x <= A[i,n]
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A and CT.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetlc(minlpstate &state, const real_2d_array &a, const integer_1d_array &ct, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetlc(state.c_ptr(), a.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets one-sided linear constraints A*x ~ AU, where "~" can be
a mix of "<=", "=" and ">=".

IMPORTANT: this function is provided here for compatibility with the  rest
           of ALGLIB optimizers which accept constraints  in  format  like
           this one. Many real-life problems feature two-sided constraints
           like a0 <= a*x <= a1. It is really inefficient to add them as a
           pair of one-sided constraints.

           Use minlpsetlc2dense(), minlpsetlc2(), minlpaddlc2()  (or   its
           sparse version) wherever possible.

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    A       -   linear constraints, array[K,N+1]. Each row of A represents
                one constraint, with first N elements being linear coefficients,
                and last element being right side.
    CT      -   constraint types, array[K]:
                * if CT[i]>0, then I-th constraint is A[i,*]*x >= A[i,n]
                * if CT[i]=0, then I-th constraint is A[i,*]*x  = A[i,n]
                * if CT[i]<0, then I-th constraint is A[i,*]*x <= A[i,n]
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A and CT.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minlpsetlc(minlpstate &state, const real_2d_array &a, const integer_1d_array &ct, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t k;
    if( (a.rows()!=ct.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'minlpsetlc': looks like one of arguments has wrong size");
    k = a.rows();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetlc(state.c_ptr(), a.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU.

This version accepts dense matrix as  input;  internally  LP  solver  uses
sparse storage  anyway  (most  LP  problems  are  sparse),  but  for  your
convenience it may accept dense inputs. This  function  overwrites  linear
constraints set by previous calls (if such calls were made).

We recommend you to use sparse version of this function unless  you  solve
small-scale LP problem (less than few hundreds of variables).

NOTE: there also exist several versions of this function:
      * one-sided dense version which  accepts  constraints  in  the  same
        format as one used by QP and  NLP solvers
      * two-sided sparse version which accepts sparse matrix
      * two-sided dense  version which allows you to add constraints row by row
      * two-sided sparse version which allows you to add constraints row by row

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetlc2dense(minlpstate &state, const real_2d_array &a, const real_1d_array &al, const real_1d_array &au, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetlc2dense(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU.

This version accepts dense matrix as  input;  internally  LP  solver  uses
sparse storage  anyway  (most  LP  problems  are  sparse),  but  for  your
convenience it may accept dense inputs. This  function  overwrites  linear
constraints set by previous calls (if such calls were made).

We recommend you to use sparse version of this function unless  you  solve
small-scale LP problem (less than few hundreds of variables).

NOTE: there also exist several versions of this function:
      * one-sided dense version which  accepts  constraints  in  the  same
        format as one used by QP and  NLP solvers
      * two-sided sparse version which accepts sparse matrix
      * two-sided dense  version which allows you to add constraints row by row
      * two-sided sparse version which allows you to add constraints row by row

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minlpsetlc2dense(minlpstate &state, const real_2d_array &a, const real_1d_array &al, const real_1d_array &au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t k;
    if( (a.rows()!=al.length()) || (a.rows()!=au.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'minlpsetlc2dense': looks like one of arguments has wrong size");
    k = a.rows();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetlc2dense(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
sparse constraining matrix A. Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    A       -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetlc2(minlpstate &state, const sparsematrix &a, const real_1d_array &al, const real_1d_array &au, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpsetlc2(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present constraints.

This version accepts dense constraint vector as input, but  sparsifies  it
for internal storage and processing. Thus, time to add one  constraint  in
is O(N) - we have to scan entire array of length N. Sparse version of this
function is order of magnitude faster for  constraints  with  just  a  few
nonzeros per row.

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    A       -   linear constraint coefficient, array[N], right side is NOT
                included.
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint Ai*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint Ai*x<=AU
                * AU=+INF  => one-sided constraint AL<=Ai*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpaddlc2dense(minlpstate &state, const real_1d_array &a, const double al, const double au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpaddlc2dense(state.c_ptr(), a.c_ptr(), al, au, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present constraints.

Constraint is passed in compressed format: as list of non-zero entries  of
coefficient vector A. Such approach is more efficient than  dense  storage
for highly sparse constraint vectors.

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    IdxA    -   array[NNZ], indexes of non-zero elements of A:
                * can be unsorted
                * can include duplicate indexes (corresponding entries  of
                  ValA[] will be summed)
    ValA    -   array[NNZ], values of non-zero elements of A
    NNZ     -   number of non-zero coefficients in A
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpaddlc2(minlpstate &state, const integer_1d_array &idxa, const real_1d_array &vala, const ae_int_t nnz, const double al, const double au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpaddlc2(state.c_ptr(), idxa.c_ptr(), vala.c_ptr(), nnz, al, au, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function solves LP problem.

INPUT PARAMETERS:
    State   -   algorithm state

You should use minlpresults() function to access results  after  calls  to
this function.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey.
*************************************************************************/
void minlpoptimize(minlpstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpoptimize(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
LP solver results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[N], solution (on failure: last trial point)
    Rep     -   optimization report. You should check Rep.TerminationType,
                which contains completion code, and you may check  another
                fields which contain another information  about  algorithm
                functioning.

                Failure codes returned by algorithm are:
                * -4    LP problem is primal unbounded (dual infeasible)
                * -3    LP problem is primal infeasible (dual unbounded)
                * -2    IPM solver detected that problem is either
                        infeasible or unbounded

                Success codes:
                *  1..4 successful completion
                *  5    MaxIts steps was taken

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minlpresults(const minlpstate &state, real_1d_array &x, minlpreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpresults(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
LP results

Buffered implementation of MinLPResults() which uses pre-allocated  buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minlpresultsbuf(const minlpstate &state, real_1d_array &x, minlpreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlpresultsbuf(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************
This object stores linear solver state.
You should use functions provided by MinLP subpackage to work with this
object
*************************************************************************/
_minlpstate_owner::_minlpstate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minlpstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minlpstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minlpstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minlpstate));
    alglib_impl::_minlpstate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minlpstate_owner::_minlpstate_owner(alglib_impl::minlpstate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minlpstate_owner::_minlpstate_owner(const _minlpstate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minlpstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minlpstate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minlpstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minlpstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minlpstate));
    alglib_impl::_minlpstate_init_copy(p_struct, const_cast<alglib_impl::minlpstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minlpstate_owner& _minlpstate_owner::operator=(const _minlpstate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minlpstate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minlpstate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minlpstate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minlpstate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minlpstate));
    alglib_impl::_minlpstate_init_copy(p_struct, const_cast<alglib_impl::minlpstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minlpstate_owner::~_minlpstate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minlpstate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minlpstate* _minlpstate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minlpstate* _minlpstate_owner::c_ptr() const
{
    return p_struct;
}
minlpstate::minlpstate() : _minlpstate_owner() 
{
}

minlpstate::minlpstate(alglib_impl::minlpstate *attach_to):_minlpstate_owner(attach_to) 
{
}

minlpstate::minlpstate(const minlpstate &rhs):_minlpstate_owner(rhs) 
{
}

minlpstate& minlpstate::operator=(const minlpstate &rhs)
{
    if( this==&rhs )
        return *this;
    _minlpstate_owner::operator=(rhs);
    return *this;
}

minlpstate::~minlpstate()
{
}




/*************************************************************************
This structure stores optimization report:
* f                         target function value
* lagbc                     Lagrange coefficients for box constraints
* laglc                     Lagrange coefficients for linear constraints
* y                         dual variables
* stats                     array[N+M], statuses of box (N) and linear (M)
                            constraints. This array is filled only by  DSS
                            algorithm because IPM always stops at INTERIOR
                            point:
                            * stats[i]>0  =>  constraint at upper bound
                                              (also used for free non-basic
                                              variables set to zero)
                            * stats[i]<0  =>  constraint at lower bound
                            * stats[i]=0  =>  constraint is inactive, basic
                                              variable
* primalerror               primal feasibility error
* dualerror                 dual feasibility error
* slackerror                complementary slackness error
* iterationscount           iteration count
* terminationtype           completion code (see below)

COMPLETION CODES

Completion codes:
* -4    LP problem is primal unbounded (dual infeasible)
* -3    LP problem is primal infeasible (dual unbounded)
*  1..4 successful completion
*  5    MaxIts steps was taken
*  7    stopping conditions are too stringent,
        further improvement is impossible,
        X contains best point found so far.

LAGRANGE COEFFICIENTS

Positive Lagrange coefficient means that constraint is at its upper bound.
Negative coefficient means that constraint is at its lower  bound.  It  is
expected that at the solution the dual feasibility condition holds:

    C + SUM(Ei*LagBC[i],i=0..n-1) + SUM(Ai*LagLC[i],i=0..m-1) ~ 0

where
* C is a cost vector (linear term)
* Ei is a vector with 1.0 at position I and 0 in other positions
* Ai is an I-th row of linear constraint matrix
*************************************************************************/
_minlpreport_owner::_minlpreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minlpreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minlpreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minlpreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minlpreport));
    alglib_impl::_minlpreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minlpreport_owner::_minlpreport_owner(alglib_impl::minlpreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minlpreport_owner::_minlpreport_owner(const _minlpreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minlpreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minlpreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minlpreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minlpreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minlpreport));
    alglib_impl::_minlpreport_init_copy(p_struct, const_cast<alglib_impl::minlpreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minlpreport_owner& _minlpreport_owner::operator=(const _minlpreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minlpreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minlpreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minlpreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minlpreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minlpreport));
    alglib_impl::_minlpreport_init_copy(p_struct, const_cast<alglib_impl::minlpreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minlpreport_owner::~_minlpreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minlpreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minlpreport* _minlpreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minlpreport* _minlpreport_owner::c_ptr() const
{
    return p_struct;
}
minlpreport::minlpreport() : _minlpreport_owner() ,f(p_struct->f),lagbc(&p_struct->lagbc),laglc(&p_struct->laglc),y(&p_struct->y),stats(&p_struct->stats),primalerror(p_struct->primalerror),dualerror(p_struct->dualerror),slackerror(p_struct->slackerror),iterationscount(p_struct->iterationscount),terminationtype(p_struct->terminationtype)
{
}

minlpreport::minlpreport(alglib_impl::minlpreport *attach_to):_minlpreport_owner(attach_to) ,f(p_struct->f),lagbc(&p_struct->lagbc),laglc(&p_struct->laglc),y(&p_struct->y),stats(&p_struct->stats),primalerror(p_struct->primalerror),dualerror(p_struct->dualerror),slackerror(p_struct->slackerror),iterationscount(p_struct->iterationscount),terminationtype(p_struct->terminationtype)
{
}

minlpreport::minlpreport(const minlpreport &rhs):_minlpreport_owner(rhs) ,f(p_struct->f),lagbc(&p_struct->lagbc),laglc(&p_struct->laglc),y(&p_struct->y),stats(&p_struct->stats),primalerror(p_struct->primalerror),dualerror(p_struct->dualerror),slackerror(p_struct->slackerror),iterationscount(p_struct->iterationscount),terminationtype(p_struct->terminationtype)
{
}

minlpreport& minlpreport::operator=(const minlpreport &rhs)
{
    if( this==&rhs )
        return *this;
    _minlpreport_owner::operator=(rhs);
    return *this;
}

minlpreport::~minlpreport()
{
}
#endif

#if defined(AE_COMPILE_NLS) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
                DERIVATIVE-FREE NONLINEAR LEAST SQUARES

DESCRIPTION:

This function creates a NLS  solver  configured  to  solve  a  constrained
nonlinear least squares problem

    min F(x) = f[0]^2 + f[1]^2 + ... + f[m-1]^2

where f[i] are available, but not their derivatives.

The  functions  f[i]  are  assumed  to  be   smooth,  but  may  have  some
amount of numerical noise (either  random  noise  or  deterministic  noise
arising from numerical simulations or other complex numerical processes).

INPUT PARAMETERS:
    N       -   dimension, N>1
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    M       -   number of functions f[i], M>=1
    X       -   initial point, array[N]

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlscreatedfo(const ae_int_t n, const ae_int_t m, const real_1d_array &x, nlsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlscreatedfo(n, m, x.c_ptr(), state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
                DERIVATIVE-FREE NONLINEAR LEAST SQUARES

DESCRIPTION:

This function creates a NLS  solver  configured  to  solve  a  constrained
nonlinear least squares problem

    min F(x) = f[0]^2 + f[1]^2 + ... + f[m-1]^2

where f[i] are available, but not their derivatives.

The  functions  f[i]  are  assumed  to  be   smooth,  but  may  have  some
amount of numerical noise (either  random  noise  or  deterministic  noise
arising from numerical simulations or other complex numerical processes).

INPUT PARAMETERS:
    N       -   dimension, N>1
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    M       -   number of functions f[i], M>=1
    X       -   initial point, array[N]

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void nlscreatedfo(const ae_int_t m, const real_1d_array &x, nlsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlscreatedfo(n, m, x.c_ptr(), state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets the derivative-free NLS optimization algorithm  to  the
2PS (2-Point Stencil) algorithm.

This solver is recommended for the following cases:
* an expensive target function is minimized  by the commercial ALGLIB with
  callback  parallelism  activated  (see  ALGLIB Reference Manual for more
  information about parallel callbacks)
* an inexpensive target function is minimized by any ALGLIB edition  (free
  or commercial)

This function works only with solvers created with nlscreatedfo(), i.e. in
the derivative-free mode.

See the end of this comment for more information about the algorithm.

INPUT PARAMETERS:
    State           -   solver; must be created with nlscreatedfo() call -
                        passing  an  object   initialized   with   another
                        constructor function will result in an exception.
    NNoisyRestarts  -   number of restarts performed to combat a noise  in
                        the target. (see below, section 'RESTARTS', for  a
                        detailed discussion):
                        * 0     means that no restarts is performed, the
                                solver stops as soon as stopping  criteria
                                are met. Recommended for noise-free tasks.
                        * >0    means  that when the stopping criteria are
                                met, the solver will  perform  a  restart:
                                increase the trust   radius  and  resample
                                points. It often helps to  solve  problems
                                with random or deterministic noise.

ALGORITHM DESCRIPTION AND DISCUSSION

The 2PS algorithm is a derivative-free model-based nonlinear least squares
solver which builds local models by evaluating the target at  N additional
points around the current one, with geometry similar to the 2-point finite
difference stencil.

Similarly to the Levenberg-Marquardt algorithm, the solver shows quadratic
convergence despite the fact that it builds linear models.

When compared with the DFO-LSA solver, the 2PS algorithm has the following
distinctive properties:
* the 2PS algorithm performs more target function evaluations per iteration
  (at least N+1 instead of 1-2 usually performed by the DFO-LSA)
* 2PS requires several times less iterations than the DFO-LSA because each
  iteration extracts and utilizes more information about the  target. This
  difference tends to exaggerate when N increases
* contrary to that, DFO-LSA is much better at reuse of previously computed
  points. Thus, DFO-LSA needs several times less target  evaluations  than
  2PS, usually about 3-4 times less (this ratio seems to be more  or  less
  constant independently of N).

The summary is that:
* for  expensive  targets  2PS  provides better parallelism potential than
  DFO-LSA because the former issues many  simultaneous  target  evaluation
  requests which can be easily parallelized. It  is  possible  for  2PS to
  outperform DFO-LSA by parallelism  alone,  despite  the  fact  that  the
  latter needs 3-4 times less target function evaluations.
* for inexpensive targets 2PS may win  because  it  needs many  times less
  iterations, and thus the overhead associated with the working set updates
  is also many times less.

RESTARTS

Restarts is a strategy used to deal with random and deterministic noise in
the target/constraints.

Noise in the objective function can be random, arising from measurement or
simulation   uncertainty,  or  deterministic,   resulting   from   complex
underlying phenomena like numerical errors or branches in the target.  Its
influence is especially high at last stages of the optimization, when  all
computations are performed with small values of a trust radius.

Restarts allow the optimization algorithm to be robust against both  types
of noise by temporarily increasing a trust radius in order  to  capture  a
global structure of the target and avoid being trapped  by  noise-produced
local features.

A restart is usually performed when the stopping criteria  are  triggered.
Instead of stopping, the solver increases  trust  radius  to  its  initial
value and tries to rebuild a model.

If you decide to optimize with restarts,  it  is  recommended  to  perform
a small amount of restarts, up to 5. Generally, restarts do not allow  one
to completely solve the problem of noise, but still   it  is  possible  to
achieve some additional progress.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlssetalgo2ps(nlsstate &state, const ae_int_t nnoisyrestarts, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlssetalgo2ps(state.c_ptr(), nnoisyrestarts, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets the derivative-free NLS optimization algorithm  to  the
2PS (2-Point Stencil) algorithm.

This solver is recommended for the following cases:
* an expensive target function is minimized  by the commercial ALGLIB with
  callback  parallelism  activated  (see  ALGLIB Reference Manual for more
  information about parallel callbacks)
* an inexpensive target function is minimized by any ALGLIB edition  (free
  or commercial)

This function works only with solvers created with nlscreatedfo(), i.e. in
the derivative-free mode.

See the end of this comment for more information about the algorithm.

INPUT PARAMETERS:
    State           -   solver; must be created with nlscreatedfo() call -
                        passing  an  object   initialized   with   another
                        constructor function will result in an exception.
    NNoisyRestarts  -   number of restarts performed to combat a noise  in
                        the target. (see below, section 'RESTARTS', for  a
                        detailed discussion):
                        * 0     means that no restarts is performed, the
                                solver stops as soon as stopping  criteria
                                are met. Recommended for noise-free tasks.
                        * >0    means  that when the stopping criteria are
                                met, the solver will  perform  a  restart:
                                increase the trust   radius  and  resample
                                points. It often helps to  solve  problems
                                with random or deterministic noise.

ALGORITHM DESCRIPTION AND DISCUSSION

The 2PS algorithm is a derivative-free model-based nonlinear least squares
solver which builds local models by evaluating the target at  N additional
points around the current one, with geometry similar to the 2-point finite
difference stencil.

Similarly to the Levenberg-Marquardt algorithm, the solver shows quadratic
convergence despite the fact that it builds linear models.

When compared with the DFO-LSA solver, the 2PS algorithm has the following
distinctive properties:
* the 2PS algorithm performs more target function evaluations per iteration
  (at least N+1 instead of 1-2 usually performed by the DFO-LSA)
* 2PS requires several times less iterations than the DFO-LSA because each
  iteration extracts and utilizes more information about the  target. This
  difference tends to exaggerate when N increases
* contrary to that, DFO-LSA is much better at reuse of previously computed
  points. Thus, DFO-LSA needs several times less target  evaluations  than
  2PS, usually about 3-4 times less (this ratio seems to be more  or  less
  constant independently of N).

The summary is that:
* for  expensive  targets  2PS  provides better parallelism potential than
  DFO-LSA because the former issues many  simultaneous  target  evaluation
  requests which can be easily parallelized. It  is  possible  for  2PS to
  outperform DFO-LSA by parallelism  alone,  despite  the  fact  that  the
  latter needs 3-4 times less target function evaluations.
* for inexpensive targets 2PS may win  because  it  needs many  times less
  iterations, and thus the overhead associated with the working set updates
  is also many times less.

RESTARTS

Restarts is a strategy used to deal with random and deterministic noise in
the target/constraints.

Noise in the objective function can be random, arising from measurement or
simulation   uncertainty,  or  deterministic,   resulting   from   complex
underlying phenomena like numerical errors or branches in the target.  Its
influence is especially high at last stages of the optimization, when  all
computations are performed with small values of a trust radius.

Restarts allow the optimization algorithm to be robust against both  types
of noise by temporarily increasing a trust radius in order  to  capture  a
global structure of the target and avoid being trapped  by  noise-produced
local features.

A restart is usually performed when the stopping criteria  are  triggered.
Instead of stopping, the solver increases  trust  radius  to  its  initial
value and tries to rebuild a model.

If you decide to optimize with restarts,  it  is  recommended  to  perform
a small amount of restarts, up to 5. Generally, restarts do not allow  one
to completely solve the problem of noise, but still   it  is  possible  to
achieve some additional progress.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void nlssetalgo2ps(nlsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t nnoisyrestarts;

    nnoisyrestarts = 0;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlssetalgo2ps(state.c_ptr(), nnoisyrestarts, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets the derivative-free NLS optimization algorithm  to  the
DFO-LSA algorithm, an ALGLIB implementation (with several modifications)of
the original DFO-LS algorithm  by  Cartis, C., Fiala, J., Marteau, B.  and
Roberts, L. ('Improving the  Flexibility  and  Robustness  of  Model-Based
Derivative-Free Optimization Solvers', 2019). The A in DFO-LSA stands  for
ALGLIB, in order to distinguish our slightly modified implementation  from
the original algorithm.

This solver is recommended for the following  case:  an  expensive  target
function is minimized without parallelism being used (either  free  ALGLIB
is used or commercial one is used but the target callback is non-reentrant
i.e. it can not be simultaneously called from multiple threads)

This function works only with solvers created with nlscreatedfo(), i.e. in
the derivative-free mode.

See the end of this comment for more information about the algorithm.

INPUT PARAMETERS:
    State           -   solver; must be created with nlscreatedfo() call -
                        passing  an  object   initialized   with   another
                        constructor function will result in an exception.
    NNoisyRestarts  -   number of restarts performed to combat a noise  in
                        the target. (see below, section 'RESTARTS', for  a
                        detailed discussion):
                        * 0     means that no restarts is performed, the
                                solver stops as soon as stopping  criteria
                                are met. Recommended for noise-free tasks.
                        * >0    means  that when the stopping criteria are
                                met, the solver will  perform  a  restart:
                                increase the trust   radius  and  resample
                                points. It often helps to  solve  problems
                                with random or deterministic noise.

ALGORITHM DESCRIPTION AND DISCUSSION

The DFO-LSA algorithm is a derivative-free model-based  NLS  solver  which
builds local models by remembering N+1 previously computed  target  values
and updating them as optimization progresses.

Similarly to the Levenberg-Marquardt algorithm, the solver shows quadratic
convergence  despite  the  fact  that  it  builds   linear   models.   Our
implementation generally follows the same lines as the  original  DFO-LSA,
with several modifications to trust radius  update  strategies,  stability
fixes (unlike original DFO-LS, our implementation can handle  and  recover
from the target breaking down due to infeasible arguments) and other minor
implementation details.

When compared with the 2PS solver, the DFO-LSA algorithm has the following
distinctive properties:
* the 2PS algorithm performs more target function evaluations per iteration
  (at least N+1 instead of 1-2 usually performed by DFO-LSA)
* 2PS requires several times less iterations  than  DFO-LSA  because  each
  iterations extracts and utilizes more information about the target. This
  difference tends to exaggerate when N increases
* contrary to that, DFO-LSA is much better at reuse of previously computed
  points. Thus, DFO-LSA needs several times less target  evaluations  than
  2PS, usually about 3-4 times less (this ratio seems to be more  or  less
  constant independently of N).

The summary is that:
* for  expensive  targets DFO-LSA is much more efficient than 2PS  because
  it reuses previously computed target values as much as possible.
* however, DFO-LSA has little parallelism potential because  (unlike  2PS)
  it  does  not  evaluate the target  in several points simultaneously and
  independently
* additionally, because DFO-LSA performs many times more  iterations  than
  2PS, iteration overhead (working set updates and matrix  inversions)  is
  an issue here. For inexpensive targets it is possible for DFO-LSA to  be
  outperformed by 2PS merely because of the linear algebra cost.

RESTARTS

Restarts is a strategy used to deal with random and deterministic noise in
the target/constraints.

Noise in the objective function can be random, arising from measurement or
simulation   uncertainty,  or  deterministic,   resulting   from   complex
underlying phenomena like numerical errors or branches in the target.  Its
influence is especially high at last stages of the optimization, when  all
computations are performed with small values of a trust radius.

Restarts allow the optimization algorithm to be robust against both  types
of noise by temporarily increasing a trust radius in order  to  capture  a
global structure of the target and avoid being trapped  by  noise-produced
local features.

A restart is usually performed when the stopping criteria  are  triggered.
Instead of stopping, the solver increases  trust  radius  to  its  initial
value and tries to rebuild a model.

If you decide to optimize with restarts,  it  is  recommended  to  perform
a small amount of restarts, up to 5. Generally, restarts do not allow  one
to completely solve the problem of noise, but still   it  is  possible  to
achieve some additional progress.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlssetalgodfolsa(nlsstate &state, const ae_int_t nnoisyrestarts, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlssetalgodfolsa(state.c_ptr(), nnoisyrestarts, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets the derivative-free NLS optimization algorithm  to  the
DFO-LSA algorithm, an ALGLIB implementation (with several modifications)of
the original DFO-LS algorithm  by  Cartis, C., Fiala, J., Marteau, B.  and
Roberts, L. ('Improving the  Flexibility  and  Robustness  of  Model-Based
Derivative-Free Optimization Solvers', 2019). The A in DFO-LSA stands  for
ALGLIB, in order to distinguish our slightly modified implementation  from
the original algorithm.

This solver is recommended for the following  case:  an  expensive  target
function is minimized without parallelism being used (either  free  ALGLIB
is used or commercial one is used but the target callback is non-reentrant
i.e. it can not be simultaneously called from multiple threads)

This function works only with solvers created with nlscreatedfo(), i.e. in
the derivative-free mode.

See the end of this comment for more information about the algorithm.

INPUT PARAMETERS:
    State           -   solver; must be created with nlscreatedfo() call -
                        passing  an  object   initialized   with   another
                        constructor function will result in an exception.
    NNoisyRestarts  -   number of restarts performed to combat a noise  in
                        the target. (see below, section 'RESTARTS', for  a
                        detailed discussion):
                        * 0     means that no restarts is performed, the
                                solver stops as soon as stopping  criteria
                                are met. Recommended for noise-free tasks.
                        * >0    means  that when the stopping criteria are
                                met, the solver will  perform  a  restart:
                                increase the trust   radius  and  resample
                                points. It often helps to  solve  problems
                                with random or deterministic noise.

ALGORITHM DESCRIPTION AND DISCUSSION

The DFO-LSA algorithm is a derivative-free model-based  NLS  solver  which
builds local models by remembering N+1 previously computed  target  values
and updating them as optimization progresses.

Similarly to the Levenberg-Marquardt algorithm, the solver shows quadratic
convergence  despite  the  fact  that  it  builds   linear   models.   Our
implementation generally follows the same lines as the  original  DFO-LSA,
with several modifications to trust radius  update  strategies,  stability
fixes (unlike original DFO-LS, our implementation can handle  and  recover
from the target breaking down due to infeasible arguments) and other minor
implementation details.

When compared with the 2PS solver, the DFO-LSA algorithm has the following
distinctive properties:
* the 2PS algorithm performs more target function evaluations per iteration
  (at least N+1 instead of 1-2 usually performed by DFO-LSA)
* 2PS requires several times less iterations  than  DFO-LSA  because  each
  iterations extracts and utilizes more information about the target. This
  difference tends to exaggerate when N increases
* contrary to that, DFO-LSA is much better at reuse of previously computed
  points. Thus, DFO-LSA needs several times less target  evaluations  than
  2PS, usually about 3-4 times less (this ratio seems to be more  or  less
  constant independently of N).

The summary is that:
* for  expensive  targets DFO-LSA is much more efficient than 2PS  because
  it reuses previously computed target values as much as possible.
* however, DFO-LSA has little parallelism potential because  (unlike  2PS)
  it  does  not  evaluate the target  in several points simultaneously and
  independently
* additionally, because DFO-LSA performs many times more  iterations  than
  2PS, iteration overhead (working set updates and matrix  inversions)  is
  an issue here. For inexpensive targets it is possible for DFO-LSA to  be
  outperformed by 2PS merely because of the linear algebra cost.

RESTARTS

Restarts is a strategy used to deal with random and deterministic noise in
the target/constraints.

Noise in the objective function can be random, arising from measurement or
simulation   uncertainty,  or  deterministic,   resulting   from   complex
underlying phenomena like numerical errors or branches in the target.  Its
influence is especially high at last stages of the optimization, when  all
computations are performed with small values of a trust radius.

Restarts allow the optimization algorithm to be robust against both  types
of noise by temporarily increasing a trust radius in order  to  capture  a
global structure of the target and avoid being trapped  by  noise-produced
local features.

A restart is usually performed when the stopping criteria  are  triggered.
Instead of stopping, the solver increases  trust  radius  to  its  initial
value and tries to rebuild a model.

If you decide to optimize with restarts,  it  is  recommended  to  perform
a small amount of restarts, up to 5. Generally, restarts do not allow  one
to completely solve the problem of noise, but still   it  is  possible  to
achieve some additional progress.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void nlssetalgodfolsa(nlsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t nnoisyrestarts;

    nnoisyrestarts = 0;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlssetalgodfolsa(state.c_ptr(), nnoisyrestarts, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets stopping conditions

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsX    -   stop when the scaled trust region radius is  smaller  than
                EpsX.
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations   is    unlimited.

Passing  EpsX=0  and  MaxIts=0  (simultaneously)  will  lead  to automatic
stopping criterion selection (small EpsX).

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlssetcond(nlsstate &state, const double epsx, const ae_int_t maxits, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlssetcond(state.c_ptr(), epsx, maxits, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to NLSOptimize().

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlssetxrep(nlsstate &state, const bool needxrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlssetxrep(state.c_ptr(), needxrep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets variable scales

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Generally, scale is NOT considered to be a  form  of  preconditioner.  But
derivative-free optimizers often use scaling matrix both  in  the stopping
condition tests and as a preconditioner.

Proper scaling is very important for the algorithm performance. It is less
important for the quality of results, but still has some influence (it  is
easier  to  converge  when  variables  are  properly  scaled, so premature
stopping is possible when very badly scalled variables are  combined  with
relaxed stopping conditions).

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlssetscale(nlsstate &state, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlssetscale(state.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets box constraints

Box constraints are inactive by default (after initial creation). They are
preserved until explicitly turned off with another SetBC() call.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF (the latter is recommended).
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF (the latter is recommended).

NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

NOTE 2: unless  explicitly  mentioned  in  the   specific   NLS  algorithm
        description, the following holds:
        * box constraints are always satisfied exactly
        * the target is NOT evaluated outside of the box-constrained area

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlssetbc(nlsstate &state, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlssetbc(state.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function provides reverse communication interface
Reverse communication interface is not documented or recommended to use.
See below for functions which provide better documented API
*************************************************************************/
bool nlsiteration(nlsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::nlsiteration(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}


void nlsoptimize(nlsstate &state,
    void (*fvec)(const real_1d_array &x, real_1d_array &fi, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    alglib_impl::nlsstate &optimizer = *(state.c_ptr());
    alglib_impl::rcommv2_request request(
        optimizer.requesttype,
        optimizer.querysize, optimizer.queryfuncs, optimizer.queryvars, optimizer.querydim, optimizer.queryformulasize,
        optimizer.querydata.ptr.p_double, optimizer.replyfi.ptr.p_double, optimizer.replydj.ptr.p_double, optimizer.replysj, ptr,
        "nls");
    alglib_impl::rcommv2_callbacks callbacks;
    alglib_impl::rcommv2_buffers buffers(&state.c_ptr()->tmpx1, &state.c_ptr()->tmpc1, &state.c_ptr()->tmpf1, &state.c_ptr()->tmpg1, &state.c_ptr()->tmpj1, &state.c_ptr()->tmps1);
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(fvec!=NULL, "ALGLIB: error in 'nlsoptimize()' (fvec is NULL)", &_alglib_env_state);
callbacks.fvec = fvec;

    alglib_impl::nlssetprotocolv2(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::nlsiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
        if( optimizer.requesttype==3 )
        {
            const ae_int_t njobs = request.size*request.vars+request.size;
            for(alglib_impl::ae_int_t job_idx=0; job_idx<njobs; job_idx++)
                alglib_impl::process_v2request_3phase0(request, job_idx, callbacks, buffers);
            alglib_impl::process_v2request_3phase1(request);
            request.request = 0;
            continue;
        }
        if( optimizer.requesttype==5 )
        {
            const ae_int_t njobs = request.size*request.vars+request.size;
            for(alglib_impl::ae_int_t job_idx=0; job_idx<njobs; job_idx++)
                alglib_impl::process_v2request_5phase0(request, job_idx, callbacks, buffers);
            alglib_impl::process_v2request_5phase1(request);
            request.request = 0;
            continue;
        }if( optimizer.requesttype==4 )
        { 
            for(alglib_impl::ae_int_t qidx=0; qidx<optimizer.querysize; qidx++)
                alglib_impl::process_v2request_4(request, qidx, callbacks, buffers);
            optimizer.requesttype = 0;
            continue;
        }
        if( optimizer.requesttype==-1 )
        {
            memmove(&buffers.tmpX[0], optimizer.reportx.ptr.p_double, optimizer.queryvars*sizeof(double));
            if( rep!=NULL )
                rep(buffers.tmpX, optimizer.reportf, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
lbl_no_callback:
    alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'nlsoptimize' (some derivatives were not provided?)", &_alglib_env_state);
}


void nlsoptimize(nlsstate &state,
    void (*fvec)(const real_1d_array &x, real_1d_array &fi, void *ptr),
    void  (*jac)(const real_1d_array &x, real_1d_array &fi, real_2d_array &jac, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    alglib_impl::nlsstate &optimizer = *(state.c_ptr());
    alglib_impl::rcommv2_request request(
        optimizer.requesttype,
        optimizer.querysize, optimizer.queryfuncs, optimizer.queryvars, optimizer.querydim, optimizer.queryformulasize,
        optimizer.querydata.ptr.p_double, optimizer.replyfi.ptr.p_double, optimizer.replydj.ptr.p_double, optimizer.replysj, ptr,
        "nls");
    alglib_impl::rcommv2_callbacks callbacks;
    alglib_impl::rcommv2_buffers buffers(&state.c_ptr()->tmpx1, &state.c_ptr()->tmpc1, &state.c_ptr()->tmpf1, &state.c_ptr()->tmpg1, &state.c_ptr()->tmpj1, &state.c_ptr()->tmps1);
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(fvec!=NULL, "ALGLIB: error in 'nlsoptimize()' (fvec is NULL)", &_alglib_env_state);
    alglib_impl::ae_assert(jac!=NULL,  "ALGLIB: error in 'nlsoptimize()' (jac is NULL)", &_alglib_env_state);
callbacks.fvec = fvec;
callbacks.jac = jac;

    alglib_impl::nlssetprotocolv2(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::nlsiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
        if( optimizer.requesttype==2 )
        { 
            for(alglib_impl::ae_int_t qidx=0; qidx<optimizer.querysize; qidx++)
                alglib_impl::process_v2request_2(request, qidx, callbacks, buffers);
            optimizer.requesttype = 0;
            continue;
        }if( optimizer.requesttype==4 )
        { 
            for(alglib_impl::ae_int_t qidx=0; qidx<optimizer.querysize; qidx++)
                alglib_impl::process_v2request_4(request, qidx, callbacks, buffers);
            optimizer.requesttype = 0;
            continue;
        }
        if( optimizer.requesttype==-1 )
        {
            memmove(&buffers.tmpX[0], optimizer.reportx.ptr.p_double, optimizer.queryvars*sizeof(double));
            if( rep!=NULL )
                rep(buffers.tmpX, optimizer.reportf, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
lbl_no_callback:
    alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'nlsoptimize' (some derivatives were not provided?)", &_alglib_env_state);
}



/*************************************************************************
Nonlinear least squares solver results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[N], solution
    Rep     -   optimization  report;  includes  termination   codes   and
                additional information. Termination codes are returned  in
                rep.terminationtype field, its possible values are  listed
                below, see comments for this structure for more info.

                The termination code is a sum of a basic code (success  or
                failure)  and  one/several  additional  codes.  Additional
                codes are returned only for successful termination.

                The following basic codes can be returned:
                * -8    optimizer detected NAN/INF values in the target or
                        nonlinear constraints and failed to recover
                * -3    box constraints are inconsistent
                *  2    relative step is no more than EpsX.
                *  5    MaxIts steps was taken
                *  7    stopping conditions are too stringent,
                        further improvement is impossible
                *  8    terminated by user who called nlsrequesttermination().
                        X contains point which was "current accepted" when
                        termination request was submitted.

                The following additional codes can be returned  (added  to
                a basic code):
                * +800      if   during  algorithm  execution  the  solver
                            encountered NAN/INF values in  the  target  or
                            constraints but managed to recover by reducing
                            trust region radius, the  solver  returns  one
                            of SUCCESS codes but adds +800 to the code.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlsresults(const nlsstate &state, real_1d_array &x, nlsreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlsresults(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Buffered implementation of NLSResults(), which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 10.03.2009 by Bochkanov Sergey
*************************************************************************/
void nlsresultsbuf(const nlsstate &state, real_1d_array &x, nlsreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlsresultsbuf(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  subroutine  restarts solver from the new point. All optimization
parameters are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   optimizer
    X       -   new starting point.

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
void nlsrestartfrom(nlsstate &state, const real_1d_array &x, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlsrestartfrom(state.c_ptr(), x.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.

NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void nlsrequesttermination(nlsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::nlsrequesttermination(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************
Nonlinear least squares solver
*************************************************************************/
_nlsstate_owner::_nlsstate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_nlsstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::nlsstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::nlsstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::nlsstate));
    alglib_impl::_nlsstate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_nlsstate_owner::_nlsstate_owner(alglib_impl::nlsstate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_nlsstate_owner::_nlsstate_owner(const _nlsstate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_nlsstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: nlsstate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::nlsstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::nlsstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::nlsstate));
    alglib_impl::_nlsstate_init_copy(p_struct, const_cast<alglib_impl::nlsstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_nlsstate_owner& _nlsstate_owner::operator=(const _nlsstate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: nlsstate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: nlsstate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: nlsstate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_nlsstate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::nlsstate));
    alglib_impl::_nlsstate_init_copy(p_struct, const_cast<alglib_impl::nlsstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_nlsstate_owner::~_nlsstate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_nlsstate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::nlsstate* _nlsstate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::nlsstate* _nlsstate_owner::c_ptr() const
{
    return p_struct;
}
nlsstate::nlsstate() : _nlsstate_owner() 
{
}

nlsstate::nlsstate(alglib_impl::nlsstate *attach_to):_nlsstate_owner(attach_to) 
{
}

nlsstate::nlsstate(const nlsstate &rhs):_nlsstate_owner(rhs) 
{
}

nlsstate& nlsstate::operator=(const nlsstate &rhs)
{
    if( this==&rhs )
        return *this;
    _nlsstate_owner::operator=(rhs);
    return *this;
}

nlsstate::~nlsstate()
{
}




/*************************************************************************
Optimization report, filled by NLSResults() function

FIELDS:
* TerminationType, completion code, which is a sum of a BASIC code and  an
ADDITIONAL code.

  The following basic codes denote failure:
    * -8    optimizer detected NAN/INF  either  in  the  function  itself,
            or its Jacobian; recovery was impossible, abnormal termination
            reported.
    * -3    box constraints are inconsistent

  The following basic codes denote success:
    *  2    relative step is no more than EpsX.
    *  5    MaxIts steps was taken
    *  7    stopping conditions are too stringent,
            further improvement is impossible
    *  8    terminated   by  user  who  called  NLSRequestTermination().
            X contains point which was "current accepted" when termination
            request was submitted.

  Additional codes can be set on success, but not on failure:
    * +800  if   during   algorithm   execution   the   solver encountered
            NAN/INF values in the target or  constraints  but  managed  to
            recover by reducing trust region radius,  the  solver  returns
            one of SUCCESS codes but adds +800 to the code.

* IterationsCount, contains iterations count
* NFunc, number of function calculations
*************************************************************************/
_nlsreport_owner::_nlsreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_nlsreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::nlsreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::nlsreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::nlsreport));
    alglib_impl::_nlsreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_nlsreport_owner::_nlsreport_owner(alglib_impl::nlsreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_nlsreport_owner::_nlsreport_owner(const _nlsreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_nlsreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: nlsreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::nlsreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::nlsreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::nlsreport));
    alglib_impl::_nlsreport_init_copy(p_struct, const_cast<alglib_impl::nlsreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_nlsreport_owner& _nlsreport_owner::operator=(const _nlsreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: nlsreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: nlsreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: nlsreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_nlsreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::nlsreport));
    alglib_impl::_nlsreport_init_copy(p_struct, const_cast<alglib_impl::nlsreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_nlsreport_owner::~_nlsreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_nlsreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::nlsreport* _nlsreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::nlsreport* _nlsreport_owner::c_ptr() const
{
    return p_struct;
}
nlsreport::nlsreport() : _nlsreport_owner() ,iterationscount(p_struct->iterationscount),terminationtype(p_struct->terminationtype),nfunc(p_struct->nfunc)
{
}

nlsreport::nlsreport(alglib_impl::nlsreport *attach_to):_nlsreport_owner(attach_to) ,iterationscount(p_struct->iterationscount),terminationtype(p_struct->terminationtype),nfunc(p_struct->nfunc)
{
}

nlsreport::nlsreport(const nlsreport &rhs):_nlsreport_owner(rhs) ,iterationscount(p_struct->iterationscount),terminationtype(p_struct->terminationtype),nfunc(p_struct->nfunc)
{
}

nlsreport& nlsreport::operator=(const nlsreport &rhs)
{
    if( this==&rhs )
        return *this;
    _nlsreport_owner::operator=(rhs);
    return *this;
}

nlsreport::~nlsreport()
{
}
#endif

#if defined(AE_COMPILE_GIPM2) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_NLCGIPM2) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_MINBLEIC) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
                     BOUND CONSTRAINED OPTIMIZATION
       WITH ADDITIONAL LINEAR EQUALITY AND INEQUALITY CONSTRAINTS

DESCRIPTION:
The  subroutine  minimizes  function   F(x)  of N arguments subject to any
combination of:
* bound constraints
* linear inequality constraints
* linear equality constraints

REQUIREMENTS:
* user must provide function value and gradient
* starting point X0 must be feasible or
  not too far away from the feasible set
* grad(f) must be Lipschitz continuous on a level set:
  L = { x : f(x)<=f(x0) }
* function must be defined everywhere on the feasible set F

USAGE:

Constrained optimization if far more complex than the unconstrained one.
Here we give very brief outline of the BLEIC optimizer. We strongly recommend
you to read examples in the ALGLIB Reference Manual and to read ALGLIB User Guide
on optimization, which is available at http://www.alglib.net/optimization/

1. User initializes algorithm state with MinBLEICCreate() call

2. USer adds boundary and/or linear constraints by calling
   MinBLEICSetBC() and MinBLEICSetLC() functions.

3. User sets stopping conditions with MinBLEICSetCond().

4. User calls MinBLEICOptimize() function which takes algorithm  state and
   pointer (delegate, etc.) to callback function which calculates F/G.

5. User calls MinBLEICResults() to get solution

6. Optionally user may call MinBLEICRestartFrom() to solve another problem
   with same N but another starting point.
   MinBLEICRestartFrom() allows to reuse already initialized structure.

NOTE: if you have box-only constraints (no  general  linear  constraints),
      then MinBC optimizer can be better option. It uses  special,  faster
      constraint activation method, which performs better on problems with
      multiple constraints active at the solution.

      On small-scale problems performance of MinBC is similar to  that  of
      MinBLEIC, but on large-scale ones (hundreds and thousands of  active
      constraints) it can be several times faster than MinBLEIC.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size ofX
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleiccreate(const ae_int_t n, const real_1d_array &x, minbleicstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleiccreate(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
                     BOUND CONSTRAINED OPTIMIZATION
       WITH ADDITIONAL LINEAR EQUALITY AND INEQUALITY CONSTRAINTS

DESCRIPTION:
The  subroutine  minimizes  function   F(x)  of N arguments subject to any
combination of:
* bound constraints
* linear inequality constraints
* linear equality constraints

REQUIREMENTS:
* user must provide function value and gradient
* starting point X0 must be feasible or
  not too far away from the feasible set
* grad(f) must be Lipschitz continuous on a level set:
  L = { x : f(x)<=f(x0) }
* function must be defined everywhere on the feasible set F

USAGE:

Constrained optimization if far more complex than the unconstrained one.
Here we give very brief outline of the BLEIC optimizer. We strongly recommend
you to read examples in the ALGLIB Reference Manual and to read ALGLIB User Guide
on optimization, which is available at http://www.alglib.net/optimization/

1. User initializes algorithm state with MinBLEICCreate() call

2. USer adds boundary and/or linear constraints by calling
   MinBLEICSetBC() and MinBLEICSetLC() functions.

3. User sets stopping conditions with MinBLEICSetCond().

4. User calls MinBLEICOptimize() function which takes algorithm  state and
   pointer (delegate, etc.) to callback function which calculates F/G.

5. User calls MinBLEICResults() to get solution

6. Optionally user may call MinBLEICRestartFrom() to solve another problem
   with same N but another starting point.
   MinBLEICRestartFrom() allows to reuse already initialized structure.

NOTE: if you have box-only constraints (no  general  linear  constraints),
      then MinBC optimizer can be better option. It uses  special,  faster
      constraint activation method, which performs better on problems with
      multiple constraints active at the solution.

      On small-scale problems performance of MinBC is similar to  that  of
      MinBLEIC, but on large-scale ones (hundreds and thousands of  active
      constraints) it can be several times faster than MinBLEIC.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size ofX
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minbleiccreate(const real_1d_array &x, minbleicstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleiccreate(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
The subroutine is finite difference variant of MinBLEICCreate().  It  uses
finite differences in order to differentiate target function.

Description below contains information which is specific to  this function
only. We recommend to read comments on MinBLEICCreate() in  order  to  get
more information about creation of BLEIC optimizer.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[0..N-1].
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinBLEICSetScale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large truncation  errors, while too small
   step will result in too large numerical  errors.  1.0E-6  can  be  good
   value to start with.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is  less  robust and precise. CG needs exact gradient values. Imprecise
   gradient may slow  down  convergence, especially  on  highly  nonlinear
   problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
void minbleiccreatef(const ae_int_t n, const real_1d_array &x, const double diffstep, minbleicstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleiccreatef(n, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
The subroutine is finite difference variant of MinBLEICCreate().  It  uses
finite differences in order to differentiate target function.

Description below contains information which is specific to  this function
only. We recommend to read comments on MinBLEICCreate() in  order  to  get
more information about creation of BLEIC optimizer.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[0..N-1].
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinBLEICSetScale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large truncation  errors, while too small
   step will result in too large numerical  errors.  1.0E-6  can  be  good
   value to start with.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is  less  robust and precise. CG needs exact gradient values. Imprecise
   gradient may slow  down  convergence, especially  on  highly  nonlinear
   problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minbleiccreatef(const real_1d_array &x, const double diffstep, minbleicstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleiccreatef(n, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets boundary constraints for BLEIC optimizer.

Boundary constraints are inactive by default (after initial creation).
They are preserved after algorithm restart with MinBLEICRestartFrom().

NOTE: if you have box-only constraints (no  general  linear  constraints),
      then MinBC optimizer can be better option. It uses  special,  faster
      constraint activation method, which performs better on problems with
      multiple constraints active at the solution.

      On small-scale problems performance of MinBC is similar to  that  of
      MinBLEIC, but on large-scale ones (hundreds and thousands of  active
      constraints) it can be several times faster than MinBLEIC.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF.

NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

NOTE 2: this solver has following useful properties:
* bound constraints are always satisfied exactly
* function is evaluated only INSIDE area specified by  bound  constraints,
  even  when  numerical  differentiation is used (algorithm adjusts  nodes
  according to boundary constraints)

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetbc(minbleicstate &state, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicsetbc(state.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets linear constraints for BLEIC optimizer.

Linear constraints are inactive by default (after initial creation).
They are preserved after algorithm restart with MinBLEICRestartFrom().

INPUT PARAMETERS:
    State   -   structure previously allocated with MinBLEICCreate call.
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

NOTE 1: linear (non-bound) constraints are satisfied only approximately:
* there always exists some minor violation (about Epsilon in magnitude)
  due to rounding errors
* numerical differentiation, if used, may  lead  to  function  evaluations
  outside  of the feasible  area,   because   algorithm  does  NOT  change
  numerical differentiation formula according to linear constraints.
If you want constraints to be  satisfied  exactly, try to reformulate your
problem  in  such  manner  that  all constraints will become boundary ones
(this kind of constraints is always satisfied exactly, both in  the  final
solution and in all intermediate points).

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetlc(minbleicstate &state, const real_2d_array &c, const integer_1d_array &ct, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicsetlc(state.c_ptr(), c.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets linear constraints for BLEIC optimizer.

Linear constraints are inactive by default (after initial creation).
They are preserved after algorithm restart with MinBLEICRestartFrom().

INPUT PARAMETERS:
    State   -   structure previously allocated with MinBLEICCreate call.
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

NOTE 1: linear (non-bound) constraints are satisfied only approximately:
* there always exists some minor violation (about Epsilon in magnitude)
  due to rounding errors
* numerical differentiation, if used, may  lead  to  function  evaluations
  outside  of the feasible  area,   because   algorithm  does  NOT  change
  numerical differentiation formula according to linear constraints.
If you want constraints to be  satisfied  exactly, try to reformulate your
problem  in  such  manner  that  all constraints will become boundary ones
(this kind of constraints is always satisfied exactly, both in  the  final
solution and in all intermediate points).

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minbleicsetlc(minbleicstate &state, const real_2d_array &c, const integer_1d_array &ct, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t k;
    if( (c.rows()!=ct.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'minbleicsetlc': looks like one of arguments has wrong size");
    k = c.rows();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicsetlc(state.c_ptr(), c.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets stopping conditions for the optimizer.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsG    -   >=0
                The  subroutine  finishes  its  work   if   the  condition
                |v|<EpsG is satisfied, where:
                * |.| means Euclidian norm
                * v - scaled gradient vector, v[i]=g[i]*s[i]
                * g - gradient
                * s - scaling coefficients set by MinBLEICSetScale()
    EpsF    -   >=0
                The  subroutine  finishes  its work if on k+1-th iteration
                the  condition  |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1}
                is satisfied.
    EpsX    -   >=0
                The subroutine finishes its work if  on  k+1-th  iteration
                the condition |v|<=EpsX is fulfilled, where:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - step vector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinBLEICSetScale()
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead
to automatic stopping criterion selection.

NOTE: when SetCond() called with non-zero MaxIts, BLEIC solver may perform
      slightly more than MaxIts iterations. I.e., MaxIts  sets  non-strict
      limit on iterations count.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetcond(minbleicstate &state, const double epsg, const double epsf, const double epsx, const ae_int_t maxits, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicsetcond(state.c_ptr(), epsg, epsf, epsx, maxits, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets scaling coefficients for BLEIC optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Scaling is also used by finite difference variant of the optimizer  - step
along I-th axis is equal to DiffStep*S[I].

In  most  optimizers  (and  in  the  BLEIC  too)  scaling is NOT a form of
preconditioning. It just  affects  stopping  conditions.  You  should  set
preconditioner  by  separate  call  to  one  of  the  MinBLEICSetPrec...()
functions.

There is a special  preconditioning  mode, however,  which  uses   scaling
coefficients to form diagonal preconditioning matrix. You  can  turn  this
mode on, if you want.   But  you should understand that scaling is not the
same thing as preconditioning - these are two different, although  related
forms of tuning solver.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minbleicsetscale(minbleicstate &state, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicsetscale(state.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification of the preconditioner: preconditioning is turned off.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetprecdefault(minbleicstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicsetprecdefault(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification  of  the  preconditioner:  diagonal of approximate Hessian is
used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    D       -   diagonal of the approximate Hessian, array[0..N-1],
                (if larger, only leading N elements are used).

NOTE 1: D[i] should be positive. Exception will be thrown otherwise.

NOTE 2: you should pass diagonal of approximate Hessian - NOT ITS INVERSE.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetprecdiag(minbleicstate &state, const real_1d_array &d, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicsetprecdiag(state.c_ptr(), d.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification of the preconditioner: scale-based diagonal preconditioning.

This preconditioning mode can be useful when you  don't  have  approximate
diagonal of Hessian, but you know that your  variables  are  badly  scaled
(for  example,  one  variable is in [1,10], and another in [1000,100000]),
and most part of the ill-conditioning comes from different scales of vars.

In this case simple  scale-based  preconditioner,  with H[i] = 1/(s[i]^2),
can greatly improve convergence.

IMPRTANT: you should set scale of your variables  with  MinBLEICSetScale()
call  (before  or after MinBLEICSetPrecScale() call). Without knowledge of
the scale of your variables scale-based preconditioner will be  just  unit
matrix.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetprecscale(minbleicstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicsetprecscale(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinBLEICOptimize().

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetxrep(minbleicstate &state, const bool needxrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicsetxrep(state.c_ptr(), needxrep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets maximum step length

IMPORTANT: this feature is hard to combine with preconditioning. You can't
set upper limit on step length, when you solve optimization  problem  with
linear (non-boundary) constraints AND preconditioner turned on.

When  non-boundary  constraints  are  present,  you  have to either a) use
preconditioner, or b) use upper limit on step length.  YOU CAN'T USE BOTH!
In this case algorithm will terminate with appropriate error code.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    StpMax  -   maximum step length, >=0. Set StpMax to 0.0,  if you don't
                want to limit step length.

Use this subroutine when you optimize target function which contains exp()
or  other  fast  growing  functions,  and optimization algorithm makes too
large  steps  which  lead   to overflow. This function allows us to reject
steps  that  are  too  large  (and  therefore  expose  us  to the possible
overflow) without actually calculating function value at the x+stp*d.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetstpmax(minbleicstate &state, const double stpmax, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicsetstpmax(state.c_ptr(), stpmax, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function provides reverse communication interface
Reverse communication interface is not documented or recommended to use.
See below for functions which provide better documented API
*************************************************************************/
bool minbleiciteration(minbleicstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::minbleiciteration(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}


void minbleicoptimize(minbleicstate &state,
    void (*func)(const real_1d_array &x, double &func, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(func!=NULL, "ALGLIB: error in 'minbleicoptimize()' (func is NULL)", &_alglib_env_state);
    alglib_impl::minbleicsetprotocolv1(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minbleiciteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
                if( state.needf )
                {
                    func(state.x, state.f, ptr);
                    continue;
                }
        if( state.xupdated )
        {
            if( rep!=NULL )
                rep(state.x, state.f, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    lbl_no_callback:
        alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minbleicoptimize' (some derivatives were not provided?)", &_alglib_env_state);
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
}


void minbleicoptimize(minbleicstate &state,
    void (*grad)(const real_1d_array &x, double &func, real_1d_array &grad, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(grad!=NULL, "ALGLIB: error in 'minbleicoptimize()' (grad is NULL)", &_alglib_env_state);
    alglib_impl::minbleicsetprotocolv1(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minbleiciteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
                if( state.needfg )
                {
                    grad(state.x, state.f, state.g, ptr);
                    continue;
                }
        if( state.xupdated )
        {
            if( rep!=NULL )
                rep(state.x, state.f, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    lbl_no_callback:
        alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minbleicoptimize' (some derivatives were not provided?)", &_alglib_env_state);
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
}



/*************************************************************************
This  function  activates/deactivates verification  of  the  user-supplied
analytic gradient.

Upon  activation  of  this  option  OptGuard  integrity  checker  performs
numerical differentiation of your target function  at  the  initial  point
(note: future versions may also perform check  at  the  final  point)  and
compares numerical gradient with analytic one provided by you.

If difference is too large, an error flag is set and optimization  session
continues. After optimization session is over, you can retrieve the report
which  stores  both  gradients  and  specific  components  highlighted  as
suspicious by the OptGuard.

The primary OptGuard report can be retrieved with minbleicoptguardresults().

IMPORTANT: gradient check is a high-overhead option which  will  cost  you
           about 3*N additional function evaluations. In many cases it may
           cost as much as the rest of the optimization session.

           YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO
           CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY.

NOTE: unlike previous incarnation of the gradient checking code,  OptGuard
      does NOT interrupt optimization even if it discovers bad gradient.

INPUT PARAMETERS:
    State       -   structure used to store algorithm state
    TestStep    -   verification step used for numerical differentiation:
                    * TestStep=0 turns verification off
                    * TestStep>0 activates verification
                    You should carefully choose TestStep. Value  which  is
                    too large (so large that  function  behavior  is  non-
                    cubic at this scale) will lead  to  false  alarms. Too
                    short step will result in rounding  errors  dominating
                    numerical derivative.

                    You may use different step for different parameters by
                    means of setting scale with minbleicsetscale().

=== EXPLANATION ==========================================================

In order to verify gradient algorithm performs following steps:
  * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i],
    where X[i] is i-th component of the initial point and S[i] is a  scale
    of i-th parameter
  * F(X) is evaluated at these trial points
  * we perform one more evaluation in the middle point of the interval
  * we  build  cubic  model using function values and derivatives at trial
    points and we compare its prediction with actual value in  the  middle
    point

  -- ALGLIB --
     Copyright 15.06.2014 by Bochkanov Sergey
*************************************************************************/
void minbleicoptguardgradient(minbleicstate &state, const double teststep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicoptguardgradient(state.c_ptr(), teststep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0)
b) nonsmooth     target function (non-C1)

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.

      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.

      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.

      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.

=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbleicoptguardsmoothness(minbleicstate &state, const ae_int_t level, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicoptguardsmoothness(state.c_ptr(), level, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0)
b) nonsmooth     target function (non-C1)

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.

      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.

      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.

      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.

=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minbleicoptguardsmoothness(minbleicstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t level;

    level = 1;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicoptguardsmoothness(state.c_ptr(), level, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
Results of OptGuard integrity check, should be called  after  optimization
session is over.

=== PRIMARY REPORT =======================================================

OptGuard performs several checks which are intended to catch common errors
in the implementation of nonlinear function/gradient:
* incorrect analytic gradient
* discontinuous (non-C0) target functions (constraints)
* nonsmooth     (non-C1) target functions (constraints)

Each of these checks is activated with appropriate function:
* minbleicoptguardgradient() for gradient verification
* minbleicoptguardsmoothness() for C0/C1 checks

Following flags are set when these errors are suspected:
* rep.badgradsuspected, and additionally:
  * rep.badgradvidx for specific variable (gradient element) suspected
  * rep.badgradxbase, a point where gradient is tested
  * rep.badgraduser, user-provided gradient  (stored  as  2D  matrix  with
    single row in order to make  report  structure  compatible  with  more
    complex optimizers like MinNLC or MinLM)
  * rep.badgradnum,   reference    gradient    obtained    via   numerical
    differentiation (stored as  2D matrix with single row in order to make
    report structure compatible with more complex optimizers  like  MinNLC
    or MinLM)
* rep.nonc0suspected
* rep.nonc1suspected

=== ADDITIONAL REPORTS/LOGS ==============================================

Several different tests are performed to catch C0/C1 errors, you can  find
out specific test signaled error by looking to:
* rep.nonc0test0positive, for non-C0 test #0
* rep.nonc1test0positive, for non-C1 test #0
* rep.nonc1test1positive, for non-C1 test #1

Additional information (including line search logs)  can  be  obtained  by
means of:
* minbleicoptguardnonc1test0results()
* minbleicoptguardnonc1test1results()
which return detailed error reports, specific points where discontinuities
were found, and so on.

==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    rep     -   generic OptGuard report;  more  detailed  reports  can  be
                retrieved with other functions.

NOTE: false negatives (nonsmooth problems are not identified as  nonsmooth
      ones) are possible although unlikely.

      The reason  is  that  you  need  to  make several evaluations around
      nonsmoothness  in  order  to  accumulate  enough  information  about
      function curvature. Say, if you start right from the nonsmooth point,
      optimizer simply won't get enough data to understand what  is  going
      wrong before it terminates due to abrupt changes in the  derivative.
      It is also  possible  that  "unlucky"  step  will  move  us  to  the
      termination too quickly.

      Our current approach is to have less than 0.1%  false  negatives  in
      our test examples  (measured  with  multiple  restarts  from  random
      points), and to have exactly 0% false positives.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbleicoptguardresults(minbleicstate &state, optguardreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicoptguardresults(state.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #0

Nonsmoothness (non-C1) test #0 studies  function  values  (not  gradient!)
obtained during line searches and monitors  behavior  of  the  directional
derivative estimate.

This test is less powerful than test #1, but it does  not  depend  on  the
gradient values and thus it is more robust against artifacts introduced by
numerical differentiation.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], f[] - arrays of length CNT which store step lengths and  function
  values at these points; f[i] is evaluated in x0+stp[i]*d.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.

==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #0 "strong" report
    lngrep  -   C1 test #0 "long" report

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbleicoptguardnonc1test0results(const minbleicstate &state, optguardnonc1test0report &strrep, optguardnonc1test0report &lngrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicoptguardnonc1test0results(state.c_ptr(), strrep.c_ptr(), lngrep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #1

Nonsmoothness (non-C1)  test  #1  studies  individual  components  of  the
gradient computed during line search.

When precise analytic gradient is provided this test is more powerful than
test #0  which  works  with  function  values  and  ignores  user-provided
gradient.  However,  test  #0  becomes  more   powerful   when   numerical
differentiation is employed (in such cases test #1 detects  higher  levels
of numerical noise and becomes too conservative).

This test also tells specific components of the gradient which violate  C1
continuity, which makes it more informative than #0, which just tells that
continuity is violated.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* vidx - is an index of the variable in [0,N) with nonsmooth derivative
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], g[] - arrays of length CNT which store step lengths and  gradient
  values at these points; g[i] is evaluated in  x0+stp[i]*d  and  contains
  vidx-th component of the gradient.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.

==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #1 "strong" report
    lngrep  -   C1 test #1 "long" report

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbleicoptguardnonc1test1results(minbleicstate &state, optguardnonc1test1report &strrep, optguardnonc1test1report &lngrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicoptguardnonc1test1results(state.c_ptr(), strrep.c_ptr(), lngrep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
BLEIC results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report. You should check Rep.TerminationType
                in  order  to  distinguish  successful  termination  from
                unsuccessful one:
                * -8    internal integrity control  detected  infinite or
                        NAN   values   in   function/gradient.   Abnormal
                        termination signalled.
                * -3   inconsistent constraints. Feasible point is
                       either nonexistent or too hard to find. Try to
                       restart optimizer with better initial approximation
                *  1   relative function improvement is no more than EpsF.
                *  2   scaled step is no more than EpsX.
                *  4   scaled gradient norm is no more than EpsG.
                *  5   MaxIts steps was taken
                *  8   terminated by user who called minbleicrequesttermination().
                       X contains point which was "current accepted"  when
                       termination request was submitted.
                More information about fields of this  structure  can  be
                found in the comments on MinBLEICReport datatype.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicresults(const minbleicstate &state, real_1d_array &x, minbleicreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicresults(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
BLEIC results

Buffered implementation of MinBLEICResults() which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicresultsbuf(const minbleicstate &state, real_1d_array &x, minbleicreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicresultsbuf(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine restarts algorithm from new point.
All optimization parameters (including constraints) are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have  same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinBLEICCreate call.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicrestartfrom(minbleicstate &state, const real_1d_array &x, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicrestartfrom(state.c_ptr(), x.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.

NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void minbleicrequesttermination(minbleicstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicrequesttermination(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************
This object stores nonlinear optimizer state.
You should use functions provided by MinBLEIC subpackage to work with this
object
*************************************************************************/
_minbleicstate_owner::_minbleicstate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minbleicstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minbleicstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minbleicstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minbleicstate));
    alglib_impl::_minbleicstate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minbleicstate_owner::_minbleicstate_owner(alglib_impl::minbleicstate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minbleicstate_owner::_minbleicstate_owner(const _minbleicstate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minbleicstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minbleicstate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minbleicstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minbleicstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minbleicstate));
    alglib_impl::_minbleicstate_init_copy(p_struct, const_cast<alglib_impl::minbleicstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minbleicstate_owner& _minbleicstate_owner::operator=(const _minbleicstate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minbleicstate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minbleicstate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minbleicstate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minbleicstate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minbleicstate));
    alglib_impl::_minbleicstate_init_copy(p_struct, const_cast<alglib_impl::minbleicstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minbleicstate_owner::~_minbleicstate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minbleicstate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minbleicstate* _minbleicstate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minbleicstate* _minbleicstate_owner::c_ptr() const
{
    return p_struct;
}
minbleicstate::minbleicstate() : _minbleicstate_owner() ,needf(p_struct->needf),needfg(p_struct->needfg),xupdated(p_struct->xupdated),f(p_struct->f),g(&p_struct->g),x(&p_struct->x)
{
}

minbleicstate::minbleicstate(alglib_impl::minbleicstate *attach_to):_minbleicstate_owner(attach_to) ,needf(p_struct->needf),needfg(p_struct->needfg),xupdated(p_struct->xupdated),f(p_struct->f),g(&p_struct->g),x(&p_struct->x)
{
}

minbleicstate::minbleicstate(const minbleicstate &rhs):_minbleicstate_owner(rhs) ,needf(p_struct->needf),needfg(p_struct->needfg),xupdated(p_struct->xupdated),f(p_struct->f),g(&p_struct->g),x(&p_struct->x)
{
}

minbleicstate& minbleicstate::operator=(const minbleicstate &rhs)
{
    if( this==&rhs )
        return *this;
    _minbleicstate_owner::operator=(rhs);
    return *this;
}

minbleicstate::~minbleicstate()
{
}




/*************************************************************************
This structure stores optimization report:
* IterationsCount           number of iterations
* NFEV                      number of gradient evaluations
* TerminationType           termination type (see below)

TERMINATION CODES

TerminationType field contains completion code, which can be:
  -8    internal integrity control detected  infinite  or  NAN  values  in
        function/gradient. Abnormal termination signalled.
  -3    inconsistent constraints. Feasible point is
        either nonexistent or too hard to find. Try to
        restart optimizer with better initial approximation
   1    relative function improvement is no more than EpsF.
   2    relative step is no more than EpsX.
   4    gradient norm is no more than EpsG
   5    MaxIts steps was taken
   7    stopping conditions are too stringent,
        further improvement is impossible,
        X contains best point found so far.
   8    terminated by user who called minbleicrequesttermination(). X contains
        point which was "current accepted" when  termination  request  was
        submitted.

ADDITIONAL FIELDS

There are additional fields which can be used for debugging:
* DebugEqErr                error in the equality constraints (2-norm)
* DebugFS                   f, calculated at projection of initial point
                            to the feasible set
* DebugFF                   f, calculated at the final point
* DebugDX                   |X_start-X_final|
*************************************************************************/
_minbleicreport_owner::_minbleicreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minbleicreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minbleicreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minbleicreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minbleicreport));
    alglib_impl::_minbleicreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minbleicreport_owner::_minbleicreport_owner(alglib_impl::minbleicreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minbleicreport_owner::_minbleicreport_owner(const _minbleicreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minbleicreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minbleicreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minbleicreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minbleicreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minbleicreport));
    alglib_impl::_minbleicreport_init_copy(p_struct, const_cast<alglib_impl::minbleicreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minbleicreport_owner& _minbleicreport_owner::operator=(const _minbleicreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minbleicreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minbleicreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minbleicreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minbleicreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minbleicreport));
    alglib_impl::_minbleicreport_init_copy(p_struct, const_cast<alglib_impl::minbleicreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minbleicreport_owner::~_minbleicreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minbleicreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minbleicreport* _minbleicreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minbleicreport* _minbleicreport_owner::c_ptr() const
{
    return p_struct;
}
minbleicreport::minbleicreport() : _minbleicreport_owner() ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),varidx(p_struct->varidx),terminationtype(p_struct->terminationtype),debugeqerr(p_struct->debugeqerr),debugfs(p_struct->debugfs),debugff(p_struct->debugff),debugdx(p_struct->debugdx),debugfeasqpits(p_struct->debugfeasqpits),debugfeasgpaits(p_struct->debugfeasgpaits),inneriterationscount(p_struct->inneriterationscount),outeriterationscount(p_struct->outeriterationscount)
{
}

minbleicreport::minbleicreport(alglib_impl::minbleicreport *attach_to):_minbleicreport_owner(attach_to) ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),varidx(p_struct->varidx),terminationtype(p_struct->terminationtype),debugeqerr(p_struct->debugeqerr),debugfs(p_struct->debugfs),debugff(p_struct->debugff),debugdx(p_struct->debugdx),debugfeasqpits(p_struct->debugfeasqpits),debugfeasgpaits(p_struct->debugfeasgpaits),inneriterationscount(p_struct->inneriterationscount),outeriterationscount(p_struct->outeriterationscount)
{
}

minbleicreport::minbleicreport(const minbleicreport &rhs):_minbleicreport_owner(rhs) ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),varidx(p_struct->varidx),terminationtype(p_struct->terminationtype),debugeqerr(p_struct->debugeqerr),debugfs(p_struct->debugfs),debugff(p_struct->debugff),debugdx(p_struct->debugdx),debugfeasqpits(p_struct->debugfeasqpits),debugfeasgpaits(p_struct->debugfeasgpaits),inneriterationscount(p_struct->inneriterationscount),outeriterationscount(p_struct->outeriterationscount)
{
}

minbleicreport& minbleicreport::operator=(const minbleicreport &rhs)
{
    if( this==&rhs )
        return *this;
    _minbleicreport_owner::operator=(rhs);
    return *this;
}

minbleicreport::~minbleicreport()
{
}
#endif

#if defined(AE_COMPILE_MINNLC) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
                  NONLINEARLY  CONSTRAINED  OPTIMIZATION

DESCRIPTION:
The  subroutine  minimizes a function  F(x)  of N arguments subject to the
any combination of the:
* box constraints
* linear equality/inequality/range constraints CL<=Ax<=CU
* nonlinear equality/inequality/range constraints HL<=Hi(x)<=HU

REQUIREMENTS:
* F(), H() are continuously differentiable on the  feasible  set  and  its
  neighborhood
* starting point X0, which can be infeasible

USAGE:

Here we give the very brief outline  of  the MinNLC optimizer. We strongly
recommend you to study examples in the ALGLIB Reference Manual and to read
ALGLIB User Guide: https://www.alglib.net/nonlinear-programming/

1. The user initializes the solver with minnlccreate() or minnlccreatef(),
   depending on the specific solver chosen:
   * minnlccreate() for solvers capable of handling the  problem  'as is',
     without relying  on numerical differentiation (SQP/GIPM2 for problems
     with analytic derivatives, ORBIT for derivative-free problems)
   * minnlccreatef(), when a numerical differentiation is used to  provide
     derivatives to a smooth derivative-based solver (SQP or GIPM2).

   In the current release the following solvers can be used:

   * sparse large-scale filter-based SQP solver, recommended  for problems
     of any size (from several  variables  to  hundreds  of  thousands  of
     variables). Good at warm-starts. Activated with the minnlcsetalgosqp()
     function.

   * sparse large-scale nonlinear nonconvex interior point method (GIPM2),
     recommended  for  problems  of  any  size  (from several variables to
     hundreds of thousands of variables).  Has  lower  iteration  overhead
     than SQP, but is worse at using good initial  points  (it  will  need
     at  least  tens  of  iterations even when started from the solution).
     Activated with minnlcsetalgogipm2() function.

   * dense SQP-BFGS solver, recommended  for small-scale problems  (up  to
     several hundreds of variables). Requires  less  function  evaluations
     than SQP, but has more expensive iteration.
     Activated with minnlcsetalgosqpbfgs() function.

   * ORBIT, a model-based derivative  free  solver  that  uses  local  RBF
     models to optimize expensive objectives.  This  solver  is  activated
     with minnlcsetalgoorbit() function.

   * several other solvers, including legacy ones

2. [optional] user activates OptGuard  integrity checker  which  tries  to
   detect possible errors in the user-supplied callbacks:
   * discontinuity/nonsmoothness of the target/nonlinear constraints
   * errors in the analytic gradient provided by user
   This feature is essential for early prototyping stages because it helps
   to catch common coding and problem statement errors.
   OptGuard can be activated with following functions (one per each  check
   performed):
   * minnlcoptguardsmoothness()
   * minnlcoptguardgradient()

3. User adds boundary and/or linear and/or nonlinear constraints by  means
   of calling one of the following functions:
   a) minnlcsetbc() for boundary constraints
   b) minnlcsetlc2() for sparse two-sided linear constraints,
      minnlcsetlc2dense() for dense two-sided linear constraints,
      minnlcsetlc2mixed() for mixed sparse/dense two-sided linear constraints
    * minqpaddlc2dense()  to add one dense row to the dense constraint submatrix
    * minqpaddlc2()       to add one sparse row to the sparse constraint submatrix
    * minqpaddlc2sparsefromdense() to add one sparse row (passed as a dense array) to the sparse constraint submatrix
   c) minnlcsetnlc2() for nonlinear constraints
   You may combine (a), (b) and (c) in one optimization problem.

4. User sets scale of the variables with minnlcsetscale() function. It  is
   VERY important to set  scale  of  the  variables,  because  nonlinearly
   constrained problems are hard to solve when variables are badly scaled.
   Knowing  variable   scales   helps   to  check  stopping  criteria  and
   precondition the solver.

5. User sets stopping conditions with minnlcsetcond3() or minnlcsetcond().
   If NLC solver uses inner/outer  iteration  layout,  this  function sets
   stopping conditions for INNER iterations.

6. Finally, user calls minnlcoptimize()  function  which  takes  algorithm
   state and pointer (delegate, etc.) to callback function which calculates
   F/G/H.

7. User calls  minnlcresults()  to  get  solution;  additionally  you  can
   retrieve OptGuard report with minnlcoptguardresults(), and get detailed
   report about purported errors in the target function with:
   * minnlcoptguardnonc1test0results()
   * minnlcoptguardnonc1test1results()

8. Optionally user may call minnlcrestartfrom() to solve  another  problem
   with same N but another starting point. minnlcrestartfrom()  allows  to
   reuse already initialized structure.


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size ofX
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlccreate(const ae_int_t n, const real_1d_array &x, minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlccreate(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
                  NONLINEARLY  CONSTRAINED  OPTIMIZATION

DESCRIPTION:
The  subroutine  minimizes a function  F(x)  of N arguments subject to the
any combination of the:
* box constraints
* linear equality/inequality/range constraints CL<=Ax<=CU
* nonlinear equality/inequality/range constraints HL<=Hi(x)<=HU

REQUIREMENTS:
* F(), H() are continuously differentiable on the  feasible  set  and  its
  neighborhood
* starting point X0, which can be infeasible

USAGE:

Here we give the very brief outline  of  the MinNLC optimizer. We strongly
recommend you to study examples in the ALGLIB Reference Manual and to read
ALGLIB User Guide: https://www.alglib.net/nonlinear-programming/

1. The user initializes the solver with minnlccreate() or minnlccreatef(),
   depending on the specific solver chosen:
   * minnlccreate() for solvers capable of handling the  problem  'as is',
     without relying  on numerical differentiation (SQP/GIPM2 for problems
     with analytic derivatives, ORBIT for derivative-free problems)
   * minnlccreatef(), when a numerical differentiation is used to  provide
     derivatives to a smooth derivative-based solver (SQP or GIPM2).

   In the current release the following solvers can be used:

   * sparse large-scale filter-based SQP solver, recommended  for problems
     of any size (from several  variables  to  hundreds  of  thousands  of
     variables). Good at warm-starts. Activated with the minnlcsetalgosqp()
     function.

   * sparse large-scale nonlinear nonconvex interior point method (GIPM2),
     recommended  for  problems  of  any  size  (from several variables to
     hundreds of thousands of variables).  Has  lower  iteration  overhead
     than SQP, but is worse at using good initial  points  (it  will  need
     at  least  tens  of  iterations even when started from the solution).
     Activated with minnlcsetalgogipm2() function.

   * dense SQP-BFGS solver, recommended  for small-scale problems  (up  to
     several hundreds of variables). Requires  less  function  evaluations
     than SQP, but has more expensive iteration.
     Activated with minnlcsetalgosqpbfgs() function.

   * ORBIT, a model-based derivative  free  solver  that  uses  local  RBF
     models to optimize expensive objectives.  This  solver  is  activated
     with minnlcsetalgoorbit() function.

   * several other solvers, including legacy ones

2. [optional] user activates OptGuard  integrity checker  which  tries  to
   detect possible errors in the user-supplied callbacks:
   * discontinuity/nonsmoothness of the target/nonlinear constraints
   * errors in the analytic gradient provided by user
   This feature is essential for early prototyping stages because it helps
   to catch common coding and problem statement errors.
   OptGuard can be activated with following functions (one per each  check
   performed):
   * minnlcoptguardsmoothness()
   * minnlcoptguardgradient()

3. User adds boundary and/or linear and/or nonlinear constraints by  means
   of calling one of the following functions:
   a) minnlcsetbc() for boundary constraints
   b) minnlcsetlc2() for sparse two-sided linear constraints,
      minnlcsetlc2dense() for dense two-sided linear constraints,
      minnlcsetlc2mixed() for mixed sparse/dense two-sided linear constraints
    * minqpaddlc2dense()  to add one dense row to the dense constraint submatrix
    * minqpaddlc2()       to add one sparse row to the sparse constraint submatrix
    * minqpaddlc2sparsefromdense() to add one sparse row (passed as a dense array) to the sparse constraint submatrix
   c) minnlcsetnlc2() for nonlinear constraints
   You may combine (a), (b) and (c) in one optimization problem.

4. User sets scale of the variables with minnlcsetscale() function. It  is
   VERY important to set  scale  of  the  variables,  because  nonlinearly
   constrained problems are hard to solve when variables are badly scaled.
   Knowing  variable   scales   helps   to  check  stopping  criteria  and
   precondition the solver.

5. User sets stopping conditions with minnlcsetcond3() or minnlcsetcond().
   If NLC solver uses inner/outer  iteration  layout,  this  function sets
   stopping conditions for INNER iterations.

6. Finally, user calls minnlcoptimize()  function  which  takes  algorithm
   state and pointer (delegate, etc.) to callback function which calculates
   F/G/H.

7. User calls  minnlcresults()  to  get  solution;  additionally  you  can
   retrieve OptGuard report with minnlcoptguardresults(), and get detailed
   report about purported errors in the target function with:
   * minnlcoptguardnonc1test0results()
   * minnlcoptguardnonc1test1results()

8. Optionally user may call minnlcrestartfrom() to solve  another  problem
   with same N but another starting point. minnlcrestartfrom()  allows  to
   reuse already initialized structure.


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size ofX
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minnlccreate(const real_1d_array &x, minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlccreate(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
Buffered version of minnlccreate() which reuses already  allocated  memory
as much as possible.

  -- ALGLIB --
     Copyright 06.10.2024 by Bochkanov Sergey
*************************************************************************/
void minnlccreatebuf(const ae_int_t n, const real_1d_array &x, minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlccreatebuf(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Buffered version of minnlccreate() which reuses already  allocated  memory
as much as possible.

  -- ALGLIB --
     Copyright 06.10.2024 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minnlccreatebuf(const real_1d_array &x, minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlccreatebuf(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This subroutine is a finite  difference variant of MinNLCCreate(). It uses
finite differences in order to differentiate target function.

This function is relevant when you want to solve a derivative-free problem
using a solver that relies on gradient info being available (e.g.  SQP  or
GIPM2). Do not use it for derivative-free solvers  like  ORBIT,  the  algo
will generate an exception during optimization.

Description below contains information which is specific to this  function
only. We recommend to read comments on MinNLCCreate() in order to get more
information about creation of the NLC optimizer.

CALLBACK PARALLELISM

The MINNLC optimizer supports parallel parallel  numerical differentiation
('callback parallelism'). This feature, which  is  present  in  commercial
ALGLIB  editions,  greatly   accelerates   optimization   with   numerical
differentiation of an expensive target functions.

Callback parallelism is usually  beneficial  when  computing  a  numerical
gradient requires more than several  milliseconds.  In this case  the  job
of computing individual gradient components can be split between  multiple
threads. Even inexpensive targets can benefit  from  parallelism,  if  you
have many variables.

ALGLIB Reference Manual, 'Working with commercial  version' section, tells
how to activate callback parallelism for your programming language.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size ofX
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.
    DiffStep-   differentiation step, >0.
                By default, a 5-point formula is used  (actually,  only  4
                function values per variable are used because  the central
                one has zero coefficient due to symmetry; that's why  this
                formula is often called a 4-point one). It can be  changed
                with minnlcsetnumdiff() function.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

NOTES:

1. the differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinNLCSetScale() call.

2. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large TRUNCATION  errors, while too small
   step will result in too large NUMERICAL  errors.  1.0E-4  can  be  good
   value to start from.

3. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs ~N function evaluations. This function will work  for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.

   We should also say that code which relies on numerical  differentiation
   is  less   robust   and  precise.  Imprecise  gradient  may  slow  down
   convergence, especially  on  highly  nonlinear  problems  or  near  the
   solution.

   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlccreatef(const ae_int_t n, const real_1d_array &x, const double diffstep, minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlccreatef(n, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine is a finite  difference variant of MinNLCCreate(). It uses
finite differences in order to differentiate target function.

This function is relevant when you want to solve a derivative-free problem
using a solver that relies on gradient info being available (e.g.  SQP  or
GIPM2). Do not use it for derivative-free solvers  like  ORBIT,  the  algo
will generate an exception during optimization.

Description below contains information which is specific to this  function
only. We recommend to read comments on MinNLCCreate() in order to get more
information about creation of the NLC optimizer.

CALLBACK PARALLELISM

The MINNLC optimizer supports parallel parallel  numerical differentiation
('callback parallelism'). This feature, which  is  present  in  commercial
ALGLIB  editions,  greatly   accelerates   optimization   with   numerical
differentiation of an expensive target functions.

Callback parallelism is usually  beneficial  when  computing  a  numerical
gradient requires more than several  milliseconds.  In this case  the  job
of computing individual gradient components can be split between  multiple
threads. Even inexpensive targets can benefit  from  parallelism,  if  you
have many variables.

ALGLIB Reference Manual, 'Working with commercial  version' section, tells
how to activate callback parallelism for your programming language.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size ofX
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.
    DiffStep-   differentiation step, >0.
                By default, a 5-point formula is used  (actually,  only  4
                function values per variable are used because  the central
                one has zero coefficient due to symmetry; that's why  this
                formula is often called a 4-point one). It can be  changed
                with minnlcsetnumdiff() function.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

NOTES:

1. the differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinNLCSetScale() call.

2. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large TRUNCATION  errors, while too small
   step will result in too large NUMERICAL  errors.  1.0E-4  can  be  good
   value to start from.

3. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs ~N function evaluations. This function will work  for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.

   We should also say that code which relies on numerical  differentiation
   is  less   robust   and  precise.  Imprecise  gradient  may  slow  down
   convergence, especially  on  highly  nonlinear  problems  or  near  the
   solution.

   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minnlccreatef(const real_1d_array &x, const double diffstep, minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlccreatef(n, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
Buffered version of minnlccreatef() which reuses already allocated memory
as much as possible.

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlccreatefbuf(const ae_int_t n, const real_1d_array &x, const double diffstep, minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlccreatefbuf(n, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Buffered version of minnlccreatef() which reuses already allocated memory
as much as possible.

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minnlccreatefbuf(const real_1d_array &x, const double diffstep, minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlccreatefbuf(n, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets boundary constraints for NLC optimizer.

Boundary constraints are inactive by  default  (after  initial  creation).
They are preserved after algorithm restart with  MinNLCRestartFrom().

You may combine boundary constraints with  general  linear ones - and with
nonlinear ones! Boundary constraints are  handled  more  efficiently  than
other types.  Thus,  if  your  problem  has  mixed  constraints,  you  may
explicitly specify some of them as boundary and save some time/space.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF.

NOTE 1:  it is possible to specify  BndL[i]=BndU[i].  In  this  case  I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

NOTE 2:  when you solve your problem  with  augmented  Lagrangian  solver,
         boundary constraints are  satisfied  only  approximately!  It  is
         possible   that  algorithm  will  evaluate  function  outside  of
         feasible area!

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcsetbc(minnlcstate &state, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetbc(state.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets linear constraints for MinNLC optimizer.

Linear constraints are inactive by default (after initial creation).  They
are preserved after algorithm restart with MinNLCRestartFrom().

You may combine linear constraints with boundary ones - and with nonlinear
ones! If your problem has mixed constraints, you  may  explicitly  specify
some of them as linear. It  may  help  optimizer   to   handle  them  more
efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinNLCCreate call.
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

NOTE 1: when you solve your problem  with  augmented  Lagrangian   solver,
        linear constraints are  satisfied  only   approximately!   It   is
        possible   that  algorithm  will  evaluate  function  outside   of
        feasible area!

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcsetlc(minnlcstate &state, const real_2d_array &c, const integer_1d_array &ct, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetlc(state.c_ptr(), c.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets linear constraints for MinNLC optimizer.

Linear constraints are inactive by default (after initial creation).  They
are preserved after algorithm restart with MinNLCRestartFrom().

You may combine linear constraints with boundary ones - and with nonlinear
ones! If your problem has mixed constraints, you  may  explicitly  specify
some of them as linear. It  may  help  optimizer   to   handle  them  more
efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinNLCCreate call.
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

NOTE 1: when you solve your problem  with  augmented  Lagrangian   solver,
        linear constraints are  satisfied  only   approximately!   It   is
        possible   that  algorithm  will  evaluate  function  outside   of
        feasible area!

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minnlcsetlc(minnlcstate &state, const real_2d_array &c, const integer_1d_array &ct, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t k;
    if( (c.rows()!=ct.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'minnlcsetlc': looks like one of arguments has wrong size");
    k = c.rows();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetlc(state.c_ptr(), c.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU with dense
constraint matrix A.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 15.04.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcsetlc2dense(minnlcstate &state, const real_2d_array &a, const real_1d_array &al, const real_1d_array &au, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetlc2dense(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU with dense
constraint matrix A.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 15.04.2024 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minnlcsetlc2dense(minnlcstate &state, const real_2d_array &a, const real_1d_array &al, const real_1d_array &au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t k;
    if( (a.rows()!=al.length()) || (a.rows()!=au.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'minnlcsetlc2dense': looks like one of arguments has wrong size");
    k = a.rows();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetlc2dense(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
a sparse constraining matrix A. Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    A       -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 15.04.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcsetlc2(minnlcstate &state, const sparsematrix &a, const real_1d_array &al, const real_1d_array &au, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetlc2(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
a mixed constraining matrix A including a sparse part (first SparseK rows)
and a dense part (last DenseK rows). Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

This function may be useful if constraint matrix includes large number  of
both types of rows - dense and sparse. If you have just a few sparse rows,
you  may  represent  them  in  dense  format  without losing  performance.
Similarly, if you have just a few dense rows, you may store them in sparse
format with almost same performance.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    SparseA -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    SparseK -   number of sparse constraints, SparseK>=0
    DenseA  -   linear constraints, array[K,N], set of dense constraints.
                Each row of A represents one general linear constraint.
    DenseK  -   number of dense constraints, DenseK>=0
    AL, AU  -   lower and upper bounds, array[SparseK+DenseK], with former
                SparseK elements corresponding to sparse constraints,  and
                latter DenseK elements corresponding to dense constraints;
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 15.04.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcsetlc2mixed(minnlcstate &state, const sparsematrix &sparsea, const ae_int_t ksparse, const real_2d_array &densea, const ae_int_t kdense, const real_1d_array &al, const real_1d_array &au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetlc2mixed(state.c_ptr(), sparsea.c_ptr(), ksparse, densea.c_ptr(), kdense, al.c_ptr(), au.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends a two-sided linear constraint AL <= A*x <= AU to the
matrix of dense constraints.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    A       -   linear constraint coefficient, array[N], right side is NOT
                included.
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint Ai*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint Ai*x<=AU
                * AU=+INF  => one-sided constraint AL<=Ai*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 15.04.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcaddlc2dense(minnlcstate &state, const real_1d_array &a, const double al, const double au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcaddlc2dense(state.c_ptr(), a.c_ptr(), al, au, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present sparse constraints.

Constraint is passed in compressed format: as list of non-zero entries  of
coefficient vector A. Such approach is more efficient than  dense  storage
for highly sparse constraint vectors.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    IdxA    -   array[NNZ], indexes of non-zero elements of A:
                * can be unsorted
                * can include duplicate indexes (corresponding entries  of
                  ValA[] will be summed)
    ValA    -   array[NNZ], values of non-zero elements of A
    NNZ     -   number of non-zero coefficients in A
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcaddlc2(minnlcstate &state, const integer_1d_array &idxa, const real_1d_array &vala, const ae_int_t nnz, const double al, const double au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcaddlc2(state.c_ptr(), idxa.c_ptr(), vala.c_ptr(), nnz, al, au, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present sparse constraints.

Constraint vector A is  passed  as  a  dense  array  which  is  internally
sparsified by this function.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    DA      -   array[N], constraint vector
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcaddlc2sparsefromdense(minnlcstate &state, const real_1d_array &da, const double al, const double au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcaddlc2sparsefromdense(state.c_ptr(), da.c_ptr(), al, au, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets nonlinear constraints for MinNLC optimizer.

It sets constraints of the form

    Ci(x)=0 for i=0..NLEC-1
    Ci(x)<=0 for i=NLEC..NLEC+NLIC-1

See MinNLCSetNLC2() for a modern function which allows greater flexibility
in the constraint specification.

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcsetnlc(minnlcstate &state, const ae_int_t nlec, const ae_int_t nlic, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetnlc(state.c_ptr(), nlec, nlic, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets two-sided nonlinear constraints for MinNLC optimizer.

In fact, this function sets  only  constraints  COUNT  and  their  BOUNDS.
Constraints  themselves  (constraint  functions)   are   passed   to   the
MinNLCOptimize() method as callbacks.

MinNLCOptimize() method accepts a user-defined vector function F[] and its
Jacobian J[], where:
* first element of F[] and first row of J[] correspond to the target
* subsequent NNLC components of F[] (and rows of J[]) correspond  to  two-
  sided nonlinear constraints NL<=C(x)<=NU, where
  * NL[i]=NU[i] => I-th row is an equality constraint Ci(x)=NL
  * NL[i]<NU[i] => I-th tow is a  two-sided constraint NL[i]<=Ci(x)<=NU[i]
  * NL[i]=-INF  => I-th row is an one-sided constraint Ci(x)<=NU[i]
  * NU[i]=+INF  => I-th row is an one-sided constraint NL[i]<=Ci(x)
  * NL[i]=-INF, NU[i]=+INF => constraint is ignored

NOTE: you may combine nonlinear constraints with linear/boundary ones.  If
      your problem has mixed constraints, you  may explicitly specify some
      of them as linear or box ones.
      It helps optimizer to handle them more efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinNLCCreate call.
    NL      -   array[NNLC], lower bounds, can contain -INF
    NU      -   array[NNLC], lower bounds, can contain +INF
    NNLC    -   constraints count, NNLC>=0

NOTE 1: nonlinear constraints are satisfied only  approximately!   It   is
        possible that the algorithm will evaluate the function  outside of
        the feasible area!

NOTE 2: algorithm scales variables  according  to the scale  specified by
        MinNLCSetScale()  function,  so it can handle problems with badly
        scaled variables (as long as we KNOW their scales).

        However,  there  is  no  way  to  automatically  scale   nonlinear
        constraints. Inappropriate scaling  of nonlinear  constraints  may
        ruin convergence. Solving problem with  constraint  "1000*G0(x)=0"
        is NOT the same as solving it with constraint "0.001*G0(x)=0".

        It means that YOU are  the  one who is responsible for the correct
        scaling of the nonlinear constraints Gi(x) and Hi(x). We recommend
        you to scale nonlinear constraints in such a way that the Jacobian
        rows have approximately unit magnitude  (for  problems  with  unit
        scale) or have magnitude approximately equal to 1/S[i] (where S is
        a scale set by MinNLCSetScale() function).

  -- ALGLIB --
     Copyright 23.09.2023 by Bochkanov Sergey
*************************************************************************/
void minnlcsetnlc2(minnlcstate &state, const real_1d_array &nl, const real_1d_array &nu, const ae_int_t nnlc, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetnlc2(state.c_ptr(), nl.c_ptr(), nu.c_ptr(), nnlc, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets two-sided nonlinear constraints for MinNLC optimizer.

In fact, this function sets  only  constraints  COUNT  and  their  BOUNDS.
Constraints  themselves  (constraint  functions)   are   passed   to   the
MinNLCOptimize() method as callbacks.

MinNLCOptimize() method accepts a user-defined vector function F[] and its
Jacobian J[], where:
* first element of F[] and first row of J[] correspond to the target
* subsequent NNLC components of F[] (and rows of J[]) correspond  to  two-
  sided nonlinear constraints NL<=C(x)<=NU, where
  * NL[i]=NU[i] => I-th row is an equality constraint Ci(x)=NL
  * NL[i]<NU[i] => I-th tow is a  two-sided constraint NL[i]<=Ci(x)<=NU[i]
  * NL[i]=-INF  => I-th row is an one-sided constraint Ci(x)<=NU[i]
  * NU[i]=+INF  => I-th row is an one-sided constraint NL[i]<=Ci(x)
  * NL[i]=-INF, NU[i]=+INF => constraint is ignored

NOTE: you may combine nonlinear constraints with linear/boundary ones.  If
      your problem has mixed constraints, you  may explicitly specify some
      of them as linear or box ones.
      It helps optimizer to handle them more efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinNLCCreate call.
    NL      -   array[NNLC], lower bounds, can contain -INF
    NU      -   array[NNLC], lower bounds, can contain +INF
    NNLC    -   constraints count, NNLC>=0

NOTE 1: nonlinear constraints are satisfied only  approximately!   It   is
        possible that the algorithm will evaluate the function  outside of
        the feasible area!

NOTE 2: algorithm scales variables  according  to the scale  specified by
        MinNLCSetScale()  function,  so it can handle problems with badly
        scaled variables (as long as we KNOW their scales).

        However,  there  is  no  way  to  automatically  scale   nonlinear
        constraints. Inappropriate scaling  of nonlinear  constraints  may
        ruin convergence. Solving problem with  constraint  "1000*G0(x)=0"
        is NOT the same as solving it with constraint "0.001*G0(x)=0".

        It means that YOU are  the  one who is responsible for the correct
        scaling of the nonlinear constraints Gi(x) and Hi(x). We recommend
        you to scale nonlinear constraints in such a way that the Jacobian
        rows have approximately unit magnitude  (for  problems  with  unit
        scale) or have magnitude approximately equal to 1/S[i] (where S is
        a scale set by MinNLCSetScale() function).

  -- ALGLIB --
     Copyright 23.09.2023 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minnlcsetnlc2(minnlcstate &state, const real_1d_array &nl, const real_1d_array &nu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t nnlc;
    if( (nl.length()!=nu.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'minnlcsetnlc2': looks like one of arguments has wrong size");
    nnlc = nl.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetnlc2(state.c_ptr(), nl.c_ptr(), nu.c_ptr(), nnlc, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets specific finite  difference  formula  to  be  used  for
numerical differentiation.

It works only for optimizers created  with  minnlccreatef()  function;  in
other cases it has no effect.

INPUT PARAMETERS:
    State       -   structure previously allocated with MinNLCCreateF call.
    FormulaType -   formula type:
                    * 5 for a 5-point formula (actually, only 4 values per
                      variable  are  used, ones at x+h, x+h/2,  x-h/2  and
                      x-h; the central  one has  zero  multiplier  due  to
                      symmetry). The most precise and the  most  expensive
                      option, chosen by default
                    * 3 for a 3-point formula, which is also  known  as  a
                      symmetric difference quotient (the formula  actually
                      uses only two function values per variable:  at  x+h
                      and x-h). A   good  compromise  for  medium-accuracy
                      setups
                    * 2 for a forward (or backward, depending  on variable
                      bounds)  finite   difference  (f(x+h)-f(x))/h.  This
                      formula has the lowest accuracy. However, it  is  4x
                      faster than the 5-point formula and 2x  faster  than
                      the 3-point one because, in addition to the  central
                      value f(x), it needs only  one  additional  function
                      evaluation per variable.


  -- ALGLIB --
     Copyright 03.12.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcsetnumdiff(minnlcstate &state, const ae_int_t formulatype, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetnumdiff(state.c_ptr(), formulatype, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets stopping conditions for the optimizer.

This  function allows to set  iterations  limit  and  step-based  stopping
conditions. If you want the solver to stop upon having a small  change  in
the target, use minnlcsetcond3() function.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Eps     -   >=0, specific meaning depends on the algorithm:
                * for GIPM2 - stop when primal/dual/compl errors are less
                  than Eps
                * for SQP-based solvers - stop when the scaled trust region
                  radius is less than Eps
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic
selection of the stopping condition.

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcsetcond(minnlcstate &state, const double eps, const ae_int_t maxits, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetcond(state.c_ptr(), eps, maxits, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets stopping conditions for the optimizer.

This function allows to set three types of stopping conditions:
* iterations limit
* stopping upon performing a short step (depending on the specific  solver
  being used  it may stop as soon as the first short step was made, or
  only after performing several sequential short steps)
* stopping upon having a small change in  the  target  (depending  on  the
  specific solver being used it may stop as soon as the  first  step  with
  small change in the target was made, or only  after  performing  several
  sequential steps)

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsF    -   >=0
                The optimizer will stop as soon as the following condition
                is met:

                    |f_scl(k+1)-f_scl(k)| <= max(|f_scl(k+1)|,|f_scl(k)|,1)

                where f_scl is an internally used by the optimizer rescaled
                target (ALGLIB optimizers usually apply rescaling in order
                to normalize target and constraints).
    Eps     -   >=0, specific meaning depends on the algorithm:
                * for GIPM2 - stop when primal/dual/compl errors are less
                  than Eps
                * for SQP-based solvers - stop when the scaled trust region
                  radius is less than Eps
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsF, EpsX=0 and MaxIts=0 (simultaneously) will lead to the
automatic selection of the stopping condition.

  -- ALGLIB --
     Copyright 21.09.2023 by Bochkanov Sergey
*************************************************************************/
void minnlcsetcond3(minnlcstate &state, const double epsf, const double eps, const ae_int_t maxits, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetcond3(state.c_ptr(), epsf, eps, maxits, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets scaling coefficients for NLC optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison  with  tolerances).  Scales
are also used by the finite difference variant of the optimizer - the step
along I-th axis is equal to DiffStep*S[I]. Finally,  variable  scales  are
used for preconditioning (i.e. to speed up the solver).

The scale of the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcsetscale(minnlcstate &state, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetscale(state.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets maximum step length (after scaling of step vector  with
respect to variable scales specified by minnlcsetscale() call).

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    StpMax  -   maximum step length, >=0. Set StpMax to 0.0 (default),  if
                you don't want to limit step length.

Use this subroutine when you optimize target function which contains exp()
or  other  fast  growing  functions,  and optimization algorithm makes too
large  steps  which  leads  to overflow. This function allows us to reject
steps  that  are  too  large  (and  therefore  expose  us  to the possible
overflow) without actually calculating function value at the x+stp*d.

NOTE: different solvers employed by MinNLC  optimizer  may  use  different
      norms for the step.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minnlcsetstpmax(minnlcstate &state, const double stpmax, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetstpmax(state.c_ptr(), stpmax, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function tells MinNLC unit to use the large-scale augmented Lagrangian
algorithm for nonlinearly constrained optimization.

This  algorithm  is  a  significant  refactoring  of  one  described in "A
Modified Barrier-Augmented  Lagrangian Method for Constrained Minimization
(1999)" by D.GOLDFARB,  R.POLYAK,  K. SCHEINBERG,  I.YUZEFOVICH  with  the
following additions:
* improved sparsity support
* improved handling of large-scale problems with the low rank  LBFGS-based
  sparse preconditioner
* automatic selection of the penalty parameter Rho

AUL solver can be significantly faster than SQP on easy  problems  due  to
cheaper iterations, although it needs more function evaluations. On large-
scale sparse problems one iteration of the AUL solver usually  costs  tens
times less than one iteration of the SQP solver.

However, the SQP solver is more robust than the AUL. In particular, it  is
much better at constraint enforcement and will  never escape feasible area
after constraints were successfully enforced.  It  also  needs  much  less
target function evaluations.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    MaxOuterIts-upper limit on outer iterations count:
                * MaxOuterIts=0 means that the solver  will  automatically
                  choose an upper limit. Recommended value.
                * MaxOuterIts>1 means that the AUL solver will performs at
                  most specified number of outer iterations

  -- ALGLIB --
     Copyright 22.09.2023 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgoaul2(minnlcstate &state, const ae_int_t maxouterits, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetalgoaul2(state.c_ptr(), maxouterits, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function selects a legacy solver: an L1 merit function based SQP with
the sparse l-BFGS update.

It is recommended to use either SQP or SQP-BFGS solvers  instead  of  this
one.  These  solvers  use  filters  to  provide  much  faster  and  robust
convergence.
>

  -- ALGLIB --
     Copyright 02.12.2019 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgosl1qp(minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetalgosl1qp(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function selects a legacy solver: an L1 merit function based SQP with
the dense BFGS update.

It is recommended to use either SQP or SQP-BFGS solvers  instead  of  this
one.  These  solvers  use  filters  to  provide  much  faster  and  robust
convergence.

  -- ALGLIB --
     Copyright 02.12.2019 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgosl1qpbfgs(minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetalgosl1qpbfgs(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function selects large-scale  sparse  filter-based  SQP  solver,  the
most robust solver in ALGLIB, a recommended option.

This algorithm is scalable to problems with millions of variables and  can
efficiently handle sparsity of constraints.

The convergence is proved for the following case:
* function and constraints are continuously differentiable (C1 class)

This algorithm has the following nice properties:
* no parameters to tune
* no convexity requirements for target function or constraints
* the initial point can be infeasible
* the algorithm respects box constraints in all  intermediate  points  (it
  does not even evaluate the target outside of the box constrained area)
* once linear constraints are enforced, the algorithm will not violate them
* no such guarantees can be provided for nonlinear constraints,  but  once
  nonlinear  constraints  are  enforced,  the algorithm will try to respect
  them as much as possible
* numerical differentiation does  not  violate  box  constraints  (although
  general linear and nonlinear ones can be violated during differentiation)

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes significant amounts of
           time when solving large-scale problems).  It  can  also  use  a
           performance backend (e.g. Intel PARDISO  or  another  platform-
           specific library) to accelerate sparse factorization.

           Specific speed-up due to parallelism  and  performance  backend
           usage heavily depends on the sparsity pattern  of  constraints.
           For some  problem  types  performance  backends  provide  great
           speed-up. For other ones,  ALGLIB's  own  sparse  factorization
           code is the preferred option.

           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

===== TRACING SQP SOLVER =================================================

SQP solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'SQP'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
                  It also prints OptGuard  integrity  checker  report when
                  nonsmoothness of target/constraints is suspected.
* 'SQP.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'SQP'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format
* 'SQP.PROBING' - to let algorithm insert additional function  evaluations
                  before line search  in  order  to  build  human-readable
                  chart of the raw  Lagrangian  (~40  additional  function
                  evaluations is performed for  each  line  search).  This
                  symbol  also  implicitly  defines  'SQP'  and  activates
                  OptGuard integrity checker which detects continuity  and
                  smoothness violations. An OptGuard log is printed at the
                  end of the file.

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related   overhead.  Specifying  'SQP.PROBING'  adds   even  larger
overhead due to additional function evaluations being performed.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("SQP,SQP.PROBING,PREC.F6", "path/to/trace.log")
>

  -- ALGLIB --
     Copyright 02.12.2023 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgosqp(minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetalgosqp(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function selects ORBIT solver, a model-based  derivative-free  solver
for minimization of expensive derivative-free functions.

The ORBIT algorithm by Wild and Shoemaker (2013) is an algorithm that uses
objective function values to build a smooth  RBF  model  (f=r^3)  that  is
minimized over a trust region in order to  identify  step  direction.  The
algorithm saves and reuses function values at all previously known points.

ALGLIB added to the original algorithm the following modifications:
* box, linear and nonlinear constraints
* improved tolerance to noise in the objective/constraints

Its  intended  area  of  application  is  a  low-accuracy  minimization of
expensive objectives with no  gradient  available.  It  is  expected  that
additional overhead of building and minimizing an RBF model is  negligible
when compared with the objective evaluation cost. Iteration overhead grows
as O(N^3), so this solver is recommended for problems with N below 100.

This algorithm has the following nice properties:
* no parameters to tune
* no convexity requirements for target function or constraints
* the initial point can be infeasible
* the algorithm respects box constraints in all  intermediate  points  (it
  does not even evaluate the target outside of the box constrained area)
* once linear and nonlinear constraints are enforced, the  algorithm  will
  try to respect them as much as possible.

When compared with SQP solver, ORBIT:
* is much faster than the finite-difference  based  serial  SQP  at  early
  stages of optimization, being able to achieve 0.1-0.01 relative accuracy
  about 4x-10x faster than SQP solver
* has slower asymptotic convergence on ill-conditioned problems, sometimes
  being unable to reduce error in objective or constraints below  1E-5  in
  a reasonable amount of time
* has  no  obvious  benefits  over  SQP  with  analytic gradient or highly
  parallelized (more than 10 cores) finite-difference SQP

NOTE: whilst technically this algorithm supports callback parallelism,  in
      practice it can't efficiently utilize parallel resources because  it
      issues requests for objective/constraints in  an  inherently  serial
      manner.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

    Rad0    -   initial  sampling   radius  (multiplied  by   per-variable
                scales),  Rad0>=0,  zero  value  means  automatic   radius
                selection.
                An ideal  value  is  large  enough  to  allow  significant
                progress by making a Rad0-sized step, but  not  too  large
                (so  that  initial  linear  model  well  approximates  the
                objective).
                Recommended values: 0.1 or 1  (assuming  properly   chosen
                variable scales.
                The solver can tolerate inappropriately  chosen  Rad0,  at
                the expense of additional function evaluations  needed  to
                adjust it.

    MaxNFEV -   MaxNFEV>=0,  with   zero  value  meaning  no  limit.  This
                parameter allows to control computational budget (measured
                in  function  evaluations).

                It provides somewhat finer control than  MaxIts  parameter
                of minnlcsetcond(), which controls the maximum  amount  of
                iterations performed by the algorithm, with  one iteration
                usually needing more than one function evaluation.

                The  solver  does  not  stop  immediately  after  reaching
                MaxNFEV evaluations, but  will  stop  shortly  after  that
                (usually within N+1 evaluations, often within 1-2).

  -- ALGLIB --
     Copyright 02.10.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgoorbit(minnlcstate &state, const double rad0, const ae_int_t maxnfev, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetalgoorbit(state.c_ptr(), rad0, maxnfev, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function selects a SQP solver specialized on low-dimensional problems
- dense filter-based SQP-BFGS solver.

This algorithm uses a dense quadratic model of the  target  and  solves  a
dense QP subproblem at each step. Thus, it has difficulties scaling beyond
several hundreds of variables.  However,  it  usually  needs  the smallest
number of the target evaluations - sometimes  up  to  30%  less  than  the
sparse large-scale filter-based SQP.

The convergence is proved for the following case:
* function and constraints are continuously differentiable (C1 class)

This algorithm has the following nice properties:
* no parameters to tune
* no convexity requirements for target function or constraints
* the initial point can be infeasible
* the algorithm respects box constraints in all  intermediate  points  (it
  does not even evaluate the target outside of the box constrained area)
* once linear constraints are enforced, the algorithm will not violate them
* no such guarantees can be provided for nonlinear constraints,  but  once
  nonlinear  constraints  are  enforced,  the algorithm will try to respect
  them as much as possible
* numerical differentiation does  not  violate  box  constraints  (although
  general linear and nonlinear ones can be violated during differentiation)


IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). It can also use  a  performance  backend
           (e.g.  Intel  MKL  or  another  platform-specific  library)  to
           accelerate dense factorization.

           Dense Cholesky factorization is a well  studied  and  optimized
           algorithm. ALGLIB includes  a  well  optimized  implementation;
           however, using a hardware vendor-provided  performance  library
           usually results in a better performance.

           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

===== TRACING SQP SOLVER =================================================

SQP solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'SQP'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
                  It also prints OptGuard  integrity  checker  report when
                  nonsmoothness of target/constraints is suspected.
* 'SQP.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'SQP'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format
* 'SQP.PROBING' - to let algorithm insert additional function  evaluations
                  before line search  in  order  to  build  human-readable
                  chart of the raw  Lagrangian  (~40  additional  function
                  evaluations is performed for  each  line  search).  This
                  symbol  also  implicitly  defines  'SQP'  and  activates
                  OptGuard integrity checker which detects continuity  and
                  smoothness violations. An OptGuard log is printed at the
                  end of the file.

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related   overhead.  Specifying  'SQP.PROBING'  adds   even  larger
overhead due to additional function evaluations being performed.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("SQP,SQP.PROBING,PREC.F6", "path/to/trace.log")
>

  -- ALGLIB --
     Copyright 02.12.2023 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgosqpbfgs(minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetalgosqpbfgs(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function selects  large-scale sparse interior point solver (GIPM2).

This  algorithm  is  scalable  to  problems  with hundreds of thousands of
variables and can efficiently handle sparsity of constraints.

When compared to SQP, the following differences can be noted:
* GIPM2 has lower iteration overhead than SQP
* GIPM2 violates all non-box constraints (linear  and  nonlinear, equality
  and inequality ones) until it finally converges. Contrary to  that,  SQP
  respects linear constraints  after  initial  enforcement,  and  tends to
  closely follow nonlinear ones.
* as all interior point methods, GIPM2 is  bad  at  hot  starts.  Even  if
  started from solution, it will perform at least 30-60 iterations (first,
  it  will  move  away from the solution to find a centered initial point,
  then it will converge back along the central path).

The shorty summary is that SQP wins when starting close to  the  solution,
and GIPM2 tends to win on large-scale  sparse  problems  where  constraint
factorization adds significant linear algebra overhead.

The convergence is proved for the following case:
* function and constraints are continuously differentiable (C1 class)

This algorithm has the following nice properties:
* no parameters to tune
* no convexity requirements for target function or constraints
* the initial point can be infeasible
* the algorithm respects box constraints in all  intermediate  points  (it
  does not even evaluate the target outside of the box constrained area)
* numerical differentiation does  not  violate  box  constraints  (although
  general linear and nonlinear ones can be violated during differentiation)


IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes significant amounts of
           time when solving large-scale problems).  It  can  also  use  a
           performance backend (e.g. Intel PARDISO  or  another  platform-
           specific library) to accelerate sparse factorization.

           Specific speed-up due to parallelism  and  performance  backend
           usage heavily depends on the sparsity pattern  of  constraints.
           For some  problem  types  performance  backends  provide  great
           speed-up. For other ones,  ALGLIB's  own  sparse  factorization
           code is the preferred option.

           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.


INPUT PARAMETERS:
    State   -   structure which stores algorithm state

===== TRACING GIPM2 SOLVER ===============================================

GIPM2  solver  supports  advanced  tracing  capabilities.  You  can  trace
algorithm output by specifying following trace symbols  (case-insensitive)
by means of trace_file() call:
* 'GIPM2'       - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related   overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("GIPM2,PREC.F6", "path/to/trace.log")
>

  -- ALGLIB --
     Copyright 02.02.2025 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgogipm2(minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetalgogipm2(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinNLCOptimize().

NOTE: algorithm passes two parameters to rep() callback  -  current  point
      and penalized function value at current point. Important -  function
      value which is returned is NOT function being minimized. It  is  sum
      of the value of the function being minimized - and penalty term.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minnlcsetxrep(minnlcstate &state, const bool needxrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcsetxrep(state.c_ptr(), needxrep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function provides reverse communication interface
Reverse communication interface is not documented or recommended to use.
See below for functions which provide better documented API
*************************************************************************/
bool minnlciteration(minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::minnlciteration(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}


void minnlcoptimize(minnlcstate &state,
    void (*fvec)(const real_1d_array &x, real_1d_array &fi, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    alglib_impl::minnlcstate &optimizer = *(state.c_ptr());
    alglib_impl::rcommv2_request request(
        optimizer.requesttype,
        optimizer.querysize, optimizer.queryfuncs, optimizer.queryvars, optimizer.querydim, optimizer.queryformulasize,
        optimizer.querydata.ptr.p_double, optimizer.replyfi.ptr.p_double, optimizer.replydj.ptr.p_double, optimizer.replysj, ptr,
        "minnlc");
    alglib_impl::rcommv2_callbacks callbacks;
    alglib_impl::rcommv2_buffers buffers(&state.c_ptr()->tmpx1, &state.c_ptr()->tmpc1, &state.c_ptr()->tmpf1, &state.c_ptr()->tmpg1, &state.c_ptr()->tmpj1, &state.c_ptr()->tmps1);
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(fvec!=NULL, "ALGLIB: error in 'minnlcoptimize()' (fvec is NULL)", &_alglib_env_state);
callbacks.fvec = fvec;

    alglib_impl::minnlcsetprotocolv2(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minnlciteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
        if( optimizer.requesttype==3 )
        {
            const ae_int_t njobs = request.size*request.vars+request.size;
            for(alglib_impl::ae_int_t job_idx=0; job_idx<njobs; job_idx++)
                alglib_impl::process_v2request_3phase0(request, job_idx, callbacks, buffers);
            alglib_impl::process_v2request_3phase1(request);
            request.request = 0;
            continue;
        }
        if( optimizer.requesttype==5 )
        {
            const ae_int_t njobs = request.size*request.vars+request.size;
            for(alglib_impl::ae_int_t job_idx=0; job_idx<njobs; job_idx++)
                alglib_impl::process_v2request_5phase0(request, job_idx, callbacks, buffers);
            alglib_impl::process_v2request_5phase1(request);
            request.request = 0;
            continue;
        }if( optimizer.requesttype==4 )
        { 
            for(alglib_impl::ae_int_t qidx=0; qidx<optimizer.querysize; qidx++)
                alglib_impl::process_v2request_4(request, qidx, callbacks, buffers);
            optimizer.requesttype = 0;
            continue;
        }
        if( optimizer.requesttype==-1 )
        {
            memmove(&buffers.tmpX[0], optimizer.reportx.ptr.p_double, optimizer.queryvars*sizeof(double));
            if( rep!=NULL )
                rep(buffers.tmpX, optimizer.reportf, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
lbl_no_callback:
    alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minnlcoptimize' (some derivatives were not provided?)", &_alglib_env_state);
}


void minnlcoptimize(minnlcstate &state,
    void  (*jac)(const real_1d_array &x, real_1d_array &fi, real_2d_array &jac, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    alglib_impl::minnlcstate &optimizer = *(state.c_ptr());
    alglib_impl::rcommv2_request request(
        optimizer.requesttype,
        optimizer.querysize, optimizer.queryfuncs, optimizer.queryvars, optimizer.querydim, optimizer.queryformulasize,
        optimizer.querydata.ptr.p_double, optimizer.replyfi.ptr.p_double, optimizer.replydj.ptr.p_double, optimizer.replysj, ptr,
        "minnlc");
    alglib_impl::rcommv2_callbacks callbacks;
    alglib_impl::rcommv2_buffers buffers(&state.c_ptr()->tmpx1, &state.c_ptr()->tmpc1, &state.c_ptr()->tmpf1, &state.c_ptr()->tmpg1, &state.c_ptr()->tmpj1, &state.c_ptr()->tmps1);
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(jac!=NULL,  "ALGLIB: error in 'minnlcoptimize()' (jac is NULL)", &_alglib_env_state);
callbacks.jac = jac;

    alglib_impl::minnlcsetprotocolv2(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minnlciteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
        if( optimizer.requesttype==2 )
        { 
            for(alglib_impl::ae_int_t qidx=0; qidx<optimizer.querysize; qidx++)
                alglib_impl::process_v2request_2(request, qidx, callbacks, buffers);
            optimizer.requesttype = 0;
            continue;
        }
        if( optimizer.requesttype==-1 )
        {
            memmove(&buffers.tmpX[0], optimizer.reportx.ptr.p_double, optimizer.queryvars*sizeof(double));
            if( rep!=NULL )
                rep(buffers.tmpX, optimizer.reportf, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
lbl_no_callback:
    alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minnlcoptimize' (some derivatives were not provided?)", &_alglib_env_state);
}


void minnlcoptimize(minnlcstate &state,
    void (*sjac)(const real_1d_array &x, real_1d_array &fi, sparsematrix &s, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    alglib_impl::minnlcstate &optimizer = *(state.c_ptr());
    alglib_impl::rcommv2_request request(
        optimizer.requesttype,
        optimizer.querysize, optimizer.queryfuncs, optimizer.queryvars, optimizer.querydim, optimizer.queryformulasize,
        optimizer.querydata.ptr.p_double, optimizer.replyfi.ptr.p_double, optimizer.replydj.ptr.p_double, optimizer.replysj, ptr,
        "minnlc");
    alglib_impl::rcommv2_callbacks callbacks;
    alglib_impl::rcommv2_buffers buffers(&state.c_ptr()->tmpx1, &state.c_ptr()->tmpc1, &state.c_ptr()->tmpf1, &state.c_ptr()->tmpg1, &state.c_ptr()->tmpj1, &state.c_ptr()->tmps1);
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(sjac!=NULL,  "ALGLIB: error in 'minnlcoptimize()' (sjac is NULL)", &_alglib_env_state);
callbacks.sjac = sjac;

    alglib_impl::minnlcsetprotocolv2s(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minnlciteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
        if( optimizer.requesttype==1 )
        { 
            alglib_impl::sparsecreatecrsemptybuf(request.vars, &state.c_ptr()->replysj, &_alglib_env_state);
            for(alglib_impl::ae_int_t qidx=0; qidx<optimizer.querysize; qidx++)
                alglib_impl::process_v2request_1(request, qidx, callbacks, buffers, alglib_impl::rcommv2_request::query_sequential, &request.reply_sj);
            optimizer.requesttype = 0;
            continue;
        }
        if( optimizer.requesttype==-1 )
        {
            memmove(&buffers.tmpX[0], optimizer.reportx.ptr.p_double, optimizer.queryvars*sizeof(double));
            if( rep!=NULL )
                rep(buffers.tmpX, optimizer.reportf, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
lbl_no_callback:
    alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minnlcoptimize' (some derivatives were not provided?)", &_alglib_env_state);
}



/*************************************************************************
This  function  activates/deactivates verification  of  the  user-supplied
analytic gradient/Jacobian.

Upon  activation  of  this  option  OptGuard  integrity  checker  performs
numerical differentiation of your target  function  (constraints)  at  the
initial point (note: future versions may also perform check  at  the final
point) and compares numerical gradient/Jacobian with analytic one provided
by you.

If difference is too large, an error flag is set and optimization  session
continues. After optimization session is over, you can retrieve the report
which stores both gradients/Jacobians, and specific components highlighted
as suspicious by the OptGuard.

The primary OptGuard report can be retrieved with minnlcoptguardresults().

IMPORTANT: gradient check is a high-overhead option which  will  cost  you
           about 3*N additional function evaluations. In many cases it may
           cost as much as the rest of the optimization session.

           YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO
           CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY.

NOTE: unlike previous incarnation of the gradient checking code,  OptGuard
      does NOT interrupt optimization even if it discovers bad gradient.

INPUT PARAMETERS:
    State       -   structure used to store algorithm state
    TestStep    -   verification step used for numerical differentiation:
                    * TestStep=0 turns verification off
                    * TestStep>0 activates verification
                    You should carefully choose TestStep. Value  which  is
                    too large (so large that  function  behavior  is  non-
                    cubic at this scale) will lead  to  false  alarms. Too
                    short step will result in rounding  errors  dominating
                    numerical derivative.

                    You may use different step for different parameters by
                    means of setting scale with minnlcsetscale().

=== EXPLANATION ==========================================================

In order to verify gradient algorithm performs following steps:
  * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i],
    where X[i] is i-th component of the initial point and S[i] is a  scale
    of i-th parameter
  * F(X) is evaluated at these trial points
  * we perform one more evaluation in the middle point of the interval
  * we  build  cubic  model using function values and derivatives at trial
    points and we compare its prediction with actual value in  the  middle
    point

  -- ALGLIB --
     Copyright 15.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcoptguardgradient(minnlcstate &state, const double teststep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcoptguardgradient(state.c_ptr(), teststep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0) and/or constraints
b) nonsmooth     target function (non-C1) and/or constraints

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.

      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.

      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.

      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.
                      This kind of monitoring does not work well with  SQP
                      because SQP solver needs just 1-2 function evaluations
                      per step, which is not enough for OptGuard  to  make
                      any conclusions.

=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcoptguardsmoothness(minnlcstate &state, const ae_int_t level, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcoptguardsmoothness(state.c_ptr(), level, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0) and/or constraints
b) nonsmooth     target function (non-C1) and/or constraints

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.

      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.

      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.

      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.
                      This kind of monitoring does not work well with  SQP
                      because SQP solver needs just 1-2 function evaluations
                      per step, which is not enough for OptGuard  to  make
                      any conclusions.

=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minnlcoptguardsmoothness(minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t level;

    level = 1;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcoptguardsmoothness(state.c_ptr(), level, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
Results of OptGuard integrity check, should be called  after  optimization
session is over.

=== PRIMARY REPORT =======================================================

OptGuard performs several checks which are intended to catch common errors
in the implementation of nonlinear function/gradient:
* incorrect analytic gradient
* discontinuous (non-C0) target functions (constraints)
* nonsmooth     (non-C1) target functions (constraints)

Each of these checks is activated with appropriate function:
* minnlcoptguardgradient() for gradient verification
* minnlcoptguardsmoothness() for C0/C1 checks

Following flags are set when these errors are suspected:
* rep.badgradsuspected, and additionally:
  * rep.badgradfidx for specific function (Jacobian row) suspected
  * rep.badgradvidx for specific variable (Jacobian column) suspected
  * rep.badgradxbase, a point where gradient/Jacobian is tested
  * rep.badgraduser, user-provided gradient/Jacobian
  * rep.badgradnum, reference gradient/Jacobian obtained via numerical
    differentiation
* rep.nonc0suspected, and additionally:
  * rep.nonc0fidx - an index of specific function violating C0 continuity
* rep.nonc1suspected, and additionally
  * rep.nonc1fidx - an index of specific function violating C1 continuity
Here function index 0 means  target function, index 1  or  higher  denotes
nonlinear constraints.

=== ADDITIONAL REPORTS/LOGS ==============================================

Several different tests are performed to catch C0/C1 errors, you can  find
out specific test signaled error by looking to:
* rep.nonc0test0positive, for non-C0 test #0
* rep.nonc1test0positive, for non-C1 test #0
* rep.nonc1test1positive, for non-C1 test #1

Additional information (including line search logs)  can  be  obtained  by
means of:
* minnlcoptguardnonc1test0results()
* minnlcoptguardnonc1test1results()
which return detailed error reports, specific points where discontinuities
were found, and so on.

==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    rep     -   generic OptGuard report;  more  detailed  reports  can  be
                retrieved with other functions.

NOTE: false negatives (nonsmooth problems are not identified as  nonsmooth
      ones) are possible although unlikely.

      The reason  is  that  you  need  to  make several evaluations around
      nonsmoothness  in  order  to  accumulate  enough  information  about
      function curvature. Say, if you start right from the nonsmooth point,
      optimizer simply won't get enough data to understand what  is  going
      wrong before it terminates due to abrupt changes in the  derivative.
      It is also  possible  that  "unlucky"  step  will  move  us  to  the
      termination too quickly.

      Our current approach is to have less than 0.1%  false  negatives  in
      our test examples  (measured  with  multiple  restarts  from  random
      points), and to have exactly 0% false positives.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcoptguardresults(minnlcstate &state, optguardreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcoptguardresults(state.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #0

Nonsmoothness (non-C1) test #0 studies  function  values  (not  gradient!)
obtained during line searches and monitors  behavior  of  the  directional
derivative estimate.

This test is less powerful than test #1, but it does  not  depend  on  the
gradient values and thus it is more robust against artifacts introduced by
numerical differentiation.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* fidx - is an index of the function (0 for  target  function, 1 or higher
  for nonlinear constraints) which is suspected of being "non-C1"
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], f[] - arrays of length CNT which store step lengths and  function
  values at these points; f[i] is evaluated in x0+stp[i]*d.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.

==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #0 "strong" report
    lngrep  -   C1 test #0 "long" report

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcoptguardnonc1test0results(const minnlcstate &state, optguardnonc1test0report &strrep, optguardnonc1test0report &lngrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcoptguardnonc1test0results(state.c_ptr(), strrep.c_ptr(), lngrep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #1

Nonsmoothness (non-C1)  test  #1  studies  individual  components  of  the
gradient computed during line search.

When precise analytic gradient is provided this test is more powerful than
test #0  which  works  with  function  values  and  ignores  user-provided
gradient.  However,  test  #0  becomes  more   powerful   when   numerical
differentiation is employed (in such cases test #1 detects  higher  levels
of numerical noise and becomes too conservative).

This test also tells specific components of the gradient which violate  C1
continuity, which makes it more informative than #0, which just tells that
continuity is violated.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* fidx - is an index of the function (0 for  target  function, 1 or higher
  for nonlinear constraints) which is suspected of being "non-C1"
* vidx - is an index of the variable in [0,N) with nonsmooth derivative
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], g[] - arrays of length CNT which store step lengths and  gradient
  values at these points; g[i] is evaluated in  x0+stp[i]*d  and  contains
  vidx-th component of the gradient.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.

==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #1 "strong" report
    lngrep  -   C1 test #1 "long" report

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcoptguardnonc1test1results(minnlcstate &state, optguardnonc1test1report &strrep, optguardnonc1test1report &lngrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcoptguardnonc1test1results(state.c_ptr(), strrep.c_ptr(), lngrep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
MinNLC results:  the  solution  found,  completion  codes  and  additional
information.

If you activated OptGuard integrity checking functionality and want to get
OptGuard report, it can be retrieved with:
* minnlcoptguardresults() - for a primary report about (a) suspected C0/C1
  continuity violations and (b) errors in the analytic gradient.
* minnlcoptguardnonc1test0results() - for C1 continuity violation test #0,
  detailed line search log
* minnlcoptguardnonc1test1results() - for C1 continuity violation test #1,
  detailed line search log

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report, contains information about completion
                code, constraint violation at the solution and so on.

                rep.f contains objective value at the solution.

                You   should   check   rep.terminationtype  in  order   to
                distinguish successful termination from unsuccessful one:

                === FAILURE CODES ===
                * -8    internal  integrity control  detected  infinite or
                        NAN  values  in  function/gradient,  recovery  was
                        impossible. Abnormal termination signalled.
                * -3    box  constraints are infeasible.
                        Note: infeasibility of  non-box  constraints  does
                              NOT trigger emergency completion;  you  have
                              to examine rep.bcerr/rep.lcerr/rep.nlcerr to
                              detect possibly inconsistent constraints.

                === SUCCESS CODES ===
                *  1   small objective decrease indicates convergence
                *  2   scaled step is no more than EpsX.
                *  5   MaxIts steps were taken.
                *  7   stopping conditions are  too  stringent,  returning
                       the best point so far
                *  8   user   requested    algorithm    termination    via
                       minnlcrequesttermination(), last accepted point  is
                       returned.

                === ADDITIONAL CODES ===
                * +800      if   during  algorithm  execution  the  solver
                            encountered NAN/INF values in  the  target  or
                            constraints but managed to recover by reducing
                            trust region radius, the  solver  returns  one
                            of SUCCESS codes but adds +800 to the code.

                Some  solvers  (as  of  ALGLIB 4.05, SQP and GIPM2) return
                Lagrange multipliers in rep.lagbc/lagbcnz,  laglc,  lagnlc
                fields.

                More information about fields of this  structure  can  be
                found in the comments on the minnlcreport datatype.

  -- ALGLIB --
     Copyright 18.01.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcresults(const minnlcstate &state, real_1d_array &x, minnlcreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcresults(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
NLC results

Buffered implementation of MinNLCResults() which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minnlcresultsbuf(const minnlcstate &state, real_1d_array &x, minnlcreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcresultsbuf(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.

NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcrequesttermination(minnlcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcrequesttermination(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine restarts algorithm from new point.
All optimization parameters (including constraints) are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have  same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinNLCCreate call.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minnlcrestartfrom(minnlcstate &state, const real_1d_array &x, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnlcrestartfrom(state.c_ptr(), x.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************
This object stores nonlinear optimizer state.
You should use functions provided by MinNLC subpackage to work  with  this
object
*************************************************************************/
_minnlcstate_owner::_minnlcstate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minnlcstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minnlcstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minnlcstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minnlcstate));
    alglib_impl::_minnlcstate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minnlcstate_owner::_minnlcstate_owner(alglib_impl::minnlcstate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minnlcstate_owner::_minnlcstate_owner(const _minnlcstate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minnlcstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minnlcstate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minnlcstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minnlcstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minnlcstate));
    alglib_impl::_minnlcstate_init_copy(p_struct, const_cast<alglib_impl::minnlcstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minnlcstate_owner& _minnlcstate_owner::operator=(const _minnlcstate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minnlcstate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minnlcstate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minnlcstate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minnlcstate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minnlcstate));
    alglib_impl::_minnlcstate_init_copy(p_struct, const_cast<alglib_impl::minnlcstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minnlcstate_owner::~_minnlcstate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minnlcstate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minnlcstate* _minnlcstate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minnlcstate* _minnlcstate_owner::c_ptr() const
{
    return p_struct;
}
minnlcstate::minnlcstate() : _minnlcstate_owner() 
{
}

minnlcstate::minnlcstate(alglib_impl::minnlcstate *attach_to):_minnlcstate_owner(attach_to) 
{
}

minnlcstate::minnlcstate(const minnlcstate &rhs):_minnlcstate_owner(rhs) 
{
}

minnlcstate& minnlcstate::operator=(const minnlcstate &rhs)
{
    if( this==&rhs )
        return *this;
    _minnlcstate_owner::operator=(rhs);
    return *this;
}

minnlcstate::~minnlcstate()
{
}




/*************************************************************************
These fields store optimization report:
* f                         objective value at the solution
* iterationscount           total number of inner iterations
* nfev                      number of gradient evaluations
* terminationtype           termination type (see below)

Scaled constraint violations are reported:
* bcerr                     maximum violation of the box constraints
* bcidx                     index of the most violated box  constraint (or
                            -1, if all box constraints  are  satisfied  or
                            there is no box constraint)
* lcerr                     maximum violation of the  linear  constraints,
                            computed as maximum  scaled  distance  between
                            final point and constraint boundary.
* lcidx                     index of the most violated  linear  constraint
                            (or -1, if all constraints  are  satisfied  or
                            there is no general linear constraints)
* nlcerr                    maximum violation of the nonlinear constraints
* nlcidx                    index of the most violated nonlinear constraint
                            (or -1, if all constraints  are  satisfied  or
                            there is no nonlinear constraints)
* sclfeaserr                maximum  violation  over   all    constraints,
                            computed with scaling/renormalization applied.

Violations of box constraints are scaled on per-component basis  according
to  the  scale  vector s[] as specified by minnlcsetscale(). Violations of
the general linear  constraints  are  also  computed  using  user-supplied
variable scaling. Violations of nonlinear constraints are computed "as is"

LAGRANGE COEFFICIENTS

GIPM2 and modern SQP (one activated by  setalgosqp()/setalgosqpbfgs(), but
not with legacy functions) set the following fields  (other  solvers  fill
them by zeros):

* lagbc[]                   array[N],   Lagrange   multipliers   for   box
                            constraints. IMPORTANT: COEFFICIENTS FOR FIXED
                            VARIABLES ARE SET TO ZERO. See  below  for  an
                            explanation.
                            This  parameter   stores    the  same  results
                            independently of whether analytic gradient  is
                            provided or numerical differentiation is used.

* lagbcnz[]                 array[N],   Lagrange   multipliers   for   box
                            constraints, behaves differently depending  on
                            whether  analytic  gradient  is   provided  or
                            numerical differentiation is used:
                            * for analytic Jacobian,   lagbcnz[]  contains
                              correct   coefficients   for  all  kinds  of
                              variables - fixed or not.
                            * for numerical Jacobian, it is  the  same  as
                              lagbc[], i.e. components corresponding  to
                              fixed vars are zero.
                            See below for an explanation.

* laglc[]                   array[Mlin], coeffs for linear constraints

* lagnlc[]                  array[Mnlc], coeffs for nonlinear constraints


Positive Lagrange coefficient means that constraint is at its upper bound.
Negative coefficient means that constraint is at its lower  bound.  It  is
expected that at the solution the dual feasibility condition holds:

    grad + SUM(Ei*LagBC[i],i=0..n-1) +
        SUM(Ai*LagLC[i],i=0..mlin-1) +
        SUM(Ni*LagNLC[i],i=0..mnlc-1) ~ 0

    (except for fixed variables which are handled specially)

where
* grad is a gradient at the solution
* Ei is a vector with 1.0 at position I and 0 in other positions
* Ai is an I-th row of linear constraint matrix
* Ni is an gradient of I-th nonlinear constraint

Fixed variables have two sets of Lagrange multipliers  for  the  following
reasons:
* analytic gradient and numerical gradient behave  differently  for  fixed
  vars. Numerical differentiation does not violate box  constraints,  thus
  gradient components corresponding to fixed vars are zero because we have
  no way of  differentiating  for  these  vars   without   violating   box
  constraints.
  Contrary to that, analytic gradient usually returns correct values  even
  for fixed vars.
* ideally,  we  would  like  numerical  gradient  to  be an almost perfect
  replacement for an analytic one.  Thus,  we  need  Lagrange  multipliers
  which do not change when we change the gradient type.
* on the other hand, we do not want to  lose  the  possibility  of  having
  a full set of Lagrange multipliers for problems with analytic gradient.
  Thus, there is a special field lagbcnz[] whose contents depends  on  the
  information available to us.

TERMINATION CODES

TerminationType field contains completion code, which can be either FAILURE
code, SUCCESS code, or SUCCESS code + ADDITIONAL code.  The  latter option
is used for more detailed reporting.

=== FAILURE CODE ===
  -8    internal integrity control detected  infinite  or  NAN  values  in
        function/gradient, recovery was impossible.  Abnormal  termination
        signaled.
  -3    box  constraints  are  infeasible.  Note: infeasibility of non-box
        constraints does NOT trigger emergency  completion;  you  have  to
        examine  bcerr/lcerr/nlcerr   to  detect   possibly   inconsistent
        constraints.

=== SUCCESS CODE ===
   1    small objective decrease indicates convergence
   2    Eps-based condition (depending on the solver):
        * relative step is no more than Eps (for SQP and similar)
        * primal/dual/complementarity errors are less than Eps (for GIPM2)
   5    MaxIts steps was taken
   7    stopping conditions are too stringent,
        further improvement is impossible,
        X contains best point found so far.
   8    user requested algorithm termination via minnlcrequesttermination(),
        last accepted point is returned

=== ADDITIONAL CODES ===
* +800      if   during   algorithm   execution   the   solver encountered
            NAN/INF values in the target or  constraints  but  managed  to
            recover by reducing trust region radius,  the  solver  returns
            one of SUCCESS codes but adds +800 to the code.

Other fields of this structure are not documented and should not be used!
*************************************************************************/
_minnlcreport_owner::_minnlcreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minnlcreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minnlcreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minnlcreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minnlcreport));
    alglib_impl::_minnlcreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minnlcreport_owner::_minnlcreport_owner(alglib_impl::minnlcreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minnlcreport_owner::_minnlcreport_owner(const _minnlcreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minnlcreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minnlcreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minnlcreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minnlcreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minnlcreport));
    alglib_impl::_minnlcreport_init_copy(p_struct, const_cast<alglib_impl::minnlcreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minnlcreport_owner& _minnlcreport_owner::operator=(const _minnlcreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minnlcreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minnlcreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minnlcreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minnlcreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minnlcreport));
    alglib_impl::_minnlcreport_init_copy(p_struct, const_cast<alglib_impl::minnlcreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minnlcreport_owner::~_minnlcreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minnlcreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minnlcreport* _minnlcreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minnlcreport* _minnlcreport_owner::c_ptr() const
{
    return p_struct;
}
minnlcreport::minnlcreport() : _minnlcreport_owner() ,f(p_struct->f),iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype),bcerr(p_struct->bcerr),bcidx(p_struct->bcidx),lcerr(p_struct->lcerr),lcidx(p_struct->lcidx),nlcerr(p_struct->nlcerr),nlcidx(p_struct->nlcidx),sclfeaserr(p_struct->sclfeaserr),lagbc(&p_struct->lagbc),lagbcnz(&p_struct->lagbcnz),laglc(&p_struct->laglc),lagnlc(&p_struct->lagnlc),dbgphase0its(p_struct->dbgphase0its)
{
}

minnlcreport::minnlcreport(alglib_impl::minnlcreport *attach_to):_minnlcreport_owner(attach_to) ,f(p_struct->f),iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype),bcerr(p_struct->bcerr),bcidx(p_struct->bcidx),lcerr(p_struct->lcerr),lcidx(p_struct->lcidx),nlcerr(p_struct->nlcerr),nlcidx(p_struct->nlcidx),sclfeaserr(p_struct->sclfeaserr),lagbc(&p_struct->lagbc),lagbcnz(&p_struct->lagbcnz),laglc(&p_struct->laglc),lagnlc(&p_struct->lagnlc),dbgphase0its(p_struct->dbgphase0its)
{
}

minnlcreport::minnlcreport(const minnlcreport &rhs):_minnlcreport_owner(rhs) ,f(p_struct->f),iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype),bcerr(p_struct->bcerr),bcidx(p_struct->bcidx),lcerr(p_struct->lcerr),lcidx(p_struct->lcidx),nlcerr(p_struct->nlcerr),nlcidx(p_struct->nlcidx),sclfeaserr(p_struct->sclfeaserr),lagbc(&p_struct->lagbc),lagbcnz(&p_struct->lagbcnz),laglc(&p_struct->laglc),lagnlc(&p_struct->lagnlc),dbgphase0its(p_struct->dbgphase0its)
{
}

minnlcreport& minnlcreport::operator=(const minnlcreport &rhs)
{
    if( this==&rhs )
        return *this;
    _minnlcreport_owner::operator=(rhs);
    return *this;
}

minnlcreport::~minnlcreport()
{
}
#endif

#if defined(AE_COMPILE_MONBI) || !defined(AE_PARTIAL_BUILD)

#endif

#if defined(AE_COMPILE_MINMO) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
                    MULTI-OBJECTIVE  OPTIMIZATION

DESCRIPTION:

The  solver  minimizes an M-dimensional vector function F(x) of N arguments
subject to any combination of:
* box constraints
* two-sided linear equality/inequality constraints AL<=A*x<=AU, where some
  of AL/AU can be infinite (i.e. missing)
* two-sided nonlinear equality/inequality constraints NL<=C(x)<=NU,  where
  some of NL/NU can be infinite (i.e. missing)

REQUIREMENTS:
* F(), C() are continuously differentiable on the feasible set and on  its
  neighborhood

USAGE:

1. User initializes algorithm state using either:
   * minmocreate()  to perform optimization with user-supplied Jacobian
   * minmocreatef() to perform optimization with numerical differentiation

2. User chooses which multi-objective solver to use. At the present moment
   only NBI (Normal Boundary Intersection) solver is implemented, which is
   activated by calling minmosetalgonbi().

3. User adds boundary and/or linear and/or nonlinear constraints by  means
   of calling one of the following functions:
   a) minmosetbc() for boundary constraints
   b) minmosetlc2()      for two-sided sparse linear constraints;
      minmosetlc2dense() for two-sided dense  linear constraints;
      minmosetlc2mixed() for two-sided mixed sparse/dense constraints
   c) minmosetnlc2()     for two-sided nonlinear constraints
   You may combine (a), (b) and (c) in one optimization problem.

4. User sets scale of the variables with minmosetscale() function.  It  is
   VERY important to set  scale  of  the  variables,  because  nonlinearly
   constrained problems are hard to solve when variables are badly scaled.

5. User sets  stopping  conditions  with  minmosetcond().

6. Finally, user calls minmooptimize()   function  which  takes  algorithm
   state  and  pointers  (delegate, etc.) to the callback functions  which
   calculate F/C

7. User calls  minmoresults()  to  get the solution

8. Optionally user may call minmorestartfrom() to solve  another   problem
   with same M,N but another starting point. minmorestartfrom() allows  to
   reuse an already initialized optimizer structure.


INPUT PARAMETERS:
    N       -   variables count, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from the size of X
    M       -   objectives count, M>0.
                M=1 is possible, although makes little sense - it is better
                to use MinNLC directly.
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to reinforce feasibility during  initial  stages  of  the
                  optimization

OUTPUT PARAMETERS:
    State   -   structure that stores algorithm state

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmocreate(const ae_int_t n, const ae_int_t m, const real_1d_array &x, minmostate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmocreate(n, m, x.c_ptr(), state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
                    MULTI-OBJECTIVE  OPTIMIZATION

DESCRIPTION:

The  solver  minimizes an M-dimensional vector function F(x) of N arguments
subject to any combination of:
* box constraints
* two-sided linear equality/inequality constraints AL<=A*x<=AU, where some
  of AL/AU can be infinite (i.e. missing)
* two-sided nonlinear equality/inequality constraints NL<=C(x)<=NU,  where
  some of NL/NU can be infinite (i.e. missing)

REQUIREMENTS:
* F(), C() are continuously differentiable on the feasible set and on  its
  neighborhood

USAGE:

1. User initializes algorithm state using either:
   * minmocreate()  to perform optimization with user-supplied Jacobian
   * minmocreatef() to perform optimization with numerical differentiation

2. User chooses which multi-objective solver to use. At the present moment
   only NBI (Normal Boundary Intersection) solver is implemented, which is
   activated by calling minmosetalgonbi().

3. User adds boundary and/or linear and/or nonlinear constraints by  means
   of calling one of the following functions:
   a) minmosetbc() for boundary constraints
   b) minmosetlc2()      for two-sided sparse linear constraints;
      minmosetlc2dense() for two-sided dense  linear constraints;
      minmosetlc2mixed() for two-sided mixed sparse/dense constraints
   c) minmosetnlc2()     for two-sided nonlinear constraints
   You may combine (a), (b) and (c) in one optimization problem.

4. User sets scale of the variables with minmosetscale() function.  It  is
   VERY important to set  scale  of  the  variables,  because  nonlinearly
   constrained problems are hard to solve when variables are badly scaled.

5. User sets  stopping  conditions  with  minmosetcond().

6. Finally, user calls minmooptimize()   function  which  takes  algorithm
   state  and  pointers  (delegate, etc.) to the callback functions  which
   calculate F/C

7. User calls  minmoresults()  to  get the solution

8. Optionally user may call minmorestartfrom() to solve  another   problem
   with same M,N but another starting point. minmorestartfrom() allows  to
   reuse an already initialized optimizer structure.


INPUT PARAMETERS:
    N       -   variables count, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from the size of X
    M       -   objectives count, M>0.
                M=1 is possible, although makes little sense - it is better
                to use MinNLC directly.
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to reinforce feasibility during  initial  stages  of  the
                  optimization

OUTPUT PARAMETERS:
    State   -   structure that stores algorithm state

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minmocreate(const ae_int_t m, const real_1d_array &x, minmostate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmocreate(n, m, x.c_ptr(), state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This subroutine is a finite  difference variant of minmocreate().  It uses
finite differences in order to differentiate target function.

Description below contains information which is specific to this  function
only. We recommend to read comments on minmocreate() too.

INPUT PARAMETERS:
    N       -   variables count, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from the size of X
    M       -   objectives count, M>0.
                M=1 is possible, although makes little sense - it is better
                to use MinNLC directly.
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to reinforce feasibility during  initial  stages  of  the
                  optimization
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure that stores algorithm state

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is a scaling vector which can be set by minmosetscale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step means too large TRUNCATION errors,  whilst  too  small  step
   means too large NUMERICAL errors.
   1.0E-4 can be good value to start from for a unit-scaled problem.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is  less   robust   and  precise.  Imprecise  gradient  may  slow  down
   convergence, especially on highly nonlinear problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmocreatef(const ae_int_t n, const ae_int_t m, const real_1d_array &x, const double diffstep, minmostate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmocreatef(n, m, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine is a finite  difference variant of minmocreate().  It uses
finite differences in order to differentiate target function.

Description below contains information which is specific to this  function
only. We recommend to read comments on minmocreate() too.

INPUT PARAMETERS:
    N       -   variables count, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from the size of X
    M       -   objectives count, M>0.
                M=1 is possible, although makes little sense - it is better
                to use MinNLC directly.
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to reinforce feasibility during  initial  stages  of  the
                  optimization
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure that stores algorithm state

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is a scaling vector which can be set by minmosetscale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step means too large TRUNCATION errors,  whilst  too  small  step
   means too large NUMERICAL errors.
   1.0E-4 can be good value to start from for a unit-scaled problem.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is  less   robust   and  precise.  Imprecise  gradient  may  slow  down
   convergence, especially on highly nonlinear problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minmocreatef(const ae_int_t m, const real_1d_array &x, const double diffstep, minmostate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmocreatef(n, m, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
Use the NBI (Normal Boundary Intersection)  algorithm  for  multiobjective
optimization.

NBI is a simple yet powerful multiobjective  optimization  algorithm  that
has the following attractive properties:
* it generates nearly uniformly distributed Pareto points
* it is applicable to problems with more than 2 objectives
* it naturally supports a mix of box, linear and nonlinear constraints
* it is less sensitive to the bad scaling of the targets

The only drawback of the algorithm is that for more than 2  objectives  it
can miss some small parts of the Pareto front that are  located  near  its
boundaries.

INPUT PARAMETERS:
    State       -   structure which stores algorithm state
    FrontSize   -   desired Pareto front size, FrontSize>=M,
                    where M is an objectives count
    PolishSolutions-whether additional solution improving phase is needed
                    or not:
                    * if False, the original NBI as formulated  by Das and
                      Dennis is used. It quickly produces  good solutions,
                      but these solutions can be suboptimal (usually within
                      0.1% of the optimal values).
                      The reason is that the original NBI formulation does
                      not account for  degeneracies that allow significant
                      progress for one objective with no deterioration for
                      other objectives.
                    * if True,  the  original  NBI  is  followed  by   the
                      additional solution  polishing  phase.  This  solver
                      mode is several times slower than the original  NBI,
                      but produces better solutions.

  -- ALGLIB --
     Copyright 20.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetalgonbi(minmostate &state, const ae_int_t frontsize, const bool polishsolutions, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmosetalgonbi(state.c_ptr(), frontsize, polishsolutions, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets boundary constraints for the MO optimizer.

Boundary constraints are inactive by  default  (after  initial  creation).
They are preserved after algorithm restart with MinMORestartFrom().

You may combine boundary constraints with  general  linear ones - and with
nonlinear ones! Boundary constraints are  handled  more  efficiently  than
other types.  Thus,  if  your  problem  has  mixed  constraints,  you  may
explicitly specify some of them as boundary and save some time/space.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                a very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                a very large number or +INF.

NOTE 1:  it is possible to specify  BndL[i]=BndU[i].  In  this  case  I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetbc(minmostate &state, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmosetbc(state.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU with dense
constraint matrix A.

NOTE: knowing  that  constraint  matrix  is dense may help some MO solvers
      to utilize efficient dense Level 3  BLAS  for  dense  parts  of  the
      problem. If your problem has both dense and sparse constraints,  you
      can use minmosetlc2mixed() function.

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetlc2dense(minmostate &state, const real_2d_array &a, const real_1d_array &al, const real_1d_array &au, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmosetlc2dense(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU with dense
constraint matrix A.

NOTE: knowing  that  constraint  matrix  is dense may help some MO solvers
      to utilize efficient dense Level 3  BLAS  for  dense  parts  of  the
      problem. If your problem has both dense and sparse constraints,  you
      can use minmosetlc2mixed() function.

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minmosetlc2dense(minmostate &state, const real_2d_array &a, const real_1d_array &al, const real_1d_array &au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t k;
    if( (a.rows()!=al.length()) || (a.rows()!=au.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'minmosetlc2dense': looks like one of arguments has wrong size");
    k = a.rows();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmosetlc2dense(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
sparse constraining matrix A. Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    A       -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minmosetlc2(minmostate &state, const sparsematrix &a, const real_1d_array &al, const real_1d_array &au, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmosetlc2(state.c_ptr(), a.c_ptr(), al.c_ptr(), au.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
mixed constraining matrix A including sparse part (first SparseK rows) and
dense part (last DenseK rows). Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

This function may be useful if constraint matrix includes large number  of
both types of rows - dense and sparse. If you have just a few sparse rows,
you  may  represent  them  in  dense  format  without losing  performance.
Similarly, if you have just a few dense rows,  you  can  store them in the
sparse format with almost same performance.

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    SparseA -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    SparseK -   number of sparse constraints, SparseK>=0
    DenseA  -   linear constraints, array[K,N], set of dense constraints.
                Each row of A represents one general linear constraint.
    DenseK  -   number of dense constraints, DenseK>=0
    AL, AU  -   lower and upper bounds, array[SparseK+DenseK], with former
                SparseK elements corresponding to sparse constraints,  and
                latter DenseK elements corresponding to dense constraints;
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minmosetlc2mixed(minmostate &state, const sparsematrix &sparsea, const ae_int_t ksparse, const real_2d_array &densea, const ae_int_t kdense, const real_1d_array &al, const real_1d_array &au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmosetlc2mixed(state.c_ptr(), sparsea.c_ptr(), ksparse, densea.c_ptr(), kdense, al.c_ptr(), au.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends two-sided linear  constraint  AL<=A*x<=AU  to  dense
constraints list.

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    A       -   linear constraint coefficient, array[N], right side is NOT
                included.
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint Ai*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint Ai*x<=AU
                * AU=+INF  => one-sided constraint AL<=Ai*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minmoaddlc2dense(minmostate &state, const real_1d_array &a, const double al, const double au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmoaddlc2dense(state.c_ptr(), a.c_ptr(), al, au, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of sparse constraints.

Constraint is passed in the compressed  format:  as  a  list  of  non-zero
entries of the coefficient vector A. Such approach is more efficient  than
the dense storage for highly sparse constraint vectors.

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    IdxA    -   array[NNZ], indexes of non-zero elements of A:
                * can be unsorted
                * can include duplicate indexes (corresponding entries  of
                  ValA[] will be summed)
    ValA    -   array[NNZ], values of non-zero elements of A
    NNZ     -   number of non-zero coefficients in A
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minmoaddlc2(minmostate &state, const integer_1d_array &idxa, const real_1d_array &vala, const ae_int_t nnz, const double al, const double au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmoaddlc2(state.c_ptr(), idxa.c_ptr(), vala.c_ptr(), nnz, al, au, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present sparse constraints.

Constraint vector A is  passed  as  a  dense  array  which  is  internally
sparsified by this function.

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    DA      -   array[N], constraint vector
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minmoaddlc2sparsefromdense(minmostate &state, const real_1d_array &da, const double al, const double au, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmoaddlc2sparsefromdense(state.c_ptr(), da.c_ptr(), al, au, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets two-sided nonlinear constraints for MinMO optimizer.

In fact, this function sets  only  constraints  COUNT  and  their  BOUNDS.
Constraints  themselves  (constraint  functions)   are   passed   to   the
MinMOOptimize() method as callbacks.

MinMOOptimize() method accepts a user-defined vector function F[]  and its
Jacobian J[], where:
* first M components of F[]  and  first  M rows  of  J[]   correspond   to
  multiple objectives
* subsequent NNLC components of F[] (and rows of J[]) correspond  to  two-
  sided nonlinear constraints NL<=C(x)<=NU, where
  * NL[i]=NU[i] => I-th row is an equality constraint Ci(x)=NL
  * NL[i]<NU[i] => I-th tow is a  two-sided constraint NL[i]<=Ci(x)<=NU[i]
  * NL[i]=-INF  => I-th row is an one-sided constraint Ci(x)<=NU[i]
  * NU[i]=+INF  => I-th row is an one-sided constraint NL[i]<=Ci(x)
  * NL[i]=-INF, NU[i]=+INF => constraint is ignored

NOTE: you may combine nonlinear constraints with linear/boundary ones.  If
      your problem has mixed constraints, you  may explicitly specify some
      of them as linear or box ones.
      It helps optimizer to handle them more efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinMOCreate call.
    NL      -   array[NNLC], lower bounds, can contain -INF
    NU      -   array[NNLC], lower bounds, can contain +INF
    NNLC    -   constraints count, NNLC>=0

NOTE 1: nonlinear constraints are satisfied only  approximately!   It   is
        possible that the algorithm will evaluate the function  outside of
        the feasible area!

NOTE 2: algorithm scales variables  according  to the scale  specified by
        MinMOSetScale()  function,  so  it can handle problems with badly
        scaled variables (as long as we KNOW their scales).

        However,  there  is  no  way  to  automatically  scale   nonlinear
        constraints. Inappropriate scaling  of nonlinear  constraints  may
        ruin convergence. Solving problem with  constraint  "1000*G0(x)=0"
        is NOT the same as solving it with constraint "0.001*G0(x)=0".

        It means that YOU are  the  one who is responsible for the correct
        scaling of the nonlinear constraints Gi(x) and Hi(x). We recommend
        you to scale nonlinear constraints in such a way that the Jacobian
        rows have approximately unit magnitude  (for  problems  with  unit
        scale) or have magnitude approximately equal to 1/S[i] (where S is
        a scale set by MinMOSetScale() function).

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetnlc2(minmostate &state, const real_1d_array &nl, const real_1d_array &nu, const ae_int_t nnlc, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmosetnlc2(state.c_ptr(), nl.c_ptr(), nu.c_ptr(), nnlc, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets stopping conditions for inner iterations of the optimizer.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsX    -   >=0
                The subroutine finishes its work if  on  k+1-th  iteration
                the condition |v|<=EpsX is fulfilled, where:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - step vector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinMOSetScale()
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsX=0 and MaxIts=0 (simultaneously) will lead to an automatic
selection of the stopping condition.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetcond(minmostate &state, const double epsx, const ae_int_t maxits, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmosetcond(state.c_ptr(), epsx, maxits, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets scaling coefficients for the MO optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Scaling is also used by finite difference variant of the optimizer  - step
along I-th axis is equal to DiffStep*S[I].

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetscale(minmostate &state, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmosetscale(state.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function turns on/off reporting of the Pareto front points.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback  function  (if  it
was provided to MinMOOptimize) every time we find a Pareto front point.

NOTE: according to the communication protocol used by ALGLIB,  the  solver
      passes two parameters to the rep() callback - a current point and  a
      target value at the current point.
      However, because  we solve a  multi-objective  problem,  the  target
      parameter is not used and set to zero.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetxrep(minmostate &state, const bool needxrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmosetxrep(state.c_ptr(), needxrep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function provides reverse communication interface
Reverse communication interface is not documented or recommended to use.
See below for functions which provide better documented API
*************************************************************************/
bool minmoiteration(minmostate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::minmoiteration(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}


void minmooptimize(minmostate &state,
    void (*fvec)(const real_1d_array &x, real_1d_array &fi, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(fvec!=NULL, "ALGLIB: error in 'minmooptimize()' (fvec is NULL)", &_alglib_env_state);
    alglib_impl::minmosetprotocolv1(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minmoiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
                if( state.needfi )
                {
                    fvec(state.x, state.fi, ptr);
                    continue;
                }
        if( state.xupdated )
        {
            if( rep!=NULL )
                rep(state.x, state.f, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    lbl_no_callback:
        alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minmooptimize' (some derivatives were not provided?)", &_alglib_env_state);
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
}


void minmooptimize(minmostate &state,
    void  (*jac)(const real_1d_array &x, real_1d_array &fi, real_2d_array &jac, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(jac!=NULL,  "ALGLIB: error in 'minmooptimize()' (jac is NULL)", &_alglib_env_state);
    alglib_impl::minmosetprotocolv1(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minmoiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
                if( state.needfij )
                {
                    jac(state.x, state.fi, state.j, ptr);
                    continue;
                }
        if( state.xupdated )
        {
            if( rep!=NULL )
                rep(state.x, state.f, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    lbl_no_callback:
        alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minmooptimize' (some derivatives were not provided?)", &_alglib_env_state);
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
}



/*************************************************************************
MinMO results:  the  solution  found,  completion  codes  and   additional
information.

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    ParetoFront-array[FrontSize,N+M], approximate Pareto front.
                Its columns have the following structure:
                * first N columns are variable values
                * next  M columns are objectives at these points
                Its rows have the following structure:
                * first M rows contain solutions to single-objective tasks
                  with I-th row storing result for  I-th  objective  being
                  minimized ignoring other ones.
                  Thus, ParetoFront[I,N+I] for  0<=I<M  stores  so  called
                  'ideal objective vector'.
                * subsequent FrontSize-M rows  store  variables/objectives
                  at  various  randomly  and  nearly   uniformly   sampled
                  locations of the Pareto front.

    FrontSize-  front size, >=0.
                * no larger than the number passed to setalgo()
                * for  a  single-objective  task,  FrontSize=1  is  ALWAYS
                  returned, no matter what was specified during setalgo()
                  call.
                * if  the   solver   was   prematurely   terminated   with
                  minnorequesttermination(), an  incomplete  Pareto  front
                  will be returned (it may even have less than M rows)
                * if a  failure (negative completion code) was   signaled,
                  FrontSize=0 will be returned

    Rep     -   optimization report, contains information about completion
                code, constraint violation at the solution and so on.

                You   should   check   rep.terminationtype  in  order   to
                distinguish successful termination from unsuccessful one:

                === FAILURE CODES ===
                * -8    internal  integrity control  detected  infinite or
                        NAN   values    in   function/gradient.   Abnormal
                        termination signalled.
                * -3    constraint bounds are  infeasible,  i.e.  we  have
                        box/linear/nonlinear constraint  with  two  bounds
                        present, and a lower one being  greater  than  the
                        upper one.
                        Note: less obvious infeasibilities of  constraints
                              do NOT  trigger  emergency  completion;  you
                              have to examine rep.bcerr/rep.lcerr/rep.nlcerr
                              to detect possibly inconsistent constraints.

                === SUCCESS CODES ===
                *  2   scaled step is no more than EpsX.
                *  5   MaxIts steps were taken.
                *  8   user   requested    algorithm    termination    via
                       minmorequesttermination(), last accepted point   is
                       returned.

                More information about fields of this  structure  can  be
                found in the comments on minmoreport datatype.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmoresults(const minmostate &state, real_2d_array &paretofront, ae_int_t &frontsize, minmoreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmoresults(state.c_ptr(), paretofront.c_ptr(), &frontsize, rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine  submits  request  for  the  termination  of  the  running
optimizer.

It should be called from the user-supplied callback when user decides that
it is time to "smoothly" terminate optimization process, or from some other
thread. As a result, optimizer stops  at  the  state  which  was  "current
accepted" when termination request was submitted and returns error code  8
(successful termination).

Usually it results in an incomplete Pareto front being returned.

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.

NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmorequesttermination(minmostate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmorequesttermination(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine restarts algorithm from the new point.
All optimization parameters (including constraints) are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have  same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinMOCreate call.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmorestartfrom(minmostate &state, const real_1d_array &x, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minmorestartfrom(state.c_ptr(), x.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************
This object stores nonlinear optimizer state.
You should use functions provided by MinMO subpackage to work  with  this
object
*************************************************************************/
_minmostate_owner::_minmostate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minmostate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minmostate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minmostate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minmostate));
    alglib_impl::_minmostate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minmostate_owner::_minmostate_owner(alglib_impl::minmostate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minmostate_owner::_minmostate_owner(const _minmostate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minmostate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minmostate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minmostate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minmostate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minmostate));
    alglib_impl::_minmostate_init_copy(p_struct, const_cast<alglib_impl::minmostate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minmostate_owner& _minmostate_owner::operator=(const _minmostate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minmostate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minmostate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minmostate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minmostate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minmostate));
    alglib_impl::_minmostate_init_copy(p_struct, const_cast<alglib_impl::minmostate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minmostate_owner::~_minmostate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minmostate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minmostate* _minmostate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minmostate* _minmostate_owner::c_ptr() const
{
    return p_struct;
}
minmostate::minmostate() : _minmostate_owner() ,needfi(p_struct->needfi),needfij(p_struct->needfij),xupdated(p_struct->xupdated),f(p_struct->f),fi(&p_struct->fi),j(&p_struct->j),x(&p_struct->x)
{
}

minmostate::minmostate(alglib_impl::minmostate *attach_to):_minmostate_owner(attach_to) ,needfi(p_struct->needfi),needfij(p_struct->needfij),xupdated(p_struct->xupdated),f(p_struct->f),fi(&p_struct->fi),j(&p_struct->j),x(&p_struct->x)
{
}

minmostate::minmostate(const minmostate &rhs):_minmostate_owner(rhs) ,needfi(p_struct->needfi),needfij(p_struct->needfij),xupdated(p_struct->xupdated),f(p_struct->f),fi(&p_struct->fi),j(&p_struct->j),x(&p_struct->x)
{
}

minmostate& minmostate::operator=(const minmostate &rhs)
{
    if( this==&rhs )
        return *this;
    _minmostate_owner::operator=(rhs);
    return *this;
}

minmostate::~minmostate()
{
}




/*************************************************************************
These fields store optimization report:
* inneriterationscount      total number of inner iterations
* outeriterationscount      number of internal optimization sessions performed
* nfev                      number of gradient evaluations
* terminationtype           termination type (see below)

Scaled constraint violations (maximum over all Pareto points) are reported:
* bcerr                     maximum violation of the box constraints
* bcidx                     index of the most violated box  constraint (or
                            -1, if all box constraints  are  satisfied  or
                            there are no box constraint)
* lcerr                     maximum violation of the  linear  constraints,
                            computed as maximum  scaled  distance  between
                            final point and constraint boundary.
* lcidx                     index of the most violated  linear  constraint
                            (or -1, if all constraints  are  satisfied  or
                            there are no general linear constraints)
* nlcerr                    maximum violation of the nonlinear constraints
* nlcidx                    index of the most violated nonlinear constraint
                            (or -1, if all constraints  are  satisfied  or
                            there are no nonlinear constraints)

Violations  of  the  box  constraints  are  scaled  on per-component basis
according to  the  scale  vector s[]  as specified by the minmosetscale().
Violations of the general linear  constraints  are  also   computed  using
user-supplied variable scaling. Violations of  the  nonlinear  constraints
are computed "as is"

TERMINATION CODES

TerminationType field contains completion code, which can be either:

=== FAILURE CODE ===
  -8    internal integrity control detected  infinite  or  NAN  values  in
        function/gradient. Abnormal termination signaled.
  -3    box  constraints  are  infeasible.  Note: infeasibility of non-box
        constraints does NOT trigger emergency  completion;  you  have  to
        examine  bcerr/lcerr/nlcerr   to  detect   possibly   inconsistent
        constraints.

=== SUCCESS CODE ===
   2    relative step is no more than EpsX.
   5    MaxIts steps was taken
   7    stopping conditions are too stringent,
        further improvement is impossible,
        X contains best point found so far.

NOTE: The solver internally performs many optimization sessions:  one  for
      each Pareto point, and some  amount  of  preparatory  optimizations.
      Different optimization  sessions  may  return  different  completion
      codes. If at least one of internal optimizations failed, its failure
      code is returned. If none of them failed, the most frequent code  is
      returned.

Other fields of this structure are not documented and should not be used!
*************************************************************************/
_minmoreport_owner::_minmoreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minmoreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minmoreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minmoreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minmoreport));
    alglib_impl::_minmoreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minmoreport_owner::_minmoreport_owner(alglib_impl::minmoreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minmoreport_owner::_minmoreport_owner(const _minmoreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minmoreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minmoreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minmoreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minmoreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minmoreport));
    alglib_impl::_minmoreport_init_copy(p_struct, const_cast<alglib_impl::minmoreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minmoreport_owner& _minmoreport_owner::operator=(const _minmoreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minmoreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minmoreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minmoreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minmoreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minmoreport));
    alglib_impl::_minmoreport_init_copy(p_struct, const_cast<alglib_impl::minmoreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minmoreport_owner::~_minmoreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minmoreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minmoreport* _minmoreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minmoreport* _minmoreport_owner::c_ptr() const
{
    return p_struct;
}
minmoreport::minmoreport() : _minmoreport_owner() ,inneriterationscount(p_struct->inneriterationscount),outeriterationscount(p_struct->outeriterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype),bcerr(p_struct->bcerr),bcidx(p_struct->bcidx),lcerr(p_struct->lcerr),lcidx(p_struct->lcidx),nlcerr(p_struct->nlcerr),nlcidx(p_struct->nlcidx)
{
}

minmoreport::minmoreport(alglib_impl::minmoreport *attach_to):_minmoreport_owner(attach_to) ,inneriterationscount(p_struct->inneriterationscount),outeriterationscount(p_struct->outeriterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype),bcerr(p_struct->bcerr),bcidx(p_struct->bcidx),lcerr(p_struct->lcerr),lcidx(p_struct->lcidx),nlcerr(p_struct->nlcerr),nlcidx(p_struct->nlcidx)
{
}

minmoreport::minmoreport(const minmoreport &rhs):_minmoreport_owner(rhs) ,inneriterationscount(p_struct->inneriterationscount),outeriterationscount(p_struct->outeriterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype),bcerr(p_struct->bcerr),bcidx(p_struct->bcidx),lcerr(p_struct->lcerr),lcidx(p_struct->lcidx),nlcerr(p_struct->nlcerr),nlcidx(p_struct->nlcidx)
{
}

minmoreport& minmoreport::operator=(const minmoreport &rhs)
{
    if( this==&rhs )
        return *this;
    _minmoreport_owner::operator=(rhs);
    return *this;
}

minmoreport::~minmoreport()
{
}
#endif

#if defined(AE_COMPILE_MINNS) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
                  NONSMOOTH NONCONVEX OPTIMIZATION
            SUBJECT TO BOX/LINEAR/NONLINEAR-NONSMOOTH CONSTRAINTS

DESCRIPTION:

The  subroutine  minimizes  function   F(x)  of N arguments subject to any
combination of:
* bound constraints
* linear inequality constraints
* linear equality constraints
* nonlinear equality constraints Gi(x)=0
* nonlinear inequality constraints Hi(x)<=0

IMPORTANT: see MinNSSetAlgoAGS for important  information  on  performance
           restrictions of AGS solver.

REQUIREMENTS:
* starting point X0 must be feasible or not too far away from the feasible
  set
* F(), G(), H() are continuous, locally Lipschitz  and  continuously  (but
  not necessarily twice) differentiable in an open dense  subset  of  R^N.
  Functions F(), G() and H() may be nonsmooth and non-convex.
  Informally speaking, it means  that  functions  are  composed  of  large
  differentiable "patches" with nonsmoothness having  place  only  at  the
  boundaries between these "patches".
  Most real-life nonsmooth  functions  satisfy  these  requirements.  Say,
  anything which involves finite number of abs(), min() and max() is  very
  likely to pass the test.
  Say, it is possible to optimize anything of the following:
  * f=abs(x0)+2*abs(x1)
  * f=max(x0,x1)
  * f=sin(max(x0,x1)+abs(x2))
* for nonlinearly constrained problems: F()  must  be  bounded from  below
  without nonlinear constraints (this requirement is due to the fact that,
  contrary to box and linear constraints, nonlinear ones  require  special
  handling).
* user must provide function value and gradient for F(), H(), G()  at  all
  points where function/gradient can be calculated. If optimizer  requires
  value exactly at the boundary between "patches" (say, at x=0 for f=abs(x)),
  where gradient is not defined, user may resolve tie arbitrarily (in  our
  case - return +1 or -1 at its discretion).
* NS solver supports numerical differentiation, i.e. it may  differentiate
  your function for you,  but  it  results  in  2N  increase  of  function
  evaluations. Not recommended unless you solve really small problems. See
  minnscreatef() for more information on this functionality.

USAGE:

1. User initializes algorithm state with MinNSCreate() call  and   chooses
   what NLC solver to use. There is some solver which is used by  default,
   with default settings, but you should NOT rely on  default  choice.  It
   may change in future releases of ALGLIB without notice, and no one  can
   guarantee that new solver will be  able  to  solve  your  problem  with
   default settings.

   From the other side, if you choose solver explicitly, you can be pretty
   sure that it will work with new ALGLIB releases.

   In the current release following solvers can be used:
   * AGS solver (activated with MinNSSetAlgoAGS() function)

2. User adds boundary and/or linear and/or nonlinear constraints by  means
   of calling one of the following functions:
   a) MinNSSetBC() for boundary constraints
   b) MinNSSetLC() for linear constraints
   c) MinNSSetNLC() for nonlinear constraints
   You may combine (a), (b) and (c) in one optimization problem.

3. User sets scale of the variables with MinNSSetScale() function. It   is
   VERY important to set  scale  of  the  variables,  because  nonlinearly
   constrained problems are hard to solve when variables are badly scaled.

4. User sets stopping conditions with MinNSSetCond().

5. Finally, user calls MinNSOptimize()  function  which  takes   algorithm
   state and pointer (delegate, etc) to callback function which calculates
   F/G/H.

7. User calls MinNSResults() to get solution

8. Optionally user may call MinNSRestartFrom() to solve   another  problem
   with same N but another starting point. MinNSRestartFrom()  allows   to
   reuse already initialized structure.


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

NOTE: minnscreatef() function may be used if  you  do  not  have  analytic
      gradient.   This   function  creates  solver  which  uses  numerical
      differentiation with user-specified step.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnscreate(const ae_int_t n, const real_1d_array &x, minnsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnscreate(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
                  NONSMOOTH NONCONVEX OPTIMIZATION
            SUBJECT TO BOX/LINEAR/NONLINEAR-NONSMOOTH CONSTRAINTS

DESCRIPTION:

The  subroutine  minimizes  function   F(x)  of N arguments subject to any
combination of:
* bound constraints
* linear inequality constraints
* linear equality constraints
* nonlinear equality constraints Gi(x)=0
* nonlinear inequality constraints Hi(x)<=0

IMPORTANT: see MinNSSetAlgoAGS for important  information  on  performance
           restrictions of AGS solver.

REQUIREMENTS:
* starting point X0 must be feasible or not too far away from the feasible
  set
* F(), G(), H() are continuous, locally Lipschitz  and  continuously  (but
  not necessarily twice) differentiable in an open dense  subset  of  R^N.
  Functions F(), G() and H() may be nonsmooth and non-convex.
  Informally speaking, it means  that  functions  are  composed  of  large
  differentiable "patches" with nonsmoothness having  place  only  at  the
  boundaries between these "patches".
  Most real-life nonsmooth  functions  satisfy  these  requirements.  Say,
  anything which involves finite number of abs(), min() and max() is  very
  likely to pass the test.
  Say, it is possible to optimize anything of the following:
  * f=abs(x0)+2*abs(x1)
  * f=max(x0,x1)
  * f=sin(max(x0,x1)+abs(x2))
* for nonlinearly constrained problems: F()  must  be  bounded from  below
  without nonlinear constraints (this requirement is due to the fact that,
  contrary to box and linear constraints, nonlinear ones  require  special
  handling).
* user must provide function value and gradient for F(), H(), G()  at  all
  points where function/gradient can be calculated. If optimizer  requires
  value exactly at the boundary between "patches" (say, at x=0 for f=abs(x)),
  where gradient is not defined, user may resolve tie arbitrarily (in  our
  case - return +1 or -1 at its discretion).
* NS solver supports numerical differentiation, i.e. it may  differentiate
  your function for you,  but  it  results  in  2N  increase  of  function
  evaluations. Not recommended unless you solve really small problems. See
  minnscreatef() for more information on this functionality.

USAGE:

1. User initializes algorithm state with MinNSCreate() call  and   chooses
   what NLC solver to use. There is some solver which is used by  default,
   with default settings, but you should NOT rely on  default  choice.  It
   may change in future releases of ALGLIB without notice, and no one  can
   guarantee that new solver will be  able  to  solve  your  problem  with
   default settings.

   From the other side, if you choose solver explicitly, you can be pretty
   sure that it will work with new ALGLIB releases.

   In the current release following solvers can be used:
   * AGS solver (activated with MinNSSetAlgoAGS() function)

2. User adds boundary and/or linear and/or nonlinear constraints by  means
   of calling one of the following functions:
   a) MinNSSetBC() for boundary constraints
   b) MinNSSetLC() for linear constraints
   c) MinNSSetNLC() for nonlinear constraints
   You may combine (a), (b) and (c) in one optimization problem.

3. User sets scale of the variables with MinNSSetScale() function. It   is
   VERY important to set  scale  of  the  variables,  because  nonlinearly
   constrained problems are hard to solve when variables are badly scaled.

4. User sets stopping conditions with MinNSSetCond().

5. Finally, user calls MinNSOptimize()  function  which  takes   algorithm
   state and pointer (delegate, etc) to callback function which calculates
   F/G/H.

7. User calls MinNSResults() to get solution

8. Optionally user may call MinNSRestartFrom() to solve   another  problem
   with same N but another starting point. MinNSRestartFrom()  allows   to
   reuse already initialized structure.


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

NOTE: minnscreatef() function may be used if  you  do  not  have  analytic
      gradient.   This   function  creates  solver  which  uses  numerical
      differentiation with user-specified step.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minnscreate(const real_1d_array &x, minnsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnscreate(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
Version of minnscreatef() which uses numerical differentiation. I.e.,  you
do not have to calculate derivatives yourself. However, this version needs
2N times more function evaluations.

2-point differentiation formula is  used,  because  more  precise  4-point
formula is unstable when used on non-smooth functions.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.
    DiffStep-   differentiation  step,  DiffStep>0.   Algorithm   performs
                numerical differentiation  with  step  for  I-th  variable
                being equal to DiffStep*S[I] (here S[] is a  scale vector,
                set by minnssetscale() function).
                Do not use  too  small  steps,  because  it  may  lead  to
                catastrophic cancellation during intermediate calculations.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnscreatef(const ae_int_t n, const real_1d_array &x, const double diffstep, minnsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnscreatef(n, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Version of minnscreatef() which uses numerical differentiation. I.e.,  you
do not have to calculate derivatives yourself. However, this version needs
2N times more function evaluations.

2-point differentiation formula is  used,  because  more  precise  4-point
formula is unstable when used on non-smooth functions.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.
    DiffStep-   differentiation  step,  DiffStep>0.   Algorithm   performs
                numerical differentiation  with  step  for  I-th  variable
                being equal to DiffStep*S[I] (here S[] is a  scale vector,
                set by minnssetscale() function).
                Do not use  too  small  steps,  because  it  may  lead  to
                catastrophic cancellation during intermediate calculations.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minnscreatef(const real_1d_array &x, const double diffstep, minnsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnscreatef(n, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets boundary constraints.

Boundary constraints are inactive by default (after initial creation).
They are preserved after algorithm restart with minnsrestartfrom().

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF.

NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

NOTE 2: AGS solver has following useful properties:
* bound constraints are always satisfied exactly
* function is evaluated only INSIDE area specified by  bound  constraints,
  even  when  numerical  differentiation is used (algorithm adjusts  nodes
  according to boundary constraints)

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnssetbc(minnsstate &state, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnssetbc(state.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets linear constraints.

Linear constraints are inactive by default (after initial creation).
They are preserved after algorithm restart with minnsrestartfrom().

INPUT PARAMETERS:
    State   -   structure previously allocated with minnscreate() call.
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

NOTE: linear (non-bound) constraints are satisfied only approximately:

* there always exists some minor violation (about current sampling  radius
  in magnitude during optimization, about EpsX in the solution) due to use
  of penalty method to handle constraints.
* numerical differentiation, if used, may  lead  to  function  evaluations
  outside  of the feasible  area,   because   algorithm  does  NOT  change
  numerical differentiation formula according to linear constraints.

If you want constraints to be  satisfied  exactly, try to reformulate your
problem  in  such  manner  that  all constraints will become boundary ones
(this kind of constraints is always satisfied exactly, both in  the  final
solution and in all intermediate points).

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnssetlc(minnsstate &state, const real_2d_array &c, const integer_1d_array &ct, const ae_int_t k, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnssetlc(state.c_ptr(), c.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets linear constraints.

Linear constraints are inactive by default (after initial creation).
They are preserved after algorithm restart with minnsrestartfrom().

INPUT PARAMETERS:
    State   -   structure previously allocated with minnscreate() call.
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

NOTE: linear (non-bound) constraints are satisfied only approximately:

* there always exists some minor violation (about current sampling  radius
  in magnitude during optimization, about EpsX in the solution) due to use
  of penalty method to handle constraints.
* numerical differentiation, if used, may  lead  to  function  evaluations
  outside  of the feasible  area,   because   algorithm  does  NOT  change
  numerical differentiation formula according to linear constraints.

If you want constraints to be  satisfied  exactly, try to reformulate your
problem  in  such  manner  that  all constraints will become boundary ones
(this kind of constraints is always satisfied exactly, both in  the  final
solution and in all intermediate points).

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minnssetlc(minnsstate &state, const real_2d_array &c, const integer_1d_array &ct, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t k;
    if( (c.rows()!=ct.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'minnssetlc': looks like one of arguments has wrong size");
    k = c.rows();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnssetlc(state.c_ptr(), c.c_ptr(), ct.c_ptr(), k, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets nonlinear constraints.

In fact, this function sets NUMBER of nonlinear  constraints.  Constraints
itself (constraint functions) are passed to minnsoptimize() method.   This
method requires user-defined vector function F[]  and  its  Jacobian  J[],
where:
* first component of F[] and first row  of  Jacobian  J[]  correspond   to
  function being minimized
* next NLEC components of F[] (and rows  of  J)  correspond  to  nonlinear
  equality constraints G_i(x)=0
* next NLIC components of F[] (and rows  of  J)  correspond  to  nonlinear
  inequality constraints H_i(x)<=0

NOTE: you may combine nonlinear constraints with linear/boundary ones.  If
      your problem has mixed constraints, you  may explicitly specify some
      of them as linear ones. It may help optimizer to  handle  them  more
      efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnscreate() call.
    NLEC    -   number of Non-Linear Equality Constraints (NLEC), >=0
    NLIC    -   number of Non-Linear Inquality Constraints (NLIC), >=0

NOTE 1: nonlinear constraints are satisfied only  approximately!   It   is
        possible   that  algorithm  will  evaluate  function  outside   of
        the feasible area!

NOTE 2: algorithm scales variables  according  to   scale   specified   by
        minnssetscale()  function,  so  it can handle problems with  badly
        scaled variables (as long as we KNOW their scales).

        However,  there  is  no  way  to  automatically  scale   nonlinear
        constraints Gi(x) and Hi(x). Inappropriate scaling  of  Gi/Hi  may
        ruin convergence. Solving problem with  constraint  "1000*G0(x)=0"
        is NOT same as solving it with constraint "0.001*G0(x)=0".

        It  means  that  YOU  are  the  one who is responsible for correct
        scaling of nonlinear constraints Gi(x) and Hi(x). We recommend you
        to scale nonlinear constraints in such way that I-th component  of
        dG/dX (or dH/dx) has approximately unit  magnitude  (for  problems
        with unit scale)  or  has  magnitude approximately equal to 1/S[i]
        (where S is a scale set by minnssetscale() function).

NOTE 3: nonlinear constraints are always hard to handle,  no  matter  what
        algorithm you try to use. Even basic box/linear constraints modify
        function  curvature   by  adding   valleys  and  ridges.  However,
        nonlinear constraints add valleys which are very  hard  to  follow
        due to their "curved" nature.

        It means that optimization with single nonlinear constraint may be
        significantly slower than optimization with multiple linear  ones.
        It is normal situation, and we recommend you to  carefully  choose
        Rho parameter of minnssetalgoags(), because too  large  value  may
        slow down convergence.


  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnssetnlc(minnsstate &state, const ae_int_t nlec, const ae_int_t nlic, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnssetnlc(state.c_ptr(), nlec, nlic, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets stopping conditions for iterations of optimizer.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsX    -   >=0
                The AGS solver finishes its work if  on  k+1-th  iteration
                sampling radius decreases below EpsX.
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsX=0  and  MaxIts=0  (simultaneously)  will  lead  to  automatic
stopping criterion selection. We do not recommend you to rely  on  default
choice in production code.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnssetcond(minnsstate &state, const double epsx, const ae_int_t maxits, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnssetcond(state.c_ptr(), epsx, maxits, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets scaling coefficients for NLC optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Scaling is also used by finite difference variant of the optimizer  - step
along I-th axis is equal to DiffStep*S[I].

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnssetscale(minnsstate &state, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnssetscale(state.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function tells MinNS unit to use  AGS  (adaptive  gradient  sampling)
algorithm for nonsmooth constrained  optimization.  This  algorithm  is  a
slight modification of one described in  "An  Adaptive  Gradient  Sampling
Algorithm for Nonsmooth Optimization" by Frank E. Curtisy and Xiaocun Quez.

This optimizer has following benefits and drawbacks:
+ robustness; it can be used with nonsmooth and nonconvex functions.
+ relatively easy tuning; most of the metaparameters are easy to select.
- it has convergence of steepest descent, slower than CG/LBFGS.
- each iteration involves evaluation of ~2N gradient values  and  solution
  of 2Nx2N quadratic programming problem, which  limits  applicability  of
  algorithm by small-scale problems (up to 50-100).

IMPORTANT: this  algorithm  has  convergence  guarantees,   i.e.  it  will
           steadily move towards some stationary point of the function.

           However, "stationary point" does not  always  mean  "solution".
           Nonsmooth problems often have "flat spots",  i.e.  areas  where
           function do not change at all. Such "flat spots" are stationary
           points by definition, and algorithm may be caught here.

           Nonsmooth CONVEX tasks are not prone to  this  problem. Say, if
           your function has form f()=MAX(f0,f1,...), and f_i are  convex,
           then f() is convex too and you have guaranteed  convergence  to
           solution.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Radius  -   initial sampling radius, >=0.

                Internally multiplied  by  vector of  per-variable  scales
                specified by minnssetscale()).

                You should select relatively large sampling radius, roughly
                proportional to scaled length of the first  steps  of  the
                algorithm. Something close to 0.1 in magnitude  should  be
                good for most problems.

                AGS solver can automatically decrease radius, so too large
                radius is  not a problem (assuming that you  won't  choose
                so large radius that algorithm  will  sample  function  in
                too far away points, where gradient value is irrelevant).

                Too small radius won't cause algorithm to fail, but it may
                slow down algorithm (it may  have  to  perform  too  short
                steps).
    Penalty -   penalty coefficient for nonlinear constraints:
                * for problem with nonlinear constraints  should  be  some
                  problem-specific  positive   value,  large  enough  that
                  penalty term changes shape of the function.
                  Starting  from  some  problem-specific   value   penalty
                  coefficient becomes  large  enough  to  exactly  enforce
                  nonlinear constraints;  larger  values  do  not  improve
                  precision.
                  Increasing it too much may slow down convergence, so you
                  should choose it carefully.
                * can be zero for problems WITHOUT  nonlinear  constraints
                  (i.e. for unconstrained ones or ones with  just  box  or
                  linear constraints)
                * if you specify zero value for problem with at least  one
                  nonlinear  constraint,  algorithm  will  terminate  with
                  error code -1.

ALGORITHM OUTLINE

The very basic outline of unconstrained AGS algorithm is given below:

0. If sampling radius is below EpsX  or  we  performed  more  then  MaxIts
   iterations - STOP.
1. sample O(N) gradient values at random locations  around  current point;
   informally speaking, this sample is an implicit piecewise  linear model
   of the function, although algorithm formulation does  not  mention that
   explicitly
2. solve quadratic programming problem in order to find descent direction
3. if QP solver tells us that we  are  near  solution,  decrease  sampling
   radius and move to (0)
4. perform backtracking line search
5. after moving to new point, goto (0)

Constraint handling details:
* box constraints are handled exactly by algorithm
* linear/nonlinear constraints are handled by adding L1  penalty.  Because
  our solver can handle nonsmoothness, we can  use  L1  penalty  function,
  which is an exact one  (i.e.  exact  solution  is  returned  under  such
  penalty).
* penalty coefficient for  linear  constraints  is  chosen  automatically;
  however, penalty coefficient for nonlinear constraints must be specified
  by user.

===== TRACING AGS SOLVER =================================================

AGS solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'AGS'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'AGS.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'AGS'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format
* 'AGS.DETAILED.SAMPLE'-
                  for output of points being visited ,  search  directions
                  and gradient sample. May take a LOT of  space ,  do  not
                  use it on problems with more that several tens of vars.
                  This  symbol   also    implicitly   defines   'AGS'  and
                  'AGS.DETAILED'.

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("AGS,PREC.F6", "path/to/trace.log")
>


  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnssetalgoags(minnsstate &state, const double radius, const double penalty, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnssetalgoags(state.c_ptr(), radius, penalty, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to minnsoptimize().

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minnssetxrep(minnsstate &state, const bool needxrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnssetxrep(state.c_ptr(), needxrep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.

NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnsrequesttermination(minnsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnsrequesttermination(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function provides reverse communication interface
Reverse communication interface is not documented or recommended to use.
See below for functions which provide better documented API
*************************************************************************/
bool minnsiteration(minnsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::minnsiteration(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}


void minnsoptimize(minnsstate &state,
    void (*fvec)(const real_1d_array &x, real_1d_array &fi, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(fvec!=NULL, "ALGLIB: error in 'minnsoptimize()' (fvec is NULL)", &_alglib_env_state);
    alglib_impl::minnssetprotocolv1(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minnsiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
                if( state.needfi )
                {
                    fvec(state.x, state.fi, ptr);
                    continue;
                }
        if( state.xupdated )
        {
            if( rep!=NULL )
                rep(state.x, state.f, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    lbl_no_callback:
        alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minnsoptimize' (some derivatives were not provided?)", &_alglib_env_state);
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
}


void minnsoptimize(minnsstate &state,
    void  (*jac)(const real_1d_array &x, real_1d_array &fi, real_2d_array &jac, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(jac!=NULL,  "ALGLIB: error in 'minnsoptimize()' (jac is NULL)", &_alglib_env_state);
    alglib_impl::minnssetprotocolv1(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minnsiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
                if( state.needfij )
                {
                    jac(state.x, state.fi, state.j, ptr);
                    continue;
                }
        if( state.xupdated )
        {
            if( rep!=NULL )
                rep(state.x, state.f, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    lbl_no_callback:
        alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minnsoptimize' (some derivatives were not provided?)", &_alglib_env_state);
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
}



/*************************************************************************
MinNS results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report. You should check Rep.TerminationType
                in  order  to  distinguish  successful  termination  from
                unsuccessful one:
                * -8   internal integrity control  detected  infinite  or
                       NAN   values   in   function/gradient.    Abnormal
                       termination signalled.
                * -3   box constraints are inconsistent
                * -1   inconsistent parameters were passed:
                       * penalty parameter for minnssetalgoags() is zero,
                         but we have nonlinear constraints set by minnssetnlc()
                *  2   sampling radius decreased below epsx
                *  7    stopping conditions are too stringent,
                        further improvement is impossible,
                        X contains best point found so far.
                *  8    User requested termination via minnsrequesttermination()

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnsresults(const minnsstate &state, real_1d_array &x, minnsreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnsresults(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************

Buffered implementation of minnsresults() which uses pre-allocated  buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnsresultsbuf(const minnsstate &state, real_1d_array &x, minnsreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnsresultsbuf(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine restarts algorithm from new point.
All optimization parameters (including constraints) are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have  same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnscreate() call.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnsrestartfrom(minnsstate &state, const real_1d_array &x, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minnsrestartfrom(state.c_ptr(), x.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************
This object stores nonlinear optimizer state.
You should use functions provided by MinNS subpackage to work  with  this
object
*************************************************************************/
_minnsstate_owner::_minnsstate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minnsstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minnsstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minnsstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minnsstate));
    alglib_impl::_minnsstate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minnsstate_owner::_minnsstate_owner(alglib_impl::minnsstate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minnsstate_owner::_minnsstate_owner(const _minnsstate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minnsstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minnsstate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minnsstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minnsstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minnsstate));
    alglib_impl::_minnsstate_init_copy(p_struct, const_cast<alglib_impl::minnsstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minnsstate_owner& _minnsstate_owner::operator=(const _minnsstate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minnsstate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minnsstate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minnsstate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minnsstate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minnsstate));
    alglib_impl::_minnsstate_init_copy(p_struct, const_cast<alglib_impl::minnsstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minnsstate_owner::~_minnsstate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minnsstate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minnsstate* _minnsstate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minnsstate* _minnsstate_owner::c_ptr() const
{
    return p_struct;
}
minnsstate::minnsstate() : _minnsstate_owner() ,needfi(p_struct->needfi),needfij(p_struct->needfij),xupdated(p_struct->xupdated),f(p_struct->f),fi(&p_struct->fi),j(&p_struct->j),x(&p_struct->x)
{
}

minnsstate::minnsstate(alglib_impl::minnsstate *attach_to):_minnsstate_owner(attach_to) ,needfi(p_struct->needfi),needfij(p_struct->needfij),xupdated(p_struct->xupdated),f(p_struct->f),fi(&p_struct->fi),j(&p_struct->j),x(&p_struct->x)
{
}

minnsstate::minnsstate(const minnsstate &rhs):_minnsstate_owner(rhs) ,needfi(p_struct->needfi),needfij(p_struct->needfij),xupdated(p_struct->xupdated),f(p_struct->f),fi(&p_struct->fi),j(&p_struct->j),x(&p_struct->x)
{
}

minnsstate& minnsstate::operator=(const minnsstate &rhs)
{
    if( this==&rhs )
        return *this;
    _minnsstate_owner::operator=(rhs);
    return *this;
}

minnsstate::~minnsstate()
{
}




/*************************************************************************
This structure stores optimization report:
* IterationsCount           total number of inner iterations
* NFEV                      number of gradient evaluations
* TerminationType           termination type (see below)
* CErr                      maximum violation of all types of constraints
* LCErr                     maximum violation of linear constraints
* NLCErr                    maximum violation of nonlinear constraints

TERMINATION CODES

TerminationType field contains completion code, which can be:
  -8    internal integrity control detected  infinite  or  NAN  values  in
        function/gradient. Abnormal termination signalled.
  -3    box constraints are inconsistent
  -1    inconsistent parameters were passed:
        * penalty parameter for minnssetalgoags() is zero,
          but we have nonlinear constraints set by minnssetnlc()
   2    sampling radius decreased below epsx
   5    MaxIts steps was taken
   7    stopping conditions are too stringent,
        further improvement is impossible,
        X contains best point found so far.
   8    User requested termination via MinNSRequestTermination()

Other fields of this structure are not documented and should not be used!
*************************************************************************/
_minnsreport_owner::_minnsreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minnsreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minnsreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minnsreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minnsreport));
    alglib_impl::_minnsreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minnsreport_owner::_minnsreport_owner(alglib_impl::minnsreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minnsreport_owner::_minnsreport_owner(const _minnsreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minnsreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minnsreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minnsreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minnsreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minnsreport));
    alglib_impl::_minnsreport_init_copy(p_struct, const_cast<alglib_impl::minnsreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minnsreport_owner& _minnsreport_owner::operator=(const _minnsreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minnsreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minnsreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minnsreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minnsreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minnsreport));
    alglib_impl::_minnsreport_init_copy(p_struct, const_cast<alglib_impl::minnsreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minnsreport_owner::~_minnsreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minnsreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minnsreport* _minnsreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minnsreport* _minnsreport_owner::c_ptr() const
{
    return p_struct;
}
minnsreport::minnsreport() : _minnsreport_owner() ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),cerr(p_struct->cerr),lcerr(p_struct->lcerr),nlcerr(p_struct->nlcerr),terminationtype(p_struct->terminationtype),varidx(p_struct->varidx),funcidx(p_struct->funcidx)
{
}

minnsreport::minnsreport(alglib_impl::minnsreport *attach_to):_minnsreport_owner(attach_to) ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),cerr(p_struct->cerr),lcerr(p_struct->lcerr),nlcerr(p_struct->nlcerr),terminationtype(p_struct->terminationtype),varidx(p_struct->varidx),funcidx(p_struct->funcidx)
{
}

minnsreport::minnsreport(const minnsreport &rhs):_minnsreport_owner(rhs) ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),cerr(p_struct->cerr),lcerr(p_struct->lcerr),nlcerr(p_struct->nlcerr),terminationtype(p_struct->terminationtype),varidx(p_struct->varidx),funcidx(p_struct->funcidx)
{
}

minnsreport& minnsreport::operator=(const minnsreport &rhs)
{
    if( this==&rhs )
        return *this;
    _minnsreport_owner::operator=(rhs);
    return *this;
}

minnsreport::~minnsreport()
{
}
#endif

#if defined(AE_COMPILE_MINCOMP) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
Obsolete function, use MinLBFGSSetPrecDefault() instead.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetdefaultpreconditioner(minlbfgsstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgssetdefaultpreconditioner(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Obsolete function, use MinLBFGSSetCholeskyPreconditioner() instead.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetcholeskypreconditioner(minlbfgsstate &state, const real_2d_array &p, const bool isupper, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minlbfgssetcholeskypreconditioner(state.c_ptr(), p.c_ptr(), isupper, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This is obsolete function which was used by previous version of the  BLEIC
optimizer. It does nothing in the current version of BLEIC.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetbarrierwidth(minbleicstate &state, const double mu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicsetbarrierwidth(state.c_ptr(), mu, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This is obsolete function which was used by previous version of the  BLEIC
optimizer. It does nothing in the current version of BLEIC.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetbarrierdecay(minbleicstate &state, const double mudecay, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbleicsetbarrierdecay(state.c_ptr(), mudecay, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 25.03.2010 by Bochkanov Sergey
*************************************************************************/
void minasacreate(const ae_int_t n, const real_1d_array &x, const real_1d_array &bndl, const real_1d_array &bndu, minasastate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minasacreate(n, x.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 25.03.2010 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minasacreate(const real_1d_array &x, const real_1d_array &bndl, const real_1d_array &bndu, minasastate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;
    if( (x.length()!=bndl.length()) || (x.length()!=bndu.length()))
        _ALGLIB_CPP_EXCEPTION("Error while calling 'minasacreate': looks like one of arguments has wrong size");
    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minasacreate(n, x.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minasasetcond(minasastate &state, const double epsg, const double epsf, const double epsx, const ae_int_t maxits, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minasasetcond(state.c_ptr(), epsg, epsf, epsx, maxits, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minasasetxrep(minasastate &state, const bool needxrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minasasetxrep(state.c_ptr(), needxrep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minasasetalgorithm(minasastate &state, const ae_int_t algotype, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minasasetalgorithm(state.c_ptr(), algotype, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minasasetstpmax(minasastate &state, const double stpmax, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minasasetstpmax(state.c_ptr(), stpmax, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function provides reverse communication interface
Reverse communication interface is not documented or recommended to use.
See below for functions which provide better documented API
*************************************************************************/
bool minasaiteration(minasastate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::minasaiteration(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}


void minasaoptimize(minasastate &state,
    void (*grad)(const real_1d_array &x, double &func, real_1d_array &grad, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(grad!=NULL, "ALGLIB: error in 'minasaoptimize()' (grad is NULL)", &_alglib_env_state);
    alglib_impl::minasasetprotocolv1(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minasaiteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
                if( state.needfg )
                {
                    grad(state.x, state.f, state.g, ptr);
                    continue;
                }
        if( state.xupdated )
        {
            if( rep!=NULL )
                rep(state.x, state.f, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    lbl_no_callback:
        alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minasaoptimize' (some derivatives were not provided?)", &_alglib_env_state);
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
}



/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 20.03.2009 by Bochkanov Sergey
*************************************************************************/
void minasaresults(const minasastate &state, real_1d_array &x, minasareport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minasaresults(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 20.03.2009 by Bochkanov Sergey
*************************************************************************/
void minasaresultsbuf(const minasastate &state, real_1d_array &x, minasareport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minasaresultsbuf(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
void minasarestartfrom(minasastate &state, const real_1d_array &x, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minasarestartfrom(state.c_ptr(), x.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************

*************************************************************************/
_minasastate_owner::_minasastate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minasastate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minasastate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minasastate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minasastate));
    alglib_impl::_minasastate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minasastate_owner::_minasastate_owner(alglib_impl::minasastate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minasastate_owner::_minasastate_owner(const _minasastate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minasastate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minasastate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minasastate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minasastate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minasastate));
    alglib_impl::_minasastate_init_copy(p_struct, const_cast<alglib_impl::minasastate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minasastate_owner& _minasastate_owner::operator=(const _minasastate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minasastate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minasastate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minasastate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minasastate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minasastate));
    alglib_impl::_minasastate_init_copy(p_struct, const_cast<alglib_impl::minasastate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minasastate_owner::~_minasastate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minasastate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minasastate* _minasastate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minasastate* _minasastate_owner::c_ptr() const
{
    return p_struct;
}
minasastate::minasastate() : _minasastate_owner() ,needfg(p_struct->needfg),xupdated(p_struct->xupdated),f(p_struct->f),g(&p_struct->g),x(&p_struct->x)
{
}

minasastate::minasastate(alglib_impl::minasastate *attach_to):_minasastate_owner(attach_to) ,needfg(p_struct->needfg),xupdated(p_struct->xupdated),f(p_struct->f),g(&p_struct->g),x(&p_struct->x)
{
}

minasastate::minasastate(const minasastate &rhs):_minasastate_owner(rhs) ,needfg(p_struct->needfg),xupdated(p_struct->xupdated),f(p_struct->f),g(&p_struct->g),x(&p_struct->x)
{
}

minasastate& minasastate::operator=(const minasastate &rhs)
{
    if( this==&rhs )
        return *this;
    _minasastate_owner::operator=(rhs);
    return *this;
}

minasastate::~minasastate()
{
}




/*************************************************************************

*************************************************************************/
_minasareport_owner::_minasareport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minasareport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minasareport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minasareport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minasareport));
    alglib_impl::_minasareport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minasareport_owner::_minasareport_owner(alglib_impl::minasareport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minasareport_owner::_minasareport_owner(const _minasareport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minasareport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minasareport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minasareport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minasareport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minasareport));
    alglib_impl::_minasareport_init_copy(p_struct, const_cast<alglib_impl::minasareport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minasareport_owner& _minasareport_owner::operator=(const _minasareport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minasareport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minasareport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minasareport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minasareport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minasareport));
    alglib_impl::_minasareport_init_copy(p_struct, const_cast<alglib_impl::minasareport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minasareport_owner::~_minasareport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minasareport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minasareport* _minasareport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minasareport* _minasareport_owner::c_ptr() const
{
    return p_struct;
}
minasareport::minasareport() : _minasareport_owner() ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype),activeconstraints(p_struct->activeconstraints)
{
}

minasareport::minasareport(alglib_impl::minasareport *attach_to):_minasareport_owner(attach_to) ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype),activeconstraints(p_struct->activeconstraints)
{
}

minasareport::minasareport(const minasareport &rhs):_minasareport_owner(rhs) ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),terminationtype(p_struct->terminationtype),activeconstraints(p_struct->activeconstraints)
{
}

minasareport& minasareport::operator=(const minasareport &rhs)
{
    if( this==&rhs )
        return *this;
    _minasareport_owner::operator=(rhs);
    return *this;
}

minasareport::~minasareport()
{
}
#endif

#if defined(AE_COMPILE_MINBC) || !defined(AE_PARTIAL_BUILD)
/*************************************************************************
                     BOX CONSTRAINED OPTIMIZATION
          WITH FAST ACTIVATION OF MULTIPLE BOX CONSTRAINTS

DESCRIPTION:
The  subroutine  minimizes  function   F(x) of N arguments subject  to box
constraints (with some of box constraints actually being equality ones).

This optimizer uses algorithm similar to that of MinBLEIC (optimizer  with
general linear constraints), but presence of box-only  constraints  allows
us to use faster constraint activation strategies. On large-scale problems,
with multiple constraints active at the solution, this  optimizer  can  be
several times faster than BLEIC.

REQUIREMENTS:
* user must provide function value and gradient
* starting point X0 must be feasible or
  not too far away from the feasible set
* grad(f) must be Lipschitz continuous on a level set:
  L = { x : f(x)<=f(x0) }
* function must be defined everywhere on the feasible set F

USAGE:

Constrained optimization if far more complex than the unconstrained one.
Here we give very brief outline of the BC optimizer. We strongly recommend
you to read examples in the ALGLIB Reference Manual and to read ALGLIB User Guide
on optimization, which is available at http://www.alglib.net/optimization/

1. User initializes algorithm state with MinBCCreate() call

2. USer adds box constraints by calling MinBCSetBC() function.

3. User sets stopping conditions with MinBCSetCond().

4. User calls MinBCOptimize() function which takes algorithm  state and
   pointer (delegate, etc.) to callback function which calculates F/G.

5. User calls MinBCResults() to get solution

6. Optionally user may call MinBCRestartFrom() to solve another problem
   with same N but another starting point.
   MinBCRestartFrom() allows to reuse already initialized structure.


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size ofX
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbccreate(const ae_int_t n, const real_1d_array &x, minbcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbccreate(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
                     BOX CONSTRAINED OPTIMIZATION
          WITH FAST ACTIVATION OF MULTIPLE BOX CONSTRAINTS

DESCRIPTION:
The  subroutine  minimizes  function   F(x) of N arguments subject  to box
constraints (with some of box constraints actually being equality ones).

This optimizer uses algorithm similar to that of MinBLEIC (optimizer  with
general linear constraints), but presence of box-only  constraints  allows
us to use faster constraint activation strategies. On large-scale problems,
with multiple constraints active at the solution, this  optimizer  can  be
several times faster than BLEIC.

REQUIREMENTS:
* user must provide function value and gradient
* starting point X0 must be feasible or
  not too far away from the feasible set
* grad(f) must be Lipschitz continuous on a level set:
  L = { x : f(x)<=f(x0) }
* function must be defined everywhere on the feasible set F

USAGE:

Constrained optimization if far more complex than the unconstrained one.
Here we give very brief outline of the BC optimizer. We strongly recommend
you to read examples in the ALGLIB Reference Manual and to read ALGLIB User Guide
on optimization, which is available at http://www.alglib.net/optimization/

1. User initializes algorithm state with MinBCCreate() call

2. USer adds box constraints by calling MinBCSetBC() function.

3. User sets stopping conditions with MinBCSetCond().

4. User calls MinBCOptimize() function which takes algorithm  state and
   pointer (delegate, etc.) to callback function which calculates F/G.

5. User calls MinBCResults() to get solution

6. Optionally user may call MinBCRestartFrom() to solve another problem
   with same N but another starting point.
   MinBCRestartFrom() allows to reuse already initialized structure.


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size ofX
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minbccreate(const real_1d_array &x, minbcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbccreate(n, x.c_ptr(), state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
The subroutine is finite difference variant of MinBCCreate().  It  uses
finite differences in order to differentiate target function.

Description below contains information which is specific to  this function
only. We recommend to read comments on MinBCCreate() in  order  to  get
more information about creation of BC optimizer.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[0..N-1].
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinBCSetScale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large truncation  errors, while too small
   step will result in too large numerical  errors.  1.0E-6  can  be  good
   value to start with.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is  less  robust and precise. CG needs exact gradient values. Imprecise
   gradient may slow  down  convergence, especially  on  highly  nonlinear
   problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
void minbccreatef(const ae_int_t n, const real_1d_array &x, const double diffstep, minbcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbccreatef(n, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
The subroutine is finite difference variant of MinBCCreate().  It  uses
finite differences in order to differentiate target function.

Description below contains information which is specific to  this function
only. We recommend to read comments on MinBCCreate() in  order  to  get
more information about creation of BC optimizer.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[0..N-1].
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinBCSetScale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large truncation  errors, while too small
   step will result in too large numerical  errors.  1.0E-6  can  be  good
   value to start with.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is  less  robust and precise. CG needs exact gradient values. Imprecise
   gradient may slow  down  convergence, especially  on  highly  nonlinear
   problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minbccreatef(const real_1d_array &x, const double diffstep, minbcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t n;

    n = x.length();
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbccreatef(n, x.c_ptr(), diffstep, state.c_ptr(), &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
This function sets boundary constraints for BC optimizer.

Boundary constraints are inactive by default (after initial creation).
They are preserved after algorithm restart with MinBCRestartFrom().

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF.

NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

NOTE 2: this solver has following useful properties:
* bound constraints are always satisfied exactly
* function is evaluated only INSIDE area specified by  bound  constraints,
  even  when  numerical  differentiation is used (algorithm adjusts  nodes
  according to boundary constraints)

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetbc(minbcstate &state, const real_1d_array &bndl, const real_1d_array &bndu, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcsetbc(state.c_ptr(), bndl.c_ptr(), bndu.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets stopping conditions for the optimizer.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsG    -   >=0
                The  subroutine  finishes  its  work   if   the  condition
                |v|<EpsG is satisfied, where:
                * |.| means Euclidian norm
                * v - scaled gradient vector, v[i]=g[i]*s[i]
                * g - gradient
                * s - scaling coefficients set by MinBCSetScale()
    EpsF    -   >=0
                The  subroutine  finishes  its work if on k+1-th iteration
                the  condition  |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1}
                is satisfied.
    EpsX    -   >=0
                The subroutine finishes its work if  on  k+1-th  iteration
                the condition |v|<=EpsX is fulfilled, where:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - step vector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinBCSetScale()
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead
to automatic stopping criterion selection.

NOTE: when SetCond() called with non-zero MaxIts, BC solver may perform
      slightly more than MaxIts iterations. I.e., MaxIts  sets  non-strict
      limit on iterations count.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetcond(minbcstate &state, const double epsg, const double epsf, const double epsx, const ae_int_t maxits, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcsetcond(state.c_ptr(), epsg, epsf, epsx, maxits, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets scaling coefficients for BC optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Scaling is also used by finite difference variant of the optimizer  - step
along I-th axis is equal to DiffStep*S[I].

In  most  optimizers  (and  in  the  BC  too)  scaling is NOT a form of
preconditioning. It just  affects  stopping  conditions.  You  should  set
preconditioner  by  separate  call  to  one  of  the  MinBCSetPrec...()
functions.

There is a special  preconditioning  mode, however,  which  uses   scaling
coefficients to form diagonal preconditioning matrix. You  can  turn  this
mode on, if you want.   But  you should understand that scaling is not the
same thing as preconditioning - these are two different, although  related
forms of tuning solver.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minbcsetscale(minbcstate &state, const real_1d_array &s, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcsetscale(state.c_ptr(), s.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification of the preconditioner: preconditioning is turned off.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetprecdefault(minbcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcsetprecdefault(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification  of  the  preconditioner:  diagonal of approximate Hessian is
used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    D       -   diagonal of the approximate Hessian, array[0..N-1],
                (if larger, only leading N elements are used).

NOTE 1: D[i] should be positive. Exception will be thrown otherwise.

NOTE 2: you should pass diagonal of approximate Hessian - NOT ITS INVERSE.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetprecdiag(minbcstate &state, const real_1d_array &d, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcsetprecdiag(state.c_ptr(), d.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Modification of the preconditioner: scale-based diagonal preconditioning.

This preconditioning mode can be useful when you  don't  have  approximate
diagonal of Hessian, but you know that your  variables  are  badly  scaled
(for  example,  one  variable is in [1,10], and another in [1000,100000]),
and most part of the ill-conditioning comes from different scales of vars.

In this case simple  scale-based  preconditioner,  with H[i] = 1/(s[i]^2),
can greatly improve convergence.

IMPRTANT: you should set scale of your variables  with  MinBCSetScale()
call  (before  or after MinBCSetPrecScale() call). Without knowledge of
the scale of your variables scale-based preconditioner will be  just  unit
matrix.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetprecscale(minbcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcsetprecscale(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinBCOptimize().

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetxrep(minbcstate &state, const bool needxrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcsetxrep(state.c_ptr(), needxrep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function sets maximum step length

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    StpMax  -   maximum step length, >=0. Set StpMax to 0.0,  if you don't
                want to limit step length.

Use this subroutine when you optimize target function which contains exp()
or  other  fast  growing  functions,  and optimization algorithm makes too
large  steps  which  lead   to overflow. This function allows us to reject
steps  that  are  too  large  (and  therefore  expose  us  to the possible
overflow) without actually calculating function value at the x+stp*d.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetstpmax(minbcstate &state, const double stpmax, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcsetstpmax(state.c_ptr(), stpmax, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This function provides reverse communication interface
Reverse communication interface is not documented or recommended to use.
See below for functions which provide better documented API
*************************************************************************/
bool minbciteration(minbcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return 0;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    ae_bool result = alglib_impl::minbciteration(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return bool(result);
}


void minbcoptimize(minbcstate &state,
    void (*func)(const real_1d_array &x, double &func, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(func!=NULL, "ALGLIB: error in 'minbcoptimize()' (func is NULL)", &_alglib_env_state);
    alglib_impl::minbcsetprotocolv1(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minbciteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
                if( state.needf )
                {
                    func(state.x, state.f, ptr);
                    continue;
                }
        if( state.xupdated )
        {
            if( rep!=NULL )
                rep(state.x, state.f, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    lbl_no_callback:
        alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minbcoptimize' (some derivatives were not provided?)", &_alglib_env_state);
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
}


void minbcoptimize(minbcstate &state,
    void (*grad)(const real_1d_array &x, double &func, real_1d_array &grad, void *ptr),
    void  (*rep)(const real_1d_array &x, double func, void *ptr),
    void *ptr,
    const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::ae_assert(grad!=NULL, "ALGLIB: error in 'minbcoptimize()' (grad is NULL)", &_alglib_env_state);
    alglib_impl::minbcsetprotocolv1(state.c_ptr(), &_alglib_env_state);
    while( alglib_impl::minbciteration(state.c_ptr(), &_alglib_env_state) )
    {
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
                if( state.needfg )
                {
                    grad(state.x, state.f, state.g, ptr);
                    continue;
                }
        if( state.xupdated )
        {
            if( rep!=NULL )
                rep(state.x, state.f, ptr);
            continue;
        }
        goto lbl_no_callback;
        _ALGLIB_CALLBACK_EXCEPTION_GUARD_END
    lbl_no_callback:
        alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'minbcoptimize' (some derivatives were not provided?)", &_alglib_env_state);
    }
    alglib_impl::ae_state_clear(&_alglib_env_state);
}



/*************************************************************************
This  function  activates/deactivates verification  of  the  user-supplied
analytic gradient.

Upon  activation  of  this  option  OptGuard  integrity  checker  performs
numerical differentiation of your target function  at  the  initial  point
(note: future versions may also perform check  at  the  final  point)  and
compares numerical gradient with analytic one provided by you.

If difference is too large, an error flag is set and optimization  session
continues. After optimization session is over, you can retrieve the report
which  stores  both  gradients  and  specific  components  highlighted  as
suspicious by the OptGuard.

The primary OptGuard report can be retrieved with minbcoptguardresults().

IMPORTANT: gradient check is a high-overhead option which  will  cost  you
           about 3*N additional function evaluations. In many cases it may
           cost as much as the rest of the optimization session.

           YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO
           CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY.

NOTE: unlike previous incarnation of the gradient checking code,  OptGuard
      does NOT interrupt optimization even if it discovers bad gradient.

INPUT PARAMETERS:
    State       -   structure used to store algorithm state
    TestStep    -   verification step used for numerical differentiation:
                    * TestStep=0 turns verification off
                    * TestStep>0 activates verification
                    You should carefully choose TestStep. Value  which  is
                    too large (so large that  function  behavior  is  non-
                    cubic at this scale) will lead  to  false  alarms. Too
                    short step will result in rounding  errors  dominating
                    numerical derivative.

                    You may use different step for different parameters by
                    means of setting scale with minbcsetscale().

=== EXPLANATION ==========================================================

In order to verify gradient algorithm performs following steps:
  * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i],
    where X[i] is i-th component of the initial point and S[i] is a  scale
    of i-th parameter
  * F(X) is evaluated at these trial points
  * we perform one more evaluation in the middle point of the interval
  * we  build  cubic  model using function values and derivatives at trial
    points and we compare its prediction with actual value in  the  middle
    point

  -- ALGLIB --
     Copyright 15.06.2014 by Bochkanov Sergey
*************************************************************************/
void minbcoptguardgradient(minbcstate &state, const double teststep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcoptguardgradient(state.c_ptr(), teststep, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0)
b) nonsmooth     target function (non-C1)

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.

      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.

      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.

      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.

=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbcoptguardsmoothness(minbcstate &state, const ae_int_t level, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcoptguardsmoothness(state.c_ptr(), level, &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0)
b) nonsmooth     target function (non-C1)

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.

      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.

      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.

      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.

=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
#if !defined(AE_NO_EXCEPTIONS)
void minbcoptguardsmoothness(minbcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;    
    ae_int_t level;

    level = 1;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcoptguardsmoothness(state.c_ptr(), level, &_alglib_env_state);

    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}
#endif

/*************************************************************************
Results of OptGuard integrity check, should be called  after  optimization
session is over.

=== PRIMARY REPORT =======================================================

OptGuard performs several checks which are intended to catch common errors
in the implementation of nonlinear function/gradient:
* incorrect analytic gradient
* discontinuous (non-C0) target functions (constraints)
* nonsmooth     (non-C1) target functions (constraints)

Each of these checks is activated with appropriate function:
* minbcoptguardgradient() for gradient verification
* minbcoptguardsmoothness() for C0/C1 checks

Following flags are set when these errors are suspected:
* rep.badgradsuspected, and additionally:
  * rep.badgradvidx for specific variable (gradient element) suspected
  * rep.badgradxbase, a point where gradient is tested
  * rep.badgraduser, user-provided gradient  (stored  as  2D  matrix  with
    single row in order to make  report  structure  compatible  with  more
    complex optimizers like MinNLC or MinLM)
  * rep.badgradnum,   reference    gradient    obtained    via   numerical
    differentiation (stored as  2D matrix with single row in order to make
    report structure compatible with more complex optimizers  like  MinNLC
    or MinLM)
* rep.nonc0suspected
* rep.nonc1suspected

=== ADDITIONAL REPORTS/LOGS ==============================================

Several different tests are performed to catch C0/C1 errors, you can  find
out specific test signaled error by looking to:
* rep.nonc0test0positive, for non-C0 test #0
* rep.nonc1test0positive, for non-C1 test #0
* rep.nonc1test1positive, for non-C1 test #1

Additional information (including line search logs)  can  be  obtained  by
means of:
* minbcoptguardnonc1test0results()
* minbcoptguardnonc1test1results()
which return detailed error reports, specific points where discontinuities
were found, and so on.

==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    rep     -   generic OptGuard report;  more  detailed  reports  can  be
                retrieved with other functions.

NOTE: false negatives (nonsmooth problems are not identified as  nonsmooth
      ones) are possible although unlikely.

      The reason  is  that  you  need  to  make several evaluations around
      nonsmoothness  in  order  to  accumulate  enough  information  about
      function curvature. Say, if you start right from the nonsmooth point,
      optimizer simply won't get enough data to understand what  is  going
      wrong before it terminates due to abrupt changes in the  derivative.
      It is also  possible  that  "unlucky"  step  will  move  us  to  the
      termination too quickly.

      Our current approach is to have less than 0.1%  false  negatives  in
      our test examples  (measured  with  multiple  restarts  from  random
      points), and to have exactly 0% false positives.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbcoptguardresults(minbcstate &state, optguardreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcoptguardresults(state.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #0

Nonsmoothness (non-C1) test #0 studies  function  values  (not  gradient!)
obtained during line searches and monitors  behavior  of  the  directional
derivative estimate.

This test is less powerful than test #1, but it does  not  depend  on  the
gradient values and thus it is more robust against artifacts introduced by
numerical differentiation.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], f[] - arrays of length CNT which store step lengths and  function
  values at these points; f[i] is evaluated in x0+stp[i]*d.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.

==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #0 "strong" report
    lngrep  -   C1 test #0 "long" report

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbcoptguardnonc1test0results(const minbcstate &state, optguardnonc1test0report &strrep, optguardnonc1test0report &lngrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcoptguardnonc1test0results(state.c_ptr(), strrep.c_ptr(), lngrep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #1

Nonsmoothness (non-C1)  test  #1  studies  individual  components  of  the
gradient computed during line search.

When precise analytic gradient is provided this test is more powerful than
test #0  which  works  with  function  values  and  ignores  user-provided
gradient.  However,  test  #0  becomes  more   powerful   when   numerical
differentiation is employed (in such cases test #1 detects  higher  levels
of numerical noise and becomes too conservative).

This test also tells specific components of the gradient which violate  C1
continuity, which makes it more informative than #0, which just tells that
continuity is violated.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* vidx - is an index of the variable in [0,N) with nonsmooth derivative
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], g[] - arrays of length CNT which store step lengths and  gradient
  values at these points; g[i] is evaluated in  x0+stp[i]*d  and  contains
  vidx-th component of the gradient.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.

==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #1 "strong" report
    lngrep  -   C1 test #1 "long" report

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbcoptguardnonc1test1results(minbcstate &state, optguardnonc1test1report &strrep, optguardnonc1test1report &lngrep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcoptguardnonc1test1results(state.c_ptr(), strrep.c_ptr(), lngrep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
BC results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report. You should check Rep.TerminationType
                in  order  to  distinguish  successful  termination  from
                unsuccessful one:
                * -8    internal integrity control  detected  infinite or
                        NAN   values   in   function/gradient.   Abnormal
                        termination signalled.
                * -3   inconsistent constraints.
                *  1   relative function improvement is no more than EpsF.
                *  2   scaled step is no more than EpsX.
                *  4   scaled gradient norm is no more than EpsG.
                *  5   MaxIts steps was taken
                *  8   terminated by user who called minbcrequesttermination().
                       X contains point which was "current accepted"  when
                       termination request was submitted.
                More information about fields of this  structure  can  be
                found in the comments on MinBCReport datatype.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbcresults(const minbcstate &state, real_1d_array &x, minbcreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcresults(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
BC results

Buffered implementation of MinBCResults() which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbcresultsbuf(const minbcstate &state, real_1d_array &x, minbcreport &rep, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcresultsbuf(state.c_ptr(), x.c_ptr(), rep.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine restarts algorithm from new point.
All optimization parameters (including constraints) are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have  same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinBCCreate call.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbcrestartfrom(minbcstate &state, const real_1d_array &x, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcrestartfrom(state.c_ptr(), x.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}

/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.

NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void minbcrequesttermination(minbcstate &state, const xparams _xparams)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _alglib_env_state;
    alglib_impl::ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
        return;
#endif
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( _xparams.flags!=(alglib_impl::ae_uint64_t)0x0 )
        ae_state_set_flags(&_alglib_env_state, _xparams.flags);
    alglib_impl::minbcrequesttermination(state.c_ptr(), &_alglib_env_state);
    alglib_impl::ae_state_clear(&_alglib_env_state);
    return;
}


/*************************************************************************
This object stores nonlinear optimizer state.
You should use functions provided by MinBC subpackage to work with this
object
*************************************************************************/
_minbcstate_owner::_minbcstate_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minbcstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minbcstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minbcstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minbcstate));
    alglib_impl::_minbcstate_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minbcstate_owner::_minbcstate_owner(alglib_impl::minbcstate *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minbcstate_owner::_minbcstate_owner(const _minbcstate_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minbcstate_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minbcstate copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minbcstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::minbcstate), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minbcstate));
    alglib_impl::_minbcstate_init_copy(p_struct, const_cast<alglib_impl::minbcstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minbcstate_owner& _minbcstate_owner::operator=(const _minbcstate_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minbcstate assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minbcstate assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minbcstate assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minbcstate_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minbcstate));
    alglib_impl::_minbcstate_init_copy(p_struct, const_cast<alglib_impl::minbcstate*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minbcstate_owner::~_minbcstate_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minbcstate_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minbcstate* _minbcstate_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minbcstate* _minbcstate_owner::c_ptr() const
{
    return p_struct;
}
minbcstate::minbcstate() : _minbcstate_owner() ,needf(p_struct->needf),needfg(p_struct->needfg),xupdated(p_struct->xupdated),f(p_struct->f),g(&p_struct->g),x(&p_struct->x)
{
}

minbcstate::minbcstate(alglib_impl::minbcstate *attach_to):_minbcstate_owner(attach_to) ,needf(p_struct->needf),needfg(p_struct->needfg),xupdated(p_struct->xupdated),f(p_struct->f),g(&p_struct->g),x(&p_struct->x)
{
}

minbcstate::minbcstate(const minbcstate &rhs):_minbcstate_owner(rhs) ,needf(p_struct->needf),needfg(p_struct->needfg),xupdated(p_struct->xupdated),f(p_struct->f),g(&p_struct->g),x(&p_struct->x)
{
}

minbcstate& minbcstate::operator=(const minbcstate &rhs)
{
    if( this==&rhs )
        return *this;
    _minbcstate_owner::operator=(rhs);
    return *this;
}

minbcstate::~minbcstate()
{
}




/*************************************************************************
This structure stores optimization report:
* iterationscount           number of iterations
* nfev                      number of gradient evaluations
* terminationtype           termination type (see below)

TERMINATION CODES

terminationtype field contains completion code, which can be:
  -8    internal integrity control detected  infinite  or  NAN  values  in
        function/gradient. Abnormal termination signalled.
  -3    inconsistent constraints.
   1    relative function improvement is no more than EpsF.
   2    relative step is no more than EpsX.
   4    gradient norm is no more than EpsG
   5    MaxIts steps was taken
   7    stopping conditions are too stringent,
        further improvement is impossible,
        X contains best point found so far.
   8    terminated by user who called minbcrequesttermination(). X contains
        point which was "current accepted" when  termination  request  was
        submitted.
*************************************************************************/
_minbcreport_owner::_minbcreport_owner()
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minbcreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    p_struct = (alglib_impl::minbcreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minbcreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minbcreport));
    alglib_impl::_minbcreport_init(p_struct, &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minbcreport_owner::_minbcreport_owner(alglib_impl::minbcreport *attach_to)
{
    p_struct = attach_to;
    is_attached = true;
}

_minbcreport_owner::_minbcreport_owner(const _minbcreport_owner &rhs)
{
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
        if( p_struct!=NULL )
        {
            alglib_impl::_minbcreport_destroy(p_struct);
            alglib_impl::ae_free(p_struct);
        }
        p_struct = NULL;
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    p_struct = NULL;
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minbcreport copy constructor failure (source is not initialized)", &_state);
    p_struct = (alglib_impl::minbcreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::minbcreport), &_state);
    memset(p_struct, 0, sizeof(alglib_impl::minbcreport));
    alglib_impl::_minbcreport_init_copy(p_struct, const_cast<alglib_impl::minbcreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    is_attached = false;
}

_minbcreport_owner& _minbcreport_owner::operator=(const _minbcreport_owner &rhs)
{
    if( this==&rhs )
        return *this;
    jmp_buf _break_jump;
    alglib_impl::ae_state _state;
    
    alglib_impl::ae_state_init(&_state);
    if( setjmp(_break_jump) )
    {
#if !defined(AE_NO_EXCEPTIONS)
        _ALGLIB_CPP_EXCEPTION(_state.error_msg);
#else
        _ALGLIB_SET_ERROR_FLAG(_state.error_msg);
        return *this;
#endif
    }
    alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
    alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: minbcreport assignment constructor failure (destination is not initialized)", &_state);
    alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: minbcreport assignment constructor failure (source is not initialized)", &_state);
    alglib_impl::ae_assert(!is_attached, "ALGLIB: minbcreport assignment constructor failure (can not assign to the structure which is attached to something else)", &_state);
    alglib_impl::_minbcreport_destroy(p_struct);
    memset(p_struct, 0, sizeof(alglib_impl::minbcreport));
    alglib_impl::_minbcreport_init_copy(p_struct, const_cast<alglib_impl::minbcreport*>(rhs.p_struct), &_state, ae_false);
    ae_state_clear(&_state);
    return *this;
}

_minbcreport_owner::~_minbcreport_owner()
{
    if( p_struct!=NULL && !is_attached )
    {
        alglib_impl::_minbcreport_destroy(p_struct);
        ae_free(p_struct);
    }
}

alglib_impl::minbcreport* _minbcreport_owner::c_ptr()
{
    return p_struct;
}

const alglib_impl::minbcreport* _minbcreport_owner::c_ptr() const
{
    return p_struct;
}
minbcreport::minbcreport() : _minbcreport_owner() ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),varidx(p_struct->varidx),terminationtype(p_struct->terminationtype)
{
}

minbcreport::minbcreport(alglib_impl::minbcreport *attach_to):_minbcreport_owner(attach_to) ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),varidx(p_struct->varidx),terminationtype(p_struct->terminationtype)
{
}

minbcreport::minbcreport(const minbcreport &rhs):_minbcreport_owner(rhs) ,iterationscount(p_struct->iterationscount),nfev(p_struct->nfev),varidx(p_struct->varidx),terminationtype(p_struct->terminationtype)
{
}

minbcreport& minbcreport::operator=(const minbcreport &rhs)
{
    if( this==&rhs )
        return *this;
    _minbcreport_owner::operator=(rhs);
    return *this;
}

minbcreport::~minbcreport()
{
}
#endif
}

/////////////////////////////////////////////////////////////////////////
//
// THIS SECTION CONTAINS IMPLEMENTATION OF COMPUTATIONAL CORE
//
/////////////////////////////////////////////////////////////////////////
namespace alglib_impl
{
#if defined(AE_COMPILE_OPTGUARDAPI) || !defined(AE_PARTIAL_BUILD)


#endif
#if defined(AE_COMPILE_OPTS) || !defined(AE_PARTIAL_BUILD)


#endif
#if defined(AE_COMPILE_OPTSERV) || !defined(AE_PARTIAL_BUILD)
static double optserv_ognoiselevelf = 1.0E2*ae_machineepsilon;
static double optserv_ognoiselevelg = 1.0E4*ae_machineepsilon;
static double optserv_ogminrating0 = 50.0;
static double optserv_ogminrating1 = 50.0;
static double optserv_xbfgssigma2 = 0.2;
static double optserv_xbfgssigma3 = 5;
static double optserv_feasibilityerror(/* Real    */ const ae_matrix* ce,
     /* Real    */ const ae_vector* x,
     ae_int_t nmain,
     ae_int_t nslack,
     ae_int_t k,
     /* Real    */ ae_vector* tmp0,
     ae_state *_state);
static void optserv_feasibilityerrorgrad(/* Real    */ const ae_matrix* ce,
     /* Real    */ const ae_vector* x,
     ae_int_t nmain,
     ae_int_t nslack,
     ae_int_t k,
     double* err,
     /* Real    */ ae_vector* grad,
     /* Real    */ ae_vector* tmp0,
     ae_state *_state);
static void optserv_testc0continuity(double f0,
     double f1,
     double f2,
     double f3,
     double noise0,
     double noise1,
     double noise2,
     double noise3,
     double delta0,
     double delta1,
     double delta2,
     ae_bool applyspecialcorrection,
     double* rating,
     double* lipschitz,
     ae_state *_state);
static void optserv_c1continuitytest0(smoothnessmonitor* monitor,
     ae_int_t funcidx,
     ae_int_t stpidx,
     ae_int_t sortedcnt,
     ae_state *_state);
static void optserv_c1continuitytest1(smoothnessmonitor* monitor,
     ae_int_t funcidx,
     ae_int_t stpidx,
     ae_int_t sortedcnt,
     ae_state *_state);
static void optserv_popfrontxy(xbfgshessian* hess, ae_state *_state);
static void optserv_hessianupdatelowlevel(xbfgshessian* hess,
     /* Real    */ ae_matrix* h,
     /* Real    */ const ae_vector* sk,
     /* Real    */ const ae_vector* yk,
     ae_int_t* status,
     ae_state *_state);
static void optserv_hessianupdatelowlevel2(xbfgshessian* hess,
     /* Real    */ ae_matrix* h,
     /* Real    */ const ae_vector* sk,
     /* Real    */ const ae_vector* yk,
     ae_int_t* status,
     ae_state *_state);
static void optserv_resetlowrankmodel(xbfgshessian* hess,
     ae_state *_state);
static void optserv_recomputelowrankmodel(xbfgshessian* hess,
     ae_state *_state);
static void optserv_recomputelowrankmodellbfgs(xbfgshessian* hess,
     ae_state *_state);
static void optserv_recomputelowrankmodelsr1(xbfgshessian* hess,
     ae_state *_state);
static void optserv_scaleandperformfinitenessassurance(xbfgshessian* hess,
     /* Real    */ ae_matrix* s,
     /* Real    */ ae_matrix* y,
     double* sigma,
     /* Real    */ ae_matrix* l,
     /* Real    */ ae_vector* d,
     ae_int_t* filteredlen,
     ae_state *_state);
static void optserv_recomputelowrankdiagonal(xbfgshessian* hess,
     ae_state *_state);
static void optserv_recomputelowrankdiagonalbfgs(xbfgshessian* hess,
     ae_state *_state);
static void optserv_recomputelowrankdiagonalsr1(xbfgshessian* hess,
     ae_state *_state);
static ae_bool optserv_unscalexbatchfinitebndkernel(/* Real    */ const ae_vector* xs,
     ae_int_t batchsize,
     /* Real    */ const ae_vector* s,
     ae_int_t n,
     /* Real    */ const ae_vector* sclfinitebndl,
     /* Real    */ const ae_vector* sclfinitebndu,
     /* Real    */ const ae_vector* rawfinitebndl,
     /* Real    */ const ae_vector* rawfinitebndu,
     /* Real    */ ae_vector* xu,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_MINLBFGS) || !defined(AE_PARTIAL_BUILD)
static double minlbfgs_gtol = 0.4;
static void minlbfgs_clearrequestfields(minlbfgsstate* state,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_CQMODELS) || !defined(AE_PARTIAL_BUILD)
static ae_int_t cqmodels_newtonrefinementits = 3;
static ae_bool cqmodels_cqmrebuild(convexquadraticmodel* s,
     ae_state *_state);
static void cqmodels_cqmsolveea(convexquadraticmodel* s,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* tmp,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_LPQPSERV) || !defined(AE_PARTIAL_BUILD)


#endif
#if defined(AE_COMPILE_SNNLS) || !defined(AE_PARTIAL_BUILD)
static void snnls_funcgradu(const snnlssolver* s,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* r,
     /* Real    */ ae_vector* g,
     double* f,
     ae_state *_state);
static void snnls_func(const snnlssolver* s,
     /* Real    */ const ae_vector* x,
     double* f,
     ae_state *_state);
static void snnls_trdprepare(snnlssolver* s,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* diag,
     double lambdav,
     /* Real    */ ae_vector* trdd,
     /* Real    */ ae_matrix* trda,
     /* Real    */ ae_vector* tmp0,
     /* Real    */ ae_vector* tmp1,
     /* Real    */ ae_vector* tmp2,
     /* Real    */ ae_matrix* tmplq,
     ae_state *_state);
static void snnls_trdsolve(/* Real    */ const ae_vector* trdd,
     /* Real    */ const ae_matrix* trda,
     ae_int_t ns,
     ae_int_t nd,
     /* Real    */ ae_vector* d,
     ae_state *_state);
static void snnls_trdfixvariable(/* Real    */ ae_vector* trdd,
     /* Real    */ ae_matrix* trda,
     ae_int_t ns,
     ae_int_t nd,
     ae_int_t idx,
     /* Real    */ ae_vector* tmp,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_SACTIVESETS) || !defined(AE_PARTIAL_BUILD)
static ae_int_t sactivesets_maxbasisage = 5;
static double sactivesets_maxbasisdecay = 0.01;
static double sactivesets_minnormseparation = 0.25;
static void sactivesets_constraineddescent(sactiveset* state,
     /* Real    */ const ae_vector* g,
     /* Real    */ const ae_vector* h,
     /* Real    */ const ae_matrix* ha,
     ae_bool normalize,
     /* Real    */ ae_vector* d,
     ae_state *_state);
static void sactivesets_reactivateconstraints(sactiveset* state,
     /* Real    */ const ae_vector* gc,
     /* Real    */ const ae_vector* h,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_QQPSOLVER) || !defined(AE_PARTIAL_BUILD)
static ae_int_t qqpsolver_quickqprestartcg = 50;
static double qqpsolver_regz = 1.0E-9;
static double qqpsolver_projectedtargetfunction(const qqpbuffers* sstate,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* d,
     double stp,
     /* Real    */ ae_vector* tmp0,
     /* Real    */ ae_vector* tmp1,
     ae_state *_state);
static void qqpsolver_targetgradient(const qqpbuffers* sstate,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* g,
     ae_state *_state);
static void qqpsolver_quadraticmodel(const qqpbuffers* sstate,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* d,
     /* Real    */ const ae_vector* g,
     double* d1,
     ae_int_t* d1est,
     double* d2,
     ae_int_t* d2est,
     /* Real    */ ae_vector* tmp0,
     ae_state *_state);
static void qqpsolver_findbeststepandmove(const qqpbuffers* sstate,
     sactiveset* sas,
     /* Real    */ const ae_vector* d,
     double stp,
     ae_bool needact,
     ae_int_t cidx,
     double cval,
     /* Real    */ const ae_vector* addsteps,
     ae_int_t addstepscnt,
     /* Boolean */ ae_vector* activated,
     /* Real    */ ae_vector* tmp0,
     /* Real    */ ae_vector* tmp1,
     ae_state *_state);
static ae_bool qqpsolver_cnewtonbuild(qqpbuffers* sstate,
     ae_int_t sparsesolver,
     ae_int_t* ncholesky,
     ae_state *_state);
static ae_bool qqpsolver_cnewtonupdate(qqpbuffers* sstate,
     const qqpsettings* settings,
     ae_int_t* ncupdates,
     ae_state *_state);
static ae_bool qqpsolver_cnewtonstep(qqpbuffers* sstate,
     const qqpsettings* settings,
     /* Real    */ ae_vector* gc,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_QPDENSEAULSOLVER) || !defined(AE_PARTIAL_BUILD)
static double qpdenseaulsolver_evictionlevel = -0.01;
static double qpdenseaulsolver_expansionratio = 0.20;
static void qpdenseaulsolver_generateexmodel(/* Real    */ const ae_matrix* sclsfta,
     /* Real    */ const ae_vector* sclsftb,
     ae_int_t nmain,
     /* Real    */ const ae_vector* sclsftbndl,
     /* Boolean */ const ae_vector* sclsfthasbndl,
     /* Real    */ const ae_vector* sclsftbndu,
     /* Boolean */ const ae_vector* sclsfthasbndu,
     /* Real    */ const ae_matrix* sclsftcleic,
     ae_int_t sclsftnec,
     ae_int_t sclsftnic,
     /* Real    */ const ae_vector* nulc,
     double rho,
     /* Real    */ ae_matrix* exa,
     /* Real    */ ae_vector* exb,
     /* Real    */ ae_vector* exbndl,
     /* Real    */ ae_vector* exbndu,
     /* Real    */ ae_matrix* tmp2,
     ae_state *_state);
static void qpdenseaulsolver_generateexinitialpoint(/* Real    */ const ae_vector* sclsftxc,
     ae_int_t nmain,
     ae_int_t nslack,
     /* Real    */ ae_vector* exxc,
     ae_state *_state);
static void qpdenseaulsolver_updatelagrangemultipliers(/* Real    */ const ae_matrix* sclsfta,
     /* Real    */ const ae_vector* sclsftb,
     ae_int_t nmain,
     /* Real    */ const ae_vector* sclsftbndl,
     /* Boolean */ const ae_vector* sclsfthasbndl,
     /* Real    */ const ae_vector* sclsftbndu,
     /* Boolean */ const ae_vector* sclsfthasbndu,
     /* Real    */ const ae_matrix* sclsftcleic,
     ae_int_t sclsftnec,
     ae_int_t sclsftnic,
     /* Real    */ const ae_vector* exxc,
     /* Real    */ ae_vector* nulcest,
     qpdenseaulbuffers* buffers,
     ae_state *_state);
static void qpdenseaulsolver_scaleshiftoriginalproblem(const convexquadraticmodel* a,
     const sparsematrix* sparsea,
     ae_int_t akind,
     ae_bool sparseaupper,
     /* Real    */ const ae_vector* b,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t nmain,
     /* Real    */ const ae_matrix* cleic,
     ae_int_t dnec,
     ae_int_t dnic,
     const sparsematrix* scleic,
     ae_int_t snec,
     ae_int_t snic,
     ae_bool renormlc,
     qpdenseaulbuffers* state,
     /* Real    */ ae_vector* xs,
     ae_state *_state);
static double qpdenseaulsolver_normalizequadraticterm(/* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ const ae_matrix* cleic,
     ae_int_t nec,
     ae_int_t nic,
     ae_bool usecleic,
     /* Real    */ ae_matrix* tmp2,
     ae_state *_state);
static void qpdenseaulsolver_selectinitialworkingset(/* Real    */ const ae_matrix* a,
     ae_int_t nmain,
     /* Real    */ const ae_matrix* cleic,
     ae_int_t nec,
     ae_int_t nic,
     /* Real    */ ae_vector* tmp0,
     /* Real    */ ae_matrix* tmp2,
     ae_int_t* nicwork,
     ae_bool* allowwseviction,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_VIPMSOLVER) || !defined(AE_PARTIAL_BUILD)
static double vipmsolver_muquasidense = 5.0;
static ae_int_t vipmsolver_maxipmits = 200;
static double vipmsolver_initslackval = 100.0;
static double vipmsolver_maxdualslackval = 1.0E4;
static double vipmsolver_steplengthdecay = 0.95;
static double vipmsolver_stagnationdelta = 0.99999;
static double vipmsolver_primalinfeasible1 = 1.0E-3;
static double vipmsolver_dualinfeasible1 = 1.0E-3;
static double vipmsolver_bigy = 1.0E8;
static double vipmsolver_ygrowth = 1.0E6;
static ae_int_t vipmsolver_phase0length = 10;
static ae_int_t vipmsolver_itersfortoostringentcond = 25;
static ae_int_t vipmsolver_minitersbeforedroppingbounds = 3;
static ae_int_t vipmsolver_minitersbeforeinfeasible = 3;
static ae_int_t vipmsolver_minitersbeforestagnation = 5;
static ae_int_t vipmsolver_minitersbeforeeworststagnation = 50;
static ae_int_t vipmsolver_primalstagnationlen = 5;
static ae_int_t vipmsolver_dualstagnationlen = 7;
static double vipmsolver_bigconstrxtol = 1.0E-5;
static double vipmsolver_bigconstrmag = 1.0E3;
static double vipmsolver_minitersbeforesafeguards = 5;
static double vipmsolver_badsteplength = 1.0E-3;
static ae_int_t vipmsolver_maxrefinementits = 5;
static void vipmsolver_varsinitbyzero(vipmvars* vstate,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state);
static void vipmsolver_varsinitfrom(vipmvars* vstate,
     const vipmvars* vsrc,
     ae_state *_state);
static void vipmsolver_varsaddstep(vipmvars* vstate,
     const vipmvars* vdir,
     double stpp,
     double stpd,
     ae_state *_state);
static double vipmsolver_varscomputecomplementaritygap(const vipmvars* vstate,
     ae_state *_state);
static double vipmsolver_varscomputemu(const vipmstate* state,
     const vipmvars* vstate,
     ae_state *_state);
static void vipmsolver_reducedsysteminit(vipmreducedsparsesystem* s,
     const vipmstate* solver,
     ae_state *_state);
static ae_bool vipmsolver_reducedsystemfactorizewithaddends(vipmreducedsparsesystem* s,
     /* Real    */ const ae_vector* diag,
     /* Real    */ const ae_vector* damp,
     double modeps,
     double badchol,
     double* sumsq,
     double* errsq,
     ae_state *_state);
static void vipmsolver_reducedsystemsolve(vipmreducedsparsesystem* s,
     ae_bool dotrace,
     /* Real    */ ae_vector* b,
     ae_state *_state);
static void vipmsolver_vipminit(vipmstate* state,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t n,
     ae_int_t nmain,
     ae_int_t ftype,
     ae_bool normalize,
     ae_state *_state);
static double vipmsolver_vipmtarget(const vipmstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state);
static void vipmsolver_multiplygeax(const vipmstate* state,
     double alpha,
     /* Real    */ const ae_vector* x,
     ae_int_t offsx,
     double beta,
     /* Real    */ ae_vector* y,
     ae_int_t offsax,
     ae_state *_state);
static void vipmsolver_multiplygeatx(const vipmstate* state,
     double alpha,
     /* Real    */ const ae_vector* x,
     ae_int_t offsx,
     double beta,
     /* Real    */ ae_vector* y,
     ae_int_t offsy,
     ae_state *_state);
static void vipmsolver_multiplyhx(const vipmstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* hx,
     ae_state *_state);
static void vipmsolver_vipmmultiply(const vipmstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     /* Real    */ ae_vector* hx,
     /* Real    */ ae_vector* ax,
     /* Real    */ ae_vector* aty,
     ae_state *_state);
static void vipmsolver_vipmpowerup(vipmstate* state,
     double regfree,
     ae_state *_state);
static ae_bool vipmsolver_vipmfactorize(vipmstate* state,
     double alpha0,
     /* Real    */ const ae_vector* d,
     double beta0,
     /* Real    */ const ae_vector* e,
     double alpha11,
     double beta11,
     double modeps,
     double dampeps,
     ae_state *_state);
static void vipmsolver_solvereducedkktsystem(vipmstate* state,
     /* Real    */ ae_vector* deltaxy,
     ae_state *_state);
static ae_bool vipmsolver_vipmprecomputenewtonfactorization(vipmstate* state,
     const vipmvars* v0,
     double regeps,
     double modeps,
     double dampeps,
     double dampfree,
     ae_state *_state);
static void vipmsolver_solvekktsystem(vipmstate* state,
     const vipmrighthandside* rhs,
     vipmvars* sol,
     ae_state *_state);
static ae_bool vipmsolver_vipmcomputestepdirection(vipmstate* state,
     const vipmvars* v0,
     double muestimate,
     const vipmvars* vdestimate,
     vipmvars* vdresult,
     double reg,
     ae_bool isdampepslarge,
     ae_state *_state);
static void vipmsolver_vipmcomputesteplength(const vipmstate* state,
     const vipmvars* v0,
     const vipmvars* vs,
     double stepdecay,
     ae_bool separatestep,
     double* alphap,
     double* alphad,
     ae_state *_state);
static void vipmsolver_vipmperformstep(vipmstate* state,
     double alphap,
     double alphad,
     ae_state *_state);
static void vipmsolver_computeerrors(vipmstate* state,
     double* errp2,
     double* errd2,
     double* errpinf,
     double* errdinf,
     double* egap,
     ae_state *_state);
static void vipmsolver_runintegritychecks(const vipmstate* state,
     const vipmvars* v0,
     const vipmvars* vd,
     double alphap,
     double alphad,
     ae_state *_state);
static void vipmsolver_traceprogress(vipmstate* state,
     double mu,
     double muaff,
     double sigma,
     double alphap,
     double alphad,
     ae_state *_state);
static void vipmsolver_rhscompute(vipmstate* state,
     const vipmvars* v0,
     double muestimate,
     const vipmvars* direstimate,
     vipmrighthandside* rhs,
     double reg,
     ae_state *_state);
static void vipmsolver_rhssubtract(vipmstate* state,
     vipmrighthandside* rhs,
     const vipmvars* v0,
     const vipmvars* vdcandidate,
     double reg,
     ae_state *_state);
static double vipmsolver_rhsprimal2(const vipmrighthandside* rhs,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state);
static double vipmsolver_rhsdual2(const vipmrighthandside* rhs,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state);
static double vipmsolver_rhsprimalinf(const vipmrighthandside* rhs,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state);
static double vipmsolver_rhsdualinf(const vipmrighthandside* rhs,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state);
static double vipmsolver_rhscompl2(const vipmrighthandside* rhs,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state);
static double vipmsolver_minnz(/* Real    */ const ae_vector* x,
     ae_int_t n,
     ae_state *_state);
static double vipmsolver_minprodnz(/* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     ae_int_t n,
     ae_state *_state);
static double vipmsolver_maxprodnz(/* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     ae_int_t n,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_IPM2SOLVER) || !defined(AE_PARTIAL_BUILD)
static ae_int_t ipm2solver_spsymmfacttype = 21;
static double ipm2solver_muquasidense = 10.0;
static double ipm2solver_mupromote = 10.0;
static ae_int_t ipm2solver_defipmits = 200;
static double ipm2solver_initslackval = 100.0;
static double ipm2solver_steplengthdecay = 0.95;
static double ipm2solver_stagnationdelta = 0.99999;
static double ipm2solver_primalinfeasible1 = 1.0E-3;
static double ipm2solver_dualinfeasible1 = 1.0E-3;
static double ipm2solver_bigy = 1.0E8;
static double ipm2solver_ygrowth = 1.0E6;
static ae_int_t ipm2solver_itersfortoostringentcond = 25;
static ae_int_t ipm2solver_minitersbeforeinfeasible = 3;
static ae_int_t ipm2solver_minitersbeforestagnation = 5;
static ae_int_t ipm2solver_minitersbeforeeworststagnation = 15;
static ae_int_t ipm2solver_primalstagnationlen = 5;
static ae_int_t ipm2solver_dualstagnationlen = 7;
static double ipm2solver_minitersbeforesafeguards = 5;
static double ipm2solver_badsteplength = 1.0E-3;
static void ipm2solver_varsinitbyzero(ipm2vars* vstate,
     ae_int_t ntotal,
     ae_int_t m,
     ae_state *_state);
static void ipm2solver_varsinitfrom(ipm2vars* vstate,
     const ipm2vars* vsrc,
     ae_state *_state);
static void ipm2solver_varsaddstep(ipm2vars* vstate,
     const ipm2vars* vdir,
     double stp,
     ae_state *_state);
static double ipm2solver_varscomputecomplementaritygap(const ipm2vars* vstate,
     ae_state *_state);
static double ipm2solver_varscomputemu(const ipm2state* state,
     const ipm2vars* vstate,
     ae_state *_state);
static double ipm2solver_varscomputemutrial(const ipm2state* state,
     const ipm2vars* current,
     const ipm2vars* delta,
     double alpha,
     ae_state *_state);
static void ipm2solver_multiplygeax(const ipm2state* state,
     double alpha,
     /* Real    */ const ae_vector* x,
     ae_int_t offsx,
     double beta,
     /* Real    */ ae_vector* y,
     ae_int_t offsax,
     ae_state *_state);
static void ipm2solver_multiplygeatx(const ipm2state* state,
     double alpha,
     /* Real    */ const ae_vector* x,
     ae_int_t offsx,
     double beta,
     /* Real    */ ae_vector* y,
     ae_int_t offsy,
     ae_state *_state);
static void ipm2solver_multiplyhx(const ipm2state* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* hx,
     ae_state *_state);
static void ipm2solver_ipm2multiply(const ipm2state* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     /* Real    */ ae_vector* hx,
     /* Real    */ ae_vector* ax,
     /* Real    */ ae_vector* aty,
     ae_state *_state);
static void ipm2solver_ipm2powerup(ipm2state* state,
     double regfree,
     ae_state *_state);
static void ipm2solver_reducedsystempowerup(ipm2reducedsystem* s,
     ipm2state* state,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* y,
     ae_state *_state);
static ae_bool ipm2solver_reducedsystemcomputefactorization(ipm2reducedsystem* redsys,
     ipm2state* sstate,
     const ipm2vars* current,
     double dampepsmu,
     double dampepszs,
     double dampepsp,
     double dampepsd,
     ae_state *_state);
static void ipm2solver_ipm2computesteplength(const ipm2state* state,
     const ipm2vars* current,
     const ipm2vars* delta,
     double* alpha,
     ae_state *_state);
static void ipm2solver_computeerrors(const ipm2state* state,
     const ipm2righthandside* rhs,
     /* Real    */ const ae_vector* currenthx,
     /* Real    */ const ae_vector* currentax,
     /* Real    */ const ae_vector* currentaty,
     double* errp2,
     double* errd2,
     double* errpinf,
     double* errdinf,
     double* egap,
     ae_state *_state);
static void ipm2solver_traceprogress(ipm2state* state,
     double muinit,
     double alphaaff,
     double alphafin,
     ae_state *_state);
static void ipm2solver_rhscomputeprimdual(const ipm2state* state,
     const ipm2vars* current,
     /* Real    */ const ae_vector* currenthx,
     /* Real    */ const ae_vector* currentax,
     /* Real    */ const ae_vector* currentaty,
     ipm2righthandside* rhs,
     ae_state *_state);
static void ipm2solver_rhsrecomputegammapredictor(const ipm2state* state,
     const ipm2vars* current,
     double mutarget,
     ipm2righthandside* rhs,
     ae_state *_state);
static void ipm2solver_rhsrecomputegammamehrotra(const ipm2state* state,
     const ipm2vars* current,
     double mutarget,
     const ipm2vars* deltaaff,
     ipm2righthandside* rhs,
     ae_state *_state);
static ae_bool ipm2solver_reducedsystemsolve(ipm2reducedsystem* redsys,
     ipm2state* sstate,
     const ipm2vars* current,
     const ipm2righthandside* rhs,
     ipm2vars* delta,
     ae_state *_state);
static double ipm2solver_minnz(/* Real    */ const ae_vector* x,
     ae_int_t n0,
     ae_int_t n1,
     ae_state *_state);
static double ipm2solver_minprodnz(/* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     ae_int_t n0,
     ae_int_t n1,
     ae_state *_state);
static double ipm2solver_maxprodnz(/* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     ae_int_t n0,
     ae_int_t n1,
     ae_state *_state);
static double ipm2solver_maxabsrange(/* Real    */ const ae_vector* x,
     ae_int_t r0,
     ae_int_t r1,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_ECQPSOLVER) || !defined(AE_PARTIAL_BUILD)
static double ecqpsolver_mupromote = 10.0;


#endif
#if defined(AE_COMPILE_GIPM) || !defined(AE_PARTIAL_BUILD)
static double gipm_initmu = 0.1;
static double gipm_maxmu = 10000;
static double gipm_tightendist = 5.0;
static double gipm_relaxdist = 2.0;
static double gipm_penaltygrowth = 1.0;
static double gipm_maxnu = 0.001;
static double gipm_relaxfactor = 1.0;
static double gipm_maxrelaxation = 0.0001;
static double gipm_lambdamax = 1.0E6;
static double gipm_lambdagrowth = 2.0;
static double gipm_lambdarapidgrowth = 100.0;
static double gipm_lambdadecayacc = 0.90;
static ae_int_t gipm_maxsubsequentfailuresforweakgrowth = 5;
static double gipm_damptau = 0.0001;
static double gipm_fractobnd = 0.98;
static double gipm_mudecaylinear = 0.2;
static double gipm_mudecaypower = 1.2;
static double gipm_checkpointacceptance = 0.9;
static ae_int_t gipm_maxcheckpointstagnationits = 25;
static double gipm_firstiterationcheckpointstagnationeps = 0.001;
static double gipm_rhsstagnationtol1 = 0.001;
static double gipm_rhsstagnationtol2 = 0.05;
static ae_int_t gipm_maxrhsstagnationits = 5;
static double gipm_smallstep = 1.0E-8;
static double gipm_initbigval = 1.0E9;
static double gipm_initbiggrowth = 1.0E9;
static double gipm_maxbigval = 1.0E50;
static double gipm_maxbiggrowth = 1.0E20;
static double gipm_vggrowth = 1.0E3;
static ae_int_t gipm_maxsmalltrials = 3;
static ae_int_t gipm_maxrfsits = 5;
static double gipm_rhstol = (double)100*ae_machineepsilon;
static ae_int_t gipm_maxnd = 2;
static ae_int_t gipm_maxnonmonotonic = 1;
static ae_int_t gipm_nonmonotonicdelay = 10;
static double gipm_dualmeritnu = 0.1;
static void gipm_varsinitbyzero(gipmvars* vstate,
     ae_int_t nraw,
     ae_int_t mflex,
     ae_int_t mhard,
     ae_state *_state);
static void gipm_varsapplystep(gipmvars* vstate,
     const gipmvars* delta,
     double alpha,
     ae_state *_state);
static void gipm_varscopy(const gipmvars* src,
     gipmvars* dst,
     ae_state *_state);
static double gipm_computesteplength(const gipmstate* state,
     const gipmvars* current,
     const gipmvars* delta,
     ae_state *_state);
static void gipm_computerhs(gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     gipmrhs* rhs,
     double* eprim2,
     double* edual2,
     double* ecmpl2,
     double* sprim,
     double* sdual,
     double* scmpl,
     ae_state *_state);
static double gipm_computeupdateresidual(gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     double lambdatouse,
     const gipmvars* delta,
     /* Real    */ const ae_vector* hdxp,
     /* Real    */ const ae_vector* jdxp,
     /* Real    */ const ae_vector* jtdy,
     gipmrhs* rhs,
     ae_state *_state);
static void gipm_condenselhs(const gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     gipmcondensedsystem* c,
     ae_state *_state);
static void gipm_condenserhs(const gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     const gipmrhs* rhs,
     gipmcondensedsystem* c,
     ae_state *_state);
static void gipm_unpacksolution(const gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     const gipmrhs* rhs,
     gipmcondensedsystem* c,
     /* Real    */ const ae_vector* replysol,
     gipmvars* delta,
     ae_state *_state);
static void gipm_barrierpenaltyandcmplerror(const gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     double* barrierf,
     double* penaltyf,
     double* cmplerr,
     ae_state *_state);
static double gipm_meritv0(const gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     ae_state *_state);
static double gipm_meritfunctionpd(const gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     ae_state *_state);
static void gipm_preparequasinewtonupdate(gipmstate* state,
     ae_state *_state);
static double gipm_minabsnz(/* Real    */ const ae_vector* x,
     ae_int_t n1,
     ae_state *_state);
static void gipm_safeboundsfromfinitetightened(gipmstate* state,
     ae_state *_state);
static void gipm_tightenrelaxbounds(gipmstate* state, ae_state *_state);
static double gipm_getnu(double currentmu, ae_state *_state);
static void gipm_nonmonotonicinit(gipmstate* state,
     ae_int_t memlen,
     double merit0,
     ae_state *_state);
static void gipm_nonmonotonicpush(gipmstate* state,
     double meritv,
     ae_state *_state);
static double gipm_nonmonotonicgetcorr(gipmstate* state,
     double meritv,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_GQPIPM) || !defined(AE_PARTIAL_BUILD)
static double gqpipm_minconicdenominator = 0.05;
static double gqpipm_tootightconstraints = 0.0001;
static double gqpipm_mupromote = 10.0;
static double gqpipm_bigboxconstraint = 1.0E9;
static ae_int_t gqpipm_amdense = 0;
static ae_int_t gqpipm_amsparse = 1;
static ae_int_t gqpipm_amhybrid = 2;
static ae_int_t gqpipm_xbfgsdefaultmemlen = 32;
static double gqpipm_xbfgsmaxhess = 1.0E6;
static void gqpipm_appendtorckv(/* Integer */ const ae_vector* varidx,
     const sparsematrix* q,
     ae_int_t nv,
     ae_int_t id,
     /* Integer */ ae_vector* hr,
     /* Integer */ ae_vector* hc,
     /* Integer */ ae_vector* hk,
     /* Real    */ ae_vector* hv,
     ae_int_t* hcnt,
     ae_state *_state);
static void gqpipm_initializehessjac(gqpipmstate* state,
     ae_int_t offslc,
     ae_int_t offsqc,
     ae_int_t offsec,
     ae_int_t offscc,
     ae_int_t mtotal,
     ae_state *_state);
static void gqpipm_sortrck(/* Integer */ ae_vector* vecr,
     /* Integer */ ae_vector* vecc,
     /* Integer */ ae_vector* veck,
     /* Real    */ ae_vector* vecv,
     ae_int_t n,
     /* Integer */ ae_vector* tmpr,
     /* Integer */ ae_vector* tmpc,
     /* Integer */ ae_vector* tmpk,
     /* Real    */ ae_vector* tmpv,
     ae_state *_state);
static void gqpipm_sortrckrec(/* Integer */ ae_vector* vecr,
     /* Integer */ ae_vector* vecc,
     /* Integer */ ae_vector* veck,
     /* Real    */ ae_vector* vecv,
     ae_int_t idx0,
     ae_int_t idx1,
     /* Integer */ ae_vector* tmpr,
     /* Integer */ ae_vector* tmpc,
     /* Integer */ ae_vector* tmpk,
     /* Real    */ ae_vector* tmpv,
     ae_state *_state);
static void gqpipm_handlecomputeandremember(gqpipmstate* state,
     ae_int_t offslc,
     ae_int_t offsqc,
     ae_int_t offsec,
     ae_int_t offscc,
     ae_int_t mtotal,
     ae_bool* remembered,
     ae_bool* factorized,
     ae_state *_state);
static void gqpipm_handlefactorize(gqpipmstate* state,
     ae_int_t mtotal,
     ae_bool* remembered,
     ae_bool* factorized,
     ae_state *_state);
static void gqpipm_handlesolve(gqpipmstate* state,
     ae_int_t mtotal,
     ae_bool* remembered,
     ae_bool* factorized,
     ae_state *_state);
static void gqpipm_handlemv(gqpipmstate* state,
     ae_int_t offslc,
     ae_int_t offsqc,
     ae_int_t offsec,
     ae_int_t offscc,
     ae_int_t mtotal,
     ae_bool* remembered,
     ae_bool* factorized,
     ae_state *_state);
static void gqpipm_optionaldensefromsparse(gqpoptionaldense* opt,
     sparsematrix* s,
     ae_int_t nq,
     ae_state *_state);
static void gqpipm_optionaldensefromsparseqc(gqpoptionaldense* opt,
     xquadraticconstraint* qc,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_LPQPPRESOLVE) || !defined(AE_PARTIAL_BUILD)
static ae_int_t lpqppresolve_pstobjectivescaling = 0;
static ae_int_t lpqppresolve_pstcolscaling = 1;
static ae_int_t lpqppresolve_pstrowscaling = 2;
static ae_int_t lpqppresolve_pstdropemptycol = 3;
static ae_int_t lpqppresolve_pstdropemptyrow = 4;
static ae_int_t lpqppresolve_pstsingletonrow = 5;
static ae_int_t lpqppresolve_pstfixedvar = 6;
static ae_int_t lpqppresolve_pstexplicitslack = 7;
static ae_int_t lpqppresolve_pstimplicitslack = 8;
static ae_int_t lpqppresolve_pstcolshifting = 9;
static ae_int_t lpqppresolve_pstdoubletoneq = 10;
static ae_int_t lpqppresolve_pstqcscaling = 11;
static ae_int_t lpqppresolve_pstdropemptyqc = 12;
static ae_int_t lpqppresolve_actlowerboundonk0 = 0;
static ae_int_t lpqppresolve_actupperboundonk0 = 1;
static ae_int_t lpqppresolve_actlowerboundonk1 = 2;
static ae_int_t lpqppresolve_actupperboundonk1 = 3;
static ae_int_t lpqppresolve_actnone = -1;
static double lpqppresolve_smlcoeff = 0.001;
static double lpqppresolve_maxcoeffgrowth = 100.0;
static ae_bool lpqppresolve_isfixed(double bndl,
     double bndu,
     double eps,
     ae_state *_state);
static void lpqppresolve_dyncrsinitempty(dynamiccrs* r,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state);
static void lpqppresolve_dyncrscopy(const dynamiccrs* src,
     dynamiccrs* dst,
     ae_state *_state);
static void lpqppresolve_dyncrscopydeleterowcol(const dynamiccrs* src,
     ae_int_t deletepos,
     dynamiccrs* dst,
     ae_state *_state);
static void lpqppresolve_dyncrscopydeleterowscols(const dynamiccrs* src,
     const niset* deleteset,
     dynamiccrs* dst,
     /* Integer */ ae_vector* buf,
     ae_state *_state);
static void lpqppresolve_dyncrscopyinsertrowcol(const dynamiccrs* src,
     ae_int_t insertpos,
     dynamiccrs* dst,
     ae_state *_state);
static void lpqppresolve_dyncrsinitfromsparsecrs(const sparsematrix* s,
     dynamiccrs* r,
     ae_state *_state);
static void lpqppresolve_dyncrsdropzeros(dynamiccrs* a, ae_state *_state);
static void lpqppresolve_dyncrsdropoffdiagonalzeros(dynamiccrs* a,
     ae_state *_state);
static void lpqppresolve_dyncrsclearrow(dynamiccrs* a,
     ae_int_t rowidx,
     ae_state *_state);
static void lpqppresolve_dyncrsremovefromrow(dynamiccrs* a,
     ae_int_t rowidx,
     ae_int_t j,
     ae_state *_state);
static void lpqppresolve_dyncrsfactoroutfromrow(dynamiccrs* a,
     ae_int_t rowidx,
     ae_int_t j,
     double c0,
     double c1,
     ae_int_t k,
     ae_bool checkcancellation,
     double* newval,
     double* shift,
     ae_state *_state);
static void lpqppresolve_dyncrsaddrowto(dynamiccrs* a,
     ae_int_t row0,
     double c,
     ae_int_t row1,
     ae_bool retaindiagonal,
     ae_bool checkcancellation,
     ae_state *_state);
static void lpqppresolve_dyncrsinsertwithrewrite(dynamiccrs* a,
     ae_int_t tgtrow,
     /* Integer */ const ae_vector* idx,
     /* Real    */ const ae_vector* vals,
     ae_int_t cnt,
     ae_state *_state);
static void lpqppresolve_dyncrsremovesetfromrow(dynamiccrs* a,
     ae_int_t rowidx,
     const niset* s,
     ae_state *_state);
static double lpqppresolve_dyncrsgetexistingelement(dynamiccrs* a,
     ae_int_t i,
     ae_int_t j,
     ae_state *_state);
static double lpqppresolve_dyncrsgetelementifexists(dynamiccrs* a,
     ae_int_t i,
     ae_int_t j,
     ae_state *_state);
static void lpqppresolve_dyncrssetexistingelement(dynamiccrs* a,
     ae_int_t i,
     ae_int_t j,
     double v,
     ae_state *_state);
static void lpqppresolve_dynqcinitfromxqc(xquadraticconstraints* src,
     dynamiccrsqconstraints* dst,
     ae_state *_state);
static void lpqppresolve_dynqccopy(const dynamiccrsqconstraint* src,
     dynamiccrsqconstraint* dst,
     ae_state *_state);
static void lpqppresolve_dynqccopyinsertrowcol(dynamiccrsqconstraint* src,
     ae_int_t vidx,
     ae_int_t insertpos,
     dynamiccrsqconstraint* dst,
     ae_state *_state);
static void lpqppresolve_dynqccopydeleterowcol(const dynamiccrsqconstraint* src,
     ae_int_t deletepos,
     dynamiccrsqconstraint* dst,
     ae_state *_state);
static void lpqppresolve_dynqccopydeleterowscols(const dynamiccrsqconstraint* src,
     const niset* deleteset,
     dynamiccrsqconstraint* dst,
     /* Integer */ ae_vector* buf,
     ae_state *_state);
static double lpqppresolve_factoroutfromquadratic(dynamiccrs* h,
     /* Real    */ ae_vector* c,
     ae_int_t k0,
     double alpha0,
     double alpha1,
     ae_int_t k1,
     ae_state *_state);
static double lpqppresolve_fixquadraticvariable(dynamiccrs* h,
     /* Real    */ ae_vector* c,
     ae_int_t fixidx,
     double fixval,
     ae_state *_state);
static void lpqppresolve_dynqcpermtoxqc(dynamiccrsqconstraint* src,
     /* Integer */ const ae_vector* packxperm,
     /* Integer */ const ae_vector* unpackxperm,
     xquadraticconstraint* dst,
     ae_state *_state);
static void lpqppresolve_presolverstackinit(ae_int_t n,
     ae_int_t m,
     ae_int_t mqc,
     ae_int_t mcc,
     presolverstack* s,
     ae_state *_state);
static void lpqppresolve_presolverstreamb(presolverstack* s,
     ae_bool b,
     ae_state *_state);
static void lpqppresolve_presolverstreami(presolverstack* s,
     ae_int_t i,
     ae_state *_state);
static void lpqppresolve_presolverstreamr(presolverstack* s,
     double v,
     ae_state *_state);
static void lpqppresolve_presolverstreamir(presolverstack* s,
     ae_int_t i,
     double v,
     ae_state *_state);
static void lpqppresolve_presolverstreamcrsrow(presolverstack* s,
     const dynamiccrs* a,
     ae_int_t i,
     ae_state *_state);
static void lpqppresolve_presolverstreamcrsrownot1(presolverstack* s,
     const dynamiccrs* a,
     ae_int_t i,
     ae_int_t notidx,
     ae_state *_state);
static void lpqppresolve_presolverstreamcrsrownonflagged(presolverstack* s,
     const dynamiccrs* a,
     ae_int_t i,
     /* Boolean */ const ae_vector* flags,
     ae_state *_state);
static void lpqppresolve_presolverselectstreamsource(presolverstack* s,
     ae_int_t tidx,
     ae_state *_state);
static void lpqppresolve_presolverunstreamb(presolverstack* s,
     ae_bool* v,
     ae_state *_state);
static ae_bool lpqppresolve_presolverunstreambf(presolverstack* s,
     ae_state *_state);
static void lpqppresolve_presolverunstreami(presolverstack* s,
     ae_int_t* v,
     ae_state *_state);
static void lpqppresolve_presolverunstreamr(presolverstack* s,
     double* v,
     ae_state *_state);
static void lpqppresolve_presolverunstreamir(presolverstack* s,
     ae_int_t* vi,
     double* vr,
     ae_state *_state);
static void lpqppresolve_presolverunstreamsparsevec(presolverstack* s,
     ae_int_t* cnt,
     /* Integer */ ae_vector* idx,
     /* Real    */ ae_vector* vals,
     ae_state *_state);
static void lpqppresolve_presolverasserteos(presolverstack* s,
     ae_state *_state);
static void lpqppresolve_presolverappendtrf(presolverstack* s,
     ae_int_t tt,
     ae_state *_state);
static void lpqppresolve_presolverappendcostscaling(presolverstack* s,
     double vmul,
     ae_state *_state);
static void lpqppresolve_presolverappendcolscaling(presolverstack* s,
     ae_int_t colidx,
     double vmul,
     ae_state *_state);
static void lpqppresolve_presolverappendcolshifting(presolverstack* s,
     ae_int_t colidx,
     double vadd,
     ae_state *_state);
static void lpqppresolve_presolverappendrowscaling(presolverstack* s,
     ae_int_t rowidx,
     double vmul,
     ae_state *_state);
static void lpqppresolve_presolverappendqcscaling(presolverstack* s,
     ae_int_t qcidx,
     double vmul,
     ae_state *_state);
static void lpqppresolve_presolverappendccscaling(presolverstack* s,
     ae_int_t ccidx,
     double vmul,
     ae_state *_state);
static void lpqppresolve_presolverappenddropemptycol(presolverstack* s,
     ae_int_t colidx,
     double varval,
     double lagval,
     ae_int_t statval,
     ae_state *_state);
static void lpqppresolve_presolverappenddropemptyrow(presolverstack* s,
     ae_int_t rowidx,
     ae_state *_state);
static void lpqppresolve_presolverappenddropemptyqc(presolverstack* s,
     ae_int_t qcidx,
     ae_state *_state);
static void lpqppresolve_presolverappendsingletonrow(presolverstack* s,
     ae_int_t i,
     ae_int_t j,
     double v,
     double swapsign,
     double bndl,
     ae_bool bndlisbc,
     double bndu,
     ae_bool bnduisbc,
     ae_state *_state);
static void lpqppresolve_presolverappendfixedvar(presolverstack* s,
     ae_int_t colidx,
     double fixval,
     double ci,
     const dynamiccrs* sparseh,
     const presolvervcstats* vcstats,
     ae_bool hash,
     const dynamiccrs* at,
     ae_bool hasat,
     dynamiccrsqconstraints* qc,
     ae_state *_state);
static void lpqppresolve_presolverappendexplicitslack(presolverstack* s,
     ae_int_t i,
     ae_int_t j,
     double aij,
     double slackbndl,
     double slackbndu,
     double al,
     double au,
     const dynamiccrs* a,
     ae_state *_state);
static void lpqppresolve_presolverappendimplicitslack(presolverstack* s,
     ae_int_t i,
     ae_int_t j,
     double aij,
     double cj,
     double equalitybnd,
     const dynamiccrs* a,
     ae_state *_state);
static void lpqppresolve_presolverappenddoubletonfactorout(presolverstack* s,
     const presolvervcstats* vcstats,
     ae_int_t rowidx,
     ae_int_t col0,
     double bndl0,
     double bndu0,
     ae_int_t col1,
     double bndl1,
     double bndu1,
     double c0,
     double c1,
     const dynamiccrs* sparseh,
     ae_bool hash,
     const dynamiccrs* at,
     dynamiccrsqconstraints* qc,
     double ai0,
     double ai1,
     double abnd,
     double newbndl1,
     double newbndu1,
     ae_int_t bndlactivates,
     ae_int_t bnduactivates,
     ae_state *_state);
static void lpqppresolve_presolverrestoresolution(presolverstack* s,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* laglc,
     /* Real    */ ae_vector* lagqc,
     /* Integer */ ae_vector* stats,
     ae_bool needstats,
     double eps,
     ae_state *_state);
static void lpqppresolve_copyexpandh(const sparsematrix* rawh,
     ae_bool isupper,
     sparsematrix* fullh,
     ae_state *_state);
static void lpqppresolve_presolvebuffersinit(presolvebuffers* buf,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state);
static ae_bool lpqppresolve_dropemptycol(/* Real    */ ae_vector* c,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     presolvervcstats* vcstats,
     /* Boolean */ ae_vector* lagrangefromresidual,
     dynamiccrs* sparseh,
     ae_bool hash,
     ae_int_t n,
     dynamiccrs* a,
     dynamiccrs* at,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     double eps,
     ae_bool dotrace,
     presolvebuffers* buf,
     presolverstack* trfstack,
     ae_int_t* problemstatus,
     ae_bool* somethingchanged,
     ae_int_t* cntdropped,
     ae_state *_state);
static ae_bool lpqppresolve_dropclearlynonbindingrows(ae_int_t n,
     presolvervcstats* vcstats,
     dynamiccrs* a,
     dynamiccrs* at,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     dynamiccrsqconstraints* qc,
     xconicconstraints* cc,
     double eps,
     ae_bool dotrace,
     presolvebuffers* buf,
     presolverstack* trfstack,
     ae_int_t* problemstatus,
     ae_bool* somethingchanged,
     ae_int_t* cntempty,
     ae_int_t* cntemptyqc,
     ae_int_t* cntnobounds,
     ae_int_t* cntnoboundsqc,
     ae_state *_state);
static ae_bool lpqppresolve_singletonrowtobc(/* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     presolvervcstats* vcstats,
     ae_int_t n,
     dynamiccrs* a,
     dynamiccrs* at,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     double eps,
     ae_bool dotrace,
     presolvebuffers* buf,
     presolverstack* trfstack,
     ae_int_t* problemstatus,
     ae_bool* somethingchanged,
     ae_int_t* cntsingleton,
     ae_state *_state);
static ae_bool lpqppresolve_doubletonrow(/* Real    */ ae_vector* c,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     presolvervcstats* vcstats,
     dynamiccrs* sparseh,
     ae_bool hash,
     ae_int_t n,
     dynamiccrs* a,
     dynamiccrs* at,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     dynamiccrsqconstraints* qc,
     xconicconstraints* cc,
     double eps,
     ae_bool dotrace,
     presolvebuffers* buf,
     presolverstack* trfstack,
     ae_int_t* problemstatus,
     ae_bool* somethingchanged,
     ae_int_t* cntdoubleton,
     ae_state *_state);
static ae_bool lpqppresolve_singletoncols(/* Real    */ ae_vector* c,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     presolvervcstats* vcstats,
     dynamiccrs* sparseh,
     ae_bool hash,
     ae_int_t n,
     dynamiccrs* a,
     dynamiccrs* at,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     double eps,
     ae_bool dotrace,
     presolvebuffers* buf,
     presolverstack* trfstack,
     ae_int_t* problemstatus,
     ae_bool* somethingchanged,
     ae_int_t* cntslackvars,
     ae_int_t* cntimplicitslacks,
     ae_int_t* cntfreecolumnsingletons,
     ae_state *_state);
static ae_bool lpqppresolve_fixvariables(/* Real    */ ae_vector* c,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     presolvervcstats* vcstats,
     dynamiccrs* sparseh,
     ae_bool hash,
     ae_int_t n,
     dynamiccrs* a,
     dynamiccrs* at,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     dynamiccrsqconstraints* qc,
     xconicconstraints* cc,
     double eps,
     ae_bool dotrace,
     presolvebuffers* buf,
     presolverstack* trfstack,
     ae_int_t* problemstatus,
     ae_bool* somethingchanged,
     ae_int_t* cntfixed,
     ae_state *_state);
static void lpqppresolve_scaleshiftobjectiveandconstraints(/* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t n,
     /* Real    */ ae_vector* c,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     sparsematrix* sparseh,
     ae_bool hash,
     sparsematrix* sparsea,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     xquadraticconstraints* xqc,
     xconicconstraints* xcc,
     presolverstack* trfstack,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_MINQP) || !defined(AE_PARTIAL_BUILD)


#endif
#if defined(AE_COMPILE_NLCFSQP) || !defined(AE_PARTIAL_BUILD)
static double nlcfsqp_sqpdeltadecrease = 0.05;
static double nlcfsqp_sqpdeltaincrease = 0.99;
static double nlcfsqp_maxtrustraddecay = 0.1;
static double nlcfsqp_deftrustradgrowth = 1.41;
static double nlcfsqp_maxtrustradgrowth = 10.0;
static double nlcfsqp_momentumgrowth = 2;
static double nlcfsqp_augmentationfactor = 0.1;
static double nlcfsqp_definittrustrad = 0.1;
static double nlcfsqp_stagnationepsf = 1.0E-12;
static ae_int_t nlcfsqp_fstagnationlimit = 20;
static ae_int_t nlcfsqp_trustradstagnationlimit = 5;
static ae_int_t nlcfsqp_defaultbfgsresetfreq = 999999;
static double nlcfsqp_sufficientdecreasesigma = 0.1;
static ae_int_t nlcfsqp_xbfgsmemlen = 8;
static double nlcfsqp_xbfgsmaxhess = 1.0E6;
static double nlcfsqp_defsqpbigscale = 4.0;
static double nlcfsqp_defsqpsmallscale = 0.1;
static double nlcfsqp_sqpsmallscaledecrease = 0.5;
static double nlcfsqp_sqpmaxrescale = 10.0;
static double nlcfsqp_sqpminrescale = 0.1;
static ae_int_t nlcfsqp_maxipmits = 100;
static void nlcfsqp_initqpsubsolver(const minfsqpstate* sstate,
     minfsqpsubsolver* subsolver,
     ae_state *_state);
static ae_bool nlcfsqp_qpsubproblemsolvesqp(minfsqpstate* state,
     minfsqpsubsolver* subsolver,
     const varsfuncjac* model,
     xbfgshessian* hess,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_vector* lagbcmult,
     /* Real    */ ae_vector* lagxcmult,
     ae_bool dotrace,
     ae_bool* isinfeasible,
     ae_int_t* terminationtype,
     double* predictedchangemodel,
     double* predictedchangepenalty,
     double* d2trustradratio,
     ae_state *_state);
static ae_bool nlcfsqp_qpsubproblemsolveelastic(minfsqpstate* state,
     minfsqpsubsolver* subsolver,
     const varsfuncjac* model,
     xbfgshessian* hess,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_vector* lagbcmult,
     /* Real    */ ae_vector* lagxcmult,
     ae_bool dotrace,
     ae_bool* isinfeasible,
     ae_int_t* terminationtype,
     double* predictedchangemodel,
     double* predictedchangepenalty,
     double* d2trustradratio,
     ae_state *_state);
static ae_bool nlcfsqp_qpsubproblemisfeasible(const minfsqpstate* state,
     const minfsqpsubsolver* subsolver,
     const varsfuncjac* model,
     /* Real    */ const ae_vector* d,
     ae_state *_state);
static void nlcfsqp_sqpsendx(minfsqpstate* state,
     /* Real    */ const ae_vector* xs,
     ae_state *_state);
static ae_bool nlcfsqp_sqpretrievefij(const minfsqpstate* state,
     varsfuncjac* vfj,
     ae_state *_state);
static void nlcfsqp_lagrangianfg(minfsqpstate* state,
     /* Real    */ const ae_vector* x,
     double trustrad,
     /* Real    */ const ae_vector* fi,
     const sparsematrix* sj,
     /* Real    */ const ae_vector* lagbcmult,
     /* Real    */ const ae_vector* lagxcmult,
     ae_bool uselagrangeterms,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state);
static void nlcfsqp_penalizedfg(minfsqpstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     const sparsematrix* sj,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state);
static void nlcfsqp_targetandconstraints(minfsqpstate* state,
     const varsfuncjac* vfj,
     double* tgt,
     double* cv,
     ae_state *_state);
static double nlcfsqp_rawlagrangian(minfsqpstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     /* Real    */ const ae_vector* lagbcmult,
     /* Real    */ const ae_vector* lagxcmult,
     ae_state *_state);
static double nlcfsqp_gettrustregionk(/* Real    */ const ae_vector* xcur,
     ae_int_t k,
     double trustrad,
     ae_state *_state);
static ae_bool nlcfsqp_isacceptable(minfsqpstate* state,
     double tgt0,
     double h0,
     double tgt1,
     double h1,
     double predictedchangemodel,
     double predictedchangepenalty,
     ae_bool dotraceunacceptability,
     ae_state *_state);
static void nlcfsqp_autoscalevarstargetconstraints(minfsqpstate* state,
     varsfuncjac* model,
     ae_bool firstscaling,
     ae_bool dotrace,
     ae_state *_state);
static ae_int_t nlcfsqp_geteffectivemaxits(minfsqpstate* state,
     ae_state *_state);
static void nlcfsqp_sparse2dense(const sparsematrix* s,
     /* Real    */ ae_matrix* d,
     ae_state *_state);
static void nlcfsqp_appendscaledsparserow(sparsematrix* dst,
     double alpha,
     const sparsematrix* src,
     ae_int_t rowidx,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_MINLM) || !defined(AE_PARTIAL_BUILD)
static double minlm_lambdaup = 2.0;
static double minlm_lambdadown = 0.33;
static double minlm_suspiciousnu = 16;
static ae_int_t minlm_smallmodelage = 3;
static ae_int_t minlm_additers = 5;
static double minlm_stagnationfeps = 1.0E-12;
static ae_int_t minlm_stagnationfits = 20;
static void minlm_lmprepare(ae_int_t n,
     ae_int_t m,
     ae_bool havegrad,
     minlmstate* state,
     ae_state *_state);
static void minlm_clearrequestfields(minlmstate* state, ae_state *_state);
static void minlm_tracefinalreport(minlmstate* state,
     ae_bool dotrace,
     ae_state *_state);
static ae_bool minlm_increaselambda(double* lambdav,
     double* nu,
     ae_state *_state);
static void minlm_decreaselambda(double* lambdav,
     double* nu,
     ae_state *_state);
static ae_int_t minlm_checkdecrease(/* Real    */ const ae_matrix* quadraticmodel,
     /* Real    */ const ae_vector* gbase,
     double fbase,
     ae_int_t n,
     /* Real    */ const ae_vector* deltax,
     double fnew,
     double* lambdav,
     double* nu,
     ae_state *_state);
static ae_bool minlm_minlmstepfinderinit(minlmstepfinder* state,
     ae_int_t n,
     ae_int_t m,
     ae_int_t maxmodelage,
     /* Real    */ ae_vector* xbase,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_matrix* cleic,
     ae_int_t nec,
     ae_int_t nic,
     /* Real    */ const ae_vector* s,
     double stpmax,
     double epsx,
     ae_state *_state);
static void minlm_minlmstepfinderstart(minlmstepfinder* state,
     /* Real    */ const ae_matrix* quadraticmodel,
     /* Real    */ const ae_vector* gbase,
     double fbase,
     /* Real    */ const ae_vector* xbase,
     /* Real    */ const ae_vector* fibase,
     ae_int_t modelage,
     ae_state *_state);
static ae_bool minlm_minlmstepfinderiteration(minlmstepfinder* state,
     minlmstate* parentstate,
     double* lambdav,
     double* nu,
     /* Real    */ ae_vector* xnew,
     /* Real    */ ae_vector* deltax,
     ae_bool* deltaxready,
     /* Real    */ ae_vector* deltaf,
     ae_bool* deltafready,
     ae_int_t* iflag,
     double* fnew,
     ae_int_t* ncholesky,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_NLCAUL) || !defined(AE_PARTIAL_BUILD)
static double nlcaul_defaultinequalitylag = 1.0;
static double nlcaul_maxlagmult = 1.0E7;
static double nlcaul_minlagmult = 1.0E-10;
static double nlcaul_besterrstagnationcoeff = 0.75;
static ae_int_t nlcaul_besterrstagnationlimit = 10;
static double nlcaul_stagnationepsf = 1.0E-12;
static double nlcaul_stabilizingpoint = 10.0;
static ae_int_t nlcaul_fstagnationlimit = 20;
static double nlcaul_aulmaxgrowth = 10.0;
static double nlcaul_rhomax = 10000;
static double nlcaul_gtol = 0.25;
static double nlcaul_auldeltadecrease = 0.50;
static double nlcaul_auldeltaincrease = 0.99;
static double nlcaul_aulbigscale = 5.0;
static double nlcaul_aulsmallscale = 0.2;
static ae_int_t nlcaul_xbfgsmemlen = 8;
static double nlcaul_xbfgsmaxhess = 1.0E3;
static void nlcaul_preconditionerinit(const minaulstate* sstate,
     ae_int_t refreshfreq,
     minaulpreconditioner* p,
     ae_state *_state);
static void nlcaul_preconditionersendupdates(minaulstate* state,
     minaulpreconditioner* prec,
     const varsfuncjac* current,
     double rho,
     /* Real    */ const ae_vector* lagmultbc2,
     /* Real    */ const ae_vector* lagmultxc2,
     xbfgshessian* hesstgt,
     xbfgshessian* hessaug,
     ae_state *_state);
static void nlcaul_preconditionersolve(minaulstate* state,
     minaulpreconditioner* prec,
     /* Real    */ const ae_vector* g,
     /* Real    */ ae_vector* d,
     ae_state *_state);
static void nlcaul_aulsendvirtualpoint(minaulstate* state,
     /* Real    */ const ae_vector* xvirt,
     /* Real    */ ae_vector* xtrue,
     ae_state *_state);
static void nlcaul_aulsendtruepoint(minaulstate* state,
     /* Real    */ const ae_vector* xtrue,
     ae_state *_state);
static ae_bool nlcaul_aulretrievesj(minaulstate* state,
     varsfuncjac* xvirt,
     varsfuncjac* xtrue,
     ae_state *_state);
static void nlcaul_modtargetfg(minaulstate* state,
     const varsfuncjac* s,
     double rhobc,
     double rhoxc,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state);
static void nlcaul_auglagfg(minaulstate* state,
     const varsfuncjac* s,
     double rho,
     /* Real    */ const ae_vector* lagmultbc2,
     /* Real    */ const ae_vector* lagmultxc2,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state);
static void nlcaul_rawlagfg(minaulstate* state,
     const varsfuncjac* s,
     /* Real    */ const ae_vector* lagmultbc2,
     /* Real    */ const ae_vector* lagmultxc2,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state);
static void nlcaul_clearrequestfields(minaulstate* state,
     ae_state *_state);
static void nlcaul_computeerrors(minaulstate* sstate,
     const varsfuncjac* s,
     double* errprim,
     double* errdual,
     double* errcmpl,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_MINCG) || !defined(AE_PARTIAL_BUILD)
static ae_int_t mincg_rscountdownlen = 10;
static double mincg_gtol = 0.3;
static void mincg_clearrequestfields(mincgstate* state, ae_state *_state);
static void mincg_preconditionedmultiply(mincgstate* state,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* work0,
     /* Real    */ ae_vector* work1,
     ae_state *_state);
static double mincg_preconditionedmultiply2(mincgstate* state,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* work0,
     /* Real    */ ae_vector* work1,
     ae_state *_state);
static void mincg_mincginitinternal(ae_int_t n,
     double diffstep,
     mincgstate* state,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_DFGENMOD) || !defined(AE_PARTIAL_BUILD)
static double dfgenmod_gammasafety = 0.5;
static double dfgenmod_omegasafety = 0.1;
static double dfgenmod_alpha1 = 0.1;
static double dfgenmod_alpha2 = 0.5;
static double dfgenmod_eta1 = 0.1;
static double dfgenmod_eta2 = 0.7;
static double dfgenmod_gammabad = 0.1;
static double dfgenmod_gammadec = 0.5;
static double dfgenmod_gammadec2 = 0.85;
static double dfgenmod_gammainc = 2.0;
static double dfgenmod_gammainc2 = 4.0;
static double dfgenmod_twopsstep = 0.5;
static double dfgenmod_twopssteplo = 0.15;
static double dfgenmod_twopsstephi = 1.50;
static double dfgenmod_lsaimprovementlimit = 1;
static double dfgenmod_pointunacceptablyfar = 50.0;
static double dfgenmod_pointtoofar = 2.0;
static double dfgenmod_pointtooclose = 0.02;
static ae_int_t dfgenmod_minhistory = 5;
static ae_int_t dfgenmod_maxhistory = 30;
static double dfgenmod_rbfmindist = 0.10;
static double dfgenmod_rbftheta0 = 20.0;
static double dfgenmod_rbftheta1 = 0.001;
static double dfgenmod_rbfsubsolverepsx = 0.00001;
static void dfgenmod_inithistory(dfgmstate* state, ae_state *_state);
static void dfgenmod_updatehistory(dfgmstate* state, ae_state *_state);
static void dfgenmod_clearhistoryonrestart(dfgmstate* state,
     ae_state *_state);
static void dfgenmod_updatetrustregion(dfgmstate* state,
     double trustradfactor,
     ae_state *_state);
static double dfgenmod_normrelativetotrustregion(const dfgmstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state);
static void dfgenmod_preparetrialrequest(dfgmstate* state,
     double* predf,
     double* predh,
     ae_state *_state);
static void dfgenmod_processtrialrequest(dfgmstate* state,
     double* f,
     double* h,
     ae_state *_state);
static ae_bool dfgenmod_testacceptancebyfilter(dfgmstate* state,
     double fk,
     double hk,
     double fp,
     double hp,
     double fn,
     double hn,
     ae_state *_state);
static void dfgenmod_sendstepacceptancetomodel(dfgmstate* state,
     double fk,
     double hk,
     double fn,
     double hn,
     ae_state *_state);
static void dfgenmod_sendstepdenialtomodel(dfgmstate* state,
     ae_state *_state);
static ae_bool dfgenmod_deltakatminimum(const dfgmstate* state,
     ae_state *_state);
static ae_bool dfgenmod_restartautodetected(const dfgmstate* state,
     ae_state *_state);
static ae_bool dfgenmod_workingsetneedsimprovement(dfgmstate* state,
     ae_state *_state);
static ae_bool dfgenmod_updateworkingsetandmodel(dfgmstate* state,
     /* Real    */ const ae_vector* xk,
     /* Real    */ const ae_vector* xn,
     /* Real    */ const ae_vector* fnvec,
     double fn,
     double hn,
     ae_bool successfulstep,
     ae_state *_state);
static ae_bool dfgenmod_improveworkingsetgeometry(dfgmstate* state,
     ae_state *_state);
static double dfgenmod_maxabslag(/* Real    */ const ae_vector* xk,
     ae_int_t n,
     /* Real    */ const ae_vector* trustregion,
     /* Real    */ const ae_vector* finitebndl,
     /* Real    */ const ae_vector* finitebndu,
     /* Real    */ const ae_vector* c,
     double c0,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* tmpx,
     ae_bool needx,
     ae_state *_state);
static void dfgenmod_mostandleastdistant(/* Real    */ const ae_vector* invtrustregion,
     ae_int_t n,
     /* Real    */ const ae_matrix* wrkset,
     ae_int_t wrksetsize,
     double* mostdistant,
     ae_int_t* mostdistantidx,
     double* leastdistant,
     ae_int_t* leastdistantidx,
     ae_state *_state);
static void dfgenmod_initcloud(dfgmstate* state,
     ae_bool savetocloud,
     ae_state *_state);
static void dfgenmod_sendrequesttocloud(dfgmstate* state,
     /* Real    */ const ae_vector* querydata,
     ae_int_t requestsize,
     ae_state *_state);
static void dfgenmod_sendreplytocloud(dfgmstate* state,
     /* Real    */ const ae_vector* replyfi,
     ae_state *_state);
static void dfgenmod_clearcloud(dfgmstate* state, ae_state *_state);
static void dfgenmod_rbfinitializemodelfromworkingset(dfgmstate* state,
     ae_state *_state);
static void dfgenmod_rbfcomputemodel(dfgmstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* f,
     ae_bool needf,
     /* Real    */ ae_vector* g,
     ae_bool needg,
     ae_state *_state);
static void dfgenmod_rbfsolvecpdm(/* Real    */ const ae_matrix* a,
     /* Real    */ const ae_matrix* bb,
     ae_int_t ncenters,
     ae_int_t nrhs,
     ae_int_t nx,
     double lambdav,
     ae_bool iscpd,
     /* Real    */ ae_matrix* ssol,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_NLCSQP) || !defined(AE_PARTIAL_BUILD)
static double nlcsqp_sqpdeltadecrease = 0.50;
static double nlcsqp_sqpdeltaincrease = 0.99;
static double nlcsqp_maxtrustraddecay = 0.1;
static double nlcsqp_deftrustradgrowth = 1.41;
static double nlcsqp_maxtrustradgrowth = 10.0;
static double nlcsqp_momentumgrowth = 2;
static double nlcsqp_maxmeritmu = 1.0E5;
static double nlcsqp_augmentationfactor = 0.0;
static double nlcsqp_inittrustrad = 0.1;
static double nlcsqp_stagnationepsf = 1.0E-12;
static ae_int_t nlcsqp_fstagnationlimit = 20;
static ae_int_t nlcsqp_trustradstagnationlimit = 10;
static double nlcsqp_sqpbigscale = 5.0;
static double nlcsqp_sqpsmallscale = 0.2;
static double nlcsqp_sqpmaxrescale = 10.0;
static double nlcsqp_sqpminrescale = 0.1;
static ae_int_t nlcsqp_defaultbfgsresetfreq = 999999;
static double nlcsqp_sufficientdecreasesigma = 0.001;
static double nlcsqp_meritmueps = 0.15;
static ae_int_t nlcsqp_xbfgsmemlen = 8;
static double nlcsqp_xbfgsmaxhess = 1.0E6;
static ae_int_t nlcsqp_nonmonotoniclen = 3;
static double nlcsqp_constraintsstagnationeps = 1.0E-5;
static void nlcsqp_initqpsubsolver(const minsqpstate* sstate,
     minsqpsubsolver* subsolver,
     ae_state *_state);
static ae_bool nlcsqp_qpsubproblemsolve(minsqpstate* state,
     minsqpsubsolver* subsolver,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     /* Real    */ const ae_matrix* jac,
     xbfgshessian* hess,
     double alphag,
     double alphah,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_vector* lagbcmult,
     /* Real    */ ae_vector* lagxcmult,
     ae_bool dotrace,
     ae_int_t* terminationtype,
     double* predictedchangemodel,
     double* predictedchangepenalty,
     double* sumc1,
     double* d2trustradratio,
     ae_state *_state);
static void nlcsqp_sqpsendx(minsqpstate* state,
     /* Real    */ const ae_vector* xs,
     ae_state *_state);
static ae_bool nlcsqp_sqpretrievefij(const minsqpstate* state,
     varsfuncjac* vfj,
     ae_state *_state);
static void nlcsqp_lagrangianfg(minsqpstate* state,
     /* Real    */ const ae_vector* x,
     double trustrad,
     /* Real    */ const ae_vector* fi,
     /* Real    */ const ae_matrix* j,
     /* Real    */ const ae_vector* lagbcmult,
     /* Real    */ const ae_vector* lagxcmult,
     ae_bool uselagrangeterms,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state);
static double nlcsqp_meritfunction(minsqpstate* state,
     const varsfuncjac* vfj,
     double meritmu,
     ae_state *_state);
static double nlcsqp_rawlagrangian(minsqpstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     /* Real    */ const ae_vector* lagbcmult,
     /* Real    */ const ae_vector* lagxcmult,
     double meritmu,
     ae_state *_state);
static void nlcsqp_meritfunctionandrawlagrangian(minsqpstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     /* Real    */ const ae_vector* lagbcmult,
     /* Real    */ const ae_vector* lagxcmult,
     double meritmu,
     double* meritf,
     double* rawlag,
     ae_state *_state);
static ae_bool nlcsqp_penaltiesneedincrease(minsqpstate* state,
     const varsfuncjac* currentlinearization,
     /* Real    */ const ae_vector* dtrial,
     double predictedchangemodel,
     double predictedchangepenalty,
     ae_bool dotrace,
     ae_state *_state);
static double nlcsqp_gettrustregionk(/* Real    */ const ae_vector* xcur,
     ae_int_t k,
     double trustrad,
     ae_state *_state);
static double nlcsqp_nonmonotonicadjustment(minsqpstate* state,
     double fraw,
     ae_state *_state);
static void nlcsqp_nonmonotonicsave(minsqpstate* state,
     const varsfuncjac* cur,
     ae_state *_state);
static void nlcsqp_nonmonotonicmultiplyby(minsqpstate* state,
     ae_int_t jacrow,
     double v,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_SSGD) || !defined(AE_PARTIAL_BUILD)
static double ssgd_secanttoblurratio = 1.0;
static double ssgd_defaultpenalty = 100;
static double ssgd_sqjmomentum = 0.05;
static void ssgd_clearrequestfields(ssgdstate* state, ae_state *_state);
static double ssgd_penalizedtarget(ssgdstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     ae_state *_state);
static double ssgd_lcpenalty(ssgdstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state);
static double ssgd_scalednlcpenalty(ssgdstate* state,
     /* Real    */ const ae_vector* _fi,
     ae_int_t idx,
     double fscale,
     ae_state *_state);
static double ssgd_lcviolation(ssgdstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_DIFFEVO) || !defined(AE_PARTIAL_BUILD)
static double diffevo_defaultpenalty = 100;
static ae_int_t diffevo_paramscnt = 4;
static ae_int_t diffevo_minpowerupits = 10;
static ae_int_t diffevo_minpopsizefortightening = 5;
static double diffevo_tighteningmu = 0.66;
static double diffevo_bndlimit = 0.85;
static double diffevo_mulearningrate = 0.2;
static ae_int_t diffevo_stcurrenttobest1 = 2;
static ae_int_t diffevo_stcurrenttoordbest1 = 10;
static void diffevo_computefitness(const gdemostate* state,
     const gdemopopulation* refpopulation,
     ae_bool hasreference,
     gdemopopulation* trial,
     ae_state *_state);
static void diffevo_updatebest(gdemostate* state, ae_state *_state);
static void diffevo_generatetrialpoints(gdemostate* state,
     const gdemopopulation* population,
     gdemopopulation* trial,
     ae_state *_state);
static void diffevo_gencand(/* Real    */ const ae_matrix* deparams,
     /* Real    */ const ae_matrix* populationx,
     /* Real    */ const ae_matrix* populationfitness,
     ae_int_t n,
     ae_int_t m,
     ae_int_t popsize,
     /* Real    */ const ae_matrix* xbest,
     /* Real    */ const ae_vector* finitebndl,
     /* Real    */ const ae_vector* finitebndu,
     hqrndstate* rs,
     /* Real    */ ae_matrix* trialx,
     ae_state *_state);
static ae_bool diffevo_gdemogencandkernel(/* Real    */ const ae_matrix* deparams,
     /* Real    */ const ae_matrix* populationx,
     /* Real    */ const ae_matrix* populationfitness,
     ae_int_t n,
     ae_int_t m,
     ae_int_t popsize,
     /* Real    */ const ae_matrix* xbest,
     /* Real    */ const ae_vector* finitebndl,
     /* Real    */ const ae_vector* finitebndu,
     double bndsmooth,
     ae_int_t seedval,
     /* Real    */ ae_matrix* trialx,
     ae_state *_state);
static void diffevo_updatepopulation(gdemostate* state,
     const gdemopopulation* children,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_MINDF) || !defined(AE_PARTIAL_BUILD)
static double mindf_defaultmomentum = 0.9;
static void mindf_unscale(const mindfstate* state,
     /* Real    */ const ae_vector* xs,
     /* Real    */ ae_vector* xu,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_REVISEDDUALSIMPLEX) || !defined(AE_PARTIAL_BUILD)
static ae_int_t reviseddualsimplex_maxforcedrestarts = 1;
static ae_int_t reviseddualsimplex_safetrfage = 5;
static ae_int_t reviseddualsimplex_defaultmaxtrfage = 100;
static double reviseddualsimplex_minbeta = 1.0E-4;
static double reviseddualsimplex_maxudecay = 0.001;
static double reviseddualsimplex_shiftlen = 1.0E-12;
static double reviseddualsimplex_alphatrigger = 1.0E8*ae_machineepsilon;
static double reviseddualsimplex_alphatrigger2 = 0.001;
static ae_int_t reviseddualsimplex_ssinvalid = 0;
static ae_int_t reviseddualsimplex_ssvalidxn = 1;
static ae_int_t reviseddualsimplex_ssvalid = 2;
static ae_int_t reviseddualsimplex_ccfixed = 0;
static ae_int_t reviseddualsimplex_cclower = 1;
static ae_int_t reviseddualsimplex_ccupper = 2;
static ae_int_t reviseddualsimplex_ccrange = 3;
static ae_int_t reviseddualsimplex_ccfree = 4;
static ae_int_t reviseddualsimplex_ccinfeasible = 5;
static void reviseddualsimplex_subprobleminit(ae_int_t n,
     dualsimplexsubproblem* s,
     ae_state *_state);
static void reviseddualsimplex_subprobleminitphase1(const dualsimplexsubproblem* s0,
     const dualsimplexbasis* basis,
     dualsimplexsubproblem* s1,
     ae_state *_state);
static void reviseddualsimplex_subprobleminitphase3(const dualsimplexsubproblem* s0,
     dualsimplexsubproblem* s1,
     ae_state *_state);
static void reviseddualsimplex_subprobleminferinitialxn(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     ae_state *_state);
static void reviseddualsimplex_subproblemhandlexnupdate(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     ae_state *_state);
static double reviseddualsimplex_initialdualfeasibilitycorrection(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_shifting(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     const dssvector* alphar,
     double delta,
     ae_int_t q,
     double alpharpiv,
     double* thetad,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_pricingstep(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     ae_bool phase1pricing,
     ae_int_t* p,
     ae_int_t* r,
     double* delta,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_btranstep(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     ae_int_t r,
     dssvector* rhor,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_pivotrowstep(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     const dssvector* rhor,
     dssvector* alphar,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_ftranstep(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     const dssvector* rhor,
     ae_int_t q,
     /* Real    */ ae_vector* alphaq,
     /* Real    */ ae_vector* alphaqim,
     /* Real    */ ae_vector* tau,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_ratiotest(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     const dssvector* alphar,
     double delta,
     ae_int_t p,
     ae_int_t* q,
     double* alpharpiv,
     double* thetad,
     /* Integer */ ae_vector* possibleflips,
     ae_int_t* possibleflipscnt,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_updatestep(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     ae_int_t p,
     ae_int_t q,
     ae_int_t r,
     double delta,
     double alphapiv,
     double thetap,
     double thetad,
     /* Real    */ const ae_vector* alphaq,
     /* Real    */ const ae_vector* alphaqim,
     const dssvector* alphar,
     /* Real    */ const ae_vector* tau,
     /* Integer */ const ae_vector* possiblealpharflips,
     ae_int_t possiblealpharflipscnt,
     const dualsimplexsettings* settings,
     ae_state *_state);
static ae_bool reviseddualsimplex_refactorizationrequired(const dualsimplexstate* state,
     const dualsimplexsubproblem* s,
     ae_int_t q,
     double alpharpiv,
     ae_int_t r,
     double alphaqpiv,
     ae_state *_state);
static void reviseddualsimplex_cacheboundinfo(dualsimplexsubproblem* s,
     ae_int_t i,
     ae_int_t k,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_solvesubproblemdual(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     ae_bool isphase1,
     const dualsimplexsettings* settings,
     ae_int_t* info,
     ae_state *_state);
static void reviseddualsimplex_solvesubproblemprimal(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     const dualsimplexsettings* settings,
     ae_int_t* info,
     ae_state *_state);
static void reviseddualsimplex_invokephase1(dualsimplexstate* state,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_dssoptimizewrk(dualsimplexstate* state,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_solveboxonly(dualsimplexstate* state,
     ae_state *_state);
static void reviseddualsimplex_setzeroxystats(dualsimplexstate* state,
     ae_state *_state);
static void reviseddualsimplex_basisinit(ae_int_t ns,
     ae_int_t m,
     dualsimplexbasis* s,
     ae_state *_state);
static void reviseddualsimplex_basisclearstats(dualsimplexbasis* s,
     ae_state *_state);
static ae_bool reviseddualsimplex_basistryresize(dualsimplexbasis* s,
     ae_int_t newm,
     const sparsematrix* at,
     const dualsimplexsettings* settings,
     ae_state *_state);
static double reviseddualsimplex_basisminimumdiagonalelement(const dualsimplexbasis* s,
     ae_state *_state);
static void reviseddualsimplex_basisexportto(const dualsimplexbasis* s0,
     dualsimplexbasis* s1,
     ae_state *_state);
static ae_bool reviseddualsimplex_basistryimportfrom(dualsimplexbasis* s0,
     const dualsimplexbasis* s1,
     const sparsematrix* at,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_basisfreshtrf(dualsimplexbasis* s,
     const sparsematrix* at,
     const dualsimplexsettings* settings,
     ae_state *_state);
static double reviseddualsimplex_basisfreshtrfunsafe(dualsimplexbasis* s,
     const sparsematrix* at,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_basisrequestweights(dualsimplexbasis* s,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_basisupdatetrf(dualsimplexbasis* s,
     const sparsematrix* at,
     ae_int_t p,
     ae_int_t q,
     /* Real    */ const ae_vector* alphaq,
     /* Real    */ const ae_vector* alphaqim,
     ae_int_t r,
     /* Real    */ const ae_vector* tau,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_basissolve(const dualsimplexbasis* s,
     /* Real    */ const ae_vector* r,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* tmpx,
     ae_state *_state);
static void reviseddualsimplex_basissolvex(const dualsimplexbasis* s,
     /* Real    */ const ae_vector* r,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* xim,
     ae_bool needintermediate,
     /* Real    */ ae_vector* tx,
     ae_state *_state);
static void reviseddualsimplex_basissolvet(const dualsimplexbasis* s,
     /* Real    */ const ae_vector* r,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* tx,
     ae_state *_state);
static void reviseddualsimplex_computeanxn(const dualsimplexstate* state,
     const dualsimplexsubproblem* subproblem,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* y,
     ae_state *_state);
static void reviseddualsimplex_computeantv(const dualsimplexstate* state,
     /* Real    */ const ae_vector* y,
     /* Real    */ ae_vector* r,
     ae_state *_state);
static ae_bool reviseddualsimplex_hasbndl(const dualsimplexsubproblem* subproblem,
     ae_int_t i,
     ae_state *_state);
static ae_bool reviseddualsimplex_hasbndu(const dualsimplexsubproblem* subproblem,
     ae_int_t i,
     ae_state *_state);
static ae_bool reviseddualsimplex_isfree(const dualsimplexsubproblem* subproblem,
     ae_int_t i,
     ae_state *_state);
static void reviseddualsimplex_downgradestate(dualsimplexsubproblem* subproblem,
     ae_int_t s,
     ae_state *_state);
static double reviseddualsimplex_dualfeasibilityerror(const dualsimplexstate* state,
     const dualsimplexsubproblem* s,
     ae_state *_state);
static ae_bool reviseddualsimplex_isdualfeasible(const dualsimplexstate* state,
     const dualsimplexsubproblem* s,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_pivottobwd(/* Integer */ const ae_vector* p,
     ae_int_t m,
     /* Integer */ ae_vector* bwd,
     ae_state *_state);
static void reviseddualsimplex_inversecyclicpermutation(/* Integer */ ae_vector* bwd,
     ae_int_t m,
     ae_int_t d,
     /* Integer */ ae_vector* tmpi,
     ae_state *_state);
static void reviseddualsimplex_offloadbasiccomponents(dualsimplexsubproblem* s,
     const dualsimplexbasis* basis,
     const dualsimplexsettings* settings,
     ae_state *_state);
static void reviseddualsimplex_recombinebasicnonbasicx(dualsimplexsubproblem* s,
     const dualsimplexbasis* basis,
     ae_state *_state);
static void reviseddualsimplex_setxydstats(const dualsimplexstate* state,
     const dualsimplexsubproblem* s,
     const dualsimplexbasis* basis,
     apbuffers* buffers,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* laglc,
     /* Integer */ ae_vector* stats,
     ae_state *_state);
static void reviseddualsimplex_dvalloc(dssvector* x,
     ae_int_t n,
     ae_state *_state);
static void reviseddualsimplex_dvinit(dssvector* x,
     ae_int_t n,
     ae_state *_state);
static void reviseddualsimplex_dvdensetosparse(dssvector* x,
     ae_state *_state);
static void reviseddualsimplex_dvsparsetodense(dssvector* x,
     ae_state *_state);
static double reviseddualsimplex_sparsityof(/* Real    */ const ae_vector* x,
     ae_int_t n,
     ae_state *_state);
static void reviseddualsimplex_updateavgcounter(double v,
     double* acc,
     ae_int_t* cnt,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_LPSOLVERS) || !defined(AE_PARTIAL_BUILD)
static ae_int_t lpsolvers_alllogicalsbasis = 0;
static void lpsolvers_clearreportfields(minlpstate* state,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_NLS) || !defined(AE_PARTIAL_BUILD)
static void nls_unscalebatch(const nlsstate* state,
     /* Real    */ const ae_vector* xs,
     ae_int_t batchsize,
     /* Integer */ const ae_vector* idxraw2red,
     ae_int_t nreduced,
     /* Real    */ const ae_vector* scaledbndl,
     /* Real    */ const ae_vector* scaledbndu,
     /* Real    */ ae_vector* xu,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_GIPM2) || !defined(AE_PARTIAL_BUILD)
static double gipm2_initrlxpenalty = 1.0;
static double gipm2_rlxsafetyhi = 0.85;
static double gipm2_rlxsafetylo = 0.50;
static double gipm2_initmu = 0.1;
static double gipm2_relaxfactor = 1.0;
static double gipm2_lambdamax = 1.0E6;
static double gipm2_initbigval = 1.0E9;
static double gipm2_initbiggrowth = 1.0E9;
static double gipm2_maxbigval = 1.0E50;
static double gipm2_maxbiggrowth = 1.0E20;
static double gipm2_vggrowth = 1.0E3;
static double gipm2_damptau = 0.0001;
static double gipm2_lambdagrowth = 2.0;
static double gipm2_lambdarapidgrowth = 10.0;
static double gipm2_lambdadecayacc = 0.90;
static ae_int_t gipm2_maxsubsequentfailuresforweakgrowth = 5;
static ae_int_t gipm2_maxrejectedwithineps = 2;
static ae_int_t gipm2_maxrfsits = 5;
static double gipm2_rhstol = (double)100*ae_machineepsilon;
static double gipm2_fractobnd = 0.98;
static double gipm2_smallstep = 1.0E-8;
static double gipm2_tightendist = 5.0;
static double gipm2_middist = 3.0;
static double gipm2_relaxdist = 2.0;
static double gipm2_checkpointacceptancef = 0.000001;
static double gipm2_checkpointacceptanced = 0.999999;
static double gipm2_checkpointacceptancec = 0.999999;
static ae_int_t gipm2_maxsmalltrials = 3;
static double gipm2_dualmeritnu = 1.0;
static ae_int_t gipm2_maxnd = 5;
static void gipm2_varsinitbyzero(gipm2vars* vstate,
     ae_int_t np,
     ae_int_t mflex,
     ae_int_t meq,
     ae_int_t mrlxeq,
     ae_int_t m1,
     ae_int_t mrlx1,
     ae_bool hashardbc,
     ae_bool hassoftbc,
     ae_state *_state);
static void gipm2_multinitbyzero(gipm2mult* vstate,
     ae_int_t np,
     ae_int_t mflex,
     ae_int_t meq,
     ae_int_t mrlxeq,
     ae_int_t m1,
     ae_int_t mrlx1,
     ae_bool hashardbc,
     ae_bool hassoftbc,
     ae_state *_state);
static void gipm2_varsapplystep(gipm2vars* vstate,
     const gipm2vars* delta,
     double alpha,
     ae_state *_state);
static void gipm2_multapplystep(gipm2mult* vstate,
     const gipm2mult* delta,
     double alpha,
     ae_state *_state);
static void gipm2_varscopy(const gipm2vars* src,
     gipm2vars* dst,
     ae_state *_state);
static void gipm2_multcopy(const gipm2mult* src,
     gipm2mult* dst,
     ae_state *_state);
static double gipm2_computesteplengthp(const gipm2state* state,
     const gipm2vars* current,
     const gipm2vars* delta,
     ae_state *_state);
static double gipm2_computesteplengthpd(const gipm2state* state,
     const gipm2vars* current,
     const gipm2vars* delta,
     const gipm2mult* currentmult,
     const gipm2mult* deltamult,
     ae_state *_state);
static void gipm2_computeprimalmult(gipm2state* state,
     const gipm2vars* current,
     double currentmu,
     gipm2mult* mult,
     ae_state *_state);
static void gipm2_computerp(gipm2state* state,
     const gipm2vars* current,
     double currentmu,
     const gipm2mult* mult,
     /* Real    */ ae_vector* rp,
     ae_state *_state);
static void gipm2_computerhs(gipm2state* state,
     const gipm2vars* current,
     double currentmu,
     const gipm2mult* currentmult,
     gipm2rhs* rhs,
     double* epriminf,
     double* edualinf,
     double* ecmplinf,
     double* sprim,
     double* sdual,
     double* scmpl,
     ae_state *_state);
static double gipm2_computeecmpl2(const gipm2state* state,
     const gipm2rhs* rhs,
     ae_state *_state);
static void gipm2_copyrhs(const gipm2state* state,
     const gipm2rhs* src,
     gipm2rhs* dst,
     ae_state *_state);
static double gipm2_computedg(const gipm2state* state,
     const gipm2vars* delta,
     const gipm2rhs* rhs,
     ae_state *_state);
static double gipm2_computeupdateresidual(gipm2state* state,
     const gipm2vars* current,
     const gipm2mult* multlhs,
     double currentmu,
     double currentdiag,
     const gipm2vars* delta,
     const gipm2mult* deltamult,
     /* Real    */ const ae_vector* hdxp,
     /* Real    */ const ae_vector* jdxp,
     /* Real    */ const ae_vector* jtdy,
     gipm2rhs* rhs,
     ae_state *_state);
static void gipm2_condenselhs(const gipm2state* state,
     const gipm2vars* current,
     const gipm2mult* currentmult,
     double currentmu,
     double currentdiag,
     gipm2condensedsystem* c,
     ae_state *_state);
static void gipm2_condenserhs(const gipm2state* state,
     const gipm2vars* currentvars,
     const gipm2mult* currentmult,
     const gipm2rhs* rhs,
     double currentmu,
     double currentdiag,
     gipm2condensedsystem* c,
     ae_state *_state);
static void gipm2_unpacksolution(const gipm2state* state,
     const gipm2vars* currentvars,
     const gipm2mult* multlhs,
     double currentmu,
     double currentdiag,
     const gipm2rhs* rhs,
     gipm2condensedsystem* c,
     /* Real    */ const ae_vector* replysol,
     gipm2vars* delta,
     gipm2mult* deltamult,
     ae_state *_state);
static double gipm2_meritfunction(const gipm2state* state,
     const gipm2vars* current,
     double currentmu,
     ae_state *_state);
static double gipm2_meritfunctionpd(const gipm2state* state,
     const gipm2vars* current,
     const gipm2mult* currentmult,
     double currentmu,
     ae_state *_state);
static void gipm2_safeboundsfromfinitetightened(gipm2state* state,
     ae_state *_state);
static void gipm2_tunepenalties(gipm2state* state,
     const gipm2mult* currentmult,
     ae_state *_state);
static double gipm2_tgtrlxbndl(double vx,
     double vxnrm,
     double wrkbndl,
     double truebndl,
     ae_int_t* ntightened,
     ae_int_t* nrelaxed,
     ae_state *_state);
static double gipm2_tgtrlxbndu(double vx,
     double vxnrm,
     double wrkbndu,
     double truebndu,
     ae_int_t* ntightened,
     ae_int_t* nrelaxed,
     ae_state *_state);
static void gipm2_tightenrelaxbounds(gipm2state* state, ae_state *_state);
static void gipm2_updateerrorestimates(gipm2state* state,
     ae_state *_state);
static void gipm2_sendx(gipm2state* state,
     /* Real    */ const ae_vector* xs,
     ae_state *_state);
static void gipm2_sendlagrangianinfo(gipm2state* state,
     gipm2vars* current,
     gipm2mult* curmult,
     gipm2vars* cand,
     gipm2mult* candmult,
     ae_bool usebestmult,
     ae_state *_state);
static void gipm2_lagcoeffsfrommultipliers(const gipm2mult* mult,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* lagnlc,
     ae_state *_state);
static ae_bool gipm2_retrievefij(const gipm2state* state,
     gipm2vars* vfj,
     ae_state *_state);
static void gipm2_nonmonotonicinit(gipm2state* state,
     ae_int_t memlen,
     double merit0,
     ae_state *_state);
static void gipm2_nonmonotonicpush(gipm2state* state,
     double meritv,
     ae_state *_state);
static double gipm2_nonmonotonicgetcorr(gipm2state* state,
     double meritv,
     ae_state *_state);
static void gipm2_rescalecurrentbest(gipm2state* state,
     /* Real    */ const ae_vector* rescaleby,
     ae_bool rescalebest,
     ae_state *_state);
static double gipm2_aggregateerr(double p,
     double d,
     double c,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_NLCGIPM2) || !defined(AE_PARTIAL_BUILD)
static double nlcgipm2_mupromote = 10.0;
static ae_int_t nlcgipm2_xbfgsmemlen = 32;
static double nlcgipm2_xbfgsmaxhess = 1.0E6;
static double nlcgipm2_smallscaledecrease = 0.5;
static double nlcgipm2_maxrescale = 10.0;
static double nlcgipm2_minrescale = 0.1;
static double nlcgipm2_autoscalerhessdecay = 0.9;
static double nlcgipm2_maxhessnrm = 100.0;
static void nlcgipm2_savepointaslast(nlpgipm2state* state,
     double rawf0,
     ae_state *_state);
static void nlcgipm2_lastpointasbase(nlpgipm2state* state,
     /* Real    */ const ae_vector* querydata,
     ae_state *_state);
static void nlcgipm2_basepointasbest(nlpgipm2state* state,
     ae_state *_state);
static void nlcgipm2_restorebestpoint(nlpgipm2state* state,
     ae_state *_state);
static void nlcgipm2_factorizematrix(nlpgipm2state* state,
     ae_state *_state);
static void nlcgipm2_solvesystem(nlpgipm2state* state, ae_state *_state);
static void nlcgipm2_matrixvector(nlpgipm2state* state, ae_state *_state);
static void nlcgipm2_autoscaleinit(nlpgipm2state* state, ae_state *_state);
static void nlcgipm2_autoscaletargetconstraints(nlpgipm2state* state,
     ae_state *_state);
static void nlcgipm2_applyrescale(/* Real    */ ae_vector* fi,
     sparsematrix* sj,
     ae_int_t idx,
     double multiplyby,
     ae_state *_state);
static void nlcgipm2_generatesparsesys(nlpgipm2state* state,
     const sparsematrix* upcomingsj,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_MINBLEIC) || !defined(AE_PARTIAL_BUILD)
static double minbleic_gtol = 0.4;
static double minbleic_maxnonmonotoniclen = 1.0E-7;
static double minbleic_initialdecay = 0.5;
static double minbleic_mindecay = 0.1;
static double minbleic_decaycorrection = 0.8;
static double minbleic_penaltyfactor = 100;
static void minbleic_clearrequestfields(minbleicstate* state,
     ae_state *_state);
static void minbleic_minbleicinitinternal(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minbleicstate* state,
     ae_state *_state);
static void minbleic_updateestimateofgoodstep(double* estimate,
     double newstep,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_MINNLC) || !defined(AE_PARTIAL_BUILD)
static void minnlc_clearrequestfields(minnlcstate* state,
     ae_state *_state);
static void minnlc_minnlcinitinternal(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minnlcstate* state,
     ae_state *_state);
static void minnlc_unscale(const minnlcstate* state,
     /* Real    */ const ae_vector* xs,
     /* Real    */ const ae_vector* scaledbndl,
     /* Real    */ const ae_vector* scaledbndu,
     /* Real    */ ae_vector* xu,
     ae_state *_state);
static void minnlc_crsfromdense(/* Real    */ const ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     sparsematrix* s,
     ae_state *_state);
static void minnlc_crsfromdensev(/* Real    */ const ae_vector* a,
     ae_int_t m,
     ae_int_t n,
     sparsematrix* s,
     ae_state *_state);
static void minnlc_unscalebatch(const minnlcstate* state,
     /* Real    */ const ae_vector* xs,
     ae_int_t batchsize,
     /* Integer */ const ae_vector* idxraw2red,
     ae_int_t nreduced,
     /* Real    */ const ae_vector* scaledbndl,
     /* Real    */ const ae_vector* scaledbndu,
     /* Real    */ ae_vector* xu,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_MONBI) || !defined(AE_PARTIAL_BUILD)
static void monbi_clearrequestfields(nbistate* state, ae_state *_state);
static void monbi_setnlcalgo(minnlcstate* s, ae_state *_state);


#endif
#if defined(AE_COMPILE_MINMO) || !defined(AE_PARTIAL_BUILD)
static void minmo_clearrequestfields(minmostate* state, ae_state *_state);
static void minmo_minmoinitinternal(ae_int_t n,
     ae_int_t m,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minmostate* state,
     ae_state *_state);
static void minmo_unscale(const minmostate* state,
     /* Real    */ const ae_vector* xs,
     /* Real    */ const ae_vector* scaledbndl,
     /* Real    */ const ae_vector* scaledbndu,
     /* Real    */ ae_vector* xu,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_MINNS) || !defined(AE_PARTIAL_BUILD)
static void minns_clearrequestfields(minnsstate* state, ae_state *_state);
static void minns_minnsinitinternal(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minnsstate* state,
     ae_state *_state);
static ae_bool minns_agsiteration(minnsstate* state, ae_state *_state);
static void minns_unscalepointbc(const minnsstate* state,
     /* Real    */ ae_vector* x,
     ae_state *_state);
static void minns_solveqp(/* Real    */ const ae_matrix* sampleg,
     /* Real    */ const ae_vector* diagh,
     ae_int_t nsample,
     ae_int_t nvars,
     /* Real    */ ae_vector* coeffs,
     ae_int_t* dbgncholesky,
     minnsqp* state,
     ae_state *_state);
static void minns_qpcalculategradfunc(/* Real    */ const ae_matrix* sampleg,
     /* Real    */ const ae_vector* diagh,
     ae_int_t nsample,
     ae_int_t nvars,
     /* Real    */ const ae_vector* coeffs,
     /* Real    */ ae_vector* g,
     double* f,
     /* Real    */ ae_vector* tmp,
     ae_state *_state);
static void minns_qpcalculatefunc(/* Real    */ const ae_matrix* sampleg,
     /* Real    */ const ae_vector* diagh,
     ae_int_t nsample,
     ae_int_t nvars,
     /* Real    */ const ae_vector* coeffs,
     double* f,
     /* Real    */ ae_vector* tmp,
     ae_state *_state);
static void minns_qpsolveu(/* Real    */ const ae_matrix* a,
     ae_int_t n,
     /* Real    */ ae_vector* x,
     ae_state *_state);
static void minns_qpsolveut(/* Real    */ const ae_matrix* a,
     ae_int_t n,
     /* Real    */ ae_vector* x,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_MINCOMP) || !defined(AE_PARTIAL_BUILD)
static ae_int_t mincomp_n1 = 2;
static ae_int_t mincomp_n2 = 2;
static double mincomp_stpmin = 1.0E-300;
static double mincomp_gtol = 0.3;
static double mincomp_gpaftol = 0.0001;
static double mincomp_gpadecay = 0.5;
static double mincomp_asarho = 0.5;
static double mincomp_asaboundedantigradnorm(const minasastate* state,
     ae_state *_state);
static double mincomp_asaginorm(const minasastate* state,
     ae_state *_state);
static double mincomp_asad1norm(const minasastate* state,
     ae_state *_state);
static ae_bool mincomp_asauisempty(const minasastate* state,
     ae_state *_state);
static void mincomp_clearrequestfields(minasastate* state,
     ae_state *_state);


#endif
#if defined(AE_COMPILE_MINBC) || !defined(AE_PARTIAL_BUILD)
static double minbc_gtol = 0.4;
static double minbc_maxnonmonotoniclen = 1.0E-5;
static double minbc_initialdecay = 0.5;
static double minbc_mindecay = 0.1;
static double minbc_decaycorrection = 0.8;
static void minbc_clearrequestfields(minbcstate* state, ae_state *_state);
static void minbc_minbcinitinternal(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minbcstate* state,
     ae_state *_state);
static void minbc_updateestimateofgoodstep(double* estimate,
     double newstep,
     ae_state *_state);


#endif

#if defined(AE_COMPILE_OPTGUARDAPI) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
This subroutine initializes "internal" OptGuard report,  i.e. one intended
for internal use by optimizers.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
void optguardinitinternal(optguardreport* rep,
     ae_int_t n,
     ae_int_t k,
     ae_state *_state)
{


    rep->nonc0suspected = ae_false;
    rep->nonc0test0positive = ae_false;
    rep->nonc0lipschitzc = (double)(0);
    rep->nonc0fidx = -1;
    rep->nonc1suspected = ae_false;
    rep->nonc1test0positive = ae_false;
    rep->nonc1test1positive = ae_false;
    rep->nonc1lipschitzc = (double)(0);
    rep->nonc1fidx = -1;
    rep->badgradsuspected = ae_false;
    rep->badgradfidx = -1;
    rep->badgradvidx = -1;
}


/*************************************************************************
This subroutine exports report to user-readable representation (all arrays
are forced to have exactly same size as needed; unused arrays are  set  to
zero length).

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
void optguardexportreport(const optguardreport* srcrep,
     ae_int_t n,
     ae_int_t k,
     ae_bool badgradhasxj,
     optguardreport* dstrep,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    dstrep->nonc0suspected = srcrep->nonc0suspected;
    dstrep->nonc0test0positive = srcrep->nonc0test0positive;
    if( srcrep->nonc0suspected )
    {
        dstrep->nonc0lipschitzc = srcrep->nonc0lipschitzc;
        dstrep->nonc0fidx = srcrep->nonc0fidx;
    }
    else
    {
        dstrep->nonc0lipschitzc = (double)(0);
        dstrep->nonc0fidx = -1;
    }
    dstrep->nonc1suspected = srcrep->nonc1suspected;
    dstrep->nonc1test0positive = srcrep->nonc1test0positive;
    dstrep->nonc1test1positive = srcrep->nonc1test1positive;
    if( srcrep->nonc1suspected )
    {
        dstrep->nonc1lipschitzc = srcrep->nonc1lipschitzc;
        dstrep->nonc1fidx = srcrep->nonc1fidx;
    }
    else
    {
        dstrep->nonc1lipschitzc = (double)(0);
        dstrep->nonc1fidx = -1;
    }
    dstrep->badgradsuspected = srcrep->badgradsuspected;
    if( srcrep->badgradsuspected )
    {
        dstrep->badgradfidx = srcrep->badgradfidx;
        dstrep->badgradvidx = srcrep->badgradvidx;
    }
    else
    {
        dstrep->badgradfidx = -1;
        dstrep->badgradvidx = -1;
    }
    if( badgradhasxj )
    {
        ae_vector_set_length(&dstrep->badgradxbase, n, _state);
        for(j=0; j<=n-1; j++)
        {
            dstrep->badgradxbase.ptr.p_double[j] = srcrep->badgradxbase.ptr.p_double[j];
        }
        ae_matrix_set_length(&dstrep->badgraduser, k, n, _state);
        ae_matrix_set_length(&dstrep->badgradnum, k, n, _state);
        for(i=0; i<=k-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                dstrep->badgraduser.ptr.pp_double[i][j] = srcrep->badgraduser.ptr.pp_double[i][j];
                dstrep->badgradnum.ptr.pp_double[i][j] = srcrep->badgradnum.ptr.pp_double[i][j];
            }
        }
    }
    else
    {
        ae_vector_set_length(&dstrep->badgradxbase, 0, _state);
        ae_matrix_set_length(&dstrep->badgraduser, 0, 0, _state);
        ae_matrix_set_length(&dstrep->badgradnum, 0, 0, _state);
    }
}


/*************************************************************************
This subroutine exports report to user-readable representation (all arrays
are forced to have exactly same size as needed; unused arrays are  set  to
zero length).

NOTE: we assume that SrcRep contains scaled X0[] and  D[],  i.e.  explicit
      variable scaling was applied. We need to rescale them during export,
      that's why we need S[] parameter.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
void smoothnessmonitorexportc1test0report(const optguardnonc1test0report* srcrep,
     /* Real    */ const ae_vector* s,
     optguardnonc1test0report* dstrep,
     ae_state *_state)
{
    ae_int_t i;


    dstrep->positive = srcrep->positive;
    if( srcrep->positive )
    {
        dstrep->stpidxa = srcrep->stpidxa;
        dstrep->stpidxb = srcrep->stpidxb;
        dstrep->inneriter = srcrep->inneriter;
        dstrep->outeriter = srcrep->outeriter;
        dstrep->fidx = srcrep->fidx;
        dstrep->cnt = srcrep->cnt;
        dstrep->n = srcrep->n;
        ae_vector_set_length(&dstrep->x0, srcrep->n, _state);
        ae_vector_set_length(&dstrep->d, srcrep->n, _state);
        for(i=0; i<=srcrep->n-1; i++)
        {
            dstrep->x0.ptr.p_double[i] = srcrep->x0.ptr.p_double[i]*s->ptr.p_double[i];
            dstrep->d.ptr.p_double[i] = srcrep->d.ptr.p_double[i]*s->ptr.p_double[i];
        }
        ae_vector_set_length(&dstrep->stp, srcrep->cnt, _state);
        ae_vector_set_length(&dstrep->f, srcrep->cnt, _state);
        for(i=0; i<=srcrep->cnt-1; i++)
        {
            dstrep->stp.ptr.p_double[i] = srcrep->stp.ptr.p_double[i];
            dstrep->f.ptr.p_double[i] = srcrep->f.ptr.p_double[i];
        }
    }
    else
    {
        dstrep->stpidxa = -1;
        dstrep->stpidxb = -1;
        dstrep->inneriter = -1;
        dstrep->outeriter = -1;
        dstrep->fidx = -1;
        dstrep->cnt = 0;
        dstrep->n = 0;
        ae_vector_set_length(&dstrep->x0, 0, _state);
        ae_vector_set_length(&dstrep->d, 0, _state);
        ae_vector_set_length(&dstrep->stp, 0, _state);
        ae_vector_set_length(&dstrep->f, 0, _state);
    }
}


/*************************************************************************
This subroutine exports report to user-readable representation (all arrays
are forced to have exactly same size as needed; unused arrays are  set  to
zero length).

NOTE: we assume that SrcRep contains scaled X0[], D[] and G[], i.e. explicit
      variable scaling was applied. We need to rescale them during export,
      that's why we need S[] parameter.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
void smoothnessmonitorexportc1test1report(const optguardnonc1test1report* srcrep,
     /* Real    */ const ae_vector* s,
     optguardnonc1test1report* dstrep,
     ae_state *_state)
{
    ae_int_t i;


    dstrep->positive = srcrep->positive;
    if( srcrep->positive )
    {
        ae_assert(srcrep->vidx>=0&&srcrep->vidx<srcrep->n, "SmoothnessMonitorExportC1Test1Report: integrity check failed", _state);
        dstrep->stpidxa = srcrep->stpidxa;
        dstrep->stpidxb = srcrep->stpidxb;
        dstrep->inneriter = srcrep->inneriter;
        dstrep->outeriter = srcrep->outeriter;
        dstrep->fidx = srcrep->fidx;
        dstrep->vidx = srcrep->vidx;
        dstrep->cnt = srcrep->cnt;
        dstrep->n = srcrep->n;
        ae_vector_set_length(&dstrep->x0, srcrep->n, _state);
        ae_vector_set_length(&dstrep->d, srcrep->n, _state);
        for(i=0; i<=srcrep->n-1; i++)
        {
            dstrep->x0.ptr.p_double[i] = srcrep->x0.ptr.p_double[i]*s->ptr.p_double[i];
            dstrep->d.ptr.p_double[i] = srcrep->d.ptr.p_double[i]*s->ptr.p_double[i];
        }
        ae_vector_set_length(&dstrep->stp, srcrep->cnt, _state);
        ae_vector_set_length(&dstrep->g, srcrep->cnt, _state);
        for(i=0; i<=srcrep->cnt-1; i++)
        {
            dstrep->stp.ptr.p_double[i] = srcrep->stp.ptr.p_double[i];
            dstrep->g.ptr.p_double[i] = srcrep->g.ptr.p_double[i]/s->ptr.p_double[srcrep->vidx];
        }
    }
    else
    {
        dstrep->stpidxa = -1;
        dstrep->stpidxb = -1;
        dstrep->inneriter = -1;
        dstrep->outeriter = -1;
        dstrep->fidx = -1;
        dstrep->vidx = -1;
        dstrep->cnt = 0;
        dstrep->n = 0;
        ae_vector_set_length(&dstrep->x0, 0, _state);
        ae_vector_set_length(&dstrep->d, 0, _state);
        ae_vector_set_length(&dstrep->stp, 0, _state);
        ae_vector_set_length(&dstrep->g, 0, _state);
    }
}


/*************************************************************************
Returns True when all flags are clear. Intended for easy  coding  of  unit
tests.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
ae_bool optguardallclear(const optguardreport* rep, ae_state *_state)
{
    ae_bool result;


    result = !((rep->badgradsuspected||rep->nonc0suspected)||rep->nonc1suspected);
    return result;
}


void _optguardreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    optguardreport *p = (optguardreport*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->badgradxbase, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->badgraduser, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->badgradnum, 0, 0, DT_REAL, _state, make_automatic);
}


void _optguardreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    optguardreport       *dst = (optguardreport*)_dst;
    const optguardreport *src = (const optguardreport*)_src;
    dst->nonc0suspected = src->nonc0suspected;
    dst->nonc0test0positive = src->nonc0test0positive;
    dst->nonc0fidx = src->nonc0fidx;
    dst->nonc0lipschitzc = src->nonc0lipschitzc;
    dst->nonc1suspected = src->nonc1suspected;
    dst->nonc1test0positive = src->nonc1test0positive;
    dst->nonc1test1positive = src->nonc1test1positive;
    dst->nonc1fidx = src->nonc1fidx;
    dst->nonc1lipschitzc = src->nonc1lipschitzc;
    dst->badgradsuspected = src->badgradsuspected;
    dst->badgradfidx = src->badgradfidx;
    dst->badgradvidx = src->badgradvidx;
    ae_vector_init_copy(&dst->badgradxbase, &src->badgradxbase, _state, make_automatic);
    ae_matrix_init_copy(&dst->badgraduser, &src->badgraduser, _state, make_automatic);
    ae_matrix_init_copy(&dst->badgradnum, &src->badgradnum, _state, make_automatic);
}


void _optguardreport_clear(void* _p)
{
    optguardreport *p = (optguardreport*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->badgradxbase);
    ae_matrix_clear(&p->badgraduser);
    ae_matrix_clear(&p->badgradnum);
}


void _optguardreport_destroy(void* _p)
{
    optguardreport *p = (optguardreport*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->badgradxbase);
    ae_matrix_destroy(&p->badgraduser);
    ae_matrix_destroy(&p->badgradnum);
}


void _optguardnonc0report_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    optguardnonc0report *p = (optguardnonc0report*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->stp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->f, 0, DT_REAL, _state, make_automatic);
}


void _optguardnonc0report_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    optguardnonc0report       *dst = (optguardnonc0report*)_dst;
    const optguardnonc0report *src = (const optguardnonc0report*)_src;
    dst->positive = src->positive;
    dst->fidx = src->fidx;
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    dst->n = src->n;
    ae_vector_init_copy(&dst->stp, &src->stp, _state, make_automatic);
    ae_vector_init_copy(&dst->f, &src->f, _state, make_automatic);
    dst->cnt = src->cnt;
    dst->stpidxa = src->stpidxa;
    dst->stpidxb = src->stpidxb;
    dst->inneriter = src->inneriter;
    dst->outeriter = src->outeriter;
}


void _optguardnonc0report_clear(void* _p)
{
    optguardnonc0report *p = (optguardnonc0report*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->stp);
    ae_vector_clear(&p->f);
}


void _optguardnonc0report_destroy(void* _p)
{
    optguardnonc0report *p = (optguardnonc0report*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->stp);
    ae_vector_destroy(&p->f);
}


void _optguardnonc1test0report_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    optguardnonc1test0report *p = (optguardnonc1test0report*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->stp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->f, 0, DT_REAL, _state, make_automatic);
}


void _optguardnonc1test0report_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    optguardnonc1test0report       *dst = (optguardnonc1test0report*)_dst;
    const optguardnonc1test0report *src = (const optguardnonc1test0report*)_src;
    dst->positive = src->positive;
    dst->fidx = src->fidx;
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    dst->n = src->n;
    ae_vector_init_copy(&dst->stp, &src->stp, _state, make_automatic);
    ae_vector_init_copy(&dst->f, &src->f, _state, make_automatic);
    dst->cnt = src->cnt;
    dst->stpidxa = src->stpidxa;
    dst->stpidxb = src->stpidxb;
    dst->inneriter = src->inneriter;
    dst->outeriter = src->outeriter;
}


void _optguardnonc1test0report_clear(void* _p)
{
    optguardnonc1test0report *p = (optguardnonc1test0report*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->stp);
    ae_vector_clear(&p->f);
}


void _optguardnonc1test0report_destroy(void* _p)
{
    optguardnonc1test0report *p = (optguardnonc1test0report*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->stp);
    ae_vector_destroy(&p->f);
}


void _optguardnonc1test1report_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    optguardnonc1test1report *p = (optguardnonc1test1report*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->stp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
}


void _optguardnonc1test1report_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    optguardnonc1test1report       *dst = (optguardnonc1test1report*)_dst;
    const optguardnonc1test1report *src = (const optguardnonc1test1report*)_src;
    dst->positive = src->positive;
    dst->fidx = src->fidx;
    dst->vidx = src->vidx;
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    dst->n = src->n;
    ae_vector_init_copy(&dst->stp, &src->stp, _state, make_automatic);
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    dst->cnt = src->cnt;
    dst->stpidxa = src->stpidxa;
    dst->stpidxb = src->stpidxb;
    dst->inneriter = src->inneriter;
    dst->outeriter = src->outeriter;
}


void _optguardnonc1test1report_clear(void* _p)
{
    optguardnonc1test1report *p = (optguardnonc1test1report*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->stp);
    ae_vector_clear(&p->g);
}


void _optguardnonc1test1report_destroy(void* _p)
{
    optguardnonc1test1report *p = (optguardnonc1test1report*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->stp);
    ae_vector_destroy(&p->g);
}


#endif
#if defined(AE_COMPILE_OPTS) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
Initialize test LP problem.

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemcreate(ae_int_t n,
     ae_bool hasknowntarget,
     double targetf,
     lptestproblem* p,
     ae_state *_state)
{

    _lptestproblem_clear(p);

    ae_assert(n>=1, "LPTestProblemCreate: N<1", _state);
    p->n = n;
    p->hasknowntarget = hasknowntarget;
    if( hasknowntarget )
    {
        p->targetf = targetf;
    }
    else
    {
        p->targetf = _state->v_nan;
    }
    ae_vector_set_length(&p->s, n, _state);
    rsetv(n, 1.0, &p->s, _state);
    ae_vector_set_length(&p->c, n, _state);
    rsetv(n, 0.0, &p->c, _state);
    ae_vector_set_length(&p->bndl, n, _state);
    rsetv(n, 0.0, &p->bndl, _state);
    ae_vector_set_length(&p->bndu, n, _state);
    rsetv(n, 0.0, &p->bndu, _state);
    p->m = 0;
    ae_vector_set_length(&p->al, 0, _state);
    ae_vector_set_length(&p->au, 0, _state);
}


/*************************************************************************
Query test problem info

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
ae_bool lptestproblemhasknowntarget(lptestproblem* p, ae_state *_state)
{
    ae_bool result;


    result = p->hasknowntarget;
    return result;
}


/*************************************************************************
Query test problem info

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
double lptestproblemgettargetf(lptestproblem* p, ae_state *_state)
{
    double result;


    result = p->targetf;
    return result;
}


/*************************************************************************
Query test problem info

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
ae_int_t lptestproblemgetn(lptestproblem* p, ae_state *_state)
{
    ae_int_t result;


    result = p->n;
    return result;
}


/*************************************************************************
Query test problem info

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
ae_int_t lptestproblemgetm(lptestproblem* p, ae_state *_state)
{
    ae_int_t result;


    result = p->m;
    return result;
}


/*************************************************************************
Set scale for test LP problem

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemsetscale(lptestproblem* p,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{


    rcopyv(p->n, s, &p->s, _state);
}


/*************************************************************************
Set cost for test LP problem

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemsetcost(lptestproblem* p,
     /* Real    */ const ae_vector* c,
     ae_state *_state)
{


    rcopyv(p->n, c, &p->c, _state);
}


/*************************************************************************
Set box constraints for test LP problem

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemsetbc(lptestproblem* p,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{


    rcopyv(p->n, bndl, &p->bndl, _state);
    rcopyv(p->n, bndu, &p->bndu, _state);
}


/*************************************************************************
Set box constraints for test LP problem

This function is intended for internal use by ALGLIB.

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemsetlc2(lptestproblem* p,
     const sparsematrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t m,
     ae_state *_state)
{


    if( m<=0 )
    {
        p->m = 0;
        return;
    }
    ae_assert(sparsegetnrows(a, _state)==m, "LPTestProblemSetLC2: rows(A)<>M", _state);
    p->m = m;
    sparsecopytocrs(a, &p->a, _state);
    ae_vector_set_length(&p->al, m, _state);
    ae_vector_set_length(&p->au, m, _state);
    rcopyv(m, al, &p->al, _state);
    rcopyv(m, au, &p->au, _state);
}


/*************************************************************************
Serializer: allocation

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemalloc(ae_serializer* s,
     const lptestproblem* p,
     ae_state *_state)
{


    ae_serializer_alloc_entry(s);
    ae_serializer_alloc_entry(s);
    ae_serializer_alloc_entry(s);
    ae_serializer_alloc_entry(s);
    ae_serializer_alloc_entry(s);
    allocrealarray(s, &p->s, p->n, _state);
    allocrealarray(s, &p->c, p->n, _state);
    allocrealarray(s, &p->bndl, p->n, _state);
    allocrealarray(s, &p->bndu, p->n, _state);
    ae_serializer_alloc_entry(s);
    if( p->m>0 )
    {
        sparsealloc(s, &p->a, _state);
        allocrealarray(s, &p->al, p->m, _state);
        allocrealarray(s, &p->au, p->m, _state);
    }
    ae_serializer_alloc_entry(s);
}


/*************************************************************************
Serializer: serialization

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemserialize(ae_serializer* s,
     const lptestproblem* p,
     ae_state *_state)
{


    ae_serializer_serialize_int(s, getlptestserializationcode(_state), _state);
    ae_serializer_serialize_int(s, 0, _state);
    ae_serializer_serialize_int(s, p->n, _state);
    ae_serializer_serialize_bool(s, p->hasknowntarget, _state);
    ae_serializer_serialize_double(s, p->targetf, _state);
    serializerealarray(s, &p->s, p->n, _state);
    serializerealarray(s, &p->c, p->n, _state);
    serializerealarray(s, &p->bndl, p->n, _state);
    serializerealarray(s, &p->bndu, p->n, _state);
    ae_serializer_serialize_int(s, p->m, _state);
    if( p->m>0 )
    {
        sparseserialize(s, &p->a, _state);
        serializerealarray(s, &p->al, p->m, _state);
        serializerealarray(s, &p->au, p->m, _state);
    }
    ae_serializer_serialize_int(s, 872, _state);
}


/*************************************************************************
Serializer: unserialization

  -- ALGLIB --
     Copyright 20.07.2021 by Bochkanov Sergey
*************************************************************************/
void lptestproblemunserialize(ae_serializer* s,
     lptestproblem* p,
     ae_state *_state)
{
    ae_int_t k;

    _lptestproblem_clear(p);

    ae_serializer_unserialize_int(s, &k, _state);
    ae_assert(k==getlptestserializationcode(_state), "LPTestProblemUnserialize: stream header corrupted", _state);
    ae_serializer_unserialize_int(s, &k, _state);
    ae_assert(k==0, "LPTestProblemUnserialize: stream header corrupted", _state);
    ae_serializer_unserialize_int(s, &p->n, _state);
    ae_serializer_unserialize_bool(s, &p->hasknowntarget, _state);
    ae_serializer_unserialize_double(s, &p->targetf, _state);
    unserializerealarray(s, &p->s, _state);
    unserializerealarray(s, &p->c, _state);
    unserializerealarray(s, &p->bndl, _state);
    unserializerealarray(s, &p->bndu, _state);
    ae_serializer_unserialize_int(s, &p->m, _state);
    if( p->m>0 )
    {
        sparseunserialize(s, &p->a, _state);
        unserializerealarray(s, &p->al, _state);
        unserializerealarray(s, &p->au, _state);
    }
    ae_serializer_unserialize_int(s, &k, _state);
    ae_assert(k==872, "LPTestProblemUnserialize: end-of-stream marker not found", _state);
}


/*************************************************************************
Returns objective type: True for zero/linear/constant.

Present version does not return False.

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_bool qpxproblemisquadraticobjective(qpxproblem* p, ae_state *_state)
{
    ae_bool result;


    result = ae_true;
    return result;
}


/*************************************************************************
Get variables count

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t qpxproblemgetn(qpxproblem* p, ae_state *_state)
{
    ae_int_t result;


    result = p->n;
    return result;
}


/*************************************************************************
Get linear constraints count

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t qpxproblemgetmlc(qpxproblem* p, ae_state *_state)
{
    ae_int_t result;


    result = p->mlc;
    return result;
}


/*************************************************************************
Get quadratic constraints count

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t qpxproblemgetmqc(qpxproblem* p, ae_state *_state)
{
    ae_int_t result;


    result = p->mqc;
    return result;
}


/*************************************************************************
Get conic constraints count

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t qpxproblemgetmcc(qpxproblem* p, ae_state *_state)
{
    ae_int_t result;


    result = p->mcc;
    return result;
}


/*************************************************************************
Get total constraints count

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t qpxproblemgettotalconstraints(qpxproblem* p, ae_state *_state)
{
    ae_int_t result;


    result = p->mlc+p->mqc+p->mcc;
    return result;
}


/*************************************************************************
Get initial point

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetinitialpoint(qpxproblem* p,
     /* Real    */ ae_vector* x0,
     ae_state *_state)
{

    ae_vector_clear(x0);

    if( p->hasinitialpoint )
    {
        rcopyallocv(p->n, &p->x0, x0, _state);
    }
    else
    {
        rsetallocv(p->n, 1.0, x0, _state);
    }
}


/*************************************************************************
Get initial point presence

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_bool qpxproblemhasinitialpoint(qpxproblem* p, ae_state *_state)
{
    ae_bool result;


    result = p->hasinitialpoint;
    return result;
}


/*************************************************************************
Get scale

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetscale(qpxproblem* p,
     /* Real    */ ae_vector* s,
     ae_state *_state)
{

    ae_vector_clear(s);

    if( p->hasscale )
    {
        rcopyallocv(p->n, &p->s, s, _state);
    }
    else
    {
        rsetallocv(p->n, 1.0, s, _state);
    }
}


/*************************************************************************
Get scale presence

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_bool qpxproblemhasscale(qpxproblem* p, ae_state *_state)
{
    ae_bool result;


    result = p->hasscale;
    return result;
}


/*************************************************************************
Get origin

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetorigin(qpxproblem* p,
     /* Real    */ ae_vector* xorigin,
     ae_state *_state)
{

    ae_vector_clear(xorigin);

    if( p->hasorigin )
    {
        rcopyallocv(p->n, &p->xorigin, xorigin, _state);
    }
    else
    {
        rsetallocv(p->n, 0.0, xorigin, _state);
    }
}


/*************************************************************************
Get origin presence

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_bool qpxproblemhasorigin(qpxproblem* p, ae_state *_state)
{
    ae_bool result;


    result = p->hasorigin;
    return result;
}


/*************************************************************************
Get linear term

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetlinearterm(qpxproblem* p,
     /* Real    */ ae_vector* c,
     ae_state *_state)
{

    ae_vector_clear(c);

    rcopyallocv(p->n, &p->c, c, _state);
}


/*************************************************************************
Get quadratic term, returns zero matrix if no quadratic term is present

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetquadraticterm(qpxproblem* p,
     sparsematrix* q,
     ae_bool* isupper,
     ae_state *_state)
{

    _sparsematrix_clear(q);
    *isupper = ae_false;

    if( !p->hasq )
    {
        q->matrixtype = 1;
        q->m = p->n;
        q->n = p->n;
        q->ninitialized = 0;
        isetallocv(p->n, 0, &q->didx, _state);
        isetallocv(p->n, 0, &q->uidx, _state);
        isetallocv(p->n+1, 0, &q->ridx, _state);
        *isupper = ae_false;
        return;
    }
    *isupper = p->isupperq;
    sparsecopybuf(&p->q, q, _state);
}


/*************************************************************************
Returns False if no quadratic term was specified, or quadratic term is
numerically zero.

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
ae_bool qpxproblemhasquadraticterm(qpxproblem* p, ae_state *_state)
{
    ae_bool result;


    if( !p->hasq )
    {
        result = ae_false;
        return result;
    }
    result = p->q.ridx.ptr.p_int[p->n]>0;
    return result;
}


/*************************************************************************
Get box constraints

  -- ALGLIB --
     Copyright 20.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetbc(qpxproblem* p,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     ae_state *_state)
{

    ae_vector_clear(bndl);
    ae_vector_clear(bndu);

    rcopyallocv(p->n, &p->bndl, bndl, _state);
    rcopyallocv(p->n, &p->bndu, bndu, _state);
}


/*************************************************************************
Get linear constraints

  -- ALGLIB --
     Copyright 20.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetlc2(qpxproblem* p,
     sparsematrix* a,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t* m,
     ae_state *_state)
{

    _sparsematrix_clear(a);
    ae_vector_clear(al);
    ae_vector_clear(au);
    *m = 0;

    if( p->mlc==0 )
    {
        *m = 0;
        return;
    }
    *m = p->mlc;
    sparsecopybuf(&p->a, a, _state);
    rcopyallocv(*m, &p->al, al, _state);
    rcopyallocv(*m, &p->au, au, _state);
}


/*************************************************************************
Passes entire set of quadratic constraints, overwriting internally
stored ones.

  -- ALGLIB --
     Copyright 19.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemsetxqc(qpxproblem* p,
     xquadraticconstraints* src,
     ae_state *_state)
{


    xqccopy(src, &p->qc, _state);
    p->mqc = xqcgetcount(&p->qc, _state);
}


/*************************************************************************
Get IDX-th two-sided quadratic constraint, same format as minqpaddqc2(),
except for the fact that it always returns isUpper=False, even if the
original matrix was an upper triangular one.

NOTE: this function is not optimized for big matrices. Whilst still having
      O(max(N,Nonzeros)) running time, it may  be  somewhat  slow  due  to
      dynamic structures being used internally.
      

  -- ALGLIB --
     Copyright 19.08.2024 by Bochkanov Sergey
*************************************************************************/
void qpxproblemgetqc2i(qpxproblem* p,
     ae_int_t idx,
     sparsematrix* q,
     ae_bool* isupper,
     /* Real    */ ae_vector* b,
     double* cl,
     double* cu,
     ae_bool* applyorigin,
     ae_state *_state)
{
    ae_frame _frame_block;
    xquadraticconstraint *qci;
    ae_smart_ptr _qci;
    ae_int_t i;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t jj;
    ae_int_t j;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&_qci, 0, sizeof(_qci));
    _sparsematrix_clear(q);
    *isupper = ae_false;
    ae_vector_clear(b);
    *cl = 0.0;
    *cu = 0.0;
    *applyorigin = ae_false;
    ae_smart_ptr_init(&_qci, (void**)&qci, ae_false, _state, ae_true);

    ae_assert(idx>=0&&idx<p->mqc, "QPXProblemGetQC2i: Idx is outside of [0,MQC) range", _state);
    ae_obj_array_get(&p->qc.constraints, idx, &_qci, _state);
    if( qci->nvars==0 )
    {
        q->matrixtype = 1;
        q->m = p->n;
        q->n = p->n;
        q->ninitialized = 0;
        isetallocv(p->n, 0, &q->didx, _state);
        isetallocv(p->n, 0, &q->uidx, _state);
        isetallocv(p->n+1, 0, &q->ridx, _state);
        *isupper = ae_false;
        rsetallocv(p->n, 0.0, b, _state);
        *cl = qci->cl;
        *cu = qci->cu;
        *applyorigin = qci->applyorigin;
        ae_frame_leave(_state);
        return;
    }
    sparsecreatebuf(p->n, p->n, qci->lowerq.ridx.ptr.p_int[qci->nvars], q, _state);
    for(i=0; i<=qci->nvars-1; i++)
    {
        j0 = qci->lowerq.ridx.ptr.p_int[i];
        j1 = qci->lowerq.ridx.ptr.p_int[i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            j = qci->lowerq.idx.ptr.p_int[jj];
            v = qci->lowerq.vals.ptr.p_double[jj];
            sparseset(q, qci->varidx.ptr.p_int[i], qci->varidx.ptr.p_int[j], v, _state);
        }
    }
    sparseconverttocrs(q, _state);
    *isupper = ae_false;
    rsetallocv(p->n, 0.0, b, _state);
    for(i=0; i<=qci->nvars-1; i++)
    {
        b->ptr.p_double[qci->varidx.ptr.p_int[i]] = qci->b.ptr.p_double[i];
    }
    *cl = qci->cl;
    *cu = qci->cu;
    *applyorigin = qci->applyorigin;
    ae_frame_leave(_state);
}


/*************************************************************************
Copies quadratic constraints structure, target structure is cleared,
previously allocated memory is ignored.
*************************************************************************/
void xqccopy(xquadraticconstraints* src,
     xquadraticconstraints* dst,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t m;
    xquadraticconstraint *c;
    ae_smart_ptr _c;
    xquadraticconstraint *c1;
    ae_smart_ptr _c1;

    ae_frame_make(_state, &_frame_block);
    memset(&_c, 0, sizeof(_c));
    memset(&_c1, 0, sizeof(_c1));
    ae_smart_ptr_init(&_c, (void**)&c, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_c1, (void**)&c1, ae_false, _state, ae_true);

    dst->n = src->n;
    m = ae_obj_array_get_length(&src->constraints);
    ae_obj_array_clear(&dst->constraints);
    for(i=0; i<=m-1; i++)
    {
        ae_obj_array_get(&src->constraints, i, &_c, _state);
        c1 = (xquadraticconstraint*)ae_malloc(sizeof(xquadraticconstraint), _state); /* note: using c1 as a temporary prior to assigning its value to _c1 */
        memset(c1, 0, sizeof(xquadraticconstraint));
        _xquadraticconstraint_init(c1, _state, ae_false);
        ae_smart_ptr_assign(&_c1, c1, ae_true, ae_true, (ae_int_t)sizeof(xquadraticconstraint), _xquadraticconstraint_init_copy, _xquadraticconstraint_destroy);
        c1->nvars = c->nvars;
        c1->cl = c->cl;
        c1->cu = c->cu;
        c1->applyorigin = c->applyorigin;
        if( c->nvars>0 )
        {
            icopyallocv(c->nvars, &c->varidx, &c1->varidx, _state);
            rcopyallocv(c->nvars, &c->b, &c1->b, _state);
            sparsecopybuf(&c->lowerq, &c1->lowerq, _state);
        }
        ae_obj_array_append_transfer(&dst->constraints, &_c1, _state);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Get constraints count
*************************************************************************/
ae_int_t xqcgetcount(const xquadraticconstraints* xqc, ae_state *_state)
{
    ae_int_t result;


    result = ae_obj_array_get_length(&xqc->constraints);
    return result;
}


void _xlinearconstraints_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    xlinearconstraints *p = (xlinearconstraints*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_init(&p->densec, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparsec, _state, make_automatic);
    ae_vector_init(&p->dcl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dcu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scu, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->cleic, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lcsrcidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->lcsrcmult, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->effsparsea, _state, make_automatic);
    ae_vector_init(&p->effal, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->effau, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->tmps, _state, make_automatic);
}


void _xlinearconstraints_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    xlinearconstraints       *dst = (xlinearconstraints*)_dst;
    const xlinearconstraints *src = (const xlinearconstraints*)_src;
    dst->n = src->n;
    dst->ndense = src->ndense;
    dst->nsparse = src->nsparse;
    ae_matrix_init_copy(&dst->densec, &src->densec, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsec, &src->sparsec, _state, make_automatic);
    ae_vector_init_copy(&dst->dcl, &src->dcl, _state, make_automatic);
    ae_vector_init_copy(&dst->dcu, &src->dcu, _state, make_automatic);
    ae_vector_init_copy(&dst->scl, &src->scl, _state, make_automatic);
    ae_vector_init_copy(&dst->scu, &src->scu, _state, make_automatic);
    dst->nec = src->nec;
    dst->nic = src->nic;
    ae_matrix_init_copy(&dst->cleic, &src->cleic, _state, make_automatic);
    ae_vector_init_copy(&dst->lcsrcidx, &src->lcsrcidx, _state, make_automatic);
    ae_vector_init_copy(&dst->lcsrcmult, &src->lcsrcmult, _state, make_automatic);
    _sparsematrix_init_copy(&dst->effsparsea, &src->effsparsea, _state, make_automatic);
    ae_vector_init_copy(&dst->effal, &src->effal, _state, make_automatic);
    ae_vector_init_copy(&dst->effau, &src->effau, _state, make_automatic);
    _sparsematrix_init_copy(&dst->tmps, &src->tmps, _state, make_automatic);
}


void _xlinearconstraints_clear(void* _p)
{
    xlinearconstraints *p = (xlinearconstraints*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_clear(&p->densec);
    _sparsematrix_clear(&p->sparsec);
    ae_vector_clear(&p->dcl);
    ae_vector_clear(&p->dcu);
    ae_vector_clear(&p->scl);
    ae_vector_clear(&p->scu);
    ae_matrix_clear(&p->cleic);
    ae_vector_clear(&p->lcsrcidx);
    ae_vector_clear(&p->lcsrcmult);
    _sparsematrix_clear(&p->effsparsea);
    ae_vector_clear(&p->effal);
    ae_vector_clear(&p->effau);
    _sparsematrix_clear(&p->tmps);
}


void _xlinearconstraints_destroy(void* _p)
{
    xlinearconstraints *p = (xlinearconstraints*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_destroy(&p->densec);
    _sparsematrix_destroy(&p->sparsec);
    ae_vector_destroy(&p->dcl);
    ae_vector_destroy(&p->dcu);
    ae_vector_destroy(&p->scl);
    ae_vector_destroy(&p->scu);
    ae_matrix_destroy(&p->cleic);
    ae_vector_destroy(&p->lcsrcidx);
    ae_vector_destroy(&p->lcsrcmult);
    _sparsematrix_destroy(&p->effsparsea);
    ae_vector_destroy(&p->effal);
    ae_vector_destroy(&p->effau);
    _sparsematrix_destroy(&p->tmps);
}


void _lptestproblem_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    lptestproblem *p = (lptestproblem*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->c, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->a, _state, make_automatic);
    ae_vector_init(&p->al, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->au, 0, DT_REAL, _state, make_automatic);
}


void _lptestproblem_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    lptestproblem       *dst = (lptestproblem*)_dst;
    const lptestproblem *src = (const lptestproblem*)_src;
    dst->n = src->n;
    dst->hasknowntarget = src->hasknowntarget;
    dst->targetf = src->targetf;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->c, &src->c, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    dst->m = src->m;
    _sparsematrix_init_copy(&dst->a, &src->a, _state, make_automatic);
    ae_vector_init_copy(&dst->al, &src->al, _state, make_automatic);
    ae_vector_init_copy(&dst->au, &src->au, _state, make_automatic);
}


void _lptestproblem_clear(void* _p)
{
    lptestproblem *p = (lptestproblem*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->c);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    _sparsematrix_clear(&p->a);
    ae_vector_clear(&p->al);
    ae_vector_clear(&p->au);
}


void _lptestproblem_destroy(void* _p)
{
    lptestproblem *p = (lptestproblem*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->c);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    _sparsematrix_destroy(&p->a);
    ae_vector_destroy(&p->al);
    ae_vector_destroy(&p->au);
}


#endif
#if defined(AE_COMPILE_OPTSERV) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
This subroutine checks violation of the box constraints. On output it sets
bcerr to the maximum scaled violation, bcidx to the index of the violating
constraint.

if bcerr=0 (say, if no constraints are violated) then bcidx=-1.

If nonunits=false then s[] is not referenced at all (assumed unit).

  -- ALGLIB --
     Copyright 7.11.2018 by Bochkanov Sergey
*************************************************************************/
void checkbcviolation(/* Boolean */ const ae_vector* hasbndl,
     /* Real    */ const ae_vector* bndl,
     /* Boolean */ const ae_vector* hasbndu,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* x,
     ae_int_t n,
     /* Real    */ const ae_vector* s,
     ae_bool nonunits,
     double* bcerr,
     ae_int_t* bcidx,
     ae_state *_state)
{
    ae_int_t i;
    double vs;
    double ve;

    *bcerr = 0.0;
    *bcidx = 0;

    *bcerr = (double)(0);
    *bcidx = -1;
    for(i=0; i<=n-1; i++)
    {
        
        /*
         * Fetch scale
         */
        if( nonunits )
        {
            vs = (double)1/s->ptr.p_double[i];
        }
        else
        {
            vs = (double)(1);
        }
        
        /*
         * Check lower bound
         */
        if( hasbndl->ptr.p_bool[i]&&x->ptr.p_double[i]<bndl->ptr.p_double[i] )
        {
            ve = (bndl->ptr.p_double[i]-x->ptr.p_double[i])*vs;
            if( ve>*bcerr )
            {
                *bcerr = ve;
                *bcidx = i;
            }
        }
        
        /*
         * Check upper bound
         */
        if( hasbndu->ptr.p_bool[i]&&x->ptr.p_double[i]>bndu->ptr.p_double[i] )
        {
            ve = (x->ptr.p_double[i]-bndu->ptr.p_double[i])*vs;
            if( ve>*bcerr )
            {
                *bcerr = ve;
                *bcidx = i;
            }
        }
    }
}


/*************************************************************************
This subroutine checks violation of the general linear constraints.

Constraints are assumed to be un-normalized and stored in the format "NEC
equality ones followed by NIC inequality ones".

On output it sets lcerr to the maximum scaled violation, lcidx to the source
index of the most violating constraint (row indexes of CLEIC are mapped to
the indexes of the "original" constraints via LCSrcIdx[] array.

if lcerr=0 (say, if no constraints are violated) then lcidx=-1.

  -- ALGLIB --
     Copyright 07.11.2018 by Bochkanov Sergey
*************************************************************************/
void checklcviolation(/* Real    */ const ae_matrix* cleic,
     /* Integer */ const ae_vector* lcsrcidx,
     ae_int_t nec,
     ae_int_t nic,
     /* Real    */ const ae_vector* x,
     ae_int_t n,
     double* lcerr,
     ae_int_t* lcidx,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double cx;
    double cnrm;
    double v;

    *lcerr = 0.0;
    *lcidx = 0;

    *lcerr = (double)(0);
    *lcidx = -1;
    for(i=0; i<=nec+nic-1; i++)
    {
        cx = -cleic->ptr.pp_double[i][n];
        cnrm = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            v = cleic->ptr.pp_double[i][j];
            cx = cx+v*x->ptr.p_double[j];
            cnrm = cnrm+v*v;
        }
        cnrm = ae_sqrt(cnrm, _state);
        cx = cx/coalesce(cnrm, (double)(1), _state);
        if( i<nec )
        {
            cx = ae_fabs(cx, _state);
        }
        else
        {
            cx = ae_maxreal(cx, (double)(0), _state);
        }
        if( cx>*lcerr )
        {
            *lcerr = cx;
            *lcidx = lcsrcidx->ptr.p_int[i];
        }
    }
}


/*************************************************************************
This subroutine checks violation of the general linear constraints.

Constraints are assumed to be un-normalized and having the following format:

    L <= A*x <= U, with some of L/U being infinite

On output it sets lcerr to the maximum scaled violation, lcidx to the source
index of the most violating constraint (row indexes of A are mapped to the
indexes of the "original" constraints via LCSrcIdx[] array).

if lcerr=0 (say, if no constraints are violated) then lcidx=-1.

  -- ALGLIB --
     Copyright 07.11.2018 by Bochkanov Sergey
*************************************************************************/
void checklc2violation(const sparsematrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     /* Integer */ const ae_vector* lcsrcidx,
     ae_int_t cntlc,
     /* Real    */ const ae_vector* x,
     double* lcerr,
     ae_int_t* lcidx,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    ae_int_t j1;
    double cx;
    double cnrm;
    double v;

    *lcerr = 0.0;
    *lcidx = 0;

    *lcerr = (double)(0);
    *lcidx = -1;
    if( cntlc==0 )
    {
        return;
    }
    ae_assert(a->matrixtype==1, "CheckLC2Violation: non-CRS input detected", _state);
    for(i=0; i<=a->m-1; i++)
    {
        cx = 0.0;
        cnrm = (double)(0);
        j0 = a->ridx.ptr.p_int[i];
        j1 = a->ridx.ptr.p_int[i+1]-1;
        for(j=j0; j<=j1; j++)
        {
            v = a->vals.ptr.p_double[j];
            cx = cx+v*x->ptr.p_double[a->idx.ptr.p_int[j]];
            cnrm = cnrm+v*v;
        }
        cnrm = coalesce(ae_sqrt(cnrm, _state), (double)(1), _state);
        if( ae_isfinite(al->ptr.p_double[i], _state)&&(al->ptr.p_double[i]-cx)/cnrm>*lcerr )
        {
            *lcerr = (al->ptr.p_double[i]-cx)/cnrm;
            *lcidx = lcsrcidx->ptr.p_int[i];
        }
        if( ae_isfinite(au->ptr.p_double[i], _state)&&(cx-au->ptr.p_double[i])/cnrm>*lcerr )
        {
            *lcerr = (cx-au->ptr.p_double[i])/cnrm;
            *lcidx = lcsrcidx->ptr.p_int[i];
        }
    }
}


/*************************************************************************
This subroutine checks violation of the general linear constraints, computed
after on-the-fly rescaling according to variable scales vector S.

Constraints are assumed to be un-normalized and having the following format:

    L <= A*x <= U, with some of L/U being infinite
    
The function internally replaces them by

    L <= (A*S)*(x/S) <= U
    
and divides each row #i by the norm of A[i]*S before computing violation.

On output it sets lcerr to the maximum scaled violation, lcidx to the source
index of the most violating constraint (row indexes of A are mapped to the
indexes of the "original" constraints via LCSrcIdx[] array).

if lcerr=0 (say, if no constraints are violated) then lcidx=-1.

  -- ALGLIB --
     Copyright 07.11.2018 by Bochkanov Sergey
*************************************************************************/
void unscaleandchecklc2violation(/* Real    */ const ae_vector* s,
     const sparsematrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     /* Integer */ const ae_vector* lcsrcidx,
     ae_int_t cntlc,
     /* Real    */ const ae_vector* x,
     double* sumerr,
     double* maxerr,
     ae_int_t* maxidx,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t j0;
    ae_int_t j1;
    double cx;
    double cnrm;
    double v;
    double vv;

    *sumerr = 0.0;
    *maxerr = 0.0;
    *maxidx = 0;

    *sumerr = (double)(0);
    *maxerr = (double)(0);
    *maxidx = -1;
    if( cntlc==0 )
    {
        return;
    }
    ae_assert(a->matrixtype==1, "CheckLC2Violation: non-CRS input detected", _state);
    for(i=0; i<=a->m-1; i++)
    {
        cx = 0.0;
        cnrm = (double)(0);
        j0 = a->ridx.ptr.p_int[i];
        j1 = a->ridx.ptr.p_int[i+1]-1;
        for(j=j0; j<=j1; j++)
        {
            v = a->vals.ptr.p_double[j];
            k = a->idx.ptr.p_int[j];
            cx = cx+v*x->ptr.p_double[k];
            vv = v*s->ptr.p_double[k];
            cnrm = cnrm+vv*vv;
        }
        cnrm = coalesce(ae_sqrt(cnrm, _state), (double)(1), _state);
        if( ae_isfinite(al->ptr.p_double[i], _state) )
        {
            v = ae_maxreal((al->ptr.p_double[i]-cx)/cnrm, 0.0, _state);
            *sumerr = *sumerr+v;
            if( v>*maxerr )
            {
                *maxerr = v;
                *maxidx = lcsrcidx->ptr.p_int[i];
            }
        }
        if( ae_isfinite(au->ptr.p_double[i], _state) )
        {
            v = ae_maxreal((cx-au->ptr.p_double[i])/cnrm, 0.0, _state);
            *sumerr = *sumerr+v;
            if( v>*maxerr )
            {
                *maxerr = v;
                *maxidx = lcsrcidx->ptr.p_int[i];
            }
        }
    }
}


/*************************************************************************
This subroutine checks violation of the nonlinear constraints. Fi[0] is the
target value (ignored), Fi[1:NG+NH] are values of nonlinear constraints.

On output it sets nlcerr to the scaled violation, nlcidx to the index
of the most violating constraint in [0,NG+NH-1] range.

if nlcerr=0 (say, if no constraints are violated) then nlcidx=-1.

If nonunits=false then s[] is not referenced at all (assumed unit).

  -- ALGLIB --
     Copyright 7.11.2018 by Bochkanov Sergey
*************************************************************************/
void checknlcviolation(/* Real    */ const ae_vector* fi,
     ae_int_t ng,
     ae_int_t nh,
     double* nlcerr,
     ae_int_t* nlcidx,
     ae_state *_state)
{
    ae_int_t i;
    double v;

    *nlcerr = 0.0;
    *nlcidx = 0;

    *nlcerr = (double)(0);
    *nlcidx = -1;
    for(i=0; i<=ng+nh-1; i++)
    {
        v = fi->ptr.p_double[i+1];
        if( i<ng )
        {
            v = ae_fabs(v, _state);
        }
        else
        {
            v = ae_maxreal(v, (double)(0), _state);
        }
        if( v>*nlcerr )
        {
            *nlcerr = v;
            *nlcidx = i;
        }
    }
}


/*************************************************************************
This subroutine is same as CheckNLCViolation, but  is  works  with  scaled
constraints: it assumes that Fi[] were divided by FScales[] vector  BEFORE
passing them to this function.

The function checks scaled values, but reports unscaled errors.

  -- ALGLIB --
     Copyright 7.11.2018 by Bochkanov Sergey
*************************************************************************/
void unscaleandchecknlcviolation(/* Real    */ const ae_vector* fi,
     /* Real    */ const ae_vector* fscales,
     ae_int_t ng,
     ae_int_t nh,
     double* nlcerr,
     ae_int_t* nlcidx,
     ae_state *_state)
{
    ae_int_t i;
    double v;

    *nlcerr = 0.0;
    *nlcidx = 0;

    *nlcerr = (double)(0);
    *nlcidx = -1;
    for(i=0; i<=ng+nh-1; i++)
    {
        ae_assert(ae_fp_greater(fscales->ptr.p_double[i+1],(double)(0)), "UnscaleAndCheckNLCViolation: integrity check failed", _state);
        v = fi->ptr.p_double[i+1]*fscales->ptr.p_double[i+1];
        if( i<ng )
        {
            v = ae_fabs(v, _state);
        }
        else
        {
            v = ae_maxreal(v, (double)(0), _state);
        }
        if( v>*nlcerr )
        {
            *nlcerr = v;
            *nlcidx = i;
        }
    }
}


/*************************************************************************
This subroutine is same as CheckNLCViolation, but  is  works  with  scaled
constraints: it assumes that Fi[] were divided by FScales[] vector  BEFORE
passing them to this function.

The function checks scaled values, but reports unscaled errors.

  -- ALGLIB --
     Copyright 7.11.2018 by Bochkanov Sergey
*************************************************************************/
void unscaleandchecknlc2violation(/* Real    */ const ae_vector* fi,
     /* Real    */ const ae_vector* fscales,
     /* Real    */ const ae_vector* rawnl,
     /* Real    */ const ae_vector* rawnu,
     ae_int_t cntnlc,
     double* nlcerr,
     ae_int_t* nlcidx,
     ae_state *_state)
{
    ae_int_t i;
    double v;
    double err;

    *nlcerr = 0.0;
    *nlcidx = 0;

    *nlcerr = (double)(0);
    *nlcidx = -1;
    for(i=0; i<=cntnlc-1; i++)
    {
        ae_assert(ae_fp_greater(fscales->ptr.p_double[i+1],(double)(0)), "UnscaleAndCheckNLC2Violation: integrity check failed", _state);
        v = fi->ptr.p_double[i+1]*fscales->ptr.p_double[i+1];
        err = (double)(0);
        if( ae_isfinite(rawnl->ptr.p_double[i], _state) )
        {
            err = ae_maxreal(err, rawnl->ptr.p_double[i]-v, _state);
        }
        if( ae_isfinite(rawnu->ptr.p_double[i], _state) )
        {
            err = ae_maxreal(err, v-rawnu->ptr.p_double[i], _state);
        }
        if( err>*nlcerr )
        {
            *nlcerr = err;
            *nlcidx = i;
        }
    }
}


/*************************************************************************
Check violation of two-sided constraints

  -- ALGLIB --
     Copyright 7.11.2018 by Bochkanov Sergey
*************************************************************************/
void checknlc2violation(/* Real    */ const ae_vector* fi,
     /* Real    */ const ae_vector* rawnl,
     /* Real    */ const ae_vector* rawnu,
     ae_int_t cntnlc,
     double* nlcerr,
     ae_int_t* nlcidx,
     ae_state *_state)
{
    ae_int_t i;
    double v;
    double err;

    *nlcerr = 0.0;
    *nlcidx = 0;

    *nlcerr = (double)(0);
    *nlcidx = -1;
    for(i=0; i<=cntnlc-1; i++)
    {
        v = fi->ptr.p_double[i+1];
        err = (double)(0);
        if( ae_isfinite(rawnl->ptr.p_double[i], _state) )
        {
            err = ae_maxreal(err, rawnl->ptr.p_double[i]-v, _state);
        }
        if( ae_isfinite(rawnu->ptr.p_double[i], _state) )
        {
            err = ae_maxreal(err, v-rawnu->ptr.p_double[i], _state);
        }
        if( err>*nlcerr )
        {
            *nlcerr = err;
            *nlcidx = i;
        }
    }
}


/*************************************************************************
This subroutine is used to prepare threshold value which will be used for
trimming of the target function (see comments on TrimFunction() for more
information).

This function accepts only one parameter: function value at the starting
point. It returns threshold which will be used for trimming.

  -- ALGLIB --
     Copyright 10.05.2011 by Bochkanov Sergey
*************************************************************************/
void trimprepare(double f, double* threshold, ae_state *_state)
{

    *threshold = 0.0;

    *threshold = (double)10*(ae_fabs(f, _state)+(double)1);
}


/*************************************************************************
This subroutine is used to "trim" target function, i.e. to do following
transformation:

                   { {F,G}          if F<Threshold
    {F_tr, G_tr} = {
                   { {Threshold, 0} if F>=Threshold
                   
Such transformation allows us to  solve  problems  with  singularities  by
redefining function in such way that it becomes bounded from above.

  -- ALGLIB --
     Copyright 10.05.2011 by Bochkanov Sergey
*************************************************************************/
void trimfunction(double* f,
     /* Real    */ ae_vector* g,
     ae_int_t n,
     double threshold,
     ae_state *_state)
{
    ae_int_t i;


    if( ae_fp_greater_eq(*f,threshold) )
    {
        *f = threshold;
        for(i=0; i<=n-1; i++)
        {
            g->ptr.p_double[i] = 0.0;
        }
    }
}


/*************************************************************************
This function enforces boundary constraints in the X.

This function correctly (although a bit inefficient) handles BL[i] which
are -INF and BU[i] which are +INF.

We have NMain+NSlack  dimensional  X,  with first NMain components bounded
by BL/BU, and next NSlack ones bounded by non-negativity constraints.

INPUT PARAMETERS
    X       -   array[NMain+NSlack], point
    BL      -   array[NMain], lower bounds
                (may contain -INF, when bound is not present)
    HaveBL  -   array[NMain], if HaveBL[i] is False,
                then i-th bound is not present
    BU      -   array[NMain], upper bounds
                (may contain +INF, when bound is not present)
    HaveBU  -   array[NMain], if HaveBU[i] is False,
                then i-th bound is not present

OUTPUT PARAMETERS
    X       -   X with all constraints being enforced

It returns True when constraints are consistent,
False - when constraints are inconsistent.

  -- ALGLIB --
     Copyright 10.01.2012 by Bochkanov Sergey
*************************************************************************/
ae_bool enforceboundaryconstraints(/* Real    */ ae_vector* x,
     /* Real    */ const ae_vector* bl,
     /* Boolean */ const ae_vector* havebl,
     /* Real    */ const ae_vector* bu,
     /* Boolean */ const ae_vector* havebu,
     ae_int_t nmain,
     ae_int_t nslack,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool result;


    result = ae_false;
    for(i=0; i<=nmain-1; i++)
    {
        if( (havebl->ptr.p_bool[i]&&havebu->ptr.p_bool[i])&&ae_fp_greater(bl->ptr.p_double[i],bu->ptr.p_double[i]) )
        {
            return result;
        }
        if( havebl->ptr.p_bool[i]&&ae_fp_less(x->ptr.p_double[i],bl->ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = bl->ptr.p_double[i];
        }
        if( havebu->ptr.p_bool[i]&&ae_fp_greater(x->ptr.p_double[i],bu->ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = bu->ptr.p_double[i];
        }
    }
    for(i=0; i<=nslack-1; i++)
    {
        if( ae_fp_less(x->ptr.p_double[nmain+i],(double)(0)) )
        {
            x->ptr.p_double[nmain+i] = (double)(0);
        }
    }
    result = ae_true;
    return result;
}


/*************************************************************************
This function projects gradient into feasible area of boundary constrained
optimization  problem.  X  can  be  infeasible  with  respect  to boundary
constraints.  We  have  NMain+NSlack  dimensional  X,   with  first  NMain 
components bounded by BL/BU, and next NSlack ones bounded by non-negativity
constraints.

INPUT PARAMETERS
    X       -   array[NMain+NSlack], point
    G       -   array[NMain+NSlack], gradient
    BL      -   lower bounds (may contain -INF, when bound is not present)
    HaveBL  -   if HaveBL[i] is False, then i-th bound is not present
    BU      -   upper bounds (may contain +INF, when bound is not present)
    HaveBU  -   if HaveBU[i] is False, then i-th bound is not present

OUTPUT PARAMETERS
    G       -   projection of G. Components of G which satisfy one of the
                following
                    (1) (X[I]<=BndL[I]) and (G[I]>0), OR
                    (2) (X[I]>=BndU[I]) and (G[I]<0)
                are replaced by zeros.

NOTE 1: this function assumes that constraints are feasible. It throws
exception otherwise.

NOTE 2: in fact, projection of ANTI-gradient is calculated,  because  this
function trims components of -G which points outside of the feasible area.
However, working with -G is considered confusing, because all optimization
source work with G.

  -- ALGLIB --
     Copyright 10.01.2012 by Bochkanov Sergey
*************************************************************************/
void projectgradientintobc(/* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* g,
     /* Real    */ const ae_vector* bl,
     /* Boolean */ const ae_vector* havebl,
     /* Real    */ const ae_vector* bu,
     /* Boolean */ const ae_vector* havebu,
     ae_int_t nmain,
     ae_int_t nslack,
     ae_state *_state)
{
    ae_int_t i;


    for(i=0; i<=nmain-1; i++)
    {
        ae_assert((!havebl->ptr.p_bool[i]||!havebu->ptr.p_bool[i])||ae_fp_less_eq(bl->ptr.p_double[i],bu->ptr.p_double[i]), "ProjectGradientIntoBC: internal error (infeasible constraints)", _state);
        if( (havebl->ptr.p_bool[i]&&ae_fp_less_eq(x->ptr.p_double[i],bl->ptr.p_double[i]))&&ae_fp_greater(g->ptr.p_double[i],(double)(0)) )
        {
            g->ptr.p_double[i] = (double)(0);
        }
        if( (havebu->ptr.p_bool[i]&&ae_fp_greater_eq(x->ptr.p_double[i],bu->ptr.p_double[i]))&&ae_fp_less(g->ptr.p_double[i],(double)(0)) )
        {
            g->ptr.p_double[i] = (double)(0);
        }
    }
    for(i=0; i<=nslack-1; i++)
    {
        if( ae_fp_less_eq(x->ptr.p_double[nmain+i],(double)(0))&&ae_fp_greater(g->ptr.p_double[nmain+i],(double)(0)) )
        {
            g->ptr.p_double[nmain+i] = (double)(0);
        }
    }
}


/*************************************************************************
Given
    a) initial point X0[NMain+NSlack]
       (feasible with respect to bound constraints)
    b) step vector alpha*D[NMain+NSlack]
    c) boundary constraints BndL[NMain], BndU[NMain]
    d) implicit non-negativity constraints for slack variables
this  function  calculates  bound  on  the step length subject to boundary
constraints.

It returns:
    *  MaxStepLen - such step length that X0+MaxStepLen*alpha*D is exactly
       at the boundary given by constraints
    *  VariableToFreeze - index of the constraint to be activated,
       0 <= VariableToFreeze < NMain+NSlack
    *  ValueToFreeze - value of the corresponding constraint.

Notes:
    * it is possible that several constraints can be activated by the step
      at once. In such cases only one constraint is returned. It is caller
      responsibility to check other constraints. This function makes  sure
      that we activate at least one constraint, and everything else is the
      responsibility of the caller.
    * steps smaller than MaxStepLen still can activate constraints due  to
      numerical errors. Thus purpose of this  function  is  not  to  guard 
      against accidental activation of the constraints - quite the reverse, 
      its purpose is to activate at least constraint upon performing  step
      which is too long.
    * in case there is no constraints to activate, we return negative
      VariableToFreeze and zero MaxStepLen and ValueToFreeze.
    * this function assumes that constraints are consistent; it throws
      exception otherwise.

INPUT PARAMETERS
    X           -   array[NMain+NSlack], point. Must be feasible with respect 
                    to bound constraints (exception will be thrown otherwise)
    D           -   array[NMain+NSlack], step direction
    alpha       -   scalar multiplier before D, alpha<>0
    BndL        -   lower bounds, array[NMain]
                    (may contain -INF, when bound is not present)
    HaveBndL    -   array[NMain], if HaveBndL[i] is False,
                    then i-th bound is not present
    BndU        -   array[NMain], upper bounds
                    (may contain +INF, when bound is not present)
    HaveBndU    -   array[NMain], if HaveBndU[i] is False,
                    then i-th bound is not present
    NMain       -   number of main variables
    NSlack      -   number of slack variables
    
OUTPUT PARAMETERS
    VariableToFreeze:
                    * negative value     = step is unbounded, ValueToFreeze=0,
                                           MaxStepLen=0.
                    * non-negative value = at least one constraint, given by
                                           this parameter, will  be  activated
                                           upon performing maximum step.
    ValueToFreeze-  value of the variable which will be constrained
    MaxStepLen  -   maximum length of the step. Can be zero when step vector
                    looks outside of the feasible area.

  -- ALGLIB --
     Copyright 10.01.2012 by Bochkanov Sergey
*************************************************************************/
void calculatestepbound(/* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* d,
     double alpha,
     /* Real    */ const ae_vector* bndl,
     /* Boolean */ const ae_vector* havebndl,
     /* Real    */ const ae_vector* bndu,
     /* Boolean */ const ae_vector* havebndu,
     ae_int_t nmain,
     ae_int_t nslack,
     ae_int_t* variabletofreeze,
     double* valuetofreeze,
     double* maxsteplen,
     ae_state *_state)
{
    ae_int_t i;
    double prevmax;
    double initval;

    *variabletofreeze = 0;
    *valuetofreeze = 0.0;
    *maxsteplen = 0.0;

    ae_assert(ae_fp_neq(alpha,(double)(0)), "CalculateStepBound: zero alpha", _state);
    *variabletofreeze = -1;
    initval = ae_maxrealnumber;
    *maxsteplen = initval;
    for(i=0; i<=nmain-1; i++)
    {
        if( havebndl->ptr.p_bool[i]&&ae_fp_less(alpha*d->ptr.p_double[i],(double)(0)) )
        {
            ae_assert(ae_fp_greater_eq(x->ptr.p_double[i],bndl->ptr.p_double[i]), "CalculateStepBound: infeasible X", _state);
            prevmax = *maxsteplen;
            *maxsteplen = safeminposrv(x->ptr.p_double[i]-bndl->ptr.p_double[i], -alpha*d->ptr.p_double[i], *maxsteplen, _state);
            if( ae_fp_less(*maxsteplen,prevmax) )
            {
                *variabletofreeze = i;
                *valuetofreeze = bndl->ptr.p_double[i];
            }
        }
        if( havebndu->ptr.p_bool[i]&&ae_fp_greater(alpha*d->ptr.p_double[i],(double)(0)) )
        {
            ae_assert(ae_fp_less_eq(x->ptr.p_double[i],bndu->ptr.p_double[i]), "CalculateStepBound: infeasible X", _state);
            prevmax = *maxsteplen;
            *maxsteplen = safeminposrv(bndu->ptr.p_double[i]-x->ptr.p_double[i], alpha*d->ptr.p_double[i], *maxsteplen, _state);
            if( ae_fp_less(*maxsteplen,prevmax) )
            {
                *variabletofreeze = i;
                *valuetofreeze = bndu->ptr.p_double[i];
            }
        }
    }
    for(i=0; i<=nslack-1; i++)
    {
        if( ae_fp_less(alpha*d->ptr.p_double[nmain+i],(double)(0)) )
        {
            ae_assert(ae_fp_greater_eq(x->ptr.p_double[nmain+i],(double)(0)), "CalculateStepBound: infeasible X", _state);
            prevmax = *maxsteplen;
            *maxsteplen = safeminposrv(x->ptr.p_double[nmain+i], -alpha*d->ptr.p_double[nmain+i], *maxsteplen, _state);
            if( ae_fp_less(*maxsteplen,prevmax) )
            {
                *variabletofreeze = nmain+i;
                *valuetofreeze = (double)(0);
            }
        }
    }
    if( ae_fp_eq(*maxsteplen,initval) )
    {
        *valuetofreeze = (double)(0);
        *maxsteplen = (double)(0);
    }
}


/*************************************************************************
This function postprocesses bounded step by:
* analysing step length (whether it is equal to MaxStepLen) and activating 
  constraint given by VariableToFreeze if needed
* checking for additional bound constraints to activate

This function uses final point of the step, quantities calculated  by  the
CalculateStepBound()  function.  As  result,  it  returns  point  which is 
exactly feasible with respect to boundary constraints.

NOTE 1: this function does NOT handle and check linear equality constraints
NOTE 2: when StepTaken=MaxStepLen we always activate at least one constraint

INPUT PARAMETERS
    X           -   array[NMain+NSlack], final point to postprocess
    XPrev       -   array[NMain+NSlack], initial point
    BndL        -   lower bounds, array[NMain]
                    (may contain -INF, when bound is not present)
    HaveBndL    -   array[NMain], if HaveBndL[i] is False,
                    then i-th bound is not present
    BndU        -   array[NMain], upper bounds
                    (may contain +INF, when bound is not present)
    HaveBndU    -   array[NMain], if HaveBndU[i] is False,
                    then i-th bound is not present
    NMain       -   number of main variables
    NSlack      -   number of slack variables
    VariableToFreeze-result of CalculateStepBound()
    ValueToFreeze-  result of CalculateStepBound()
    StepTaken   -   actual step length (actual step is equal to the possibly 
                    non-unit step direction vector times this parameter).
                    StepTaken<=MaxStepLen.
    MaxStepLen  -   result of CalculateStepBound()
    
OUTPUT PARAMETERS
    X           -   point bounded with respect to constraints.
                    components corresponding to active constraints are exactly
                    equal to the boundary values.
                    
RESULT:
    number of constraints activated in addition to previously active ones.
    Constraints which were DEACTIVATED are ignored (do not influence
    function value).

  -- ALGLIB --
     Copyright 10.01.2012 by Bochkanov Sergey
*************************************************************************/
ae_int_t postprocessboundedstep(/* Real    */ ae_vector* x,
     /* Real    */ const ae_vector* xprev,
     /* Real    */ const ae_vector* bndl,
     /* Boolean */ const ae_vector* havebndl,
     /* Real    */ const ae_vector* bndu,
     /* Boolean */ const ae_vector* havebndu,
     ae_int_t nmain,
     ae_int_t nslack,
     ae_int_t variabletofreeze,
     double valuetofreeze,
     double steptaken,
     double maxsteplen,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool wasactivated;
    ae_int_t result;


    ae_assert(variabletofreeze<0||ae_fp_less_eq(steptaken,maxsteplen), "Assertion failed", _state);
    
    /*
     * Activate constraints
     */
    if( variabletofreeze>=0&&ae_fp_eq(steptaken,maxsteplen) )
    {
        x->ptr.p_double[variabletofreeze] = valuetofreeze;
    }
    for(i=0; i<=nmain-1; i++)
    {
        if( havebndl->ptr.p_bool[i]&&ae_fp_less(x->ptr.p_double[i],bndl->ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = bndl->ptr.p_double[i];
        }
        if( havebndu->ptr.p_bool[i]&&ae_fp_greater(x->ptr.p_double[i],bndu->ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = bndu->ptr.p_double[i];
        }
    }
    for(i=0; i<=nslack-1; i++)
    {
        if( ae_fp_less_eq(x->ptr.p_double[nmain+i],(double)(0)) )
        {
            x->ptr.p_double[nmain+i] = (double)(0);
        }
    }
    
    /*
     * Calculate number of constraints being activated
     */
    result = 0;
    for(i=0; i<=nmain-1; i++)
    {
        wasactivated = ae_fp_neq(x->ptr.p_double[i],xprev->ptr.p_double[i])&&((havebndl->ptr.p_bool[i]&&ae_fp_eq(x->ptr.p_double[i],bndl->ptr.p_double[i]))||(havebndu->ptr.p_bool[i]&&ae_fp_eq(x->ptr.p_double[i],bndu->ptr.p_double[i])));
        wasactivated = wasactivated||variabletofreeze==i;
        if( wasactivated )
        {
            result = result+1;
        }
    }
    for(i=0; i<=nslack-1; i++)
    {
        wasactivated = ae_fp_neq(x->ptr.p_double[nmain+i],xprev->ptr.p_double[nmain+i])&&ae_fp_eq(x->ptr.p_double[nmain+i],0.0);
        wasactivated = wasactivated||variabletofreeze==nmain+i;
        if( wasactivated )
        {
            result = result+1;
        }
    }
    return result;
}


/*************************************************************************
The  purpose  of  this  function is to prevent algorithm from "unsticking" 
from  the  active  bound  constraints  because  of  numerical noise in the
gradient or Hessian.

It is done by zeroing some components of the search direction D.  D[i]  is
zeroed when both (a) and (b) are true:
a) corresponding X[i] is exactly at the boundary
b) |D[i]*S[i]| <= DropTol*Sqrt(SUM(D[i]^2*S[I]^2))

D  can  be  step  direction , antigradient, gradient, or anything similar. 
Sign of D does not matter, nor matters step length.

NOTE 1: boundary constraints are expected to be consistent, as well as X
        is expected to be feasible. Exception will be thrown otherwise.

INPUT PARAMETERS
    D           -   array[NMain+NSlack], direction
    X           -   array[NMain+NSlack], current point
    BndL        -   lower bounds, array[NMain]
                    (may contain -INF, when bound is not present)
    HaveBndL    -   array[NMain], if HaveBndL[i] is False,
                    then i-th bound is not present
    BndU        -   array[NMain], upper bounds
                    (may contain +INF, when bound is not present)
    HaveBndU    -   array[NMain], if HaveBndU[i] is False,
                    then i-th bound is not present
    S           -   array[NMain+NSlack], scaling of the variables
    NMain       -   number of main variables
    NSlack      -   number of slack variables
    DropTol     -   drop tolerance, >=0
    
OUTPUT PARAMETERS
    X           -   point bounded with respect to constraints.
                    components corresponding to active constraints are exactly
                    equal to the boundary values.

  -- ALGLIB --
     Copyright 10.01.2012 by Bochkanov Sergey
*************************************************************************/
void filterdirection(/* Real    */ ae_vector* d,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* bndl,
     /* Boolean */ const ae_vector* havebndl,
     /* Real    */ const ae_vector* bndu,
     /* Boolean */ const ae_vector* havebndu,
     /* Real    */ const ae_vector* s,
     ae_int_t nmain,
     ae_int_t nslack,
     double droptol,
     ae_state *_state)
{
    ae_int_t i;
    double scalednorm;
    ae_bool isactive;


    scalednorm = 0.0;
    for(i=0; i<=nmain+nslack-1; i++)
    {
        scalednorm = scalednorm+ae_sqr(d->ptr.p_double[i]*s->ptr.p_double[i], _state);
    }
    scalednorm = ae_sqrt(scalednorm, _state);
    for(i=0; i<=nmain-1; i++)
    {
        ae_assert(!havebndl->ptr.p_bool[i]||ae_fp_greater_eq(x->ptr.p_double[i],bndl->ptr.p_double[i]), "FilterDirection: infeasible point", _state);
        ae_assert(!havebndu->ptr.p_bool[i]||ae_fp_less_eq(x->ptr.p_double[i],bndu->ptr.p_double[i]), "FilterDirection: infeasible point", _state);
        isactive = (havebndl->ptr.p_bool[i]&&ae_fp_eq(x->ptr.p_double[i],bndl->ptr.p_double[i]))||(havebndu->ptr.p_bool[i]&&ae_fp_eq(x->ptr.p_double[i],bndu->ptr.p_double[i]));
        if( isactive&&ae_fp_less_eq(ae_fabs(d->ptr.p_double[i]*s->ptr.p_double[i], _state),droptol*scalednorm) )
        {
            d->ptr.p_double[i] = 0.0;
        }
    }
    for(i=0; i<=nslack-1; i++)
    {
        ae_assert(ae_fp_greater_eq(x->ptr.p_double[nmain+i],(double)(0)), "FilterDirection: infeasible point", _state);
        if( ae_fp_eq(x->ptr.p_double[nmain+i],(double)(0))&&ae_fp_less_eq(ae_fabs(d->ptr.p_double[nmain+i]*s->ptr.p_double[nmain+i], _state),droptol*scalednorm) )
        {
            d->ptr.p_double[nmain+i] = 0.0;
        }
    }
}


/*************************************************************************
This function returns number of bound constraints whose state was  changed
(either activated or deactivated) when making step from XPrev to X.

Constraints are considered:
* active - when we are exactly at the boundary
* inactive - when we are not at the boundary

You should note that antigradient direction is NOT taken into account when
we make decions on the constraint status.

INPUT PARAMETERS
    X           -   array[NMain+NSlack], final point.
                    Must be feasible with respect to bound constraints.
    XPrev       -   array[NMain+NSlack], initial point.
                    Must be feasible with respect to bound constraints.
    BndL        -   lower bounds, array[NMain]
                    (may contain -INF, when bound is not present)
    HaveBndL    -   array[NMain], if HaveBndL[i] is False,
                    then i-th bound is not present
    BndU        -   array[NMain], upper bounds
                    (may contain +INF, when bound is not present)
    HaveBndU    -   array[NMain], if HaveBndU[i] is False,
                    then i-th bound is not present
    NMain       -   number of main variables
    NSlack      -   number of slack variables
    
RESULT:
    number of constraints whose state was changed.

  -- ALGLIB --
     Copyright 10.01.2012 by Bochkanov Sergey
*************************************************************************/
ae_int_t numberofchangedconstraints(/* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* xprev,
     /* Real    */ const ae_vector* bndl,
     /* Boolean */ const ae_vector* havebndl,
     /* Real    */ const ae_vector* bndu,
     /* Boolean */ const ae_vector* havebndu,
     ae_int_t nmain,
     ae_int_t nslack,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool statuschanged;
    ae_int_t result;


    result = 0;
    for(i=0; i<=nmain-1; i++)
    {
        if( ae_fp_neq(x->ptr.p_double[i],xprev->ptr.p_double[i]) )
        {
            statuschanged = ae_false;
            if( havebndl->ptr.p_bool[i]&&(ae_fp_eq(x->ptr.p_double[i],bndl->ptr.p_double[i])||ae_fp_eq(xprev->ptr.p_double[i],bndl->ptr.p_double[i])) )
            {
                statuschanged = ae_true;
            }
            if( havebndu->ptr.p_bool[i]&&(ae_fp_eq(x->ptr.p_double[i],bndu->ptr.p_double[i])||ae_fp_eq(xprev->ptr.p_double[i],bndu->ptr.p_double[i])) )
            {
                statuschanged = ae_true;
            }
            if( statuschanged )
            {
                result = result+1;
            }
        }
    }
    for(i=0; i<=nslack-1; i++)
    {
        if( ae_fp_neq(x->ptr.p_double[nmain+i],xprev->ptr.p_double[nmain+i])&&(ae_fp_eq(x->ptr.p_double[nmain+i],(double)(0))||ae_fp_eq(xprev->ptr.p_double[nmain+i],(double)(0))) )
        {
            result = result+1;
        }
    }
    return result;
}


/*************************************************************************
This function finds feasible point of  (NMain+NSlack)-dimensional  problem
subject to NMain explicit boundary constraints (some  constraints  can  be
omitted), NSlack implicit non-negativity constraints,  K  linear  equality
constraints.

INPUT PARAMETERS
    X           -   array[NMain+NSlack], initial point.
    BndL        -   lower bounds, array[NMain]
                    (may contain -INF, when bound is not present)
    HaveBndL    -   array[NMain], if HaveBndL[i] is False,
                    then i-th bound is not present
    BndU        -   array[NMain], upper bounds
                    (may contain +INF, when bound is not present)
    HaveBndU    -   array[NMain], if HaveBndU[i] is False,
                    then i-th bound is not present
    NMain       -   number of main variables
    NSlack      -   number of slack variables
    CE          -   array[K,NMain+NSlack+1], equality  constraints CE*x=b.
                    Rows contain constraints, first  NMain+NSlack  columns
                    contain coefficients before X[], last  column  contain
                    right part.
    K           -   number of linear constraints
    EpsI        -   infeasibility (error in the right part) allowed in the
                    solution

OUTPUT PARAMETERS:
    X           -   feasible point or best infeasible point found before
                    algorithm termination
    QPIts       -   number of QP iterations (for debug purposes)
    GPAIts      -   number of GPA iterations (for debug purposes)
    
RESULT:
    True in case X is feasible, False - if it is infeasible.

  -- ALGLIB --
     Copyright 20.01.2012 by Bochkanov Sergey
*************************************************************************/
ae_bool findfeasiblepoint(/* Real    */ ae_vector* x,
     /* Real    */ const ae_vector* bndl,
     /* Boolean */ const ae_vector* havebndl,
     /* Real    */ const ae_vector* bndu,
     /* Boolean */ const ae_vector* havebndu,
     ae_int_t nmain,
     ae_int_t nslack,
     /* Real    */ const ae_matrix* _ce,
     ae_int_t k,
     double epsi,
     ae_int_t* qpits,
     ae_int_t* gpaits,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ce;
    ae_int_t i;
    ae_int_t j;
    ae_int_t idx0;
    ae_int_t idx1;
    ae_vector permx;
    ae_vector xn;
    ae_vector xa;
    ae_vector newtonstep;
    ae_vector g;
    ae_vector pg;
    ae_vector tau;
    ae_vector s;
    double armijostep;
    double armijobeststep;
    double armijobestfeas;
    double v;
    double vv;
    double mx;
    double feaserr;
    double feaserr0;
    double feaserr1;
    double feasold;
    double feasnew;
    double pgnorm;
    double vn;
    double vd;
    double stp;
    ae_int_t vartofreeze;
    double valtofreeze;
    double maxsteplen;
    ae_bool werechangesinconstraints;
    ae_bool stage1isover;
    ae_bool converged;
    ae_vector activeconstraints;
    ae_vector tmpk;
    ae_vector colnorms;
    ae_int_t nactive;
    ae_int_t nfree;
    ae_vector p1;
    ae_vector p2;
    apbuffers buf;
    ae_int_t itscount;
    ae_int_t itswithintolerance;
    ae_int_t maxitswithintolerance;
    ae_int_t badits;
    ae_int_t maxbadits;
    ae_int_t gparuns;
    ae_int_t maxarmijoruns;
    double infeasibilityincreasetolerance;
    ae_matrix permce;
    ae_matrix q;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&ce, 0, sizeof(ce));
    memset(&permx, 0, sizeof(permx));
    memset(&xn, 0, sizeof(xn));
    memset(&xa, 0, sizeof(xa));
    memset(&newtonstep, 0, sizeof(newtonstep));
    memset(&g, 0, sizeof(g));
    memset(&pg, 0, sizeof(pg));
    memset(&tau, 0, sizeof(tau));
    memset(&s, 0, sizeof(s));
    memset(&activeconstraints, 0, sizeof(activeconstraints));
    memset(&tmpk, 0, sizeof(tmpk));
    memset(&colnorms, 0, sizeof(colnorms));
    memset(&p1, 0, sizeof(p1));
    memset(&p2, 0, sizeof(p2));
    memset(&buf, 0, sizeof(buf));
    memset(&permce, 0, sizeof(permce));
    memset(&q, 0, sizeof(q));
    ae_matrix_init_copy(&ce, _ce, _state, ae_true);
    *qpits = 0;
    *gpaits = 0;
    ae_vector_init(&permx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xn, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xa, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&newtonstep, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&pg, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tau, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&activeconstraints, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmpk, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&colnorms, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p1, 0, DT_INT, _state, ae_true);
    ae_vector_init(&p2, 0, DT_INT, _state, ae_true);
    _apbuffers_init(&buf, _state, ae_true);
    ae_matrix_init(&permce, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);

    maxitswithintolerance = 3;
    maxbadits = 3;
    maxarmijoruns = 5;
    *qpits = 0;
    *gpaits = 0;
    
    /*
     * Initial enforcement of the feasibility with respect to boundary constraints
     * NOTE: after this block we assume that boundary constraints are consistent.
     */
    if( !enforceboundaryconstraints(x, bndl, havebndl, bndu, havebndu, nmain, nslack, _state) )
    {
        result = ae_false;
        ae_frame_leave(_state);
        return result;
    }
    if( k==0 )
    {
        
        /*
         * No linear constraints, we can exit right now
         */
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Scale rows of CE in such way that max(CE[i,0..nmain+nslack-1])=1 for any i=0..k-1
     */
    for(i=0; i<=k-1; i++)
    {
        v = 0.0;
        for(j=0; j<=nmain+nslack-1; j++)
        {
            v = ae_maxreal(v, ae_fabs(ce.ptr.pp_double[i][j], _state), _state);
        }
        if( ae_fp_neq(v,(double)(0)) )
        {
            v = (double)1/v;
            ae_v_muld(&ce.ptr.pp_double[i][0], 1, ae_v_len(0,nmain+nslack), v);
        }
    }
    
    /*
     * Allocate temporaries
     */
    ae_vector_set_length(&xn, nmain+nslack, _state);
    ae_vector_set_length(&xa, nmain+nslack, _state);
    ae_vector_set_length(&permx, nmain+nslack, _state);
    ae_vector_set_length(&g, nmain+nslack, _state);
    ae_vector_set_length(&pg, nmain+nslack, _state);
    ae_vector_set_length(&tmpk, k, _state);
    ae_matrix_set_length(&permce, k, nmain+nslack, _state);
    ae_vector_set_length(&activeconstraints, nmain+nslack, _state);
    ae_vector_set_length(&newtonstep, nmain+nslack, _state);
    ae_vector_set_length(&s, nmain+nslack, _state);
    ae_vector_set_length(&colnorms, nmain+nslack, _state);
    for(i=0; i<=nmain+nslack-1; i++)
    {
        s.ptr.p_double[i] = 1.0;
        colnorms.ptr.p_double[i] = 0.0;
        for(j=0; j<=k-1; j++)
        {
            colnorms.ptr.p_double[i] = colnorms.ptr.p_double[i]+ae_sqr(ce.ptr.pp_double[j][i], _state);
        }
    }
    
    /*
     * K>0, we have linear equality constraints combined with bound constraints.
     *
     * Try to find feasible point as minimizer of the quadratic function
     *     F(x) = 0.5*||CE*x-b||^2 = 0.5*x'*(CE'*CE)*x - (b'*CE)*x + 0.5*b'*b
     * subject to boundary constraints given by BL, BU and non-negativity of
     * the slack variables. BTW, we drop constant term because it does not
     * actually influences on the solution.
     *
     * Below we will assume that K>0.
     */
    itswithintolerance = 0;
    badits = 0;
    itscount = 0;
    for(;;)
    {
        
        /*
         * Dynamically adjust infeasibility error tolerance
         */
        infeasibilityincreasetolerance = ae_maxreal(rmaxabsv(nmain+nslack, x, _state), (double)(1), _state)*(double)(1000+nmain)*ae_machineepsilon;
        
        /*
         * Stage 0: check for exact convergence
         */
        converged = ae_true;
        feaserr = optserv_feasibilityerror(&ce, x, nmain, nslack, k, &tmpk, _state);
        for(i=0; i<=k-1; i++)
        {
            
            /*
             * Calculate MX - maximum term in the left part
             *
             * Terminate if error in the right part is not greater than 100*Eps*MX.
             *
             * IMPORTANT: we must perform check for non-strict inequality, i.e. to use <= instead of <.
             *            it will allow us to easily handle situations with zero rows of CE.
             *
             * NOTE:      it is important to calculate feasibility error with dedicated
             *            function. Once we had a situation when usage of "inline" code
             *            resulted in different numerical values calculated at different
             *            parts of program for exactly same X. However, this value is
             *            essential for algorithm's ability to terminate before entering
             *            infinite loop, so reproducibility of numerical results is very
             *            important.
             */
            mx = (double)(0);
            v = -ce.ptr.pp_double[i][nmain+nslack];
            for(j=0; j<=nmain+nslack-1; j++)
            {
                mx = ae_maxreal(mx, ae_fabs(ce.ptr.pp_double[i][j]*x->ptr.p_double[j], _state), _state);
                v = v+ce.ptr.pp_double[i][j]*x->ptr.p_double[j];
            }
            converged = converged&&ae_fp_less_eq(ae_fabs(v, _state),(double)100*ae_machineepsilon*mx);
        }
        feaserr0 = feaserr;
        if( converged )
        {
            result = ae_fp_less_eq(feaserr,epsi);
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         * Stage 1: equality constrained quadratic programming
         *
         * * treat active bound constraints as equality ones (constraint is considered 
         *   active when we are at the boundary, independently of the antigradient direction)
         * * calculate unrestricted Newton step to point XM (which may be infeasible)
         *   calculate MaxStepLen = largest step in direction of XM which retains feasibility.
         * * perform bounded step from X to XN:
         *   a) XN=XM                  (if XM is feasible)
         *   b) XN=X-MaxStepLen*(XM-X) (otherwise)
         * * X := XN
         * * if XM (Newton step subject to currently active constraints) was feasible, goto Stage 2
         * * repeat Stage 1
         *
         * NOTE 1: in order to solve constrained qudratic subproblem we will have to reorder
         *         variables in such way that ones corresponding to inactive constraints will
         *         be first, and active ones will be last in the list. CE and X are now
         *                                                       [ xi ]
         *         separated into two parts: CE = [CEi CEa], x = [    ], where CEi/Xi correspond
         *                                                       [ xa ]
         *         to INACTIVE constraints, and CEa/Xa correspond to the ACTIVE ones.
         *
         *         Now, instead of F=0.5*x'*(CE'*CE)*x - (b'*CE)*x + 0.5*b'*b, we have
         *         F(xi) = 0.5*(CEi*xi,CEi*xi) + (CEa*xa-b,CEi*xi) + (0.5*CEa*xa-b,CEa*xa).
         *         Here xa is considered constant, i.e. we optimize with respect to xi, leaving xa fixed.
         *
         *         We can solve it by performing SVD of CEi and calculating pseudoinverse of the
         *         Hessian matrix. Of course, we do NOT calculate pseudoinverse explicitly - we
         *         just use singular vectors to perform implicit multiplication by it.
         *
         */
        for(;;)
        {
            
            /*
             * Calculate G - gradient subject to equality constraints,
             * multiply it by inverse of the Hessian diagonal to obtain initial
             * step vector.
             *
             * Bound step subject to constraints which can be activated,
             * run Armijo search with increasing step size.
             * Search is terminated when feasibility error stops to decrease.
             *
             * NOTE: it is important to test for "stops to decrease" instead
             * of "starts to increase" in order to correctly handle cases with
             * zero CE.
             */
            armijobeststep = 0.0;
            optserv_feasibilityerrorgrad(&ce, x, nmain, nslack, k, &armijobestfeas, &g, &tmpk, _state);
            for(i=0; i<=nmain-1; i++)
            {
                if( havebndl->ptr.p_bool[i]&&ae_fp_eq(x->ptr.p_double[i],bndl->ptr.p_double[i]) )
                {
                    g.ptr.p_double[i] = 0.0;
                }
                if( havebndu->ptr.p_bool[i]&&ae_fp_eq(x->ptr.p_double[i],bndu->ptr.p_double[i]) )
                {
                    g.ptr.p_double[i] = 0.0;
                }
            }
            for(i=0; i<=nslack-1; i++)
            {
                if( ae_fp_eq(x->ptr.p_double[nmain+i],0.0) )
                {
                    g.ptr.p_double[nmain+i] = 0.0;
                }
            }
            v = 0.0;
            for(i=0; i<=nmain+nslack-1; i++)
            {
                if( ae_fp_neq(ae_sqr(colnorms.ptr.p_double[i], _state),(double)(0)) )
                {
                    newtonstep.ptr.p_double[i] = -g.ptr.p_double[i]/ae_sqr(colnorms.ptr.p_double[i], _state);
                }
                else
                {
                    newtonstep.ptr.p_double[i] = 0.0;
                }
                v = v+ae_sqr(newtonstep.ptr.p_double[i], _state);
            }
            if( ae_fp_eq(v,(double)(0)) )
            {
                
                /*
                 * Constrained gradient is zero, QP iterations are over
                 */
                break;
            }
            calculatestepbound(x, &newtonstep, 1.0, bndl, havebndl, bndu, havebndu, nmain, nslack, &vartofreeze, &valtofreeze, &maxsteplen, _state);
            if( vartofreeze>=0&&ae_fp_eq(maxsteplen,(double)(0)) )
            {
                
                /*
                 * Can not perform step, QP iterations are over
                 */
                break;
            }
            if( vartofreeze>=0 )
            {
                armijostep = ae_minreal(1.0, maxsteplen, _state);
            }
            else
            {
                armijostep = (double)(1);
            }
            for(;;)
            {
                ae_v_move(&xa.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1));
                ae_v_addd(&xa.ptr.p_double[0], 1, &newtonstep.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1), armijostep);
                enforceboundaryconstraints(&xa, bndl, havebndl, bndu, havebndu, nmain, nslack, _state);
                feaserr = optserv_feasibilityerror(&ce, &xa, nmain, nslack, k, &tmpk, _state);
                if( ae_fp_greater_eq(feaserr,armijobestfeas) )
                {
                    break;
                }
                armijobestfeas = feaserr;
                armijobeststep = armijostep;
                armijostep = 2.0*armijostep;
            }
            ae_v_addd(&x->ptr.p_double[0], 1, &newtonstep.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1), armijobeststep);
            enforceboundaryconstraints(x, bndl, havebndl, bndu, havebndu, nmain, nslack, _state);
            
            /*
             * Determine number of active and free constraints
             */
            nactive = 0;
            for(i=0; i<=nmain-1; i++)
            {
                activeconstraints.ptr.p_double[i] = (double)(0);
                if( havebndl->ptr.p_bool[i]&&ae_fp_eq(x->ptr.p_double[i],bndl->ptr.p_double[i]) )
                {
                    activeconstraints.ptr.p_double[i] = (double)(1);
                }
                if( havebndu->ptr.p_bool[i]&&ae_fp_eq(x->ptr.p_double[i],bndu->ptr.p_double[i]) )
                {
                    activeconstraints.ptr.p_double[i] = (double)(1);
                }
                if( ae_fp_greater(activeconstraints.ptr.p_double[i],(double)(0)) )
                {
                    nactive = nactive+1;
                }
            }
            for(i=0; i<=nslack-1; i++)
            {
                activeconstraints.ptr.p_double[nmain+i] = (double)(0);
                if( ae_fp_eq(x->ptr.p_double[nmain+i],0.0) )
                {
                    activeconstraints.ptr.p_double[nmain+i] = (double)(1);
                }
                if( ae_fp_greater(activeconstraints.ptr.p_double[nmain+i],(double)(0)) )
                {
                    nactive = nactive+1;
                }
            }
            nfree = nmain+nslack-nactive;
            if( nfree==0 )
            {
                break;
            }
            *qpits = *qpits+1;
            
            /*
             * Reorder variables: CE is reordered to PermCE, X is reordered to PermX
             */
            tagsortbuf(&activeconstraints, nmain+nslack, &p1, &p2, &buf, _state);
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=nmain+nslack-1; j++)
                {
                    permce.ptr.pp_double[i][j] = ce.ptr.pp_double[i][j];
                }
            }
            for(j=0; j<=nmain+nslack-1; j++)
            {
                permx.ptr.p_double[j] = x->ptr.p_double[j];
            }
            for(j=0; j<=nmain+nslack-1; j++)
            {
                if( p2.ptr.p_int[j]!=j )
                {
                    idx0 = p2.ptr.p_int[j];
                    idx1 = j;
                    for(i=0; i<=k-1; i++)
                    {
                        v = permce.ptr.pp_double[i][idx0];
                        permce.ptr.pp_double[i][idx0] = permce.ptr.pp_double[i][idx1];
                        permce.ptr.pp_double[i][idx1] = v;
                    }
                    v = permx.ptr.p_double[idx0];
                    permx.ptr.p_double[idx0] = permx.ptr.p_double[idx1];
                    permx.ptr.p_double[idx1] = v;
                }
            }
            
            /*
             * Calculate (unprojected) gradient:
             * G(xi) = CEi'*(CEi*xi + CEa*xa - b)
             */
            for(i=0; i<=nfree-1; i++)
            {
                g.ptr.p_double[i] = (double)(0);
            }
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&permce.ptr.pp_double[i][0], 1, &permx.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1));
                tmpk.ptr.p_double[i] = v-ce.ptr.pp_double[i][nmain+nslack];
            }
            for(i=0; i<=k-1; i++)
            {
                v = tmpk.ptr.p_double[i];
                ae_v_addd(&g.ptr.p_double[0], 1, &permce.ptr.pp_double[i][0], 1, ae_v_len(0,nfree-1), v);
            }
            
            /*
             * Calculate Newton step using pseudoinverse PermCE:
             *     F(xi)  = 0.5*xi'*H*xi + g'*xi    (Taylor decomposition)
             *     XN     = -H^(-1)*g               (new point, solution of the QP subproblem)
             *     H      = CEi'*CEi                
             *     H^(-1) can be calculated via QR or LQ decomposition (see below)
             *     step   = -H^(-1)*g
             *
             * NOTE: PermCE is destroyed after this block
             */
            for(i=0; i<=nmain+nslack-1; i++)
            {
                newtonstep.ptr.p_double[i] = (double)(0);
            }
            if( k<=nfree )
            {
                
                /*
                 * CEi    = L*Q
                 * H      = Q'*L'*L*Q
                 * inv(H) = Q'*inv(L)*inv(L')*Q
                 *
                 * NOTE: we apply minor regularizing perturbation to diagonal of L,
                 *       which is equal to 10*K*Eps
                 */
                rmatrixlq(&permce, k, nfree, &tau, _state);
                rmatrixlqunpackq(&permce, k, nfree, &tau, k, &q, _state);
                v = (double)(0);
                for(i=0; i<=k-1; i++)
                {
                    v = ae_maxreal(v, ae_fabs(permce.ptr.pp_double[i][i], _state), _state);
                }
                v = coalesce(v, (double)(1), _state);
                for(i=0; i<=k-1; i++)
                {
                    permce.ptr.pp_double[i][i] = permce.ptr.pp_double[i][i]+(double)(10*k)*ae_machineepsilon*v;
                }
                rmatrixgemv(k, nfree, 1.0, &q, 0, 0, 0, &g, 0, 0.0, &tmpk, 0, _state);
                rmatrixtrsv(k, &permce, 0, 0, ae_false, ae_false, 1, &tmpk, 0, _state);
                rmatrixtrsv(k, &permce, 0, 0, ae_false, ae_false, 0, &tmpk, 0, _state);
                rmatrixgemv(nfree, k, -1.0, &q, 0, 0, 1, &tmpk, 0, 0.0, &newtonstep, 0, _state);
            }
            else
            {
                
                /*
                 * CEi    = Q*R
                 * H      = R'*R
                 * inv(H) = inv(R)*inv(R')
                 *
                 * NOTE: we apply minor regularizing perturbation to diagonal of R,
                 *       which is equal to 10*K*Eps
                 */
                rmatrixqr(&permce, k, nfree, &tau, _state);
                v = (double)(0);
                for(i=0; i<=nfree-1; i++)
                {
                    v = ae_maxreal(v, ae_fabs(permce.ptr.pp_double[i][i], _state), _state);
                }
                v = coalesce(v, (double)(1), _state);
                for(i=0; i<=nfree-1; i++)
                {
                    vv = (double)(10*nfree)*ae_machineepsilon*v;
                    if( ae_fp_less(permce.ptr.pp_double[i][i],(double)(0)) )
                    {
                        vv = -vv;
                    }
                    permce.ptr.pp_double[i][i] = permce.ptr.pp_double[i][i]+vv;
                }
                ae_v_moveneg(&newtonstep.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,nfree-1));
                rmatrixtrsv(nfree, &permce, 0, 0, ae_true, ae_false, 1, &newtonstep, 0, _state);
                rmatrixtrsv(nfree, &permce, 0, 0, ae_true, ae_false, 0, &newtonstep, 0, _state);
            }
            
            /*
             * Post-reordering of Newton step
             */
            for(j=nmain+nslack-1; j>=0; j--)
            {
                if( p2.ptr.p_int[j]!=j )
                {
                    idx0 = p2.ptr.p_int[j];
                    idx1 = j;
                    v = newtonstep.ptr.p_double[idx0];
                    newtonstep.ptr.p_double[idx0] = newtonstep.ptr.p_double[idx1];
                    newtonstep.ptr.p_double[idx1] = v;
                }
            }
            
            /*
             * NewtonStep contains Newton step subject to active bound constraints.
             *
             * Such step leads us to the minimizer of the equality constrained F,
             * but such minimizer may be infeasible because some constraints which
             * are inactive at the initial point can be violated at the solution.
             *
             * Thus, we perform optimization in two stages:
             * a) perform bounded Newton step, i.e. step in the Newton direction
             *    until activation of the first constraint
             * b) in case (MaxStepLen>0)and(MaxStepLen<1), perform additional iteration
             *    of the Armijo line search in the rest of the Newton direction.
             */
            calculatestepbound(x, &newtonstep, 1.0, bndl, havebndl, bndu, havebndu, nmain, nslack, &vartofreeze, &valtofreeze, &maxsteplen, _state);
            if( vartofreeze>=0&&ae_fp_eq(maxsteplen,(double)(0)) )
            {
                
                /*
                 * Activation of the constraints prevent us from performing step,
                 * QP iterations are over
                 */
                break;
            }
            if( vartofreeze>=0 )
            {
                v = ae_minreal(1.0, maxsteplen, _state);
            }
            else
            {
                v = 1.0;
            }
            ae_v_moved(&xn.ptr.p_double[0], 1, &newtonstep.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1), v);
            ae_v_add(&xn.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1));
            postprocessboundedstep(&xn, x, bndl, havebndl, bndu, havebndu, nmain, nslack, vartofreeze, valtofreeze, v, maxsteplen, _state);
            if( ae_fp_greater(maxsteplen,(double)(0))&&ae_fp_less(maxsteplen,(double)(1)) )
            {
                
                /*
                 * Newton step was restricted by activation of the constraints,
                 * perform Armijo iteration.
                 *
                 * Initial estimate for best step is zero step. We try different
                 * step sizes, from the 1-MaxStepLen (residual of the full Newton
                 * step) to progressively smaller and smaller steps.
                 */
                armijobeststep = 0.0;
                armijobestfeas = optserv_feasibilityerror(&ce, &xn, nmain, nslack, k, &tmpk, _state);
                armijostep = (double)1-maxsteplen;
                for(j=0; j<=maxarmijoruns-1; j++)
                {
                    ae_v_move(&xa.ptr.p_double[0], 1, &xn.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1));
                    ae_v_addd(&xa.ptr.p_double[0], 1, &newtonstep.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1), armijostep);
                    enforceboundaryconstraints(&xa, bndl, havebndl, bndu, havebndu, nmain, nslack, _state);
                    feaserr = optserv_feasibilityerror(&ce, &xa, nmain, nslack, k, &tmpk, _state);
                    if( ae_fp_less(feaserr,armijobestfeas) )
                    {
                        armijobestfeas = feaserr;
                        armijobeststep = armijostep;
                    }
                    armijostep = 0.5*armijostep;
                }
                ae_v_move(&xa.ptr.p_double[0], 1, &xn.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1));
                ae_v_addd(&xa.ptr.p_double[0], 1, &newtonstep.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1), armijobeststep);
                enforceboundaryconstraints(&xa, bndl, havebndl, bndu, havebndu, nmain, nslack, _state);
            }
            else
            {
                
                /*
                 * Armijo iteration is not performed
                 */
                ae_v_move(&xa.ptr.p_double[0], 1, &xn.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1));
            }
            stage1isover = ae_fp_greater_eq(maxsteplen,(double)(1))||ae_fp_eq(maxsteplen,(double)(0));
            
            /*
             * Calculate feasibility errors for old and new X.
             * These quantinies are used for debugging purposes only.
             * However, we can leave them in release code because performance impact is insignificant.
             *
             * Update X. Exit if needed.
             */
            feasold = optserv_feasibilityerror(&ce, x, nmain, nslack, k, &tmpk, _state);
            feasnew = optserv_feasibilityerror(&ce, &xa, nmain, nslack, k, &tmpk, _state);
            if( ae_fp_greater_eq(feasnew,feasold+infeasibilityincreasetolerance) )
            {
                break;
            }
            ae_v_move(&x->ptr.p_double[0], 1, &xa.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1));
            if( stage1isover )
            {
                break;
            }
        }
        
        /*
         * Stage 2: gradient projection algorithm (GPA)
         *
         * * calculate feasibility error (with respect to linear equality constraints)
         * * calculate gradient G of F, project it into feasible area (G => PG)
         * * exit if norm(PG) is exactly zero or feasibility error is smaller than EpsC
         * * let XM be exact minimum of F along -PG (XM may be infeasible).
         *   calculate MaxStepLen = largest step in direction of -PG which retains feasibility.
         * * perform bounded step from X to XN:
         *   a) XN=XM              (if XM is feasible)
         *   b) XN=X-MaxStepLen*PG (otherwise)
         * * X := XN
         * * stop after specified number of iterations or when no new constraints was activated
         *
         * NOTES:
         * * grad(F) = (CE'*CE)*x - (b'*CE)^T
         * * CE[i] denotes I-th row of CE
         * * XM = X+stp*(-PG) where stp=(grad(F(X)),PG)/(CE*PG,CE*PG).
         *   Here PG is a projected gradient, but in fact it can be arbitrary non-zero 
         *   direction vector - formula for minimum of F along PG still will be correct.
         */
        werechangesinconstraints = ae_false;
        for(gparuns=1; gparuns<=k; gparuns++)
        {
            
            /*
             * calculate feasibility error and G
             */
            optserv_feasibilityerrorgrad(&ce, x, nmain, nslack, k, &feaserr, &g, &tmpk, _state);
            
            /*
             * project G, filter it (strip numerical noise)
             */
            ae_v_move(&pg.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1));
            projectgradientintobc(x, &pg, bndl, havebndl, bndu, havebndu, nmain, nslack, _state);
            filterdirection(&pg, x, bndl, havebndl, bndu, havebndu, &s, nmain, nslack, 1.0E-9, _state);
            for(i=0; i<=nmain+nslack-1; i++)
            {
                if( ae_fp_neq(ae_sqr(colnorms.ptr.p_double[i], _state),(double)(0)) )
                {
                    pg.ptr.p_double[i] = pg.ptr.p_double[i]/ae_sqr(colnorms.ptr.p_double[i], _state);
                }
                else
                {
                    pg.ptr.p_double[i] = 0.0;
                }
            }
            
            /*
             * Check GNorm and feasibility.
             * Exit when GNorm is exactly zero.
             */
            pgnorm = ae_v_dotproduct(&pg.ptr.p_double[0], 1, &pg.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1));
            pgnorm = ae_sqrt(pgnorm, _state);
            if( ae_fp_eq(pgnorm,(double)(0)) )
            {
                result = ae_fp_less_eq(feaserr,epsi);
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * calculate planned step length
             */
            vn = ae_v_dotproduct(&g.ptr.p_double[0], 1, &pg.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1));
            vd = (double)(0);
            rmatrixgemv(k, nmain+nslack, 1.0, &ce, 0, 0, 0, &pg, 0, 0.0, &tmpk, 0, _state);
            for(i=0; i<=k-1; i++)
            {
                vd = vd+ae_sqr(tmpk.ptr.p_double[i], _state);
            }
            stp = vn/vd;
            
            /*
             * Calculate step bound.
             * Perform bounded step and post-process it
             */
            calculatestepbound(x, &pg, -1.0, bndl, havebndl, bndu, havebndu, nmain, nslack, &vartofreeze, &valtofreeze, &maxsteplen, _state);
            if( vartofreeze>=0&&ae_fp_eq(maxsteplen,(double)(0)) )
            {
                result = ae_false;
                ae_frame_leave(_state);
                return result;
            }
            if( vartofreeze>=0 )
            {
                v = ae_minreal(stp, maxsteplen, _state);
            }
            else
            {
                v = stp;
            }
            ae_v_move(&xn.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1));
            ae_v_subd(&xn.ptr.p_double[0], 1, &pg.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1), v);
            postprocessboundedstep(&xn, x, bndl, havebndl, bndu, havebndu, nmain, nslack, vartofreeze, valtofreeze, v, maxsteplen, _state);
            
            /*
             * update X
             * check stopping criteria
             */
            werechangesinconstraints = werechangesinconstraints||numberofchangedconstraints(&xn, x, bndl, havebndl, bndu, havebndu, nmain, nslack, _state)>0;
            ae_v_move(&x->ptr.p_double[0], 1, &xn.ptr.p_double[0], 1, ae_v_len(0,nmain+nslack-1));
            *gpaits = *gpaits+1;
            if( !werechangesinconstraints )
            {
                break;
            }
        }
        
        /*
         * Stage 3: decide to stop algorithm or not to stop
         *
         * 1. we can stop when last GPA run did NOT changed constraints status.
         *    It means that we've found final set of the active constraints even
         *    before GPA made its run. And it means that Newton step moved us to
         *    the minimum subject to the present constraints.
         *    Depending on feasibility error, True or False is returned.
         */
        feaserr = optserv_feasibilityerror(&ce, x, nmain, nslack, k, &tmpk, _state);
        feaserr1 = feaserr;
        if( ae_fp_greater_eq(feaserr1,feaserr0-infeasibilityincreasetolerance) )
        {
            inc(&badits, _state);
        }
        else
        {
            badits = 0;
        }
        if( ae_fp_less_eq(feaserr,epsi) )
        {
            inc(&itswithintolerance, _state);
        }
        else
        {
            itswithintolerance = 0;
        }
        if( (!werechangesinconstraints||itswithintolerance>=maxitswithintolerance)||badits>=maxbadits )
        {
            result = ae_fp_less_eq(feaserr,epsi);
            ae_frame_leave(_state);
            return result;
        }
        itscount = itscount+1;
        
        /*
         * Block below is never executed; it is necessary just to avoid
         * "unreachable code" warning about automatically generated code.
         *
         * We just need a way to transfer control to the end of the function,
         * even a fake way which is never actually traversed.
         */
        if( alwaysfalse(_state) )
        {
            result = ae_false;
            ae_assert(ae_false, "Assertion failed", _state);
            break;
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
    This function checks that input derivatives are right. First it scales
parameters DF0 and DF1 from segment [A;B] to [0;1]. Then it builds Hermite
spline and derivative of it in 0.5. Search scale as Max(DF0,DF1, |F0-F1|).
Right derivative has to satisfy condition:
    |H-F|/S<=0,001, |H'-F'|/S<=0,001.
    
INPUT PARAMETERS:
    F0  -   function's value in X-TestStep point;
    DF0 -   derivative's value in X-TestStep point;
    F1  -   function's value in X+TestStep point;
    DF1 -   derivative's value in X+TestStep point;
    F   -   testing function's value;
    DF  -   testing derivative's value;
   Width-   width of verification segment.

RESULT:
    If input derivatives is right then function returns true, else 
    function returns false.
    
  -- ALGLIB --
     Copyright 29.05.2012 by Bochkanov Sergey
*************************************************************************/
ae_bool derivativecheck(double f0,
     double df0,
     double f1,
     double df1,
     double f,
     double df,
     double width,
     ae_state *_state)
{
    double s;
    double h;
    double dh;
    ae_bool result;


    
    /*
     * Rescale input data to [0,1]
     */
    df = width*df;
    df0 = width*df0;
    df1 = width*df1;
    
    /*
     * Compute error scale, two sources are used:
     * * magnitudes of derivatives and secants
     * * magnitudes of input data times sqrt(machine_epsilon)
     */
    s = 0.0;
    s = ae_maxreal(s, ae_fabs(df0, _state), _state);
    s = ae_maxreal(s, ae_fabs(df1, _state), _state);
    s = ae_maxreal(s, ae_fabs(f1-f0, _state), _state);
    s = ae_maxreal(s, ae_sqrt(ae_machineepsilon, _state)*ae_fabs(f0, _state), _state);
    s = ae_maxreal(s, ae_sqrt(ae_machineepsilon, _state)*ae_fabs(f1, _state), _state);
    
    /*
     * Compute H and dH/dX at the middle of interval
     */
    h = 0.5*(f0+f1)+0.125*(df0-df1);
    dh = 1.5*(f1-f0)-0.250*(df0+df1);
    
    /*
     * Check
     */
    if( ae_fp_neq(s,(double)(0)) )
    {
        if( ae_fp_greater(ae_fabs(h-f, _state)/s,0.001)||ae_fp_greater(ae_fabs(dh-df, _state)/s,0.001) )
        {
            result = ae_false;
            return result;
        }
    }
    else
    {
        if( ae_fp_neq(h-f,0.0)||ae_fp_neq(dh-df,0.0) )
        {
            result = ae_false;
            return result;
        }
    }
    result = ae_true;
    return result;
}


/*************************************************************************
Having quadratic target function

    f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b)
    
and its parabolic model along direction D

    F(x0+alpha*D) = D2*alpha^2 + D1*alpha
    
this function estimates numerical errors in the coefficients of the model.
    
It is important that this  function  does  NOT calculate D1/D2  -  it only
estimates numerical errors introduced during evaluation and compares their
magnitudes against magnitudes of numerical errors. As result, one of three
outcomes is returned for each coefficient:
    * "true" coefficient is almost surely positive
    * "true" coefficient is almost surely negative
    * numerical errors in coefficient are so large that it can not be
      reliably distinguished from zero

INPUT PARAMETERS:
    AbsASum -   SUM(|A[i,j]|)
    AbsASum2-   SUM(A[i,j]^2)
    MB      -   max(|B|)
    MX      -   max(|X|)
    MD      -   max(|D|)
    D1      -   linear coefficient
    D2      -   quadratic coefficient

OUTPUT PARAMETERS:
    D1Est   -   estimate of D1 sign,  accounting  for  possible  numerical
                errors:
                * >0    means "almost surely positive" (D1>0 and large)
                * <0    means "almost surely negative" (D1<0 and large)
                * =0    means "pessimistic estimate  of  numerical  errors
                        in D1 is larger than magnitude of D1 itself; it is
                        impossible to reliably distinguish D1 from zero".
    D2Est   -   estimate of D2 sign,  accounting  for  possible  numerical
                errors:
                * >0    means "almost surely positive" (D2>0 and large)
                * <0    means "almost surely negative" (D2<0 and large)
                * =0    means "pessimistic estimate  of  numerical  errors
                        in D2 is larger than magnitude of D2 itself; it is
                        impossible to reliably distinguish D2 from zero".
            
  -- ALGLIB --
     Copyright 14.05.2014 by Bochkanov Sergey
*************************************************************************/
void estimateparabolicmodel(double absasum,
     double absasum2,
     double mx,
     double mb,
     double md,
     double d1,
     double d2,
     ae_int_t* d1est,
     ae_int_t* d2est,
     ae_state *_state)
{
    double d1esterror;
    double d2esterror;
    double eps;
    double e1;
    double e2;

    *d1est = 0;
    *d2est = 0;

    
    /*
     * Error estimates:
     *
     * * error in D1=d'*(A*x+b) is estimated as
     *   ED1 = eps*MAX_ABS(D)*(MAX_ABS(X)*ENORM(A)+MAX_ABS(B))
     * * error in D2=0.5*d'*A*d is estimated as
     *   ED2 = eps*MAX_ABS(D)^2*ENORM(A)
     *
     * Here ENORM(A) is some pseudo-norm which reflects the way numerical
     * error accumulates during addition. Two ways of accumulation are
     * possible - worst case (errors always increase) and mean-case (errors
     * may cancel each other). We calculate geometrical average of both:
     * * ENORM_WORST(A) = SUM(|A[i,j]|)         error in N-term sum grows as O(N)
     * * ENORM_MEAN(A)  = SQRT(SUM(A[i,j]^2))   error in N-term sum grows as O(sqrt(N))
     * * ENORM(A)       = SQRT(ENORM_WORST(A),ENORM_MEAN(A))
     */
    eps = (double)4*ae_machineepsilon;
    e1 = eps*md*(mx*absasum+mb);
    e2 = eps*md*(mx*ae_sqrt(absasum2, _state)+mb);
    d1esterror = ae_sqrt(e1*e2, _state);
    if( ae_fp_less_eq(ae_fabs(d1, _state),d1esterror) )
    {
        *d1est = 0;
    }
    else
    {
        *d1est = ae_sign(d1, _state);
    }
    e1 = eps*md*md*absasum;
    e2 = eps*md*md*ae_sqrt(absasum2, _state);
    d2esterror = ae_sqrt(e1*e2, _state);
    if( ae_fp_less_eq(ae_fabs(d2, _state),d2esterror) )
    {
        *d2est = 0;
    }
    else
    {
        *d2est = ae_sign(d2, _state);
    }
}


/*************************************************************************
This function calculates inexact rank-K preconditioner for Hessian  matrix
H=D+W'*C*W, where:
* H is a Hessian matrix, which is approximated by D/W/C
* D is a diagonal matrix with positive entries
* W is a rank-K correction
* C is a diagonal factor of rank-K correction

This preconditioner is inexact but fast - it requires O(N*K)  time  to  be
applied. Its main purpose - to be  used  in  barrier/penalty/AUL  methods,
where ill-conditioning is created by combination of two factors:
* simple bounds on variables => ill-conditioned D
* general barrier/penalty => correction W  with large coefficient C (makes
  problem ill-conditioned) but W itself is well conditioned.

Preconditioner P is calculated by artificially constructing a set of  BFGS
updates which tries to reproduce behavior of H:
* Sk = Wk (k-th row of W)
* Yk = (D+Wk'*Ck*Wk)*Sk
* Yk/Sk are reordered by ascending of C[k]*norm(Wk)^2

Here we assume that rows of Wk are orthogonal or nearly orthogonal,  which
allows us to have O(N*K+K^2) update instead of O(N*K^2) one. Reordering of
updates is essential for having good performance on non-orthogonal problems
(updates which do not add much of curvature are added first,  and  updates
which add very large eigenvalues are added last and override effect of the
first updates).

On input this function takes direction S and components of H.
On output it returns inv(H)*S

  -- ALGLIB --
     Copyright 30.06.2014 by Bochkanov Sergey
*************************************************************************/
void inexactlbfgspreconditioner(/* Real    */ ae_vector* s,
     ae_int_t n,
     /* Real    */ const ae_vector* d,
     /* Real    */ const ae_vector* c,
     /* Real    */ const ae_matrix* w,
     ae_int_t k,
     precbuflbfgs* buf,
     ae_state *_state)
{
    ae_int_t idx;
    ae_int_t i;
    ae_int_t j;
    double v;
    double v0;
    double v1;
    double vx;
    double vy;


    rvectorsetlengthatleast(&buf->norms, k, _state);
    rvectorsetlengthatleast(&buf->alpha, k, _state);
    rvectorsetlengthatleast(&buf->rho, k, _state);
    rmatrixsetlengthatleast(&buf->yk, k, n, _state);
    ivectorsetlengthatleast(&buf->idx, k, _state);
    
    /*
     * Check inputs
     */
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_fp_greater(d->ptr.p_double[i],(double)(0)), "InexactLBFGSPreconditioner: D[]<=0", _state);
    }
    for(i=0; i<=k-1; i++)
    {
        ae_assert(ae_fp_greater_eq(c->ptr.p_double[i],(double)(0)), "InexactLBFGSPreconditioner: C[]<0", _state);
    }
    
    /*
     * Reorder linear terms according to increase of second derivative.
     * Fill Norms[] array.
     */
    for(idx=0; idx<=k-1; idx++)
    {
        v = ae_v_dotproduct(&w->ptr.pp_double[idx][0], 1, &w->ptr.pp_double[idx][0], 1, ae_v_len(0,n-1));
        buf->norms.ptr.p_double[idx] = v*c->ptr.p_double[idx];
        buf->idx.ptr.p_int[idx] = idx;
    }
    tagsortfasti(&buf->norms, &buf->idx, &buf->bufa, &buf->bufb, k, _state);
    
    /*
     * Apply updates
     */
    for(idx=0; idx<=k-1; idx++)
    {
        
        /*
         * Select update to perform (ordered by ascending of second derivative)
         */
        i = buf->idx.ptr.p_int[idx];
        
        /*
         * Calculate YK and Rho
         */
        v = ae_v_dotproduct(&w->ptr.pp_double[i][0], 1, &w->ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
        v = v*c->ptr.p_double[i];
        for(j=0; j<=n-1; j++)
        {
            buf->yk.ptr.pp_double[i][j] = (d->ptr.p_double[j]+v)*w->ptr.pp_double[i][j];
        }
        v = 0.0;
        v0 = 0.0;
        v1 = 0.0;
        for(j=0; j<=n-1; j++)
        {
            vx = w->ptr.pp_double[i][j];
            vy = buf->yk.ptr.pp_double[i][j];
            v = v+vx*vy;
            v0 = v0+vx*vx;
            v1 = v1+vy*vy;
        }
        if( (ae_fp_greater(v,(double)(0))&&ae_fp_greater(v0*v1,(double)(0)))&&ae_fp_greater(v/ae_sqrt(v0*v1, _state),(double)(n*10)*ae_machineepsilon) )
        {
            buf->rho.ptr.p_double[i] = (double)1/v;
        }
        else
        {
            buf->rho.ptr.p_double[i] = 0.0;
        }
    }
    for(idx=k-1; idx>=0; idx--)
    {
        
        /*
         * Select update to perform (ordered by ascending of second derivative)
         */
        i = buf->idx.ptr.p_int[idx];
        
        /*
         * Calculate Alpha[] according to L-BFGS algorithm
         * and update S[]
         */
        v = ae_v_dotproduct(&w->ptr.pp_double[i][0], 1, &s->ptr.p_double[0], 1, ae_v_len(0,n-1));
        v = buf->rho.ptr.p_double[i]*v;
        buf->alpha.ptr.p_double[i] = v;
        ae_v_subd(&s->ptr.p_double[0], 1, &buf->yk.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
    }
    for(j=0; j<=n-1; j++)
    {
        s->ptr.p_double[j] = s->ptr.p_double[j]/d->ptr.p_double[j];
    }
    for(idx=0; idx<=k-1; idx++)
    {
        
        /*
         * Select update to perform (ordered by ascending of second derivative)
         */
        i = buf->idx.ptr.p_int[idx];
        
        /*
         * Calculate Beta according to L-BFGS algorithm
         * and update S[]
         */
        v = ae_v_dotproduct(&buf->yk.ptr.pp_double[i][0], 1, &s->ptr.p_double[0], 1, ae_v_len(0,n-1));
        v = buf->alpha.ptr.p_double[i]-buf->rho.ptr.p_double[i]*v;
        ae_v_addd(&s->ptr.p_double[0], 1, &w->ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
    }
}


/*************************************************************************
This function prepares exact low-rank preconditioner  for  Hessian  matrix
H=D+W'*C*W, where:
* H is a Hessian matrix, which is approximated by D/W/C
* D is a diagonal matrix with positive entries
* W is a rank-K correction
* C is a diagonal factor of rank-K correction, positive semidefinite

This preconditioner is exact but relatively slow -  it  requires  O(N*K^2)
time to be prepared and O(N*K) time to be applied. It is  calculated  with
the help of Woodbury matrix identity.

It should be used as follows:
* PrepareLowRankPreconditioner() call PREPARES data structure
* subsequent calls to ApplyLowRankPreconditioner() APPLY preconditioner to
  user-specified search direction.

  -- ALGLIB --
     Copyright 30.06.2014 by Bochkanov Sergey
*************************************************************************/
void preparelowrankpreconditioner(/* Real    */ const ae_vector* d,
     /* Real    */ const ae_vector* c,
     /* Real    */ const ae_matrix* w,
     ae_int_t n,
     ae_int_t k,
     precbuflowrank* buf,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_bool b;


    
    /*
     * Check inputs
     */
    ae_assert(n>0, "PrepareLowRankPreconditioner: N<=0", _state);
    ae_assert(k>=0, "PrepareLowRankPreconditioner: N<=0", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_fp_greater(d->ptr.p_double[i],(double)(0)), "PrepareLowRankPreconditioner: D[]<=0", _state);
    }
    for(i=0; i<=k-1; i++)
    {
        ae_assert(ae_fp_greater_eq(c->ptr.p_double[i],(double)(0)), "PrepareLowRankPreconditioner: C[]<0", _state);
    }
    
    /*
     * Prepare buffer structure; skip zero entries of update.
     */
    rvectorsetlengthatleast(&buf->d, n, _state);
    rmatrixsetlengthatleast(&buf->v, k, n, _state);
    rvectorsetlengthatleast(&buf->bufc, k, _state);
    rmatrixsetlengthatleast(&buf->bufw, k+1, n, _state);
    buf->n = n;
    buf->k = 0;
    for(i=0; i<=k-1; i++)
    {
        
        /*
         * Estimate magnitude of update row; skip zero rows (either W or C are zero)
         */
        v = 0.0;
        for(j=0; j<=n-1; j++)
        {
            v = v+w->ptr.pp_double[i][j]*w->ptr.pp_double[i][j];
        }
        v = v*c->ptr.p_double[i];
        if( ae_fp_eq(v,(double)(0)) )
        {
            continue;
        }
        ae_assert(ae_fp_greater(v,(double)(0)), "PrepareLowRankPreconditioner: internal error", _state);
        
        /*
         * Copy non-zero update to buffer
         */
        buf->bufc.ptr.p_double[buf->k] = c->ptr.p_double[i];
        for(j=0; j<=n-1; j++)
        {
            buf->v.ptr.pp_double[buf->k][j] = w->ptr.pp_double[i][j];
            buf->bufw.ptr.pp_double[buf->k][j] = w->ptr.pp_double[i][j];
        }
        inc(&buf->k, _state);
    }
    
    /*
     * Reset K (for convenience)
     */
    k = buf->k;
    
    /*
     * Prepare diagonal factor; quick exit for K=0
     */
    for(i=0; i<=n-1; i++)
    {
        buf->d.ptr.p_double[i] = (double)1/d->ptr.p_double[i];
    }
    if( k==0 )
    {
        return;
    }
    
    /*
     * Use Woodbury matrix identity
     */
    rmatrixsetlengthatleast(&buf->bufz, k, k, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            buf->bufz.ptr.pp_double[i][j] = 0.0;
        }
    }
    for(i=0; i<=k-1; i++)
    {
        buf->bufz.ptr.pp_double[i][i] = (double)1/buf->bufc.ptr.p_double[i];
    }
    for(j=0; j<=n-1; j++)
    {
        buf->bufw.ptr.pp_double[k][j] = (double)1/ae_sqrt(d->ptr.p_double[j], _state);
    }
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            buf->bufw.ptr.pp_double[i][j] = buf->bufw.ptr.pp_double[i][j]*buf->bufw.ptr.pp_double[k][j];
        }
    }
    rmatrixgemm(k, k, n, 1.0, &buf->bufw, 0, 0, 0, &buf->bufw, 0, 0, 1, 1.0, &buf->bufz, 0, 0, _state);
    b = spdmatrixcholeskyrec(&buf->bufz, 0, k, ae_true, &buf->tmp, _state);
    ae_assert(b, "PrepareLowRankPreconditioner: internal error (Cholesky failure)", _state);
    rmatrixlefttrsm(k, n, &buf->bufz, 0, 0, ae_true, ae_false, 1, &buf->v, 0, 0, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            buf->v.ptr.pp_double[i][j] = buf->v.ptr.pp_double[i][j]*buf->d.ptr.p_double[j];
        }
    }
}


/*************************************************************************
This function apply exact low-rank preconditioner prepared by
PrepareLowRankPreconditioner function (see its comments for more information).

  -- ALGLIB --
     Copyright 30.06.2014 by Bochkanov Sergey
*************************************************************************/
void applylowrankpreconditioner(/* Real    */ ae_vector* s,
     precbuflowrank* buf,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t k;
    ae_int_t i;
    ae_int_t j;
    double v;


    n = buf->n;
    k = buf->k;
    rvectorsetlengthatleast(&buf->tmp, n, _state);
    for(j=0; j<=n-1; j++)
    {
        buf->tmp.ptr.p_double[j] = buf->d.ptr.p_double[j]*s->ptr.p_double[j];
    }
    for(i=0; i<=k-1; i++)
    {
        v = 0.0;
        for(j=0; j<=n-1; j++)
        {
            v = v+buf->v.ptr.pp_double[i][j]*s->ptr.p_double[j];
        }
        for(j=0; j<=n-1; j++)
        {
            buf->tmp.ptr.p_double[j] = buf->tmp.ptr.p_double[j]-v*buf->v.ptr.pp_double[i][j];
        }
    }
    for(i=0; i<=n-1; i++)
    {
        s->ptr.p_double[i] = buf->tmp.ptr.p_double[i];
    }
}


/*************************************************************************
This subroutine initializes smoothness monitor at  the  beginning  of  the
optimization session. It requires variable scales to be passed.

It is possible to perform "dummy" initialization with N=K=0.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
void smoothnessmonitorinit(smoothnessmonitor* monitor,
     /* Real    */ const ae_vector* s,
     ae_int_t n,
     ae_int_t k,
     ae_bool checksmoothness,
     ae_state *_state)
{
    ae_int_t i;


    monitor->n = n;
    monitor->k = k;
    monitor->checksmoothness = checksmoothness;
    monitor->linesearchspoiled = ae_false;
    monitor->linesearchstarted = ae_false;
    monitor->linesearchinneridx = -1;
    monitor->linesearchouteridx = -1;
    monitor->enqueuedcnt = 0;
    monitor->sortedcnt = 0;
    rvectorsetlengthatleast(&monitor->s, n, _state);
    for(i=0; i<=n-1; i++)
    {
        monitor->s.ptr.p_double[i] = s->ptr.p_double[i];
    }
    monitor->nonc0currentrating = 0.0;
    monitor->nonc1currentrating = 0.0;
    optguardinitinternal(&monitor->rep, n, k, _state);
    monitor->nonc0strrating = 0.0;
    monitor->nonc0lngrating = -ae_maxrealnumber;
    monitor->nonc0strrep.positive = ae_false;
    monitor->nonc0lngrep.positive = ae_false;
    monitor->nonc1test0strrating = 0.0;
    monitor->nonc1test0lngrating = -ae_maxrealnumber;
    monitor->nonc1test0strrep.positive = ae_false;
    monitor->nonc1test0lngrep.positive = ae_false;
    monitor->nonc1test1strrating = 0.0;
    monitor->nonc1test1lngrating = -ae_maxrealnumber;
    monitor->nonc1test1strrep.positive = ae_false;
    monitor->nonc1test1lngrep.positive = ae_false;
    monitor->badgradhasxj = ae_false;
    ae_vector_set_length(&monitor->rstateg0.ia, 4+1, _state);
    ae_vector_set_length(&monitor->rstateg0.ra, 3+1, _state);
    monitor->rstateg0.stage = -1;
}


/*************************************************************************
This subroutine starts line search

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
void smoothnessmonitorstartlinesearch(smoothnessmonitor* monitor,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     /* Real    */ const ae_matrix* jac,
     ae_int_t inneriter,
     ae_int_t outeriter,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t k;
    ae_int_t i;
    ae_int_t j;
    double v;


    n = monitor->n;
    k = monitor->k;
    
    /*
     * Skip if inactive or spoiled by NAN
     */
    if( !monitor->checksmoothness )
    {
        return;
    }
    v = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        v = 0.5*v+x->ptr.p_double[i];
    }
    for(i=0; i<=k-1; i++)
    {
        v = 0.5*v+fi->ptr.p_double[i];
    }
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = 0.5*v+jac->ptr.pp_double[i][j];
        }
    }
    if( !ae_isfinite(v, _state) )
    {
        monitor->linesearchspoiled = ae_true;
        return;
    }
    
    /*
     * Finalize previous line search
     */
    if( monitor->enqueuedcnt>0 )
    {
        smoothnessmonitorfinalizelinesearch(monitor, _state);
    }
    
    /*
     * Store initial point
     */
    monitor->linesearchstarted = ae_true;
    monitor->linesearchinneridx = inneriter;
    monitor->linesearchouteridx = outeriter;
    monitor->enqueuedcnt = 1;
    rvectorgrowto(&monitor->enqueuedstp, monitor->enqueuedcnt, _state);
    rvectorgrowto(&monitor->enqueuedx, monitor->enqueuedcnt*n, _state);
    rvectorgrowto(&monitor->enqueuedfunc, monitor->enqueuedcnt*k, _state);
    rmatrixgrowrowsto(&monitor->enqueuedjac, monitor->enqueuedcnt*k, n, _state);
    monitor->enqueuedstp.ptr.p_double[0] = 0.0;
    for(j=0; j<=n-1; j++)
    {
        monitor->enqueuedx.ptr.p_double[j] = x->ptr.p_double[j];
    }
    for(i=0; i<=k-1; i++)
    {
        monitor->enqueuedfunc.ptr.p_double[i] = fi->ptr.p_double[i];
    }
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            monitor->enqueuedjac.ptr.pp_double[i][j] = jac->ptr.pp_double[i][j];
        }
    }
    
    /*
     * Initialize sorted representation
     */
    rvectorgrowto(&monitor->sortedstp, 1, _state);
    ivectorgrowto(&monitor->sortedidx, 1, _state);
    monitor->sortedstp.ptr.p_double[0] = 0.0;
    monitor->sortedidx.ptr.p_int[0] = 0;
    monitor->sortedcnt = 1;
}


/*************************************************************************
This subroutine starts line search for a scalar function - convenience
wrapper for ....StartLineSearch() with unscaled variables.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
void smoothnessmonitorstartlinesearch1u(smoothnessmonitor* monitor,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* invs,
     /* Real    */ const ae_vector* x,
     double f0,
     /* Real    */ const ae_vector* j0,
     ae_int_t inneriter,
     ae_int_t outeriter,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t k;
    ae_int_t i;


    n = monitor->n;
    k = monitor->k;
    if( !monitor->checksmoothness )
    {
        return;
    }
    ae_assert(k==1, "SmoothnessMonitorStartLineSearch1: K<>1", _state);
    rvectorsetlengthatleast(&monitor->xu, n, _state);
    rvectorsetlengthatleast(&monitor->f0, 1, _state);
    rmatrixsetlengthatleast(&monitor->j0, 1, n, _state);
    monitor->f0.ptr.p_double[0] = f0;
    for(i=0; i<=n-1; i++)
    {
        monitor->xu.ptr.p_double[i] = x->ptr.p_double[i]*invs->ptr.p_double[i];
        monitor->j0.ptr.pp_double[0][i] = j0->ptr.p_double[i]*s->ptr.p_double[i];
    }
    smoothnessmonitorstartlinesearch(monitor, &monitor->xu, &monitor->f0, &monitor->j0, inneriter, outeriter, _state);
}


/*************************************************************************
This subroutine enqueues one more trial point

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
void smoothnessmonitorenqueuepoint(smoothnessmonitor* monitor,
     /* Real    */ const ae_vector* d,
     double stp,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     /* Real    */ const ae_matrix* jac,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t k;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_int_t enqueuedcnt;
    ae_int_t sortedcnt;
    ae_bool hasduplicates;
    ae_int_t funcidx;
    ae_int_t stpidx;
    double f0;
    double f1;
    double f2;
    double f3;
    double f4;
    double noise0;
    double noise1;
    double noise2;
    double noise3;
    double rating;
    double lipschitz;
    double nrm;
    double lengthrating;


    n = monitor->n;
    k = monitor->k;
    
    /*
     * Skip if inactive or spoiled by NAN
     */
    if( (!monitor->checksmoothness||monitor->linesearchspoiled)||!monitor->linesearchstarted )
    {
        return;
    }
    v = stp;
    for(i=0; i<=n-1; i++)
    {
        v = 0.5*v+x->ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        v = 0.5*v+d->ptr.p_double[i];
    }
    for(i=0; i<=k-1; i++)
    {
        v = 0.5*v+fi->ptr.p_double[i];
    }
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = 0.5*v+jac->ptr.pp_double[i][j];
        }
    }
    if( !ae_isfinite(v, _state) )
    {
        monitor->linesearchspoiled = ae_true;
        return;
    }
    
    /*
     * Enqueue
     */
    inc(&monitor->enqueuedcnt, _state);
    enqueuedcnt = monitor->enqueuedcnt;
    rvectorgrowto(&monitor->dcur, n, _state);
    rvectorgrowto(&monitor->enqueuedstp, enqueuedcnt, _state);
    rvectorgrowto(&monitor->enqueuedx, enqueuedcnt*n, _state);
    rvectorgrowto(&monitor->enqueuedfunc, enqueuedcnt*k, _state);
    rmatrixgrowrowsto(&monitor->enqueuedjac, enqueuedcnt*k, n, _state);
    monitor->enqueuedstp.ptr.p_double[enqueuedcnt-1] = stp;
    for(j=0; j<=n-1; j++)
    {
        monitor->dcur.ptr.p_double[j] = d->ptr.p_double[j];
    }
    for(j=0; j<=n-1; j++)
    {
        monitor->enqueuedx.ptr.p_double[(enqueuedcnt-1)*n+j] = x->ptr.p_double[j];
    }
    for(i=0; i<=k-1; i++)
    {
        monitor->enqueuedfunc.ptr.p_double[(enqueuedcnt-1)*k+i] = fi->ptr.p_double[i];
    }
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            monitor->enqueuedjac.ptr.pp_double[(enqueuedcnt-1)*k+i][j] = jac->ptr.pp_double[i][j];
        }
    }
    
    /*
     * Update sorted representation: insert to the end, reorder
     */
    sortedcnt = monitor->sortedcnt;
    hasduplicates = ae_false;
    for(i=0; i<=sortedcnt-1; i++)
    {
        hasduplicates = hasduplicates||monitor->sortedstp.ptr.p_double[i]==stp;
    }
    if( !hasduplicates )
    {
        inc(&monitor->sortedcnt, _state);
        sortedcnt = monitor->sortedcnt;
        rvectorgrowto(&monitor->sortedstp, sortedcnt, _state);
        ivectorgrowto(&monitor->sortedidx, sortedcnt, _state);
        monitor->sortedstp.ptr.p_double[sortedcnt-1] = stp;
        monitor->sortedidx.ptr.p_int[sortedcnt-1] = enqueuedcnt-1;
        for(i=sortedcnt-2; i>=0; i--)
        {
            if( monitor->sortedstp.ptr.p_double[i]<=monitor->sortedstp.ptr.p_double[i+1] )
            {
                break;
            }
            v = monitor->sortedstp.ptr.p_double[i];
            monitor->sortedstp.ptr.p_double[i] = monitor->sortedstp.ptr.p_double[i+1];
            monitor->sortedstp.ptr.p_double[i+1] = v;
            j = monitor->sortedidx.ptr.p_int[i];
            monitor->sortedidx.ptr.p_int[i] = monitor->sortedidx.ptr.p_int[i+1];
            monitor->sortedidx.ptr.p_int[i+1] = j;
        }
    }
    
    /*
     * Scan sorted representation, check for C0 and C1 continuity
     * violations.
     */
    rvectorsetlengthatleast(&monitor->f, sortedcnt, _state);
    rvectorsetlengthatleast(&monitor->g, sortedcnt*n, _state);
    for(funcidx=0; funcidx<=k-1; funcidx++)
    {
        
        /*
         * Fetch current function and its gradient to the contiguous storage
         */
        for(i=0; i<=sortedcnt-1; i++)
        {
            monitor->f.ptr.p_double[i] = monitor->enqueuedfunc.ptr.p_double[monitor->sortedidx.ptr.p_int[i]*k+funcidx];
            for(j=0; j<=n-1; j++)
            {
                monitor->g.ptr.p_double[i*n+j] = monitor->enqueuedjac.ptr.pp_double[monitor->sortedidx.ptr.p_int[i]*k+funcidx][j];
            }
        }
        
        /*
         * Check C0 continuity.
         *
         * The basis approach is that we find appropriate candidate point
         * (either a local minimum along the line - for target; or an interval
         * where function sign is changed - for constraints), calculate left
         * and right estimates of the Lipschitz constant (slopes between points
         * #0 and #1, #2 and #3), and then calculate slope between points #1 and
         * #2 and compare it with left/right estimates.
         *
         * The actual approach is a bit more complex to account for different
         * sources of numerical noise and different false positive scenarios.
         */
        if( funcidx==0 )
        {
            for(stpidx=0; stpidx<=sortedcnt-4; stpidx++)
            {
                f0 = monitor->f.ptr.p_double[stpidx+0];
                f1 = monitor->f.ptr.p_double[stpidx+1];
                f2 = monitor->f.ptr.p_double[stpidx+2];
                f3 = monitor->f.ptr.p_double[stpidx+3];
                noise0 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f0, _state), 1.0, _state);
                noise1 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f1, _state), 1.0, _state);
                noise2 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f2, _state), 1.0, _state);
                noise3 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f3, _state), 1.0, _state);
                if( !(f1<f0+(noise0+noise1)&&f1<f2) )
                {
                    continue;
                }
                optserv_testc0continuity(f0, f1, f2, f3, noise0, noise1, noise2, noise3, monitor->sortedstp.ptr.p_double[stpidx+1]-monitor->sortedstp.ptr.p_double[stpidx+0], monitor->sortedstp.ptr.p_double[stpidx+2]-monitor->sortedstp.ptr.p_double[stpidx+1], monitor->sortedstp.ptr.p_double[stpidx+3]-monitor->sortedstp.ptr.p_double[stpidx+2], ae_false, &rating, &lipschitz, _state);
                if( rating>optserv_ogminrating0 )
                {
                    
                    /*
                     * Store to total report
                     */
                    monitor->rep.nonc0suspected = ae_true;
                    monitor->rep.nonc0test0positive = ae_true;
                    if( rating>monitor->nonc0currentrating )
                    {
                        monitor->nonc0currentrating = rating;
                        monitor->rep.nonc0lipschitzc = lipschitz;
                        monitor->rep.nonc0fidx = funcidx;
                    }
                    
                    /*
                     * Store to "strongest" report
                     */
                    if( rating>monitor->nonc0strrating )
                    {
                        monitor->nonc0strrating = rating;
                        monitor->nonc0strrep.positive = ae_true;
                        monitor->nonc0strrep.fidx = funcidx;
                        monitor->nonc0strrep.n = n;
                        monitor->nonc0strrep.cnt = sortedcnt;
                        monitor->nonc0strrep.stpidxa = stpidx+0;
                        monitor->nonc0strrep.stpidxb = stpidx+3;
                        monitor->nonc0strrep.inneriter = monitor->linesearchinneridx;
                        monitor->nonc0strrep.outeriter = monitor->linesearchouteridx;
                        rvectorsetlengthatleast(&monitor->nonc0strrep.x0, n, _state);
                        rvectorsetlengthatleast(&monitor->nonc0strrep.d, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            monitor->nonc0strrep.x0.ptr.p_double[i] = monitor->enqueuedx.ptr.p_double[monitor->sortedidx.ptr.p_int[0]*n+i];
                            monitor->nonc0strrep.d.ptr.p_double[i] = monitor->dcur.ptr.p_double[i];
                        }
                        rvectorsetlengthatleast(&monitor->nonc0strrep.stp, sortedcnt, _state);
                        rvectorsetlengthatleast(&monitor->nonc0strrep.f, sortedcnt, _state);
                        for(i=0; i<=sortedcnt-1; i++)
                        {
                            monitor->nonc0strrep.stp.ptr.p_double[i] = monitor->sortedstp.ptr.p_double[i];
                            monitor->nonc0strrep.f.ptr.p_double[i] = monitor->f.ptr.p_double[i];
                        }
                    }
                    
                    /*
                     * Store to "longest" report
                     */
                    nrm = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        nrm = nrm+ae_sqr(monitor->enqueuedx.ptr.p_double[monitor->sortedidx.ptr.p_int[0]*n+i]-monitor->enqueuedx.ptr.p_double[monitor->sortedidx.ptr.p_int[sortedcnt-1]*n+i], _state);
                    }
                    nrm = ae_sqrt(nrm, _state);
                    nrm = coalesce(nrm, ae_machineepsilon, _state);
                    lengthrating = ae_log(nrm, _state)+0.2*ae_log((double)(sortedcnt), _state)+0.2*ae_log(rating, _state);
                    if( lengthrating>monitor->nonc0lngrating )
                    {
                        monitor->nonc0lngrating = lengthrating;
                        monitor->nonc0lngrep.positive = ae_true;
                        monitor->nonc0lngrep.fidx = funcidx;
                        monitor->nonc0lngrep.n = n;
                        monitor->nonc0lngrep.cnt = sortedcnt;
                        monitor->nonc0lngrep.stpidxa = stpidx+0;
                        monitor->nonc0lngrep.stpidxb = stpidx+3;
                        monitor->nonc0lngrep.inneriter = monitor->linesearchinneridx;
                        monitor->nonc0lngrep.outeriter = monitor->linesearchouteridx;
                        rvectorsetlengthatleast(&monitor->nonc0lngrep.x0, n, _state);
                        rvectorsetlengthatleast(&monitor->nonc0lngrep.d, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            monitor->nonc0lngrep.x0.ptr.p_double[i] = monitor->enqueuedx.ptr.p_double[monitor->sortedidx.ptr.p_int[0]*n+i];
                            monitor->nonc0lngrep.d.ptr.p_double[i] = monitor->dcur.ptr.p_double[i];
                        }
                        rvectorsetlengthatleast(&monitor->nonc0lngrep.stp, sortedcnt, _state);
                        rvectorsetlengthatleast(&monitor->nonc0lngrep.f, sortedcnt, _state);
                        for(i=0; i<=sortedcnt-1; i++)
                        {
                            monitor->nonc0lngrep.stp.ptr.p_double[i] = monitor->sortedstp.ptr.p_double[i];
                            monitor->nonc0lngrep.f.ptr.p_double[i] = monitor->f.ptr.p_double[i];
                        }
                    }
                }
            }
        }
        
        /*
         * C1 continuity test #0
         */
        for(stpidx=0; stpidx<=sortedcnt-7; stpidx++)
        {
            
            /*
             * Fetch function values
             */
            f2 = monitor->f.ptr.p_double[stpidx+2];
            f3 = monitor->f.ptr.p_double[stpidx+3];
            f4 = monitor->f.ptr.p_double[stpidx+4];
            noise2 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f2, _state), 1.0, _state);
            noise3 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f3, _state), 1.0, _state);
            
            /*
             * Decide whether we want to test this interval or not; for target
             * function we test intervals around minimum, for constraints we
             * additionally test intervals of sign change.
             */
            if( funcidx==0 )
            {
                
                /*
                 * Target function: skip if not minimum
                 */
                if( !(f3<f2+(noise2+noise3)&&f3<f4) )
                {
                    continue;
                }
            }
            else
            {
                
                /*
                 * Constraint: skip if both (a) sign does not change, and (b) is not minumum
                 */
                if( ae_sign(f2*f4, _state)>0&&!(f3<f2+(noise2+noise3)&&f3<f4) )
                {
                    continue;
                }
            }
            optserv_c1continuitytest0(monitor, funcidx, stpidx+0, sortedcnt, _state);
            optserv_c1continuitytest0(monitor, funcidx, stpidx+1, sortedcnt, _state);
        }
        
        /*
         * C1 continuity test #1
         */
        for(stpidx=0; stpidx<=sortedcnt-4; stpidx++)
        {
            
            /*
             * Fetch function values from the interval being tested
             */
            f0 = monitor->f.ptr.p_double[stpidx+0];
            f1 = monitor->f.ptr.p_double[stpidx+1];
            f2 = monitor->f.ptr.p_double[stpidx+2];
            f3 = monitor->f.ptr.p_double[stpidx+3];
            noise0 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f0, _state), 1.0, _state);
            noise1 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f1, _state), 1.0, _state);
            noise2 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f2, _state), 1.0, _state);
            noise3 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f3, _state), 1.0, _state);
            
            /*
             * Decide whether we want to test this interval or not; for target
             * function we test intervals around minimum, for constraints we
             * additionally test intervals of sign change.
             */
            if( funcidx==0 )
            {
                
                /*
                 * Skip if not minimum
                 */
                if( !(f1<f0+(noise0+noise1)&&f2<f3+noise2+noise3) )
                {
                    continue;
                }
            }
            else
            {
                
                /*
                 * Skip if sign does not change
                 */
                if( ae_sign(f0*f3, _state)>0&&!(f1<f0+(noise0+noise1)&&f2<f3+noise2+noise3) )
                {
                    continue;
                }
            }
            optserv_c1continuitytest1(monitor, funcidx, stpidx, sortedcnt, _state);
        }
    }
}


/*************************************************************************
This subroutine enqueues one more trial point for a task with scalar
function with unscaled variables - a convenience wrapper for more general
EnqueuePoint()

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
void smoothnessmonitorenqueuepoint1u(smoothnessmonitor* monitor,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* invs,
     /* Real    */ const ae_vector* d,
     double stp,
     /* Real    */ const ae_vector* x,
     double f0,
     /* Real    */ const ae_vector* j0,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t k;
    ae_int_t i;


    n = monitor->n;
    k = monitor->k;
    if( !monitor->checksmoothness )
    {
        return;
    }
    ae_assert(k==1, "SmoothnessMonitorEnqueuePoint1: K<>1", _state);
    rvectorsetlengthatleast(&monitor->xu, n, _state);
    rvectorsetlengthatleast(&monitor->du, n, _state);
    rvectorsetlengthatleast(&monitor->f0, 1, _state);
    rmatrixsetlengthatleast(&monitor->j0, 1, n, _state);
    monitor->f0.ptr.p_double[0] = f0;
    for(i=0; i<=n-1; i++)
    {
        monitor->xu.ptr.p_double[i] = x->ptr.p_double[i]*invs->ptr.p_double[i];
        monitor->du.ptr.p_double[i] = d->ptr.p_double[i]*invs->ptr.p_double[i];
        monitor->j0.ptr.pp_double[0][i] = j0->ptr.p_double[i]*s->ptr.p_double[i];
    }
    smoothnessmonitorenqueuepoint(monitor, &monitor->du, stp, &monitor->xu, &monitor->f0, &monitor->j0, _state);
}


/*************************************************************************
This subroutine finalizes line search

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
void smoothnessmonitorfinalizelinesearch(smoothnessmonitor* monitor,
     ae_state *_state)
{


    
    /*
     * As for now - nothing to be done.
     */
}


/*************************************************************************
This function starts aggressive probing of the Lagrangian for a  range  of
step lengths [0,StpMax].

INPUT PARAMETERS:
    Monitor         -   monitor object
    LagMult         -   array[K-1], lagrange multipliers for nonlinear
                        constraints
    X               -   array[N], initial point for probing
    D               -   array[N], probing direction
    StpMax          -   range of steps to probe
    

  -- ALGLIB --
     Copyright 10.10.2019 by Bochkanov Sergey
*************************************************************************/
void smoothnessmonitorstartlagrangianprobing(smoothnessmonitor* monitor,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* d,
     double stpmax,
     ae_int_t inneriter,
     ae_int_t outeriter,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t k;
    ae_int_t i;


    n = monitor->n;
    k = monitor->k;
    ae_assert(isfinitevector(x, n, _state), "SmoothnessMonitorStartLagrangianProbing: bad X[] array", _state);
    ae_assert(isfinitevector(d, n, _state), "SmoothnessMonitorStartLagrangianProbing: bad D[] array", _state);
    ae_assert(ae_isfinite(stpmax, _state)&&ae_fp_greater(stpmax,(double)(0)), "SmoothnessMonitorStartLagrangianProbing: StpMax<=0", _state);
    ae_assert(k>=1, "SmoothnessMonitorStartLagrangianProbing: monitor object is initialized with K<=0", _state);
    monitor->lagprobnstepsstored = 0;
    monitor->lagprobstepmax = stpmax;
    monitor->lagprobinneriter = inneriter;
    monitor->lagprobouteriter = outeriter;
    rvectorsetlengthatleast(&monitor->lagprobxs, n, _state);
    rvectorsetlengthatleast(&monitor->lagprobd, n, _state);
    for(i=0; i<=n-1; i++)
    {
        monitor->lagprobxs.ptr.p_double[i] = x->ptr.p_double[i];
        monitor->lagprobd.ptr.p_double[i] = d->ptr.p_double[i];
    }
    rvectorsetlengthatleast(&monitor->lagprobx, n, _state);
    rvectorsetlengthatleast(&monitor->lagprobfi, k, _state);
    rmatrixsetlengthatleast(&monitor->lagprobj, k, n, _state);
    ae_vector_set_length(&monitor->lagrangianprobingrcomm.ia, 3+1, _state);
    ae_vector_set_length(&monitor->lagrangianprobingrcomm.ra, 4+1, _state);
    monitor->lagrangianprobingrcomm.stage = -1;
}


/*************************************************************************
This function performs aggressive probing and sends points  to  smoothness
monitoring queue via EnqueuePoint() call.

After  each  call  it  returns  point to evaluate in  Monitor.LagProbX and
current step in Monitor.LagProbStp. Caller has to load function values and
Jacobian at X into Monitor.LagProbFi and Monitor.LagProbJ, current Lagrangian
to Monitor.LagProbRawLag and continue iteration.

NOTE: LagProbX[] is not guarded against constraint violation. Both non-box
      and box constraints are ignored.  It  is  caller  responsibility  to
      provide appropriate  X[],  D[]  and  StpMax  which  do  not  violate
      important constraints

  -- ALGLIB --
     Copyright 10.10.2019 by Bochkanov Sergey
*************************************************************************/
ae_bool smoothnessmonitorprobelagrangian(smoothnessmonitor* monitor,
     ae_state *_state)
{
    ae_int_t stpidx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t idx;
    double stp;
    double vlargest;
    double v;
    double v0;
    double v1;
    ae_bool result;


    
    /*
     * 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( monitor->lagrangianprobingrcomm.stage>=0 )
    {
        stpidx = monitor->lagrangianprobingrcomm.ia.ptr.p_int[0];
        i = monitor->lagrangianprobingrcomm.ia.ptr.p_int[1];
        j = monitor->lagrangianprobingrcomm.ia.ptr.p_int[2];
        idx = monitor->lagrangianprobingrcomm.ia.ptr.p_int[3];
        stp = monitor->lagrangianprobingrcomm.ra.ptr.p_double[0];
        vlargest = monitor->lagrangianprobingrcomm.ra.ptr.p_double[1];
        v = monitor->lagrangianprobingrcomm.ra.ptr.p_double[2];
        v0 = monitor->lagrangianprobingrcomm.ra.ptr.p_double[3];
        v1 = monitor->lagrangianprobingrcomm.ra.ptr.p_double[4];
    }
    else
    {
        stpidx = 359;
        i = -58;
        j = -919;
        idx = -909;
        stp = 81.0;
        vlargest = 255.0;
        v = 74.0;
        v0 = -788.0;
        v1 = 809.0;
    }
    if( monitor->lagrangianprobingrcomm.stage==0 )
    {
        goto lbl_0;
    }
    
    /*
     * Routine body
     */
    stpidx = 0;
lbl_1:
    if( stpidx>40 )
    {
        goto lbl_3;
    }
    
    /*
     * Increase storage size
     */
    rvectorgrowto(&monitor->lagprobsteps, monitor->lagprobnstepsstored+1, _state);
    rvectorgrowto(&monitor->lagproblagrangians, monitor->lagprobnstepsstored+1, _state);
    rmatrixgrowrowsto(&monitor->lagprobvalues, monitor->lagprobnstepsstored+1, monitor->k, _state);
    rmatrixgrowrowsto(&monitor->lagprobjacobians, monitor->lagprobnstepsstored+1, monitor->n*monitor->k, _state);
    
    /*
     * Determine probing step length, save step to the end of the storage
     */
    if( stpidx<=10 )
    {
        
        /*
         * First 11 steps are performed over equidistant grid
         */
        stp = (double)stpidx/(double)10*monitor->lagprobstepmax;
    }
    else
    {
        
        /*
         * Subsequent steps target interesting points
         */
        ae_assert(monitor->lagprobnstepsstored>=3, "SMonitor: critical integrity check failed", _state);
        stp = (double)(0);
        if( stpidx%3==0 )
        {
            
            /*
             * Target interval with maximum change in Lagrangian
             */
            idx = -1;
            vlargest = (double)(0);
            for(j=0; j<=monitor->lagprobnstepsstored-2; j++)
            {
                v = ae_fabs(monitor->lagproblagrangians.ptr.p_double[j+1]-monitor->lagproblagrangians.ptr.p_double[j], _state);
                if( idx<0||ae_fp_greater(v,vlargest) )
                {
                    idx = j;
                    vlargest = v;
                }
            }
            stp = 0.5*(monitor->lagprobsteps.ptr.p_double[idx]+monitor->lagprobsteps.ptr.p_double[idx+1]);
        }
        if( stpidx%3==1 )
        {
            
            /*
             * Target interval [J,J+2] with maximum change in slope of Lagrangian,
             * select subinterval [J,J+1] or [J+1,J+2] (one with maximum length).
             */
            idx = -1;
            vlargest = (double)(0);
            for(j=0; j<=monitor->lagprobnstepsstored-3; j++)
            {
                v0 = (monitor->lagproblagrangians.ptr.p_double[j+1]-monitor->lagproblagrangians.ptr.p_double[j+0])/(monitor->lagprobsteps.ptr.p_double[j+1]-monitor->lagprobsteps.ptr.p_double[j+0]+ae_machineepsilon);
                v1 = (monitor->lagproblagrangians.ptr.p_double[j+2]-monitor->lagproblagrangians.ptr.p_double[j+1])/(monitor->lagprobsteps.ptr.p_double[j+2]-monitor->lagprobsteps.ptr.p_double[j+1]+ae_machineepsilon);
                v = ae_fabs(v0-v1, _state);
                if( idx<0||ae_fp_greater(v,vlargest) )
                {
                    idx = j;
                    vlargest = v;
                }
            }
            if( ae_fp_greater(monitor->lagprobsteps.ptr.p_double[idx+2]-monitor->lagprobsteps.ptr.p_double[idx+1],monitor->lagprobsteps.ptr.p_double[idx+1]-monitor->lagprobsteps.ptr.p_double[idx+0]) )
            {
                stp = 0.5*(monitor->lagprobsteps.ptr.p_double[idx+2]+monitor->lagprobsteps.ptr.p_double[idx+1]);
            }
            else
            {
                stp = 0.5*(monitor->lagprobsteps.ptr.p_double[idx+1]+monitor->lagprobsteps.ptr.p_double[idx+0]);
            }
        }
        if( stpidx%3==2 )
        {
            
            /*
             * Target interval with maximum change in sum of squared Jacobian differences
             */
            idx = -1;
            vlargest = (double)(0);
            for(j=0; j<=monitor->lagprobnstepsstored-2; j++)
            {
                v = (double)(0);
                for(i=0; i<=monitor->k*monitor->n-1; i++)
                {
                    v = v+ae_sqr(monitor->lagprobjacobians.ptr.pp_double[j+1][i]-monitor->lagprobjacobians.ptr.pp_double[j][i], _state);
                }
                if( idx<0||ae_fp_greater(v,vlargest) )
                {
                    idx = j;
                    vlargest = v;
                }
            }
            stp = 0.5*(monitor->lagprobsteps.ptr.p_double[idx]+monitor->lagprobsteps.ptr.p_double[idx+1]);
        }
    }
    monitor->lagprobsteps.ptr.p_double[monitor->lagprobnstepsstored] = stp;
    
    /*
     * Retrieve user values
     */
    for(i=0; i<=monitor->n-1; i++)
    {
        monitor->lagprobx.ptr.p_double[i] = monitor->lagprobxs.ptr.p_double[i]+monitor->lagprobd.ptr.p_double[i]*stp;
    }
    monitor->lagprobstp = stp;
    monitor->lagrangianprobingrcomm.stage = 0;
    goto lbl_rcomm;
lbl_0:
    for(i=0; i<=monitor->k-1; i++)
    {
        monitor->lagprobvalues.ptr.pp_double[monitor->lagprobnstepsstored][i] = monitor->lagprobfi.ptr.p_double[i];
        for(j=0; j<=monitor->n-1; j++)
        {
            monitor->lagprobjacobians.ptr.pp_double[monitor->lagprobnstepsstored][i*monitor->n+j] = monitor->lagprobj.ptr.pp_double[i][j];
        }
    }
    monitor->lagproblagrangians.ptr.p_double[monitor->lagprobnstepsstored] = monitor->lagprobrawlag;
    inc(&monitor->lagprobnstepsstored, _state);
    if( stpidx==0 )
    {
        ae_assert(ae_fp_eq(stp,(double)(0)), "SmoothnessMonitorProbeLagrangian: integrity check failed", _state);
        smoothnessmonitorstartlinesearch(monitor, &monitor->lagprobx, &monitor->lagprobfi, &monitor->lagprobj, monitor->lagprobinneriter, monitor->lagprobouteriter, _state);
    }
    else
    {
        smoothnessmonitorenqueuepoint(monitor, &monitor->lagprobd, stp, &monitor->lagprobx, &monitor->lagprobfi, &monitor->lagprobj, _state);
    }
    
    /*
     * Resort
     */
    for(j=monitor->lagprobnstepsstored-1; j>=1; j--)
    {
        if( ae_fp_less_eq(monitor->lagprobsteps.ptr.p_double[j-1],monitor->lagprobsteps.ptr.p_double[j]) )
        {
            break;
        }
        swapelements(&monitor->lagprobsteps, j-1, j, _state);
        swapelements(&monitor->lagproblagrangians, j-1, j, _state);
        swaprows(&monitor->lagprobvalues, j-1, j, monitor->k, _state);
        swaprows(&monitor->lagprobjacobians, j-1, j, monitor->n*monitor->k, _state);
    }
    stpidx = stpidx+1;
    goto lbl_1;
lbl_3:
    smoothnessmonitorfinalizelinesearch(monitor, _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    monitor->lagrangianprobingrcomm.ia.ptr.p_int[0] = stpidx;
    monitor->lagrangianprobingrcomm.ia.ptr.p_int[1] = i;
    monitor->lagrangianprobingrcomm.ia.ptr.p_int[2] = j;
    monitor->lagrangianprobingrcomm.ia.ptr.p_int[3] = idx;
    monitor->lagrangianprobingrcomm.ra.ptr.p_double[0] = stp;
    monitor->lagrangianprobingrcomm.ra.ptr.p_double[1] = vlargest;
    monitor->lagrangianprobingrcomm.ra.ptr.p_double[2] = v;
    monitor->lagrangianprobingrcomm.ra.ptr.p_double[3] = v0;
    monitor->lagrangianprobingrcomm.ra.ptr.p_double[4] = v1;
    return result;
}


/*************************************************************************
This function prints probing results to trace log.

Tracing is performed using fixed width for all columns, so you  may  print
a header before printing trace - and reasonably expect that its width will
match that of the trace. This function promises that it wont change  trace
output format without introducing breaking changes into its signature.

NOTE: this function ALWAYS tries to print results; it is caller's responsibility
      to decide whether he needs tracing or not.

  -- ALGLIB --
     Copyright 10.10.2019 by Bochkanov Sergey
*************************************************************************/
void smoothnessmonitortracelagrangianprobingresults(smoothnessmonitor* monitor,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    double steplen;
    double mxd;
    ae_vector lagrangianslopes;
    ae_vector targetslopes;

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

    
    /*
     * Compute slopes
     */
    ae_assert(monitor->lagprobnstepsstored>=2, "SmoothnessMonitorTraceLagrangianProbingResults: less than 2 probing steps", _state);
    ae_vector_set_length(&lagrangianslopes, monitor->lagprobnstepsstored, _state);
    ae_vector_set_length(&targetslopes, monitor->lagprobnstepsstored, _state);
    mxd = (double)(0);
    for(i=0; i<=monitor->n-1; i++)
    {
        mxd = ae_maxreal(mxd, ae_fabs(monitor->lagprobd.ptr.p_double[i], _state), _state);
    }
    for(i=0; i<=monitor->lagprobnstepsstored-2; i++)
    {
        steplen = monitor->lagprobsteps.ptr.p_double[i+1]-monitor->lagprobsteps.ptr.p_double[i]+(double)100*ae_machineepsilon;
        steplen = steplen*(mxd+(double)100*ae_machineepsilon);
        lagrangianslopes.ptr.p_double[i] = (monitor->lagproblagrangians.ptr.p_double[i+1]-monitor->lagproblagrangians.ptr.p_double[i])/steplen;
        targetslopes.ptr.p_double[i] = (monitor->lagprobvalues.ptr.pp_double[i+1][0]-monitor->lagprobvalues.ptr.pp_double[i][0])/steplen;
    }
    lagrangianslopes.ptr.p_double[monitor->lagprobnstepsstored-1] = lagrangianslopes.ptr.p_double[monitor->lagprobnstepsstored-2];
    targetslopes.ptr.p_double[monitor->lagprobnstepsstored-1] = targetslopes.ptr.p_double[monitor->lagprobnstepsstored-2];
    
    /*
     * Print to trace log
     */
    ae_trace("*** ------------------------------------------------------------\n");
    for(i=0; i<=monitor->lagprobnstepsstored-1; i++)
    {
        ae_trace("*** | %0.4f |",
            (double)(monitor->lagprobsteps.ptr.p_double[i]));
        ae_trace(" %11.3e %10.2e |",
            (double)(monitor->lagproblagrangians.ptr.p_double[i]-monitor->lagproblagrangians.ptr.p_double[0]),
            (double)(lagrangianslopes.ptr.p_double[i]));
        ae_trace(" %11.3e %10.2e |",
            (double)(monitor->lagprobvalues.ptr.pp_double[i][0]-monitor->lagprobvalues.ptr.pp_double[0][0]),
            (double)(targetslopes.ptr.p_double[i]));
        ae_trace("\n");
    }
    ae_trace("*** ------------------------------------------------------------\n");
    ae_frame_leave(_state);
}


/*************************************************************************
This subroutine tells monitor to output trace info.

If CallerSuggestsTrace=True, monitor  ALWAYS  prints  trace,  even  if  no
suspicions were raised during optimization. If  CallerSuggestsTrace=False,
the monitor will print trace only if:
* trace was requested by trace tag 'OPTGUARD' AND suspicious  points  were
  found during optimization
* trace was requested by trace tag 'OPTGUARD.ALWAYS' - always

  -- ALGLIB --
     Copyright 11.10.2019 by Bochkanov Sergey
*************************************************************************/
void smoothnessmonitortracestatus(const smoothnessmonitor* monitor,
     ae_bool callersuggeststrace,
     ae_state *_state)
{
    ae_bool needreport;
    ae_bool needxdreport;
    ae_bool suspicionsraised;
    ae_int_t i;
    double slope;


    
    /*
     * Do we need trace report?
     */
    suspicionsraised = (monitor->rep.nonc0suspected||monitor->rep.nonc1suspected)||monitor->rep.badgradsuspected;
    needreport = ae_false;
    needreport = needreport||callersuggeststrace;
    needreport = needreport||ae_is_trace_enabled("OPTGUARD.ALWAYS");
    needreport = needreport||(ae_is_trace_enabled("OPTGUARD")&&suspicionsraised);
    if( !needreport )
    {
        return;
    }
    needxdreport = needreport&&ae_is_trace_enabled("OPTIMIZERS.X");
    
    /*
     *
     */
    ae_trace("\n");
    ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
    ae_trace("// OPTGUARD INTEGRITY CHECKER REPORT                                                              //\n");
    ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
    if( !suspicionsraised )
    {
        ae_trace("> no discontinuity/nonsmoothness/bad-gradient suspicions were raised during optimization\n");
        return;
    }
    if( monitor->rep.nonc0suspected )
    {
        ae_trace("> [WARNING] suspected discontinuity (aka C0-discontinuity)\n");
    }
    if( monitor->rep.nonc1suspected )
    {
        ae_trace("> [WARNING] suspected nonsmoothness (aka C1-discontinuity)\n");
    }
    ae_trace("> printing out test reports...\n");
    if( monitor->rep.nonc0suspected&&monitor->rep.nonc0test0positive )
    {
        ae_trace("> printing out discontinuity test #0 report:\n");
        ae_trace("*** -------------------------------------------------------\n");
        ae_trace("*** | Test #0 for discontinuity was triggered  (this test |\n");
        ae_trace("*** | analyzes changes in function values).               |\n");
        ae_trace("*** |                                                     |\n");
        ae_trace("*** | Function information:                               |\n");
        ae_trace("*** | * function index:       %10d",
            (int)(monitor->nonc0lngrep.fidx));
        if( monitor->nonc0lngrep.fidx==0 )
        {
            ae_trace(" (target)         |\n");
        }
        else
        {
            ae_trace(" (constraint)     |\n");
        }
        ae_trace("*** | * F() Lipschitz const:  %10.2e                  |\n",
            (double)(monitor->rep.nonc0lipschitzc));
        ae_trace("*** |                                                     |\n");
        ae_trace("*** | Iteration information:                              |\n");
        if( monitor->nonc0lngrep.inneriter>=0 )
        {
            ae_trace("*** | * inner iter idx:       %10d                  |\n",
                (int)(monitor->nonc0lngrep.inneriter));
        }
        if( monitor->nonc0lngrep.outeriter>=0 )
        {
            ae_trace("*** | * outer iter idx:       %10d                  |\n",
                (int)(monitor->nonc0lngrep.outeriter));
        }
        ae_trace("*** |                                                     |\n");
        ae_trace("*** | Printing out log of suspicious line search XK+Stp*D |\n");
        ae_trace("*** | Look for abrupt changes in slope.                   |\n");
        if( !needxdreport )
        {
            ae_trace("*** | NOTE: XK and D are  not printed  by default. If you |\n");
            ae_trace("*** |       need them, add trace tag OPTIMIZERS.X         |\n");
        }
        ae_trace("*** -------------------------------------------------------\n");
        ae_trace("*** |  step along D   |     delta F     |      slope      |\n");
        ae_trace("*** ------------------------------------------------------|\n");
        for(i=0; i<=monitor->nonc0lngrep.cnt-1; i++)
        {
            slope = monitor->nonc0lngrep.f.ptr.p_double[ae_minint(i+1, monitor->nonc0lngrep.cnt-1, _state)]-monitor->nonc0lngrep.f.ptr.p_double[i];
            slope = slope/(1.0e-15+monitor->nonc0lngrep.stp.ptr.p_double[ae_minint(i+1, monitor->nonc0lngrep.cnt-1, _state)]-monitor->nonc0lngrep.stp.ptr.p_double[i]);
            ae_trace("*** |  %13.5e  |  %13.5e  |   %11.3e   |",
                (double)(monitor->nonc0lngrep.stp.ptr.p_double[i]),
                (double)(monitor->nonc0lngrep.f.ptr.p_double[i]-monitor->nonc0lngrep.f.ptr.p_double[0]),
                (double)(slope));
            if( i>=monitor->nonc0lngrep.stpidxa&&i<=monitor->nonc0lngrep.stpidxb )
            {
                ae_trace(" <---");
            }
            ae_trace("\n");
        }
        ae_trace("*** ------------------------------------------------------|\n");
        if( needxdreport )
        {
            ae_trace("*** > printing raw variables\n");
            ae_trace("*** XK = ");
            tracevectorunscaledunshiftedautoprec(&monitor->nonc0lngrep.x0, monitor->n, &monitor->s, ae_true, &monitor->s, ae_false, _state);
            ae_trace("\n");
            ae_trace("*** D  = ");
            tracevectorunscaledunshiftedautoprec(&monitor->nonc0lngrep.d, monitor->n, &monitor->s, ae_true, &monitor->s, ae_false, _state);
            ae_trace("\n");
            ae_trace("*** > printing scaled variables (values are divided by user-specified scales)\n");
            ae_trace("*** XK = ");
            tracevectorautoprec(&monitor->nonc0lngrep.x0, 0, monitor->n, _state);
            ae_trace("\n");
            ae_trace("*** D  = ");
            tracevectorautoprec(&monitor->nonc0lngrep.d, 0, monitor->n, _state);
            ae_trace("\n");
        }
    }
    if( monitor->rep.nonc1suspected&&monitor->rep.nonc1test0positive )
    {
        ae_trace("> printing out nonsmoothness test #0 report:\n");
        ae_trace("*** -------------------------------------------------------\n");
        ae_trace("*** | Test #0 for nonsmoothness was triggered  (this test |\n");
        ae_trace("*** | analyzes changes in  function  values  and  ignores |\n");
        ae_trace("*** | gradient info).                                     |\n");
        ae_trace("*** |                                                     |\n");
        ae_trace("*** | Function information:                               |\n");
        ae_trace("*** | * function index:         %10d",
            (int)(monitor->nonc1test0lngrep.fidx));
        if( monitor->nonc1test0lngrep.fidx==0 )
        {
            ae_trace(" (target)       |\n");
        }
        else
        {
            ae_trace(" (constraint)   |\n");
        }
        ae_trace("*** | * dF/dX Lipschitz const:  %10.2e                |\n",
            (double)(monitor->rep.nonc1lipschitzc));
        ae_trace("*** |                                                     |\n");
        ae_trace("*** | Iteration information:                              |\n");
        if( monitor->nonc1test0lngrep.inneriter>=0 )
        {
            ae_trace("*** | * inner iter idx:       %10d                  |\n",
                (int)(monitor->nonc1test0lngrep.inneriter));
        }
        if( monitor->nonc1test0lngrep.outeriter>=0 )
        {
            ae_trace("*** | * outer iter idx:       %10d                  |\n",
                (int)(monitor->nonc1test0lngrep.outeriter));
        }
        ae_trace("*** |                                                     |\n");
        ae_trace("*** | Printing out log of suspicious line search XK+Stp*D |\n");
        ae_trace("*** | Look for abrupt changes in slope.                   |\n");
        if( !needxdreport )
        {
            ae_trace("*** | NOTE: XK and D are  not printed  by default. If you |\n");
            ae_trace("*** |       need them, add trace tag OPTIMIZERS.X         |\n");
        }
        ae_trace("*** -------------------------------------------------------\n");
        ae_trace("*** |  step along D   |     delta F     |      slope      |\n");
        ae_trace("*** ------------------------------------------------------|\n");
        for(i=0; i<=monitor->nonc1test0lngrep.cnt-1; i++)
        {
            slope = monitor->nonc1test0lngrep.f.ptr.p_double[ae_minint(i+1, monitor->nonc1test0lngrep.cnt-1, _state)]-monitor->nonc1test0lngrep.f.ptr.p_double[i];
            slope = slope/(1.0e-15+monitor->nonc1test0lngrep.stp.ptr.p_double[ae_minint(i+1, monitor->nonc1test0lngrep.cnt-1, _state)]-monitor->nonc1test0lngrep.stp.ptr.p_double[i]);
            ae_trace("*** |  %13.5e  |  %13.5e  |   %11.3e   |",
                (double)(monitor->nonc1test0lngrep.stp.ptr.p_double[i]),
                (double)(monitor->nonc1test0lngrep.f.ptr.p_double[i]-monitor->nonc1test0lngrep.f.ptr.p_double[0]),
                (double)(slope));
            if( i>=monitor->nonc1test0lngrep.stpidxa&&i<=monitor->nonc1test0lngrep.stpidxb )
            {
                ae_trace(" <---");
            }
            ae_trace("\n");
        }
        ae_trace("*** ------------------------------------------------------|\n");
        if( needxdreport )
        {
            ae_trace("*** > printing raw variables\n");
            ae_trace("*** XK = ");
            tracevectorunscaledunshiftedautoprec(&monitor->nonc1test0lngrep.x0, monitor->n, &monitor->s, ae_true, &monitor->s, ae_false, _state);
            ae_trace("\n");
            ae_trace("*** D  = ");
            tracevectorunscaledunshiftedautoprec(&monitor->nonc1test0lngrep.d, monitor->n, &monitor->s, ae_true, &monitor->s, ae_false, _state);
            ae_trace("\n");
            ae_trace("*** > printing scaled variables (values are divided by user-specified scales)\n");
            ae_trace("*** XK = ");
            tracevectorautoprec(&monitor->nonc1test0lngrep.x0, 0, monitor->n, _state);
            ae_trace("\n");
            ae_trace("*** D  = ");
            tracevectorautoprec(&monitor->nonc1test0lngrep.d, 0, monitor->n, _state);
            ae_trace("\n");
        }
    }
    if( monitor->rep.nonc1suspected&&monitor->rep.nonc1test1positive )
    {
        ae_trace("> printing out nonsmoothness test #1 report:\n");
        ae_trace("*** -------------------------------------------------------\n");
        ae_trace("*** | Test #1 for nonsmoothness was triggered  (this test |\n");
        ae_trace("*** | analyzes changes in gradient components).           |\n");
        ae_trace("*** |                                                     |\n");
        ae_trace("*** | Function information:                               |\n");
        ae_trace("*** | * function index:         %10d",
            (int)(monitor->nonc1test1lngrep.fidx));
        if( monitor->nonc1test1lngrep.fidx==0 )
        {
            ae_trace(" (target)       |\n");
        }
        else
        {
            ae_trace(" (constraint)   |\n");
        }
        ae_trace("*** | * variable index I:       %10d                |\n",
            (int)(monitor->nonc1test1lngrep.vidx));
        ae_trace("*** | * dF/dX Lipschitz const:  %10.2e                |\n",
            (double)(monitor->rep.nonc1lipschitzc));
        ae_trace("*** |                                                     |\n");
        ae_trace("*** | Iteration information:                              |\n");
        if( monitor->nonc1test1lngrep.inneriter>=0 )
        {
            ae_trace("*** | * inner iter idx:       %10d                  |\n",
                (int)(monitor->nonc1test1lngrep.inneriter));
        }
        if( monitor->nonc1test1lngrep.outeriter>=0 )
        {
            ae_trace("*** | * outer iter idx:       %10d                  |\n",
                (int)(monitor->nonc1test1lngrep.outeriter));
        }
        ae_trace("*** |                                                     |\n");
        ae_trace("*** | Printing out log of suspicious line search XK+Stp*D |\n");
        ae_trace("*** | Look for abrupt changes in slope.                   |\n");
        if( !needxdreport )
        {
            ae_trace("*** | NOTE: XK and D are  not printed  by default. If you |\n");
            ae_trace("*** |       need them, add trace tag OPTIMIZERS.X         |\n");
        }
        ae_trace("*** -------------------------------------------------------\n");
        ae_trace("*** |  step along D   |     delta Gi    |      slope      |\n");
        ae_trace("*** ------------------------------------------------------|\n");
        for(i=0; i<=monitor->nonc1test1lngrep.cnt-1; i++)
        {
            slope = monitor->nonc1test1lngrep.g.ptr.p_double[ae_minint(i+1, monitor->nonc1test1lngrep.cnt-1, _state)]-monitor->nonc1test1lngrep.g.ptr.p_double[i];
            slope = slope/(1.0e-15+monitor->nonc1test1lngrep.stp.ptr.p_double[ae_minint(i+1, monitor->nonc1test1lngrep.cnt-1, _state)]-monitor->nonc1test1lngrep.stp.ptr.p_double[i]);
            ae_trace("*** |  %13.5e  |  %13.5e  |   %11.3e   |",
                (double)(monitor->nonc1test1lngrep.stp.ptr.p_double[i]),
                (double)(monitor->nonc1test1lngrep.g.ptr.p_double[i]-monitor->nonc1test1lngrep.g.ptr.p_double[0]),
                (double)(slope));
            if( i>=monitor->nonc1test1lngrep.stpidxa&&i<=monitor->nonc1test1lngrep.stpidxb )
            {
                ae_trace(" <---");
            }
            ae_trace("\n");
        }
        ae_trace("*** ------------------------------------------------------|\n");
        if( needxdreport )
        {
            ae_trace("*** > printing raw variables\n");
            ae_trace("*** XK = ");
            tracevectorunscaledunshiftedautoprec(&monitor->nonc1test1lngrep.x0, monitor->n, &monitor->s, ae_true, &monitor->s, ae_false, _state);
            ae_trace("\n");
            ae_trace("*** D  = ");
            tracevectorunscaledunshiftedautoprec(&monitor->nonc1test1lngrep.d, monitor->n, &monitor->s, ae_true, &monitor->s, ae_false, _state);
            ae_trace("\n");
            ae_trace("*** > printing scaled variables (values are divided by user-specified scales)\n");
            ae_trace("*** XK = ");
            tracevectorautoprec(&monitor->nonc1test1lngrep.x0, 0, monitor->n, _state);
            ae_trace("\n");
            ae_trace("*** D  = ");
            tracevectorautoprec(&monitor->nonc1test1lngrep.d, 0, monitor->n, _state);
            ae_trace("\n");
        }
    }
}


/*************************************************************************
This subroutine exports report to user-readable representation (all arrays
are forced to have exactly same size as needed; unused arrays are  set  to
zero length).

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
void smoothnessmonitorexportreport(smoothnessmonitor* monitor,
     optguardreport* rep,
     ae_state *_state)
{


    
    /*
     * Finalize last line search, just to be sure
     */
    if( monitor->enqueuedcnt>0 )
    {
        smoothnessmonitorfinalizelinesearch(monitor, _state);
    }
    
    /*
     * Export report
     */
    optguardexportreport(&monitor->rep, monitor->n, monitor->k, monitor->badgradhasxj, rep, _state);
}


/*************************************************************************
Check numerical gradient at point X0 (unscaled variables!), with  optional
box constraints [BndL,BndU] (if  HasBoxConstraints=True)  and  with  scale
vector S[].

Step S[i]*TestStep is performed along I-th variable.

NeedFiJ rcomm protocol is used to request derivative information.

Box constraints BndL/BndU are expected to be feasible. It is  possible  to
have BndL=BndU.

  -- ALGLIB --
     Copyright 06.12.2018 by Bochkanov Sergey
*************************************************************************/
ae_bool smoothnessmonitorcheckgradientatx0(smoothnessmonitor* monitor,
     /* Real    */ const ae_vector* unscaledx0,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_bool hasboxconstraints,
     double teststep,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t k;
    ae_int_t i;
    ae_int_t j;
    ae_int_t varidx;
    double v;
    double vp;
    double vm;
    double vc;
    ae_bool result;


    
    /*
     * 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( monitor->rstateg0.stage>=0 )
    {
        n = monitor->rstateg0.ia.ptr.p_int[0];
        k = monitor->rstateg0.ia.ptr.p_int[1];
        i = monitor->rstateg0.ia.ptr.p_int[2];
        j = monitor->rstateg0.ia.ptr.p_int[3];
        varidx = monitor->rstateg0.ia.ptr.p_int[4];
        v = monitor->rstateg0.ra.ptr.p_double[0];
        vp = monitor->rstateg0.ra.ptr.p_double[1];
        vm = monitor->rstateg0.ra.ptr.p_double[2];
        vc = monitor->rstateg0.ra.ptr.p_double[3];
    }
    else
    {
        n = 205;
        k = -838;
        i = 939;
        j = -526;
        varidx = 763;
        v = -541.0;
        vp = -698.0;
        vm = -900.0;
        vc = -318.0;
    }
    if( monitor->rstateg0.stage==0 )
    {
        goto lbl_0;
    }
    if( monitor->rstateg0.stage==1 )
    {
        goto lbl_1;
    }
    if( monitor->rstateg0.stage==2 )
    {
        goto lbl_2;
    }
    if( monitor->rstateg0.stage==3 )
    {
        goto lbl_3;
    }
    
    /*
     * Routine body
     */
    n = monitor->n;
    k = monitor->k;
    monitor->needfij = ae_false;
    
    /*
     * Quick exit
     */
    if( ((n<=0||k<=0)||!ae_isfinite(teststep, _state))||ae_fp_eq(teststep,(double)(0)) )
    {
        result = ae_false;
        return result;
    }
    teststep = ae_fabs(teststep, _state);
    
    /*
     * Allocate storage
     */
    rvectorsetlengthatleast(&monitor->x, n, _state);
    rvectorsetlengthatleast(&monitor->fi, k, _state);
    rmatrixsetlengthatleast(&monitor->j, k, n, _state);
    rvectorsetlengthatleast(&monitor->xbase, n, _state);
    rvectorsetlengthatleast(&monitor->fbase, k, _state);
    rvectorsetlengthatleast(&monitor->fm, k, _state);
    rvectorsetlengthatleast(&monitor->fc, k, _state);
    rvectorsetlengthatleast(&monitor->fp, k, _state);
    rvectorsetlengthatleast(&monitor->jm, k, _state);
    rvectorsetlengthatleast(&monitor->jc, k, _state);
    rvectorsetlengthatleast(&monitor->jp, k, _state);
    rmatrixsetlengthatleast(&monitor->jbaseusr, k, n, _state);
    rmatrixsetlengthatleast(&monitor->jbasenum, k, n, _state);
    rvectorsetlengthatleast(&monitor->rep.badgradxbase, n, _state);
    rmatrixsetlengthatleast(&monitor->rep.badgraduser, k, n, _state);
    rmatrixsetlengthatleast(&monitor->rep.badgradnum, k, n, _state);
    
    /*
     * Set XBase/Jacobian presence flag
     */
    monitor->badgradhasxj = ae_true;
    
    /*
     * Determine reference point, compute function vector and user-supplied Jacobian
     */
    for(i=0; i<=n-1; i++)
    {
        v = unscaledx0->ptr.p_double[i];
        if( (hasboxconstraints&&ae_isfinite(bndl->ptr.p_double[i], _state))&&ae_fp_less(v,bndl->ptr.p_double[i]) )
        {
            v = bndl->ptr.p_double[i];
        }
        if( (hasboxconstraints&&ae_isfinite(bndu->ptr.p_double[i], _state))&&ae_fp_greater(v,bndu->ptr.p_double[i]) )
        {
            v = bndu->ptr.p_double[i];
        }
        monitor->xbase.ptr.p_double[i] = v;
        monitor->rep.badgradxbase.ptr.p_double[i] = v;
        monitor->x.ptr.p_double[i] = v;
    }
    monitor->needfij = ae_true;
    monitor->rstateg0.stage = 0;
    goto lbl_rcomm;
lbl_0:
    monitor->needfij = ae_false;
    for(i=0; i<=k-1; i++)
    {
        monitor->fbase.ptr.p_double[i] = monitor->fi.ptr.p_double[i];
        for(j=0; j<=n-1; j++)
        {
            monitor->jbaseusr.ptr.pp_double[i][j] = monitor->j.ptr.pp_double[i][j];
            monitor->rep.badgraduser.ptr.pp_double[i][j] = monitor->j.ptr.pp_double[i][j];
        }
    }
    
    /*
     * Check Jacobian column by column
     */
    varidx = 0;
lbl_4:
    if( varidx>n-1 )
    {
        goto lbl_6;
    }
    
    /*
     * Determine test location.
     */
    v = monitor->xbase.ptr.p_double[varidx];
    vm = v-s->ptr.p_double[varidx]*teststep;
    vp = v+s->ptr.p_double[varidx]*teststep;
    if( (hasboxconstraints&&ae_isfinite(bndl->ptr.p_double[varidx], _state))&&ae_fp_less(vm,bndl->ptr.p_double[varidx]) )
    {
        vm = bndl->ptr.p_double[varidx];
    }
    if( (hasboxconstraints&&ae_isfinite(bndu->ptr.p_double[varidx], _state))&&ae_fp_greater(vp,bndu->ptr.p_double[varidx]) )
    {
        vp = bndu->ptr.p_double[varidx];
    }
    vc = vm+(vp-vm)/(double)2;
    
    /*
     * Quickly skip fixed variables
     */
    if( (ae_fp_eq(vm,vp)||ae_fp_eq(vc,vm))||ae_fp_eq(vc,vp) )
    {
        for(i=0; i<=k-1; i++)
        {
            monitor->rep.badgradnum.ptr.pp_double[i][varidx] = (double)(0);
        }
        goto lbl_5;
    }
    
    /*
     * Compute F/J at three trial points
     */
    for(i=0; i<=n-1; i++)
    {
        monitor->x.ptr.p_double[i] = monitor->xbase.ptr.p_double[i];
    }
    monitor->x.ptr.p_double[varidx] = vm;
    monitor->needfij = ae_true;
    monitor->rstateg0.stage = 1;
    goto lbl_rcomm;
lbl_1:
    monitor->needfij = ae_false;
    for(i=0; i<=k-1; i++)
    {
        monitor->fm.ptr.p_double[i] = monitor->fi.ptr.p_double[i];
        monitor->jm.ptr.p_double[i] = monitor->j.ptr.pp_double[i][varidx];
    }
    for(i=0; i<=n-1; i++)
    {
        monitor->x.ptr.p_double[i] = monitor->xbase.ptr.p_double[i];
    }
    monitor->x.ptr.p_double[varidx] = vc;
    monitor->needfij = ae_true;
    monitor->rstateg0.stage = 2;
    goto lbl_rcomm;
lbl_2:
    monitor->needfij = ae_false;
    for(i=0; i<=k-1; i++)
    {
        monitor->fc.ptr.p_double[i] = monitor->fi.ptr.p_double[i];
        monitor->jc.ptr.p_double[i] = monitor->j.ptr.pp_double[i][varidx];
    }
    for(i=0; i<=n-1; i++)
    {
        monitor->x.ptr.p_double[i] = monitor->xbase.ptr.p_double[i];
    }
    monitor->x.ptr.p_double[varidx] = vp;
    monitor->needfij = ae_true;
    monitor->rstateg0.stage = 3;
    goto lbl_rcomm;
lbl_3:
    monitor->needfij = ae_false;
    for(i=0; i<=k-1; i++)
    {
        monitor->fp.ptr.p_double[i] = monitor->fi.ptr.p_double[i];
        monitor->jp.ptr.p_double[i] = monitor->j.ptr.pp_double[i][varidx];
    }
    
    /*
     * Check derivative
     */
    for(i=0; i<=k-1; i++)
    {
        monitor->rep.badgradnum.ptr.pp_double[i][varidx] = (monitor->fp.ptr.p_double[i]-monitor->fm.ptr.p_double[i])/(vp-vm);
        if( !derivativecheck(monitor->fm.ptr.p_double[i], monitor->jm.ptr.p_double[i]*s->ptr.p_double[varidx], monitor->fp.ptr.p_double[i], monitor->jp.ptr.p_double[i]*s->ptr.p_double[varidx], monitor->fc.ptr.p_double[i], monitor->jc.ptr.p_double[i]*s->ptr.p_double[varidx], (vp-vm)/s->ptr.p_double[varidx], _state) )
        {
            monitor->rep.badgradsuspected = ae_true;
            monitor->rep.badgradfidx = i;
            monitor->rep.badgradvidx = varidx;
        }
    }
lbl_5:
    varidx = varidx+1;
    goto lbl_4;
lbl_6:
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    monitor->rstateg0.ia.ptr.p_int[0] = n;
    monitor->rstateg0.ia.ptr.p_int[1] = k;
    monitor->rstateg0.ia.ptr.p_int[2] = i;
    monitor->rstateg0.ia.ptr.p_int[3] = j;
    monitor->rstateg0.ia.ptr.p_int[4] = varidx;
    monitor->rstateg0.ra.ptr.p_double[0] = v;
    monitor->rstateg0.ra.ptr.p_double[1] = vp;
    monitor->rstateg0.ra.ptr.p_double[2] = vm;
    monitor->rstateg0.ra.ptr.p_double[3] = vc;
    return result;
}


/*************************************************************************
This function initializes approximation of a Hessian.

Direct BFGS (Hessian matrix H is stored) with stability improvements is used.

INPUT PARAMETERS:
    Hess            -   Hessian structure, initial state is ignored, but
                        previously allocated memory is reused as much as
                        possible
    N               -   dimensions count
    ResetFreq       -   reset frequency for BFGS :
                        * ResetFreq=0 for standard BFGS
                        * ResetFreq>0 for BFGS with periodic resets (helps
                          to maintain fresh curvature information, works
                          better for highly nonquadratic problems)
    StpShort        -   short step length (INF-norm); steps shorter than that
                        are not used for Hessian updates
                        

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
void hessianinitbfgs(xbfgshessian* hess,
     ae_int_t n,
     ae_int_t resetfreq,
     double stpshort,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(resetfreq>=0, "HessianInit: ResetFreq<0", _state);
    resetfreq = ae_minint(resetfreq, n, _state);
    hess->htype = 0;
    hess->n = n;
    hess->resetfreq = resetfreq;
    hess->stpshort = stpshort;
    hess->hage = 0;
    hess->gammasml = 0.000001;
    hess->reg = (double)100*ae_sqrt(ae_machineepsilon, _state);
    hess->smallreg = 0.01*ae_sqrt(ae_machineepsilon, _state);
    hess->microreg = ((double)1000+ae_sqrt((double)(n), _state))*ae_machineepsilon;
    hess->wolfeeps = 0.001;
    hess->maxhess = 1.0E8;
    hess->sumsy = ae_sqr(ae_machineepsilon, _state);
    hess->sumy2 = hess->sumsy*hess->gammasml;
    hess->sums2 = (double)(0);
    hess->updatestatus = 0;
    rvectorsetlengthatleast(&hess->sk, n, _state);
    rvectorsetlengthatleast(&hess->yk, n, _state);
    rsetallocm(n, n, 0.0, &hess->hcurrent, _state);
    rsetallocm(n, n, 0.0, &hess->hincoming, _state);
    for(i=0; i<=n-1; i++)
    {
        hess->hcurrent.ptr.pp_double[i][i] = (double)(1);
        hess->hincoming.ptr.pp_double[i][i] = (double)(1);
    }
}


/*************************************************************************
This function initializes approximation of a Hessian.

Explicit low-rank representation of LBFGS is used.

INPUT PARAMETERS:
    Hess            -   Hessian structure, initial state is ignored, but
                        previously allocated memory is reused as much as
                        possible
    N               -   dimensions count, N>0
    M               -   memory size, M>=0 (values above N will be reduced
                        to N)
    StpShort        -   short step length (INF-norm); steps shorter than that
                        are not used for Hessian updates
    MaxHess         -   maximum Hessian curvature; steps producing curvature
                        larger than that are rejected

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
void hessianinitlowrank(xbfgshessian* hess,
     ae_int_t n,
     ae_int_t m,
     double stpshort,
     double maxhess,
     ae_state *_state)
{


    ae_assert(n>0, "HessianInitLowRank: N<=0", _state);
    ae_assert(m>=0, "HessianInitLowRank: M<0", _state);
    m = ae_minint(m, n, _state);
    
    /*
     * Initialize generic fields
     */
    hess->htype = 3;
    hess->n = n;
    
    /*
     * Initialize mode-specific fields
     */
    hess->m = m;
    hess->memlen = 0;
    hess->sigma = (double)(1);
    if( m>0 )
    {
        rallocm(m, n, &hess->s, _state);
        rallocm(m, n, &hess->y, _state);
        rallocm(m, m, &hess->lowranksst, _state);
        rallocm(m, m, &hess->lowranksyt, _state);
    }
    optserv_resetlowrankmodel(hess, _state);
    
    /*
     * Other fields
     */
    hess->resetfreq = 0;
    hess->stpshort = stpshort;
    hess->hage = 0;
    hess->gammasml = 0.000001;
    hess->reg = (double)100*ae_sqrt(ae_machineepsilon, _state);
    hess->smallreg = 0.01*ae_sqrt(ae_machineepsilon, _state);
    hess->microreg = ((double)1000+ae_sqrt((double)(n), _state))*ae_machineepsilon;
    hess->sumsy = ae_sqr(ae_machineepsilon, _state);
    hess->sumy2 = hess->sumsy*hess->gammasml;
    hess->sums2 = ae_sqr(ae_machineepsilon, _state);
    hess->wolfeeps = 0.001;
    hess->maxhess = maxhess;
    hess->updatestatus = 0;
    rallocv(n, &hess->sk, _state);
    rallocv(n, &hess->yk, _state);
}


/*************************************************************************
This function initializes approximation of a Hessian as a  limited  memory
SR1 model.

INPUT PARAMETERS:
    Hess            -   Hessian structure, initial state is ignored, but
                        previously allocated memory is reused as much as
                        possible
    N               -   dimensions count, N>0
    M               -   memory size, M>=0 (values above N will be reduced
                        to N)
    StpShort        -   short step length (INF-norm); steps shorter than that
                        are not used for Hessian updates
    MaxHess         -   maximum Hessian curvature; steps producing curvature
                        larger than that are rejected

  -- ALGLIB --
     Copyright 08.02.2024 by Bochkanov Sergey
*************************************************************************/
void hessianinitlowranksr1(xbfgshessian* hess,
     ae_int_t n,
     ae_int_t m,
     double stpshort,
     double maxhess,
     ae_state *_state)
{


    ae_assert(n>0, "HessianInitLowRankSR1: N<=0", _state);
    ae_assert(m>=0, "HessianInitLowRankSR1: M<0", _state);
    m = ae_minint(m, n, _state);
    
    /*
     * Initialize generic fields
     */
    hess->htype = 4;
    hess->n = n;
    
    /*
     * Initialize mode-specific fields
     */
    hess->m = m;
    hess->memlen = 0;
    rsetallocv(n, 1.0, &hess->varscale, _state);
    rsetallocv(n, 1.0, &hess->invscale, _state);
    if( m>0 )
    {
        rallocm(m, n, &hess->s, _state);
        rallocm(m, n, &hess->y, _state);
        rallocm(m, n, &hess->corr2, _state);
    }
    optserv_resetlowrankmodel(hess, _state);
    
    /*
     * Other fields
     */
    hess->stpshort = stpshort;
    hess->hage = 0;
    hess->maxhess = maxhess;
    hess->updatestatus = 0;
    hess->mincostheta = (double)500*ae_sqrt(ae_machineepsilon, _state);
    hess->mincrv = (double)10*ae_sqrt(ae_machineepsilon, _state);
    rallocv(n, &hess->sk, _state);
    rallocv(n, &hess->yk, _state);
    rallocv(n, &hess->rk, _state);
}


/*************************************************************************
This function changes upper limit on Hessian norm. It will take effect
upon the next model recomputation.

  -- ALGLIB --
     Copyright 23.09.2023 by Bochkanov Sergey
*************************************************************************/
void hessiansetmaxhess(xbfgshessian* hess,
     double maxhess,
     ae_state *_state)
{


    hess->maxhess = maxhess;
}


/*************************************************************************
This function changes variable scales. The model is invalidated.

Variable scales must be strictly positive. Variable scales are ignored by
models other than SR1.

  -- ALGLIB --
     Copyright 23.09.2023 by Bochkanov Sergey
*************************************************************************/
void hessiansetscales(xbfgshessian* hess,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    ae_assert((hess->htype==0||hess->htype==3)||hess->htype==4, "HessianSetScales: Hessian mode not supported", _state);
    if( hess->htype!=4 )
    {
        return;
    }
    n = hess->n;
    for(i=0; i<=n-1; i++)
    {
        if( s->ptr.p_double[i]<=0.0 )
        {
            ae_assert(ae_false, "HessianSetScales: non-positive scales were supplied", _state);
        }
    }
    rcopyv(n, s, &hess->varscale, _state);
    rsetv(n, 1.0, &hess->invscale, _state);
    rmergedivv(n, s, &hess->invscale, _state);
    optserv_resetlowrankmodel(hess, _state);
}


/*************************************************************************
This function changes variable scales, trying to move them closer to user-
supplied ones, but staying within [0.5,2.0] of the former  ones  in  order
to avoid sharp changes in the Hessian  curvature.  Specific  interval  may
change in future versions without notice.

This function invalidates the model.

Variable scales must be strictly positive. Variable scales are ignored by
models other than SR1.

  -- ALGLIB --
     Copyright 23.09.2023 by Bochkanov Sergey
*************************************************************************/
void hessiansetscalesinertial(xbfgshessian* hess,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    double s0;
    double s1;


    ae_assert((hess->htype==0||hess->htype==3)||hess->htype==4, "HessianSetScales: Hessian mode not supported", _state);
    if( hess->htype!=4 )
    {
        return;
    }
    n = hess->n;
    for(i=0; i<=n-1; i++)
    {
        s0 = hess->varscale.ptr.p_double[i];
        s1 = s->ptr.p_double[i];
        if( s1<=0.0 )
        {
            ae_assert(ae_false, "HessianSetScales: non-positive scales were supplied", _state);
        }
        if( s1<0.5*s0 )
        {
            s1 = 0.5*s0;
        }
        if( s1>2.0*s0 )
        {
            s1 = 2.0*s0;
        }
        hess->varscale.ptr.p_double[i] = s1;
        hess->invscale.ptr.p_double[i] = (double)1/s1;
    }
    optserv_resetlowrankmodel(hess, _state);
}


/*************************************************************************
Updates Hessian estimate, uses regularized formula which prevents  Hessian
eigenvalues from decreasing below ~sqrt(Eps)  and  rejects  updates larger
than ~1/sqrt(Eps) in magnitude.

Either BFGS or LBFGS formula is used, depending on Hessian model settings.
Exception is generated if called for SR1.

INPUT PARAMETERS:
    Hess            -   Hessian state
    X0, G0          -   point #0 and gradient at #0, array[N]
    X1, G1          -   point #1 and gradient at #1, array[N]
    DoTrace         -   True if we want to print trace messages

On return sets Hess.UpdateStatus flag

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
void hessianupdate(xbfgshessian* hess,
     /* Real    */ const ae_vector* x0,
     /* Real    */ const ae_vector* g0,
     /* Real    */ const ae_vector* x1,
     /* Real    */ const ae_vector* g1,
     ae_bool dotrace,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    double gamma;
    double sy;
    double snrm2;
    double ynrm2;
    double ski;
    double yki;
    double yk2;
    double skyk;
    double skg0;
    double skg1;
    double wolfedecay;


    n = hess->n;
    
    /*
     * Prepare Sk, Yk
     */
    sy = (double)(0);
    snrm2 = (double)(0);
    ynrm2 = (double)(0);
    skg0 = (double)(0);
    skg1 = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        ski = x1->ptr.p_double[i]-x0->ptr.p_double[i];
        yki = g1->ptr.p_double[i]-g0->ptr.p_double[i];
        hess->sk.ptr.p_double[i] = ski;
        hess->yk.ptr.p_double[i] = yki;
        sy = sy+ski*yki;
        snrm2 = snrm2+ski*ski;
        ynrm2 = ynrm2+yki*yki;
        skg0 = skg0+ski*g0->ptr.p_double[i];
        skg1 = skg1+ski*g1->ptr.p_double[i];
    }
    hess->updatestatus = 0;
    
    /*
     * Update current and incoming Hessians
     */
    ae_assert(hess->htype==0||hess->htype==3, "HessianUpdate: Hessian mode not supported", _state);
    if( hess->htype==0 )
    {
        
        /*
         * Update dense Hessian using BFGS formula for Bk
         */
        optserv_hessianupdatelowlevel(hess, &hess->hcurrent, &hess->sk, &hess->yk, &hess->updatestatus, _state);
        optserv_hessianupdatelowlevel(hess, &hess->hincoming, &hess->sk, &hess->yk, &i, _state);
        if( ae_fp_greater(sy,(double)(0)) )
        {
            hess->sumsy = hess->sumsy+sy;
            hess->sumy2 = hess->sumy2+ynrm2;
        }
        hess->sums2 = hess->sums2+snrm2;
        hess->hage = hess->hage+1;
        
        /*
         * Perform Hessian reset if needed
         */
        if( hess->resetfreq>0&&hess->hage>=hess->resetfreq )
        {
            rmatrixcopy(n, n, &hess->hincoming, 0, 0, &hess->hcurrent, 0, 0, _state);
            gamma = hess->sumy2/(hess->sumsy+hess->reg*hess->sumy2+hess->smallreg*hess->sums2);
            rsetm(n, n, 0.0, &hess->hincoming, _state);
            for(i=0; i<=n-1; i++)
            {
                hess->hincoming.ptr.pp_double[i][i] = gamma;
            }
            hess->sumsy = ae_sqr(ae_machineepsilon, _state);
            hess->sumy2 = hess->sumsy*hess->gammasml;
            hess->sums2 = (double)(0);
            hess->hage = 0;
            hess->updatestatus = 3;
        }
        return;
    }
    if( hess->htype==3 )
    {
        if( dotrace )
        {
            ae_trace("> analyzing Hessian update:\n>> (Sk,G0)=%0.15e  (Sk,G1)=%0.15e  (Yk,Yk)/(Sk.Yk)=%0.15e\n",
                (double)(skg0),
                (double)(skg1),
                (double)(ynrm2/sy));
        }
        
        /*
         * Decide whether update is good enough to be remembered
         */
        if( hess->m==0 )
        {
            
            /*
             * Zero memory was specified, update ignored
             */
            if( dotrace )
            {
                ae_trace(">> zero memory length, update rejected\n");
            }
            return;
        }
        if( ae_fp_less_eq(rmaxabsv(n, &hess->sk, _state),hess->stpshort) )
        {
            
            /*
             * Sk is too small, skip update
             */
            if( dotrace )
            {
                ae_trace(">> step is too short, update rejected\n");
            }
            return;
        }
        
        /*
         * Reject bad steps
         */
        if( ae_fp_eq(rdotv2(n, &hess->yk, _state),(double)(0)) )
        {
            
            /*
             * The function is linear in the step direction, no update applied
             */
            optserv_popfrontxy(hess, _state);
            hess->sigma = ae_maxreal(0.1*hess->sigma, 1.0E-4, _state);
            optserv_resetlowrankmodel(hess, _state);
            if( dotrace )
            {
                ae_trace(">> zero Yk (linear function?), update rejected, queue size decreased by 1, diagonal scaling sigma=%0.2e (decreased)\n",
                    (double)(hess->sigma));
            }
            return;
        }
        wolfedecay = (double)1-hess->wolfeeps*ae_minreal(ae_sqrt(snrm2, _state), 1.0, _state);
        if( !((ae_fp_less(skg0,(double)(0))&&ae_fp_greater(skg1,wolfedecay*skg0))||(ae_fp_greater(skg1,(double)(0))&&ae_fp_less(skg0,wolfedecay*skg1))) )
        {
            
            /*
             * Wolfe decay condition does not hold
             */
            optserv_popfrontxy(hess, _state);
            hess->sigma = ae_maxreal(0.1*hess->sigma, 1.0E-4, _state);
            optserv_resetlowrankmodel(hess, _state);
            if( dotrace )
            {
                ae_trace(">> Wolfe decay condition does not hold, update rejected, queue size decreased by 1, diagonal scaling sigma=%0.2e (decreased)\n",
                    (double)(hess->sigma));
            }
            return;
        }
        if( ae_fp_greater(sy,(double)(0))&&ae_fp_greater(ynrm2/sy,hess->maxhess) )
        {
            
            /*
             * Hessian norm is too high
             */
            optserv_popfrontxy(hess, _state);
            hess->sigma = ae_maxreal(0.1*hess->sigma, 1.0E-4, _state);
            optserv_resetlowrankmodel(hess, _state);
            if( dotrace )
            {
                ae_trace(">> Hessian norm is too high (%0.2e), update rejected, queue size decreased by 1, diagonal scaling sigma=%0.2e (decreased)\n",
                    (double)(ynrm2/sy),
                    (double)(hess->sigma));
            }
            return;
        }
        
        /*
         * Update historical averages
         */
        hess->sumsy = hess->sumsy+sy;
        hess->sumy2 = hess->sumy2+ynrm2;
        hess->sums2 = hess->sums2+snrm2;
        
        /*
         * Apply regularization:
         * * first,  add REG*Sk to Yk in order to make model at least slightly convex (Sigma is at least REG
         * * second, add REG*Yk to Xk in order to limit model curvature (Sigma is at most 1/REG)
         *
         * Whilst specific order of these operations is not very important, we prefer to Yk+=REG*Sk be first
         * due to Sk being better guarded away from zero.
         */
        raddv(n, hess->reg, &hess->sk, &hess->yk, _state);
        raddv(n, hess->reg, &hess->yk, &hess->sk, _state);
        
        /*
         * Update low rank Hessian data
         */
        ae_assert(hess->memlen<=hess->m, "HessianUpdate: integrity check 5763 failed", _state);
        if( hess->memlen==hess->m )
        {
            optserv_popfrontxy(hess, _state);
        }
        
        /*
         * Append to S and Y
         */
        ae_assert(hess->memlen<hess->m, "HessianUpdate: integrity check 5764 failed", _state);
        hess->memlen = hess->memlen+1;
        rcopyvr(n, &hess->sk, &hess->s, hess->memlen-1, _state);
        rcopyvr(n, &hess->yk, &hess->y, hess->memlen-1, _state);
        
        /*
         * Append row/col to LowRankSST and LowRankSYT
         */
        rallocv(hess->memlen, &hess->buf, _state);
        rgemv(hess->memlen, n, 1.0, &hess->s, 0, &hess->sk, 0.0, &hess->buf, _state);
        rcopyvr(hess->memlen, &hess->buf, &hess->lowranksst, hess->memlen-1, _state);
        rcopyvc(hess->memlen, &hess->buf, &hess->lowranksst, hess->memlen-1, _state);
        rgemv(hess->memlen, n, 1.0, &hess->y, 0, &hess->sk, 0.0, &hess->buf, _state);
        rcopyvr(hess->memlen, &hess->buf, &hess->lowranksyt, hess->memlen-1, _state);
        rgemv(hess->memlen, n, 1.0, &hess->s, 0, &hess->yk, 0.0, &hess->buf, _state);
        rcopyvc(hess->memlen, &hess->buf, &hess->lowranksyt, hess->memlen-1, _state);
        
        /*
         * Recompute scaling
         */
        yk2 = rdotv2(n, &hess->yk, _state);
        skyk = rdotv(n, &hess->sk, &hess->yk, _state);
        hess->sigma = boundval(yk2/skyk, 0.1*hess->sigma, (double)10*hess->sigma, _state);
        hess->sigma = ae_minreal(hess->sigma, (double)1/(hess->reg+ae_machineepsilon), _state);
        if( dotrace )
        {
            ae_trace(">> diagonal scaling sigma=%0.2e\n",
                (double)(hess->sigma));
        }
        
        /*
         * Invalidate model
         */
        optserv_resetlowrankmodel(hess, _state);
    }
}


/*************************************************************************
Updates Hessian estimate,  uses  damped  formula  which  prevents  Hessian
eigenvalues from decreasing below ~sqrt(Eps)  and  rejects  updates larger
than ~1/sqrt(Eps) in magnitude.

Either BFGS, LBFGS or SR1 formula is used, depending on Hessian model
settings. For SR1 models it simply forwards call to HessianUpdateSR1().

INPUT PARAMETERS:
    Hess            -   Hessian state
    X0, G0          -   point #0 and gradient at #0, array[N]
    X1, G1          -   point #1 and gradient at #1, array[N]
    Strategy        -   update stabilization strategy:
                        * 0     do nothing
                        * 1     apply damped (L)BFGS, safeguarding from
                                rapid curvature increase and decrease
                        * 2     apply damped (L)BFGS for too high curvature,
                                ignore Y and decrease curvature conservatively
                                for low values of S'Y
                        Ignored by SR1 update.
    TryReplaceLast  -   whether we append update or try to replace the last
                        one in a queue:
                        * if False, update is applied as usual
                        * if True, then:
                          * if we have BFGS Hessian - applied as usual
                          * if we have L-BFGS Hessian, we pop the last one
                            in a memory (if have any), then add this update
    DoTrace         -   True if we want to print trace messages
    TraceLevel      -   amount of ">" symbols to append to trace messages


  -- ALGLIB --
     Copyright 28.09.2023 by Bochkanov Sergey
*************************************************************************/
void hessianupdatev2(xbfgshessian* hess,
     /* Real    */ const ae_vector* x0,
     /* Real    */ const ae_vector* g0,
     /* Real    */ const ae_vector* x1,
     /* Real    */ const ae_vector* g1,
     ae_int_t strategy,
     ae_bool tryreplacelast,
     ae_bool dotrace,
     ae_int_t tracelevel,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    double gamma;
    double sy;
    double snrm2;
    double ynrm2;
    double ski;
    double yki;
    double skg0;
    double skg1;
    double shs;
    double tau;
    double phi;
    double eps2;
    double eps3;
    double v;


    if( hess->htype==4 )
    {
        hessianupdatesr1(hess, x0, g0, x1, g1, dotrace, tracelevel, _state);
        return;
    }
    n = hess->n;
    eps2 = (double)10*ae_sqrt(ae_machineepsilon, _state);
    eps3 = (double)10*ae_machineepsilon*((double)1+ae_sqrt((double)(n), _state));
    
    /*
     * Print initial trace line
     */
    if( dotrace )
    {
        traceangles(tracelevel, _state);
        ae_trace(" analyzing Hessian update\n");
    }
    
    /*
     * Prepare Sk, Yk, compute some necessary quantities
     */
    sy = (double)(0);
    snrm2 = (double)(0);
    ynrm2 = (double)(0);
    skg0 = (double)(0);
    skg1 = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        ski = x1->ptr.p_double[i]-x0->ptr.p_double[i];
        yki = g1->ptr.p_double[i]-g0->ptr.p_double[i];
        hess->sk.ptr.p_double[i] = ski;
        hess->yk.ptr.p_double[i] = yki;
        sy = sy+ski*yki;
        snrm2 = snrm2+ski*ski;
        ynrm2 = ynrm2+yki*yki;
        skg0 = skg0+ski*g0->ptr.p_double[i];
        skg1 = skg1+ski*g1->ptr.p_double[i];
    }
    hess->updatestatus = 0;
    
    /*
     * Skip updates with zero or short steps
     */
    if( ae_fp_less_eq(rmaxabsv(n, &hess->sk, _state),hess->stpshort) )
    {
        
        /*
         * Sk is too small, skip update
         */
        if( dotrace )
        {
            traceangles(tracelevel+1, _state);
            ae_trace(" step is too short, update rejected\n");
        }
        return;
    }
    
    /*
     * Analyze existing curvature along S
     */
    hessianmv(hess, &hess->sk, &hess->hsk, _state);
    shs = rdotv(n, &hess->sk, &hess->hsk, _state);
    if( ae_fp_greater(shs,eps3*snrm2) )
    {
        
        /*
         * Hessian has sufficiently good curvature along Sk, can proceed with analysis of update.
         * Apply damping or other stabilization
         */
        ae_assert((strategy==0||strategy==1)||strategy==2, "OPTSERV: integrity check 9223 failed", _state);
        phi = 1.0;
        tau = sy/shs;
        if( strategy==1&&ae_fp_less(tau,optserv_xbfgssigma2) )
        {
            if( dotrace )
            {
                traceangles(tracelevel+1, _state);
                ae_trace(" applying damping to correct too low (or rapidly changing) curvature\n");
            }
            phi = ((double)1-optserv_xbfgssigma2)/((double)1-tau);
        }
        if( strategy==2&&ae_fp_less(tau,optserv_xbfgssigma2) )
        {
            if( dotrace )
            {
                traceangles(tracelevel+1, _state);
                ae_trace(" applying conservative curvature decrease to correct too low (or rapidly changing) curvature\n");
            }
            rcopyv(n, &hess->hsk, &hess->yk, _state);
            rmulv(n, optserv_xbfgssigma2, &hess->yk, _state);
            sy = optserv_xbfgssigma2*shs;
            ynrm2 = rdotv2(n, &hess->yk, _state);
        }
        if( (strategy==1||strategy==2)&&ae_fp_greater(tau,optserv_xbfgssigma3) )
        {
            if( dotrace )
            {
                traceangles(tracelevel+1, _state);
                ae_trace(" applying damping to correct too high (or rapidly changing) curvature\n");
            }
            phi = (optserv_xbfgssigma3-(double)1)/(tau-(double)1);
        }
        if( ae_fp_neq(phi,(double)(1)) )
        {
            rmulv(n, phi, &hess->yk, _state);
            raddv(n, (double)1-phi, &hess->hsk, &hess->yk, _state);
            sy = rdotv(n, &hess->sk, &hess->yk, _state);
            ynrm2 = rdotv2(n, &hess->yk, _state);
        }
    }
    else
    {
        
        /*
         * Hessian is not sufficiently good along Sk, propose conservative curvature-preserving update
         */
        if( dotrace )
        {
            traceangles(tracelevel+1, _state);
            ae_trace(" Hessian curvature along Sk is too low, proposing conservative corrector update\n");
        }
        rcopymulv(n, eps2, &hess->sk, &hess->yk, _state);
        sy = rdotv(n, &hess->sk, &hess->yk, _state);
        ynrm2 = rdotv2(n, &hess->yk, _state);
    }
    
    /*
     * Do not allow a  lower bound on Hessian eigenvalues (S'Y)/(S'S) to decrease below Eps2,
     * do not allow an upper bound on Hessian eigenvalues (Y'Y)/(S'Y) to increase above 1/Eps2
     */
    if( ae_fp_less_eq(sy,(double)(0))||ae_fp_less(sy/snrm2,eps2) )
    {
        if( dotrace )
        {
            traceangles(tracelevel+1, _state);
            ae_trace(" damped curvature is still too low (lower estimate is SY/SS=%0.2e), truncating to ~sqrt(epsilon)\n",
                (double)(sy/snrm2));
        }
        rcopymulv(n, eps2, &hess->sk, &hess->yk, _state);
        sy = rdotv(n, &hess->sk, &hess->yk, _state);
        ynrm2 = rdotv2(n, &hess->yk, _state);
    }
    if( ae_fp_greater(sy,(double)(0))&&ae_fp_greater(ynrm2/sy,(double)1/eps2) )
    {
        if( dotrace )
        {
            traceangles(tracelevel+1, _state);
            ae_trace(" damped curvature is still too high (upper estimate is YY/SY=%0.2e), truncating to ~1/sqrt(epsilon)\n",
                (double)(ynrm2/sy));
        }
        v = (double)1/eps2/(ynrm2/sy);
        rmulv(n, v, &hess->yk, _state);
        sy = v*sy;
        ynrm2 = v*v*ynrm2;
    }
    
    /*
     * Update current and incoming Hessians
     */
    ae_assert(hess->htype==0||hess->htype==3, "HessianUpdate: Hessian mode not supported", _state);
    if( hess->htype==0 )
    {
        
        /*
         * Update dense Hessian using BFGS formula for Bk
         */
        optserv_hessianupdatelowlevel2(hess, &hess->hcurrent, &hess->sk, &hess->yk, &hess->updatestatus, _state);
        optserv_hessianupdatelowlevel2(hess, &hess->hincoming, &hess->sk, &hess->yk, &i, _state);
        if( ae_fp_greater(sy,(double)(0)) )
        {
            hess->sumsy = hess->sumsy+sy;
            hess->sumy2 = hess->sumy2+ynrm2;
        }
        hess->sums2 = hess->sums2+snrm2;
        hess->hage = hess->hage+1;
        if( dotrace )
        {
            traceangles(tracelevel+1, _state);
            ae_trace(" BFGS update applied\n");
        }
        
        /*
         * Perform Hessian reset if needed
         */
        if( hess->resetfreq>0&&hess->hage>=hess->resetfreq )
        {
            rmatrixcopy(n, n, &hess->hincoming, 0, 0, &hess->hcurrent, 0, 0, _state);
            gamma = hess->sumy2/(hess->sumsy+hess->reg*hess->sumy2+hess->smallreg*hess->sums2);
            rsetm(n, n, 0.0, &hess->hincoming, _state);
            for(i=0; i<=n-1; i++)
            {
                hess->hincoming.ptr.pp_double[i][i] = gamma;
            }
            hess->sumsy = ae_sqr(ae_machineepsilon, _state);
            hess->sumy2 = hess->sumsy*hess->gammasml;
            hess->sums2 = (double)(0);
            hess->hage = 0;
            hess->updatestatus = 3;
        }
        return;
    }
    if( hess->htype==3 )
    {
        
        /*
         * Decide whether update is good enough to be remembered
         */
        if( hess->m==0 )
        {
            
            /*
             * Zero memory was specified, update ignored
             */
            if( dotrace )
            {
                traceangles(tracelevel+1, _state);
                ae_trace(" zero memory length, update rejected\n");
            }
            return;
        }
        
        /*
         * Update historical averages
         */
        hess->sumsy = hess->sumsy+sy;
        hess->sumy2 = hess->sumy2+ynrm2;
        hess->sums2 = hess->sums2+snrm2;
        
        /*
         * Remove items from memory, if necessary
         */
        ae_assert(hess->memlen<=hess->m, "HessianUpdate: integrity check 5763 failed", _state);
        if( tryreplacelast )
        {
            if( dotrace )
            {
                traceangles(tracelevel+1, _state);
                ae_trace(" rewriting most recent update pair\n");
            }
            hessianpoplatestifpossible(hess, _state);
        }
        if( hess->memlen==hess->m )
        {
            optserv_popfrontxy(hess, _state);
        }
        
        /*
         * Append to S and Y
         */
        ae_assert(hess->memlen<hess->m, "HessianUpdate: integrity check 5764 failed", _state);
        hess->memlen = hess->memlen+1;
        rcopyvr(n, &hess->sk, &hess->s, hess->memlen-1, _state);
        rcopyvr(n, &hess->yk, &hess->y, hess->memlen-1, _state);
        
        /*
         * Append row/col to LowRankSST and LowRankSYT
         */
        rallocv(hess->memlen, &hess->buf, _state);
        rgemv(hess->memlen, n, 1.0, &hess->s, 0, &hess->sk, 0.0, &hess->buf, _state);
        rcopyvr(hess->memlen, &hess->buf, &hess->lowranksst, hess->memlen-1, _state);
        rcopyvc(hess->memlen, &hess->buf, &hess->lowranksst, hess->memlen-1, _state);
        rgemv(hess->memlen, n, 1.0, &hess->y, 0, &hess->sk, 0.0, &hess->buf, _state);
        rcopyvr(hess->memlen, &hess->buf, &hess->lowranksyt, hess->memlen-1, _state);
        rgemv(hess->memlen, n, 1.0, &hess->s, 0, &hess->yk, 0.0, &hess->buf, _state);
        rcopyvc(hess->memlen, &hess->buf, &hess->lowranksyt, hess->memlen-1, _state);
        
        /*
         * Recompute scaling
         */
        hess->sigma = boundval(ynrm2/sy, eps2, (double)1/eps2, _state);
        if( dotrace )
        {
            traceangles(tracelevel+1, _state);
            ae_trace(" LBFGS update applied, diagonal scaling sigma=%0.2e\n",
                (double)(hess->sigma));
        }
        
        /*
         * Invalidate model
         */
        optserv_resetlowrankmodel(hess, _state);
    }
}


/*************************************************************************
Updates Hessian estimate using SR1 formula. Fails for other model types.

INPUT PARAMETERS:
    Hess            -   Hessian state
    X0, G0          -   point #0 and gradient at #0, array[N]
    X1, G1          -   point #1 and gradient at #1, array[N]
    DoTrace         -   True if we want to print trace messages
    TraceLevel      -   amount of ">" symbols to append to trace messages


  -- ALGLIB --
     Copyright 08.02.2024 by Bochkanov Sergey
*************************************************************************/
void hessianupdatesr1(xbfgshessian* hess,
     /* Real    */ const ae_vector* x0,
     /* Real    */ const ae_vector* g0,
     /* Real    */ const ae_vector* x1,
     /* Real    */ const ae_vector* g1,
     ae_bool dotrace,
     ae_int_t tracelevel,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    double dbgsigma;
    double dbgsy;
    double dbgsnrm2;


    ae_assert(hess->htype==4, "HessianUpdateSR1: only SR1 models are supported", _state);
    n = hess->n;
    
    /*
     * Print initial trace line
     */
    if( dotrace )
    {
        traceangles(tracelevel, _state);
        ae_trace(" analyzing Hessian update\n");
    }
    
    /*
     * Prepare Sk, Yk, compute some necessary quantities
     */
    for(i=0; i<=n-1; i++)
    {
        hess->sk.ptr.p_double[i] = x1->ptr.p_double[i]-x0->ptr.p_double[i];
        hess->yk.ptr.p_double[i] = g1->ptr.p_double[i]-g0->ptr.p_double[i];
    }
    
    /*
     * Skip updates with zero or short steps, zero memory, etc
     */
    if( hess->m==0 )
    {
        
        /*
         * Zero memory was specified, update ignored
         */
        if( dotrace )
        {
            traceangles(tracelevel+1, _state);
            ae_trace(" zero memory length, update rejected\n");
        }
        return;
    }
    if( ae_fp_less_eq(rmaxabsv(n, &hess->sk, _state),hess->stpshort) )
    {
        
        /*
         * Sk is too small, skip update
         */
        if( dotrace )
        {
            traceangles(tracelevel+1, _state);
            ae_trace(" step is too short, update rejected, enforcing Hessian decay\n");
        }
        hessianmultiplyby(hess, 0.2, _state);
        return;
    }
    
    /*
     * Prepare update
     */
    ae_assert(hess->memlen<=hess->m, "HessianUpdateSR1: integrity check 7929 failed", _state);
    if( hess->memlen==hess->m )
    {
        optserv_popfrontxy(hess, _state);
    }
    ae_assert(hess->memlen<hess->m, "HessianUpdateSR1: integrity check 7930 failed", _state);
    hess->memlen = hess->memlen+1;
    rcopyvr(n, &hess->sk, &hess->s, hess->memlen-1, _state);
    rcopyvr(n, &hess->yk, &hess->y, hess->memlen-1, _state);
    if( dotrace )
    {
        traceangles(tracelevel+1, _state);
        dbgsy = (double)(0);
        dbgsnrm2 = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            dbgsy = dbgsy+hess->sk.ptr.p_double[i]*hess->yk.ptr.p_double[i];
            dbgsnrm2 = dbgsnrm2+ae_sqr(hess->sk.ptr.p_double[i]/hess->varscale.ptr.p_double[i], _state);
        }
        dbgsigma = dbgsy/(dbgsnrm2+ae_machineepsilon*ae_machineepsilon);
        ae_trace(" SR1 update applied, diagonal scaling sigma=SY/SS=%0.2e (SY=%0.2e, SS=%0.2e in scaled vars)\n",
            (double)(dbgsigma),
            (double)(dbgsy),
            (double)(dbgsnrm2));
    }
    optserv_resetlowrankmodel(hess, _state);
}


/*************************************************************************
Removes newest update pair from the limited memory Hessian model and
invalidates Hessian model. If Hessian does not allow to pop latest pair,
does nothing.

  -- ALGLIB --
     Copyright 28.09.2023 by Bochkanov Sergey
*************************************************************************/
void hessianpoplatestifpossible(xbfgshessian* hess, ae_state *_state)
{


    ae_assert(hess->htype==0||hess->htype==3, "HessianPopLatestIfPossible: Hessian mode is not supported", _state);
    if( hess->htype==0 )
    {
        return;
    }
    if( hess->memlen==0 )
    {
        return;
    }
    ae_assert(hess->htype==3, "OPTSERV: integrity check 0140 failed", _state);
    hess->memlen = hess->memlen-1;
    optserv_resetlowrankmodel(hess, _state);
}


/*************************************************************************
Multiplies internally stored model (and all internally stored corrections)
by S.

This function works only with the following Hessian modes:
* direct BFGS
* low-rank LBFGS

Any attempt to call it for other Hessian type will result in exception.

INPUT PARAMETERS:
    Hess            -   Hessian state
    S               -   scale factor

  -- ALGLIB --
     Copyright 05.06.2023 by Bochkanov Sergey
*************************************************************************/
void hessianmultiplyby(xbfgshessian* hess, double s, ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;


    ae_assert((hess->htype==0||hess->htype==3)||hess->htype==4, "HessianMultiplyBy: Hessian mode is not supported", _state);
    n = hess->n;
    if( hess->htype==0 )
    {
        
        /*
         * Explicit dense Hessian
         */
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                hess->hcurrent.ptr.pp_double[i][j] = hess->hcurrent.ptr.pp_double[i][j]*s;
                hess->hincoming.ptr.pp_double[i][j] = hess->hincoming.ptr.pp_double[i][j]*s;
            }
        }
    }
    if( hess->htype==3 )
    {
        
        /*
         * Low rank model
         */
        hess->sigma = s*hess->sigma;
        for(i=0; i<=hess->memlen-1; i++)
        {
            rmulr(n, s, &hess->y, i, _state);
            rmulr(hess->memlen, s, &hess->lowranksyt, i, _state);
        }
        optserv_resetlowrankmodel(hess, _state);
    }
    if( hess->htype==4 )
    {
        
        /*
         * Low rank model
         */
        for(i=0; i<=hess->memlen-1; i++)
        {
            rmulr(n, s, &hess->y, i, _state);
        }
        optserv_resetlowrankmodel(hess, _state);
    }
}


/*************************************************************************
Get diagonal of the Hessian.

This function works only with the following Hessian modes:
* direct BFGS
* low-rank LBFGS

Any attempt to call it for other Hessian type will result in exception.

INPUT PARAMETERS:
    Hess            -   Hessian state
    D               -   possibly preallocated array; resized if needed
    
OUTPUT PARAMETERS:
    D               -   first N elements are filled with Hessian diagonal

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
void hessiangetdiagonal(xbfgshessian* hess,
     /* Real    */ ae_vector* d,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    ae_assert((hess->htype==0||hess->htype==3)||hess->htype==4, "HessianGetDiagonal: Hessian mode is not supported", _state);
    n = hess->n;
    rallocv(n, d, _state);
    if( hess->htype==0 )
    {
        
        /*
         * Explicit dense Hessian
         */
        for(i=0; i<=n-1; i++)
        {
            d->ptr.p_double[i] = hess->hcurrent.ptr.pp_double[i][i];
        }
    }
    if( hess->htype==3 )
    {
        
        /*
         * Low-rank model
         */
        optserv_recomputelowrankmodel(hess, _state);
        optserv_recomputelowrankdiagonal(hess, _state);
        rcopyv(n, &hess->lowrankeffd, d, _state);
    }
    if( hess->htype==4 )
    {
        
        /*
         * Low-rank model
         */
        optserv_recomputelowrankmodel(hess, _state);
        optserv_recomputelowrankdiagonal(hess, _state);
        rcopyv(n, &hess->sr1effd, d, _state);
    }
}


/*************************************************************************
Get Hessian matrix in a dense format.

This function works only with the following Hessian modes:
* direct BFGS
* low-rank LBFGS (needs k*N*N operations)

Any attempt to call it for other Hessian type will result in exception.

INPUT PARAMETERS:
    Hess            -   Hessian state
    IsUpper         -   whether upper or lower triangle is needed
    H               -   possibly preallocated array; resized if needed
    
OUTPUT PARAMETERS:
    H               -   either upper or lower NxN elements are filled
                        with Hessian

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
void hessiangetmatrix(xbfgshessian* hess,
     ae_bool isupper,
     /* Real    */ ae_matrix* h,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    ae_assert((hess->htype==0||hess->htype==3)||hess->htype==4, "HessianGetMatrix: Hessian mode is not supported", _state);
    n = hess->n;
    rallocm(n, n, h, _state);
    if( hess->htype==0 )
    {
        
        /*
         * Dense direct Hessian
         */
        rcopym(n, n, &hess->hcurrent, h, _state);
    }
    if( hess->htype==3 )
    {
        
        /*
         * Low-rank BFGS model
         */
        optserv_recomputelowrankmodel(hess, _state);
        rsetm(n, n, 0.0, h, _state);
        for(i=0; i<=n-1; i++)
        {
            h->ptr.pp_double[i][i] = hess->sigma;
        }
        rmatrixgemm(n, n, hess->lowrankk, 1.0, &hess->lowrankcp, 0, 0, 1, &hess->lowrankcp, 0, 0, 0, 1.0, h, 0, 0, _state);
        rmatrixgemm(n, n, hess->lowrankk, -1.0, &hess->lowrankcm, 0, 0, 1, &hess->lowrankcm, 0, 0, 0, 1.0, h, 0, 0, _state);
    }
    if( hess->htype==4 )
    {
        
        /*
         * Low-rank SR1 model
         */
        optserv_recomputelowrankmodel(hess, _state);
        rsetm(n, n, 0.0, h, _state);
        for(i=0; i<=n-1; i++)
        {
            h->ptr.pp_double[i][i] = hess->sr1d.ptr.p_double[i];
        }
        if( hess->sr1k>0 )
        {
            ae_assert(hess->sr1k<=hess->m, "OPTSERV: integrity check 4215 failed", _state);
            rcopym(hess->sr1k, n, &hess->sr1c, &hess->corr2, _state);
            for(i=0; i<=hess->sr1k-1; i++)
            {
                rmulr(n, hess->sr1z.ptr.p_double[i], &hess->corr2, i, _state);
            }
            rmatrixgemm(n, n, hess->sr1k, 1.0, &hess->sr1c, 0, 0, 1, &hess->corr2, 0, 0, 0, 1.0, h, 0, 0, _state);
        }
    }
}


/*************************************************************************
Returns 2-norm of the Hessian (can be cheaply computed due to its low-rank
structure, always computed during the update)

This function works only with the following Hessian modes:
* low-rank SR1

Any attempt to call it for other Hessian type will result in exception.

INPUT PARAMETERS:
    Hess            -   Hessian state
    
RESULT:
    2-norm of the unmodified Hessian (no positive-definiteness correction).
    The norm is computed in scaled variables, as set by HessianSetScales.
    

  -- ALGLIB --
     Copyright 28.02.2024 by Bochkanov Sergey
*************************************************************************/
double hessiangetnrm2(xbfgshessian* hess, ae_state *_state)
{
    double result;


    ae_assert(hess->htype==4, "HessianGetNrm2: Hessian mode is not supported", _state);
    result = (double)(0);
    if( hess->htype==4 )
    {
        
        /*
         * Low-rank SR1 model
         */
        optserv_recomputelowrankmodel(hess, _state);
        result = hess->sr1nrm2;
    }
    return result;
}


/*************************************************************************
Returns 2-norm of the stabilized Hessian (can be cheaply computed  due  to
its low-rank structure, always computed during the update)

This function works only with the following Hessian modes:
* low-rank SR1

Any attempt to call it for other Hessian type will result in exception.

INPUT PARAMETERS:
    Hess            -   Hessian state
    
RESULT:
    2-norm of the stabilized Hessian (positive-definiteness correction).
    The norm is computed in scaled variables, as set by HessianSetScales.
    

  -- ALGLIB --
     Copyright 28.02.2024 by Bochkanov Sergey
*************************************************************************/
double hessiangetnrm2stab(xbfgshessian* hess, ae_state *_state)
{
    double result;


    ae_assert(hess->htype==4, "HessianGetNrm2: Hessian mode is not supported", _state);
    result = (double)(0);
    if( hess->htype==4 )
    {
        
        /*
         * Low-rank SR1 model
         */
        optserv_recomputelowrankmodel(hess, _state);
        result = hess->sr1nrm2stab;
    }
    return result;
}


/*************************************************************************
Get Hessian matrix in a low-rank format D + C'*S*C where D is  a  diagonal
matrix, C is a low rank matrix, S is a matrix of +1 and -1.

This function works only with the following Hessian modes:
* low-rank LBFGS (needs k*N operations)

Any attempt to call it for other Hessian type will result in an exception.

IMPORTANT: thus function uses original formulas  from  'REPRESENTATIONS OF
           QUASI-NEWTON MATRICES AND THEIR USE IN LIMITED MEMORY  METHODS'
           which produce a positive definite D+C'*S*C.
           However, only  the  final  result  is  positive  definite.  Any
           partial update D+G'*U*G, where G/U are subsets of C/S,  is  NOT
           guaranteed to be positive definite. Lack  of  these  guarantees
           usually destabilizes interior point low rank  QP   solvers  and
           other algorithms which rely on positive definiteness of partial
           updates.
           Use HessianGetLowRankStabilized() if you need robust updates.

INPUT PARAMETERS:
    Hess            -   Hessian state
    D               -   possibly preallocated array, reused if large enough
    CorrC           -   possibly preallocated array, reused if large enough
    CorrS           -   possibly preallocated array, reused if large enough
    
OUTPUT PARAMETERS:
    D               -   array[N], diagonal matrix
    CorrC           -   array[CorrK,N], low rank correction
    CorrS           -   array[CorrK], signs
    CorrK           -   correction rank, CorrK>=0

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
void hessiangetlowrank(xbfgshessian* hess,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_matrix* corrc,
     /* Real    */ ae_vector* corrs,
     ae_int_t* corrk,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;

    *corrk = 0;

    ae_assert(hess->htype==3||hess->htype==4, "HessianGetMatrixLowRank: Hessian mode is not supported", _state);
    n = hess->n;
    if( hess->htype==3 )
    {
        
        /*
         * Low-rank model
         */
        optserv_recomputelowrankmodel(hess, _state);
        *corrk = 2*hess->lowrankk;
        rsetallocv(n, hess->sigma, d, _state);
        if( hess->lowrankk>0 )
        {
            rallocm(*corrk, n, corrc, _state);
            rallocv(*corrk, corrs, _state);
            for(i=0; i<=hess->lowrankk-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    corrc->ptr.pp_double[2*i+0][j] = hess->lowrankcp.ptr.pp_double[i][j];
                    corrc->ptr.pp_double[2*i+1][j] = hess->lowrankcm.ptr.pp_double[i][j];
                }
                corrs->ptr.p_double[2*i+0] = (double)(1);
                corrs->ptr.p_double[2*i+1] = (double)(-1);
            }
        }
    }
    if( hess->htype==4 )
    {
        
        /*
         * Low-rank model
         */
        optserv_recomputelowrankmodel(hess, _state);
        *corrk = hess->sr1k;
        rcopyallocv(n, &hess->sr1d, d, _state);
        if( hess->sr1k>0 )
        {
            rcopyallocm(*corrk, n, &hess->sr1c, corrc, _state);
            rcopyallocv(*corrk, &hess->sr1z, corrs, _state);
        }
    }
}


/*************************************************************************
Get LBFGS memory contents:
* steps S
* gradient changes Y
* diagonal scaling Sigma

The function guarantees that for each k we have (Sk,Yk)>0, however it does
NOT guarantee that all steps are stored and that steps are stored  as  is.
It may modify S and Y in order to improve conditioning, and  it  may  skip
some update pairs if it decides to do so.

This function works only with the following Hessian modes:
* low-rank LBFGS

Any attempt to call it for other Hessian type will result in an exception.

INPUT PARAMETERS:
    Hess            -   Hessian state
    S               -   possibly preallocated array, reused if large enough
    Y               -   possibly preallocated array, reused if large enough
    
OUTPUT PARAMETERS:
    Sigma           -   Hessian diagonal magnitude, Sigma>0
    S               -   array[UpdCnt,N], steps stored (latest steps are
                        stored at the end)
    Y               -   array[UpdCnt,N], gradient changes
    UpdCnt          -   updates count

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
void hessiangetlowrankmemory(xbfgshessian* hess,
     double* sigma,
     /* Real    */ ae_matrix* s,
     /* Real    */ ae_matrix* y,
     ae_int_t* updcnt,
     ae_state *_state)
{
    ae_int_t n;

    *sigma = 0.0;
    *updcnt = 0;

    ae_assert(hess->htype==3, "HessianGetMatrixLowRank: Hessian mode is not supported", _state);
    n = hess->n;
    if( hess->htype==3 )
    {
        *sigma = hess->sigma;
        *updcnt = hess->memlen;
        if( hess->memlen>0 )
        {
            rcopyallocm(hess->memlen, n, &hess->s, s, _state);
            rcopyallocm(hess->memlen, n, &hess->y, y, _state);
        }
    }
}


/*************************************************************************
Get Hessian matrix in a low-rank format D + C'*S*C where D is  a  diagonal
matrix, C is a low rank matrix, S is a matrix of +1 and -1.

This is a stabilized version of a low rank representation which guarantees
that both complete update C'*S*C of D and any partial update are  positive
definite. It is more expensive  than  HessianGetLowRank()  because  a  KxK
symmetric eigenproblem has to be solved in order to compute robust update.

Unlike non-stabilized version, this function produces  C  with  orthogonal
rows (rows are mutually orthogonal, although not normalized and  can  even
be zero).

This function works only with the following Hessian modes:
* low-rank LBFGS
* low-rank SR1, in which case it returns a positive definite approximation

Any attempt to call it for other Hessian type will result in an exception.

INPUT PARAMETERS:
    Hess            -   Hessian state
    D               -   possibly preallocated array, reused if large enough
    CorrC           -   possibly preallocated array, reused if large enough
    CorrS           -   possibly preallocated array, reused if large enough
    
OUTPUT PARAMETERS:
    D               -   array[N], diagonal matrix
    CorrC           -   array[CorrK,N], low rank correction
    CorrS           -   array[CorrK], signs
    CorrK           -   correction rank, CorrK>=0

  -- ALGLIB --
     Copyright 10.02.2024 by Bochkanov Sergey
*************************************************************************/
void hessiangetlowrankstabilized(xbfgshessian* hess,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_matrix* corrc,
     /* Real    */ ae_vector* corrs,
     ae_int_t* corrk,
     ae_state *_state)
{

    *corrk = 0;

    ae_assert(hess->htype==3||hess->htype==4, "HessianGetMatrixLowRankStabilized: Hessian mode is not supported", _state);
    if( hess->htype==3 )
    {
        hessiangetlowrankstabilizedlbfgs(hess, d, corrc, corrs, corrk, _state);
    }
    if( hess->htype==4 )
    {
        hessiangetlowrankstabilizedsr1(hess, d, corrc, corrs, corrk, _state);
    }
}


/*************************************************************************
Low rank stabilized LBFGS model

  -- ALGLIB --
     Copyright 06.06.2023 by Bochkanov Sergey
*************************************************************************/
void hessiangetlowrankstabilizedlbfgs(xbfgshessian* hess,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_matrix* corrc,
     /* Real    */ ae_vector* corrs,
     ae_int_t* corrk,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;

    *corrk = 0;

    ae_assert(hess->htype==3, "HessianGetMatrixLowRankStabilized: Hessian mode is not supported", _state);
    n = hess->n;
    
    /*
     * Get non-modified low rank representation D+C'*S*C
     */
    hessiangetlowrank(hess, d, &hess->tmpunstablec, &hess->tmpunstables, corrk, _state);
    if( *corrk==0 )
    {
        return;
    }
    
    /*
     * Handle low-dimensional case where K>N. We handle it by
     * forming C'*S*C explicitly and by performing its EVD
     * decomposition.
     *
     * As a result, CorrK is decreased down to N.
     *
     * A degenerate case which does not need much sophistication.
     */
    if( *corrk>n )
    {
        rallocm(*corrk, n, &hess->tmpsl, _state);
        rallocm(*corrk, n, &hess->tmpl, _state);
        for(i=0; i<=*corrk-1; i++)
        {
            rcopyrr(n, &hess->tmpunstablec, i, &hess->tmpl, i, _state);
            rcopyrr(n, &hess->tmpunstablec, i, &hess->tmpsl, i, _state);
            rmulr(n, hess->tmpunstables.ptr.p_double[i], &hess->tmpsl, i, _state);
        }
        rallocm(n, n, &hess->tmpw, _state);
        rmatrixgemm(n, n, *corrk, 1.0, &hess->tmpl, 0, 0, 1, &hess->tmpsl, 0, 0, 0, 0.0, &hess->tmpw, 0, 0, _state);
        if( !smatrixevd(&hess->tmpw, n, 1, ae_false, &hess->tmpe, &hess->tmpq, _state) )
        {
            ae_assert(ae_false, "HessianGetLowRankStabilizedLBFGS: eigensolver failure", _state);
        }
        *corrk = n;
        rallocm(n, n, corrc, _state);
        rmatrixtranspose(n, n, &hess->tmpq, 0, 0, corrc, 0, 0, _state);
        for(i=0; i<=n-1; i++)
        {
            rmulr(n, ae_sqrt(ae_fabs(hess->tmpe.ptr.p_double[i], _state), _state), corrc, i, _state);
        }
        rallocv(n, corrs, _state);
        for(i=0; i<=n-1; i++)
        {
            corrs->ptr.p_double[i] = rcase2(ae_fp_greater_eq(hess->tmpe.ptr.p_double[i],(double)(0)), (double)(1), (double)(-1), _state);
        }
        return;
    }
    
    /*
     * A non-degenerate case with CorrK<=N.
     *
     * Compute LQ decomposition of C, use it to transform C'*S*C to
     * U'*W*U with orthogonal U being a factor from LQ decomposition
     * and W being a KxK matrix equal to L'*S*L
     * 
     */
    rmatrixlq(&hess->tmpunstablec, *corrk, n, &hess->tmptau, _state);
    rmatrixlqunpackq(&hess->tmpunstablec, *corrk, n, &hess->tmptau, *corrk, &hess->tmpu, _state);
    rsetallocm(*corrk, *corrk, 0.0, &hess->tmpsl, _state);
    rsetallocm(*corrk, *corrk, 0.0, &hess->tmpl, _state);
    for(i=0; i<=*corrk-1; i++)
    {
        rcopyrr(i+1, &hess->tmpunstablec, i, &hess->tmpl, i, _state);
        rcopyrr(i+1, &hess->tmpunstablec, i, &hess->tmpsl, i, _state);
        rmulr(i+1, hess->tmpunstables.ptr.p_double[i], &hess->tmpsl, i, _state);
    }
    rallocm(*corrk, *corrk, &hess->tmpw, _state);
    rmatrixgemm(*corrk, *corrk, *corrk, 1.0, &hess->tmpl, 0, 0, 1, &hess->tmpsl, 0, 0, 0, 0.0, &hess->tmpw, 0, 0, _state);
    
    /*
     * Now we have C'*S*C = U'*W*U, with S being diagonal and W being a general matrix.
     * Another different is that C is non-orthogonal whilst U is orthogonal.
     *
     * Use EVD to obtain orthogonal decomposition W=Q*E*Q' with orthogonal Q and diagonal E.
     */
    if( !smatrixevd(&hess->tmpw, *corrk, 1, ae_false, &hess->tmpe, &hess->tmpq, _state) )
    {
        ae_assert(ae_false, "HessianGetLowRankStabilizedLBFGS: eigensolver failure", _state);
    }
    
    /*
     * Finally, output robust representation:
     * * C = sqrt(abs(E))*Q'*U
     * * S = E/abs(E)
     */
    rallocm(*corrk, n, corrc, _state);
    rmatrixgemm(*corrk, n, *corrk, 1.0, &hess->tmpq, 0, 0, 1, &hess->tmpu, 0, 0, 0, 0.0, corrc, 0, 0, _state);
    for(i=0; i<=*corrk-1; i++)
    {
        rmulr(n, ae_sqrt(ae_fabs(hess->tmpe.ptr.p_double[i], _state), _state), corrc, i, _state);
    }
    rallocv(*corrk, corrs, _state);
    for(i=0; i<=*corrk-1; i++)
    {
        corrs->ptr.p_double[i] = rcase2(ae_fp_greater_eq(hess->tmpe.ptr.p_double[i],(double)(0)), (double)(1), (double)(-1), _state);
    }
}


/*************************************************************************
Stabilized low rank model for SR1 update.

  -- ALGLIB --
     Copyright 10.02.2024 by Bochkanov Sergey
*************************************************************************/
void hessiangetlowrankstabilizedsr1(xbfgshessian* hess,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_matrix* corrc,
     /* Real    */ ae_vector* corrs,
     ae_int_t* corrk,
     ae_state *_state)
{
    ae_int_t n;

    *corrk = 0;

    ae_assert(hess->htype==4, "HessianGetMatrixLowRankStabilizedSR1: Hessian mode is not supported", _state);
    n = hess->n;
    optserv_recomputelowrankmodel(hess, _state);
    rcopyallocv(n, &hess->sr1stabd, d, _state);
    rcopyallocm(hess->sr1k, n, &hess->sr1stabc, corrc, _state);
    rcopyallocv(hess->sr1k, &hess->sr1stabz, corrs, _state);
    *corrk = hess->sr1k;
}


/*************************************************************************
Returns maximum rank of the models returned by the HessianGetLowRank()
function.

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
ae_int_t hessiangetmaxrank(const xbfgshessian* hess, ae_state *_state)
{
    ae_int_t result;


    ae_assert(hess->htype==3||hess->htype==4, "HessianGetMaxRank: Hessian mode is not supported", _state);
    result = 0;
    if( hess->htype==3 )
    {
        result = 2*hess->m;
    }
    if( hess->htype==4 )
    {
        result = hess->m;
    }
    return result;
}


/*************************************************************************
Computes direct product H*x (here H is a Hessian matrix, not its inverse).
Either BFGS or LBFGS formula is used, depending on Hessian model settings.

NOTE: this function modifies internal state of Hess,  it  uses  temporary
      arrays allocated in Hess. Thus, it is not thread-safe.

INPUT PARAMETERS:
    Hess            -   Hessian state
    X               -   array[N]

OUTPUT PARAMETERS:
    HX              -   array[N], H*x

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
void hessianmv(xbfgshessian* hess,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* hx,
     ae_state *_state)
{
    ae_int_t n;


    ae_assert((hess->htype==0||hess->htype==3)||hess->htype==4, "HessianMV: Hessian mode is not supported", _state);
    n = hess->n;
    rallocv(n, hx, _state);
    if( hess->htype==0 )
    {
        
        /*
         * Dense direct Hessian
         */
        rgemv(n, n, 1.0, &hess->hcurrent, 0, x, 0.0, hx, _state);
    }
    if( hess->htype==3 )
    {
        
        /*
         * Low-rank model
         */
        optserv_recomputelowrankmodel(hess, _state);
        rcopymulv(n, hess->sigma, x, hx, _state);
        if( hess->lowrankk>0 )
        {
            rallocv(hess->lowrankk, &hess->buf, _state);
            rgemv(hess->lowrankk, n, 1.0, &hess->lowrankcp, 0, x, 0.0, &hess->buf, _state);
            rgemv(n, hess->lowrankk, 1.0, &hess->lowrankcp, 1, &hess->buf, 1.0, hx, _state);
            rgemv(hess->lowrankk, n, 1.0, &hess->lowrankcm, 0, x, 0.0, &hess->buf, _state);
            rgemv(n, hess->lowrankk, -1.0, &hess->lowrankcm, 1, &hess->buf, 1.0, hx, _state);
        }
    }
    if( hess->htype==4 )
    {
        
        /*
         * Low-rank model
         */
        optserv_recomputelowrankmodel(hess, _state);
        rcopyv(n, x, hx, _state);
        rmergemulv(n, &hess->sr1d, hx, _state);
        if( hess->sr1k>0 )
        {
            rallocv(hess->sr1k, &hess->buf, _state);
            rgemv(hess->sr1k, n, 1.0, &hess->sr1c, 0, x, 0.0, &hess->buf, _state);
            rmergemulv(hess->sr1k, &hess->sr1z, &hess->buf, _state);
            rgemv(n, hess->sr1k, 1.0, &hess->sr1c, 1, &hess->buf, 1.0, hx, _state);
        }
    }
}


/*************************************************************************
Computes direct product x'*H*x (here H is a Hessian matrix, not its inverse).
Either BFGS or LBFGS formula is used, depending on Hessian model settings.

NOTE: this function modifies internal state of Hess,  it  uses  temporary
      arrays allocated in Hess. Thus, it is not thread-safe.

NOTE: for low-rank models this function is much more precise than computing
      H*x and then computing dot product with x.

INPUT PARAMETERS:
    Hess            -   Hessian state
    X               -   array[N]

RESULT:
    x'*H*x

  -- ALGLIB --
     Copyright 14.03.2023 by Bochkanov Sergey
*************************************************************************/
double hessianvmv(xbfgshessian* hess,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    double result;


    ae_assert((hess->htype==0||hess->htype==3)||hess->htype==4, "HessianVMV: Hessian mode is not supported", _state);
    result = (double)(0);
    n = hess->n;
    if( hess->htype==0 )
    {
        
        /*
         * Dense direct Hessian
         */
        hessianmv(hess, x, &hess->bufvmv, _state);
        result = rdotv(n, x, &hess->bufvmv, _state);
        return result;
    }
    if( hess->htype==3 )
    {
        
        /*
         * Low-rank model
         */
        optserv_recomputelowrankmodel(hess, _state);
        result = hess->sigma*rdotv2(n, x, _state);
        if( hess->lowrankk>0 )
        {
            rallocv(hess->lowrankk, &hess->bufvmv, _state);
            rgemv(hess->lowrankk, n, 1.0, &hess->lowrankcp, 0, x, 0.0, &hess->bufvmv, _state);
            result = result+rdotv2(hess->lowrankk, &hess->bufvmv, _state);
            rgemv(hess->lowrankk, n, 1.0, &hess->lowrankcm, 0, x, 0.0, &hess->bufvmv, _state);
            result = result-rdotv2(hess->lowrankk, &hess->bufvmv, _state);
        }
        return result;
    }
    if( hess->htype==4 )
    {
        
        /*
         * Low-rank model
         */
        optserv_recomputelowrankmodel(hess, _state);
        result = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            result = result+hess->sr1d.ptr.p_double[i]*(x->ptr.p_double[i]*x->ptr.p_double[i]);
        }
        if( hess->sr1k>0 )
        {
            rallocv(hess->sr1k, &hess->bufvmv, _state);
            rgemv(hess->sr1k, n, 1.0, &hess->sr1c, 0, x, 0.0, &hess->bufvmv, _state);
            for(i=0; i<=hess->sr1k-1; i++)
            {
                result = result+hess->sr1z.ptr.p_double[i]*(hess->bufvmv.ptr.p_double[i]*hess->bufvmv.ptr.p_double[i]);
            }
        }
        return result;
    }
    return result;
}


/*************************************************************************
Computes direct product x'*H*x (here H is a Hessian matrix, not its inverse)
along with direct product H*x.

Either BFGS or LBFGS formula is used, depending on Hessian model settings.

NOTE: this function modifies internal state of Hess,  it  uses  temporary
      arrays allocated in Hess. Thus, it is not thread-safe.

NOTE: for low-rank models this function computes more precise x'*H*x than
      H*x multiplied by x.

INPUT PARAMETERS:
    Hess            -   Hessian state
    X               -   array[N]

OUTPUT PARAMETERS:
    HX              -   array[N], H*x
    XHX             -   x'*H*x

  -- ALGLIB --
     Copyright 14.03.2023 by Bochkanov Sergey
*************************************************************************/
void hessianxmv(xbfgshessian* hess,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* hx,
     double* xhx,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;

    *xhx = 0.0;

    ae_assert((hess->htype==0||hess->htype==3)||hess->htype==4, "HessianXMV: Hessian mode is not supported", _state);
    n = hess->n;
    rallocv(n, hx, _state);
    *xhx = (double)(0);
    if( hess->htype==0 )
    {
        
        /*
         * Dense direct Hessian
         */
        rgemv(n, n, 1.0, &hess->hcurrent, 0, x, 0.0, hx, _state);
        *xhx = rdotv(n, x, hx, _state);
        return;
    }
    if( hess->htype==3 )
    {
        
        /*
         * Low-rank model
         */
        optserv_recomputelowrankmodel(hess, _state);
        rcopymulv(n, hess->sigma, x, hx, _state);
        *xhx = hess->sigma*rdotv2(n, x, _state);
        if( hess->lowrankk>0 )
        {
            rallocv(hess->lowrankk, &hess->buf, _state);
            rgemv(hess->lowrankk, n, 1.0, &hess->lowrankcp, 0, x, 0.0, &hess->buf, _state);
            rgemv(n, hess->lowrankk, 1.0, &hess->lowrankcp, 1, &hess->buf, 1.0, hx, _state);
            *xhx = *xhx+rdotv2(hess->lowrankk, &hess->buf, _state);
            rgemv(hess->lowrankk, n, 1.0, &hess->lowrankcm, 0, x, 0.0, &hess->buf, _state);
            rgemv(n, hess->lowrankk, -1.0, &hess->lowrankcm, 1, &hess->buf, 1.0, hx, _state);
            *xhx = *xhx-rdotv2(hess->lowrankk, &hess->buf, _state);
        }
        return;
    }
    if( hess->htype==4 )
    {
        
        /*
         * Low-rank model
         */
        optserv_recomputelowrankmodel(hess, _state);
        rcopyv(n, x, hx, _state);
        rmergemulv(n, &hess->sr1d, hx, _state);
        *xhx = rdotv(n, x, hx, _state);
        if( hess->sr1k>0 )
        {
            rallocv(hess->sr1k, &hess->buf, _state);
            rgemv(hess->sr1k, n, 1.0, &hess->sr1c, 0, x, 0.0, &hess->buf, _state);
            for(i=0; i<=hess->sr1k-1; i++)
            {
                *xhx = *xhx+hess->sr1z.ptr.p_double[i]*(hess->buf.ptr.p_double[i]*hess->buf.ptr.p_double[i]);
                hess->buf.ptr.p_double[i] = hess->sr1z.ptr.p_double[i]*hess->buf.ptr.p_double[i];
            }
            rgemv(n, hess->sr1k, 1.0, &hess->sr1c, 1, &hess->buf, 1.0, hx, _state);
        }
        return;
    }
}


/*************************************************************************
Creates random multiobjective test problem with no known answers

INPUT PARAMETERS:
    N               -   vars cnt
    M               -   targets cnt
    NEquality       -   desired count of equality constraints of all kinds
                        (box, linear, nonlinear)
    NInequality     -   desired count of non-box inequality constraints
    TaskKind        -   task type:
                        * 0 = convex quartic
                        * 1 = convex quadratic
                        * 2 = convex low rank quadratic
                        * 3 = linear
                        * 4 = constant
    NLQuadratic     -   scale of the quadratic term of nonlinear constraints
    NLQuartic       -   scale of the quadratic term of nonlinear constraints

NOTE: this function does NOT set Lagrange multipliers because it does not
      know them.

  -- ALGLIB --
     Copyright 04.03.2023 by Bochkanov Sergey
*************************************************************************/
void motfcreaterandomunknown(ae_int_t n,
     ae_int_t m,
     ae_int_t nequality,
     ae_int_t ninequality,
     ae_int_t taskkind,
     double nlquadratic,
     double nlquartic,
     hqrndstate* rs,
     multiobjectivetestfunction* problem,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t rk;
    ae_matrix q;
    ae_vector grad;
    ae_vector x;
    ae_vector ax;
    ae_vector c;
    ae_bool issemidefinite;
    ae_bool haslinearterm;
    ae_bool hasnonlinearterms;
    ae_bool hasquadraticterm;
    ae_bool hasquarticterm;
    ae_int_t ngenerated;
    ae_int_t cidx;
    ae_int_t ctype;
    double v;
    double vmul;
    ae_vector xtrial;
    ae_int_t passcount;
    ae_int_t nboxinequality;
    ae_int_t nlinearinequality;
    ae_int_t nnonlinearinequality;

    ae_frame_make(_state, &_frame_block);
    memset(&q, 0, sizeof(q));
    memset(&grad, 0, sizeof(grad));
    memset(&x, 0, sizeof(x));
    memset(&ax, 0, sizeof(ax));
    memset(&c, 0, sizeof(c));
    memset(&xtrial, 0, sizeof(xtrial));
    _multiobjectivetestfunction_clear(problem);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&grad, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ax, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xtrial, 0, DT_REAL, _state, ae_true);

    haslinearterm = ae_false;
    hasnonlinearterms = ae_false;
    hasquadraticterm = ae_false;
    hasquarticterm = ae_false;
    issemidefinite = ae_false;
    touchboolean(&haslinearterm, _state);
    touchboolean(&hasnonlinearterms, _state);
    touchboolean(&hasquadraticterm, _state);
    touchboolean(&hasquarticterm, _state);
    touchboolean(&issemidefinite, _state);
    
    /*
     * Generate random test functions
     */
    problem->isrotated = ae_false;
    problem->problemtype = 0;
    problem->n = n;
    problem->m = m;
    rsetallocv(m, 0.0, &problem->tgtc, _state);
    rsetallocm(m, n, 0.0, &problem->tgtb, _state);
    rsetallocm(m*n, n, 0.0, &problem->tgta, _state);
    rsetallocm(m, n, 0.0, &problem->tgtd, _state);
    ae_assert(taskkind>=0&&taskkind<=4, "MOTFCreate: incorrect TaskKind", _state);
    if( taskkind==0 )
    {
        
        /*
         * Quartic term
         */
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                problem->tgtd.ptr.pp_double[i][j] = ae_pow((double)(2), 0.33*hqrndnormal(rs, _state), _state);
            }
        }
        hasnonlinearterms = ae_true;
        hasquarticterm = ae_true;
    }
    if( taskkind<=2 )
    {
        
        /*
         * Quadratic term
         */
        if( taskkind!=2 )
        {
            
            /*
             * well conditioned matrix
             */
            for(k=0; k<=m-1; k++)
            {
                spdmatrixrndcond(n, 100.0, &q, _state);
                rmatrixcopy(n, n, &q, 0, 0, &problem->tgta, k*n, 0, _state);
            }
        }
        else
        {
            
            /*
             * Low rank matrix
             */
            rk = ae_minint(n, 1+hqrnduniformi(rs, 5, _state), _state);
            for(k=0; k<=m-1; k++)
            {
                hqrndnormalm(rs, rk, n, &q, _state);
                rmatrixgemm(n, n, rk, 1.0, &q, 0, 0, 1, &q, 0, 0, 0, 0.0, &problem->tgta, k*n, 0, _state);
            }
        }
        hasquadraticterm = ae_true;
        hasnonlinearterms = ae_true;
    }
    if( taskkind<=3 )
    {
        
        /*
         * Linear term
         */
        hqrndnormalm(rs, m, n, &problem->tgtb, _state);
        haslinearterm = ae_true;
    }
    if( taskkind<=4 )
    {
        
        /*
         * Constant term
         */
        hqrndnormalv(rs, m, &problem->tgtc, _state);
    }
    issemidefinite = taskkind>=2;
    
    /*
     * No initial points
     */
    ae_matrix_set_length(&problem->x0, 0, 0, _state);
    problem->k0 = 0;
    
    /*
     * No known solutions
     */
    ae_matrix_set_length(&problem->xsol, 0, 0, _state);
    ae_matrix_set_length(&problem->fsol, 0, 0, _state);
    problem->ksol = 0;
    
    /*
     * No Lagrange multipliers
     */
    ae_vector_set_length(&problem->lagmultbc, 0, _state);
    ae_vector_set_length(&problem->lagmultlc, 0, _state);
    ae_vector_set_length(&problem->lagmultnlc, 0, _state);
    
    /*
     * Generate random trial point which will be strictly feasible
     */
    hqrndnormalv(rs, n, &xtrial, _state);
    
    /*
     *
     * Generate empty set of constraints
     */
    rsetallocv(n, _state->v_neginf, &problem->bndl, _state);
    rsetallocv(n, _state->v_posinf, &problem->bndu, _state);
    problem->nlinear = 0;
    problem->nnonlinear = 0;
    
    /*
     * Randomly generate equality constraints using trial point
     */
    ngenerated = 0;
    passcount = 0;
    while(ngenerated<nequality&&passcount<2*n)
    {
        passcount = passcount+1;
        ctype = hqrnduniformi(rs, 3, _state);
        
        /*
         * Try adding a box constraint
         */
        if( ctype==0 )
        {
            
            /*
             * Try to find a box constraint that is not defined yet
             */
            i = hqrnduniformi(rs, n, _state);
            if( ae_isneginf(problem->bndl.ptr.p_double[i], _state)&&ae_isposinf(problem->bndu.ptr.p_double[i], _state) )
            {
                
                /*
                 * Use this box constraint.
                 * Fix a bound corresponding to the Lagrange multiplier direction,
                 * with probability 15% fix other bound too (when it is free).
                 */
                problem->bndl.ptr.p_double[i] = xtrial.ptr.p_double[i];
                problem->bndu.ptr.p_double[i] = xtrial.ptr.p_double[i];
                ngenerated = ngenerated+1;
            }
        }
        
        /*
         * Add a linear equality constraint.
         */
        if( ctype==1 )
        {
            hqrndnormalv(rs, n, &c, _state);
            v = rdotv(n, &c, &xtrial, _state);
            rmatrixgrowrowsto(&problem->densea, problem->nlinear+1, n, _state);
            rgrowv(problem->nlinear+1, &problem->al, _state);
            rgrowv(problem->nlinear+1, &problem->au, _state);
            rcopyvr(n, &c, &problem->densea, problem->nlinear, _state);
            problem->al.ptr.p_double[problem->nlinear] = v;
            problem->au.ptr.p_double[problem->nlinear] = v;
            problem->nlinear = problem->nlinear+1;
            ngenerated = ngenerated+1;
        }
        
        /*
         * Add a nonlinear equality constraint.
         */
        if( ctype==2 )
        {
            
            /*
             * Allocate place
             */
            rgrowv(problem->nnonlinear+1, &problem->nlc, _state);
            rmatrixgrowrowsto(&problem->nlb, problem->nnonlinear+1, n, _state);
            rmatrixgrowrowsto(&problem->nla, (problem->nnonlinear+1)*n, n, _state);
            rmatrixgrowrowsto(&problem->nld, problem->nnonlinear+1, n, _state);
            rgrowv(problem->nnonlinear+1, &problem->hl, _state);
            rgrowv(problem->nnonlinear+1, &problem->hu, _state);
            
            /*
             * Generate and compute nonlinear constraint
             */
            v = (double)(0);
            problem->nlc.ptr.p_double[problem->nnonlinear] = hqrndnormal(rs, _state);
            v = v+problem->nlc.ptr.p_double[problem->nnonlinear];
            hqrndnormalv(rs, n, &c, _state);
            rcopyvr(n, &c, &problem->nlb, problem->nnonlinear, _state);
            v = v+rdotv(n, &c, &xtrial, _state);
            spdmatrixrndcond(n, 10.0, &q, _state);
            vmul = nlquadratic*ae_fabs(hqrndnormal(rs, _state), _state);
            for(i=0; i<=n-1; i++)
            {
                rmulr(n, vmul, &q, i, _state);
            }
            rmatrixcopy(n, n, &q, 0, 0, &problem->nla, problem->nnonlinear*n, 0, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = v+0.5*xtrial.ptr.p_double[i]*q.ptr.pp_double[i][j]*xtrial.ptr.p_double[j];
                }
            }
            for(i=0; i<=n-1; i++)
            {
                problem->nld.ptr.pp_double[problem->nnonlinear][i] = nlquartic*ae_pow((double)(2), 0.33*hqrndnormal(rs, _state), _state);
                v = v+problem->nld.ptr.pp_double[problem->nnonlinear][i]*ae_pow(xtrial.ptr.p_double[i], (double)(4), _state);
            }
            problem->hl.ptr.p_double[problem->nnonlinear] = v;
            problem->hu.ptr.p_double[problem->nnonlinear] = v;
            problem->nnonlinear = problem->nnonlinear+1;
            ngenerated = ngenerated+1;
        }
    }
    
    /*
     * Decide on desired count of inequality constraints of all types (for box constraints - only approximate count)
     */
    nboxinequality = hqrnduniformi(rs, ninequality+1, _state);
    nlinearinequality = hqrnduniformi(rs, ninequality-nboxinequality+1, _state);
    nnonlinearinequality = ninequality-nboxinequality-nlinearinequality;
    
    /*
     * Randomly generate box/linear/nonlinear inequality constraints inactive at the trial point
     */
    for(k=0; k<=nboxinequality-1; k++)
    {
        i = hqrnduniformi(rs, n, _state);
        if( ae_isneginf(problem->bndl.ptr.p_double[i], _state)&&ae_isposinf(problem->bndu.ptr.p_double[i], _state) )
        {
            ctype = hqrnduniformi(rs, 3, _state);
            problem->bndl.ptr.p_double[i] = rcase2(ctype==0||ctype==2, xtrial.ptr.p_double[i]-ae_pow((double)(2), hqrndnormal(rs, _state), _state), _state->v_neginf, _state);
            problem->bndu.ptr.p_double[i] = rcase2(ctype==1||ctype==2, xtrial.ptr.p_double[i]+ae_pow((double)(2), hqrndnormal(rs, _state), _state), _state->v_posinf, _state);
        }
    }
    for(cidx=0; cidx<=nlinearinequality-1; cidx++)
    {
        hqrndnormalv(rs, n, &c, _state);
        v = rdotv(n, &c, &xtrial, _state);
        ctype = hqrnduniformi(rs, 3, _state);
        rmatrixgrowrowsto(&problem->densea, problem->nlinear+1, n, _state);
        rgrowv(problem->nlinear+1, &problem->al, _state);
        rgrowv(problem->nlinear+1, &problem->au, _state);
        rcopyvr(n, &c, &problem->densea, problem->nlinear, _state);
        problem->al.ptr.p_double[problem->nlinear] = rcase2(ctype==0||ctype==2, v-ae_pow((double)(2), hqrndnormal(rs, _state), _state), _state->v_neginf, _state);
        problem->au.ptr.p_double[problem->nlinear] = rcase2(ctype==1||ctype==2, v+ae_pow((double)(2), hqrndnormal(rs, _state), _state), _state->v_posinf, _state);
        problem->nlinear = problem->nlinear+1;
    }
    for(cidx=0; cidx<=nnonlinearinequality-1; cidx++)
    {
        
        /*
         * Allocate place
         */
        rgrowv(problem->nnonlinear+1, &problem->nlc, _state);
        rmatrixgrowrowsto(&problem->nlb, problem->nnonlinear+1, n, _state);
        rmatrixgrowrowsto(&problem->nla, (problem->nnonlinear+1)*n, n, _state);
        rmatrixgrowrowsto(&problem->nld, problem->nnonlinear+1, n, _state);
        rgrowv(problem->nnonlinear+1, &problem->hl, _state);
        rgrowv(problem->nnonlinear+1, &problem->hu, _state);
        
        /*
         * Generate and compute nonlinear constraint
         */
        v = (double)(0);
        problem->nlc.ptr.p_double[problem->nnonlinear] = hqrndnormal(rs, _state);
        v = v+problem->nlc.ptr.p_double[problem->nnonlinear];
        hqrndnormalv(rs, n, &c, _state);
        rcopyvr(n, &c, &problem->nlb, problem->nnonlinear, _state);
        v = v+rdotv(n, &c, &xtrial, _state);
        spdmatrixrndcond(n, 10.0, &q, _state);
        vmul = nlquadratic*ae_fabs(hqrndnormal(rs, _state), _state);
        for(i=0; i<=n-1; i++)
        {
            rmulr(n, vmul, &q, i, _state);
        }
        rmatrixcopy(n, n, &q, 0, 0, &problem->nla, problem->nnonlinear*n, 0, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = v+0.5*xtrial.ptr.p_double[i]*q.ptr.pp_double[i][j]*xtrial.ptr.p_double[j];
            }
        }
        for(i=0; i<=n-1; i++)
        {
            problem->nld.ptr.pp_double[problem->nnonlinear][i] = nlquartic*ae_pow((double)(2), 0.33*hqrndnormal(rs, _state), _state);
            v = v+problem->nld.ptr.pp_double[problem->nnonlinear][i]*ae_pow(xtrial.ptr.p_double[i], (double)(4), _state);
        }
        ctype = hqrnduniformi(rs, 3, _state);
        problem->hl.ptr.p_double[problem->nnonlinear] = rcase2(ctype==0||ctype==2, v-ae_pow((double)(2), hqrndnormal(rs, _state), _state), _state->v_neginf, _state);
        problem->hu.ptr.p_double[problem->nnonlinear] = rcase2(ctype==1||ctype==2, v+ae_pow((double)(2), hqrndnormal(rs, _state), _state), _state->v_posinf, _state);
        problem->nnonlinear = problem->nnonlinear+1;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Creates random multiobjective test problem with known answer and Lagrange
multipliers

INPUT PARAMETERS:
    N               -   vars cnt, N>=3
    NLQuadratic     -   scale of the quadratic term of nonlinear constraints
    NLQuartic       -   scale of the quartic term of nonlinear constraints

  -- ALGLIB --
     Copyright 04.03.2023 by Bochkanov Sergey
*************************************************************************/
void motfcreate1knownanswer(ae_int_t n,
     double nlquadratic,
     double nlquartic,
     hqrndstate* rs,
     multiobjectivetestfunction* problem,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_vector grad;
    ae_vector c;
    ae_int_t nactive;
    ae_int_t ngenerated;
    ae_int_t nadditionallin;
    ae_int_t nadditionalnl;
    double lagmult;
    ae_int_t passcount;
    ae_int_t ctype;
    double v;
    double vmul;
    ae_matrix q;
    double padinactive;

    ae_frame_make(_state, &_frame_block);
    memset(&grad, 0, sizeof(grad));
    memset(&c, 0, sizeof(c));
    memset(&q, 0, sizeof(q));
    _multiobjectivetestfunction_clear(problem);
    ae_vector_init(&grad, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);

    padinactive = 10.0;
    
    /*
     * Set problem metrics
     */
    problem->isrotated = ae_false;
    problem->problemtype = 0;
    problem->n = n;
    problem->m = 1;
    
    /*
     * Set empty constraints
     */
    rsetallocv(n, _state->v_neginf, &problem->bndl, _state);
    rsetallocv(n, _state->v_posinf, &problem->bndu, _state);
    problem->nlinear = 0;
    problem->nnonlinear = 0;
    
    /*
     * No initial points
     */
    ae_matrix_set_length(&problem->x0, 0, 0, _state);
    problem->k0 = 0;
    
    /*
     * Generate a priori known solution
     */
    ae_matrix_set_length(&problem->xsol, 1, n, _state);
    ae_matrix_set_length(&problem->fsol, 1, 1, _state);
    for(i=0; i<=n-1; i++)
    {
        problem->xsol.ptr.pp_double[0][i] = hqrndnormal(rs, _state);
    }
    problem->fsol.ptr.pp_double[0][0] = (double)(0);
    problem->ksol = 1;
    
    /*
     * Initial state of Lagrange multipliers
     */
    ae_vector_set_length(&problem->lagmultbc, n, _state);
    rsetv(n, 0.0, &problem->lagmultbc, _state);
    ae_vector_set_length(&problem->lagmultlc, 0, _state);
    ae_vector_set_length(&problem->lagmultnlc, 0, _state);
    
    /*
     * Compute gradient introduced by constraints multiplied by Lagrange coefficients
     * Randomly generate set of active (equality or inequality) constraints
     */
    ae_assert(n>=3, "MOTFCreate1KnownAnswer: N<3", _state);
    rsetallocv(n, 0.0, &grad, _state);
    ngenerated = 0;
    nactive = hqrnduniformi(rs, n-2, _state);
    passcount = 0;
    while(ngenerated<nactive&&passcount<2*n)
    {
        passcount = passcount+1;
        ctype = hqrnduniformi(rs, 3, _state);
        lagmult = hqrndnormal(rs, _state);
        lagmult = lagmult+0.05*possign(lagmult, _state);
        
        /*
         * Try adding a box constraint
         */
        if( ctype==0 )
        {
            
            /*
             * Try to find a box constraint that is not defined yet
             */
            i = hqrnduniformi(rs, n, _state);
            if( ae_isneginf(problem->bndl.ptr.p_double[i], _state)&&ae_isposinf(problem->bndu.ptr.p_double[i], _state) )
            {
                
                /*
                 * Use this box constraint.
                 * Fix a bound corresponding to the Lagrange multiplier direction,
                 * with probability 15% fix other bound too (when it is free).
                 */
                ae_assert(ae_fp_neq(lagmult,(double)(0)), "OPTSERV: integrity check 0948 failed", _state);
                if( ae_fp_less(lagmult,(double)(0)) )
                {
                    problem->bndl.ptr.p_double[i] = problem->xsol.ptr.pp_double[0][i];
                }
                if( ae_fp_greater(lagmult,(double)(0)) )
                {
                    problem->bndu.ptr.p_double[i] = problem->xsol.ptr.pp_double[0][i];
                }
                if( ae_fp_less(hqrnduniformr(rs, _state),0.15) )
                {
                    problem->bndl.ptr.p_double[i] = problem->xsol.ptr.pp_double[0][i];
                    problem->bndu.ptr.p_double[i] = problem->xsol.ptr.pp_double[0][i];
                }
                grad.ptr.p_double[i] = grad.ptr.p_double[i]+lagmult;
                problem->lagmultbc.ptr.p_double[i] = problem->lagmultbc.ptr.p_double[i]+lagmult;
                ngenerated = ngenerated+1;
            }
            continue;
        }
        
        /*
         * Add a linear constraint.
         */
        if( ctype==1 )
        {
            ae_assert(ae_fp_neq(lagmult,(double)(0)), "OPTSERV: integrity check 0948 failed", _state);
            hqrndnormalv(rs, n, &c, _state);
            v = rdotvr(n, &c, &problem->xsol, 0, _state);
            rmatrixgrowrowsto(&problem->densea, problem->nlinear+1, n, _state);
            rgrowv(problem->nlinear+1, &problem->al, _state);
            rgrowv(problem->nlinear+1, &problem->au, _state);
            rcopyvr(n, &c, &problem->densea, problem->nlinear, _state);
            problem->al.ptr.p_double[problem->nlinear] = _state->v_neginf;
            problem->au.ptr.p_double[problem->nlinear] = _state->v_posinf;
            if( ae_fp_less(lagmult,(double)(0)) )
            {
                problem->al.ptr.p_double[problem->nlinear] = v;
            }
            if( ae_fp_greater(lagmult,(double)(0)) )
            {
                problem->au.ptr.p_double[problem->nlinear] = v;
            }
            if( ae_fp_less(hqrnduniformr(rs, _state),0.15) )
            {
                problem->al.ptr.p_double[problem->nlinear] = v;
                problem->au.ptr.p_double[problem->nlinear] = v;
            }
            raddv(n, lagmult, &c, &grad, _state);
            rvectorresize(&problem->lagmultlc, problem->nlinear+1, _state);
            problem->lagmultlc.ptr.p_double[problem->nlinear] = lagmult;
            problem->nlinear = problem->nlinear+1;
            ngenerated = ngenerated+1;
            continue;
        }
        
        /*
         * Add a nonlinear constraint.
         */
        if( ctype==2 )
        {
            ae_assert(ae_fp_neq(lagmult,(double)(0)), "OPTSERV: integrity check 0948 failed", _state);
            
            /*
             * Allocate place
             */
            rgrowv(problem->nnonlinear+1, &problem->nlc, _state);
            rmatrixgrowrowsto(&problem->nlb, problem->nnonlinear+1, n, _state);
            rmatrixgrowrowsto(&problem->nla, (problem->nnonlinear+1)*n, n, _state);
            rmatrixgrowrowsto(&problem->nld, problem->nnonlinear+1, n, _state);
            rgrowv(problem->nnonlinear+1, &problem->hl, _state);
            rgrowv(problem->nnonlinear+1, &problem->hu, _state);
            
            /*
             * Generate and compute value of the nonlinear constraint, update gradient
             */
            v = (double)(0);
            problem->nlc.ptr.p_double[problem->nnonlinear] = hqrndnormal(rs, _state);
            v = v+problem->nlc.ptr.p_double[problem->nnonlinear];
            hqrndnormalv(rs, n, &c, _state);
            rcopyvr(n, &c, &problem->nlb, problem->nnonlinear, _state);
            v = v+rdotvr(n, &c, &problem->xsol, 0, _state);
            raddv(n, lagmult, &c, &grad, _state);
            spdmatrixrndcond(n, 10.0, &q, _state);
            vmul = nlquadratic*ae_fabs(hqrndnormal(rs, _state), _state);
            for(i=0; i<=n-1; i++)
            {
                rmulr(n, vmul, &q, i, _state);
            }
            rmatrixcopy(n, n, &q, 0, 0, &problem->nla, problem->nnonlinear*n, 0, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = v+0.5*problem->xsol.ptr.pp_double[0][i]*q.ptr.pp_double[i][j]*problem->xsol.ptr.pp_double[0][j];
                    grad.ptr.p_double[i] = grad.ptr.p_double[i]+lagmult*q.ptr.pp_double[i][j]*problem->xsol.ptr.pp_double[0][j];
                }
            }
            for(i=0; i<=n-1; i++)
            {
                problem->nld.ptr.pp_double[problem->nnonlinear][i] = nlquartic*ae_pow((double)(2), 0.33*hqrndnormal(rs, _state), _state);
                v = v+problem->nld.ptr.pp_double[problem->nnonlinear][i]*ae_pow(problem->xsol.ptr.pp_double[0][i], (double)(4), _state);
                grad.ptr.p_double[i] = grad.ptr.p_double[i]+lagmult*problem->nld.ptr.pp_double[problem->nnonlinear][i]*(double)4*ae_pow(problem->xsol.ptr.pp_double[0][i], (double)(3), _state);
            }
            problem->hl.ptr.p_double[problem->nnonlinear] = _state->v_neginf;
            problem->hu.ptr.p_double[problem->nnonlinear] = _state->v_posinf;
            if( ae_fp_less(lagmult,(double)(0)) )
            {
                problem->hl.ptr.p_double[problem->nnonlinear] = v;
            }
            if( ae_fp_greater(lagmult,(double)(0)) )
            {
                problem->hu.ptr.p_double[problem->nnonlinear] = v;
            }
            if( ae_fp_less(hqrnduniformr(rs, _state),0.15) )
            {
                problem->hl.ptr.p_double[problem->nnonlinear] = v;
                problem->hu.ptr.p_double[problem->nnonlinear] = v;
            }
            rvectorresize(&problem->lagmultnlc, problem->nnonlinear+1, _state);
            problem->lagmultnlc.ptr.p_double[problem->nnonlinear] = lagmult;
            problem->nnonlinear = problem->nnonlinear+1;
            ngenerated = ngenerated+1;
            continue;
        }
    }
    
    /*
     * Generate convex target which exactly balances gradient introduced by constraints
     */
    rsetallocv(1, 0.0, &problem->tgtc, _state);
    rsetallocm(1, n, 0.0, &problem->tgtb, _state);
    rsetallocm(n, n, 0.0, &problem->tgta, _state);
    rsetallocm(1, n, 0.0, &problem->tgtd, _state);
    for(i=0; i<=n-1; i++)
    {
        problem->tgtd.ptr.pp_double[0][i] = 0.05*ae_pow((double)(2), 0.33*hqrndnormal(rs, _state), _state);
        problem->fsol.ptr.pp_double[0][0] = problem->fsol.ptr.pp_double[0][0]+problem->tgtd.ptr.pp_double[0][i]*ae_pow(problem->xsol.ptr.pp_double[0][i], (double)(4), _state);
        grad.ptr.p_double[i] = grad.ptr.p_double[i]+problem->tgtd.ptr.pp_double[0][i]*(double)4*ae_pow(problem->xsol.ptr.pp_double[0][i], (double)(3), _state);
    }
    for(i=0; i<=n-1; i++)
    {
        problem->tgtb.ptr.pp_double[0][i] = -grad.ptr.p_double[i];
        problem->fsol.ptr.pp_double[0][0] = problem->fsol.ptr.pp_double[0][0]+problem->tgtb.ptr.pp_double[0][i]*problem->xsol.ptr.pp_double[0][i];
    }
    
    /*
     * Randomly generate constraints which are inactive at the trial point
     */
    k = hqrnduniformi(rs, ae_minint(1+n/2, 5, _state), _state);
    nadditionallin = hqrnduniformi(rs, k+1, _state);
    nadditionalnl = k-nadditionallin;
    for(i=0; i<=n-1; i++)
    {
        if( (ae_fp_less(hqrnduniformr(rs, _state),0.5)&&ae_isneginf(problem->bndl.ptr.p_double[i], _state))&&ae_isposinf(problem->bndu.ptr.p_double[i], _state) )
        {
            ctype = hqrnduniformi(rs, 3, _state);
            problem->bndl.ptr.p_double[i] = rcase2(ctype==0||ctype==2, problem->xsol.ptr.pp_double[0][i]-padinactive-ae_pow((double)(2), hqrndnormal(rs, _state), _state), _state->v_neginf, _state);
            problem->bndu.ptr.p_double[i] = rcase2(ctype==1||ctype==2, problem->xsol.ptr.pp_double[0][i]+padinactive+ae_pow((double)(2), hqrndnormal(rs, _state), _state), _state->v_posinf, _state);
        }
    }
    for(k=0; k<=nadditionallin-1; k++)
    {
        hqrndnormalv(rs, n, &c, _state);
        v = rdotvr(n, &c, &problem->xsol, 0, _state);
        rmatrixgrowrowsto(&problem->densea, problem->nlinear+1, n, _state);
        rgrowv(problem->nlinear+1, &problem->al, _state);
        rgrowv(problem->nlinear+1, &problem->au, _state);
        rcopyvr(n, &c, &problem->densea, problem->nlinear, _state);
        ctype = hqrnduniformi(rs, 3, _state);
        problem->al.ptr.p_double[problem->nlinear] = rcase2(ctype==0||ctype==2, v-padinactive-ae_pow((double)(2), hqrndnormal(rs, _state), _state), _state->v_neginf, _state);
        problem->au.ptr.p_double[problem->nlinear] = rcase2(ctype==1||ctype==2, v+padinactive+ae_pow((double)(2), hqrndnormal(rs, _state), _state), _state->v_posinf, _state);
        problem->nlinear = problem->nlinear+1;
    }
    for(k=0; k<=nadditionalnl-1; k++)
    {
        
        /*
         * Allocate place
         */
        rgrowv(problem->nnonlinear+1, &problem->nlc, _state);
        rmatrixgrowrowsto(&problem->nlb, problem->nnonlinear+1, n, _state);
        rmatrixgrowrowsto(&problem->nla, (problem->nnonlinear+1)*n, n, _state);
        rmatrixgrowrowsto(&problem->nld, problem->nnonlinear+1, n, _state);
        rgrowv(problem->nnonlinear+1, &problem->hl, _state);
        rgrowv(problem->nnonlinear+1, &problem->hu, _state);
        
        /*
         * Generate and compute value of the nonlinear constraint
         */
        v = (double)(0);
        problem->nlc.ptr.p_double[problem->nnonlinear] = hqrndnormal(rs, _state);
        v = v+problem->nlc.ptr.p_double[problem->nnonlinear];
        hqrndnormalv(rs, n, &c, _state);
        rcopyvr(n, &c, &problem->nlb, problem->nnonlinear, _state);
        v = v+rdotvr(n, &c, &problem->xsol, 0, _state);
        spdmatrixrndcond(n, 10.0, &q, _state);
        vmul = nlquadratic*ae_fabs(hqrndnormal(rs, _state), _state);
        for(i=0; i<=n-1; i++)
        {
            rmulr(n, vmul, &q, i, _state);
        }
        rmatrixcopy(n, n, &q, 0, 0, &problem->nla, problem->nnonlinear*n, 0, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = v+0.5*problem->xsol.ptr.pp_double[0][i]*q.ptr.pp_double[i][j]*problem->xsol.ptr.pp_double[0][j];
            }
        }
        for(i=0; i<=n-1; i++)
        {
            problem->nld.ptr.pp_double[problem->nnonlinear][i] = nlquartic*ae_pow((double)(2), 0.33*hqrndnormal(rs, _state), _state);
            v = v+problem->nld.ptr.pp_double[problem->nnonlinear][i]*ae_pow(problem->xsol.ptr.pp_double[0][i], (double)(4), _state);
        }
        ctype = hqrnduniformi(rs, 3, _state);
        problem->hl.ptr.p_double[problem->nnonlinear] = rcase2(ctype==0||ctype==2, v-padinactive-ae_pow((double)(2), hqrndnormal(rs, _state), _state), _state->v_neginf, _state);
        problem->hu.ptr.p_double[problem->nnonlinear] = rcase2(ctype==1||ctype==2, v+padinactive+ae_pow((double)(2), hqrndnormal(rs, _state), _state), _state->v_posinf, _state);
        problem->nnonlinear = problem->nnonlinear+1;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Returns the size of the METAHEURISTIC-U1 collection

  -- ALGLIB --
     Copyright 04.03.2023 by Bochkanov Sergey
*************************************************************************/
ae_int_t motfgetmetaheuristicu1size(ae_state *_state)
{
    ae_int_t result;


    result = 11;
    return result;
}


/*************************************************************************
Returns the size of the METAHEURISTIC-U2 collection

  -- ALGLIB --
     Copyright 04.03.2023 by Bochkanov Sergey
*************************************************************************/
ae_int_t motfgetmetaheuristicu2size(ae_state *_state)
{
    ae_int_t result;


    result = 10;
    return result;
}


/*************************************************************************
Returns the size of the METAHEURISTIC-U3 collection

  -- ALGLIB --
     Copyright 04.03.2023 by Bochkanov Sergey
*************************************************************************/
ae_int_t motfgetmetaheuristicu3size(ae_state *_state)
{
    ae_int_t result;


    result = 4;
    return result;
}


/*************************************************************************
Returns the size of the NLS-1 collection

  -- ALGLIB --
     Copyright 04.04.2023 by Bochkanov Sergey
*************************************************************************/
ae_int_t motfgetnls1size(ae_state *_state)
{
    ae_int_t result;


    result = 16;
    return result;
}


/*************************************************************************
Returns the size of the NLS-2 collection

  -- ALGLIB --
     Copyright 04.04.2023 by Bochkanov Sergey
*************************************************************************/
ae_int_t motfgetnls2size(ae_state *_state)
{
    ae_int_t result;


    result = 1;
    return result;
}


/*************************************************************************
Creates a single-objective test problem from the METAHEURISTIC-U1 collection

INPUT PARAMETERS:
    ProblemIdx      -   a value from [0,MOTFGetMetaheuristicU1Size)
    IsRotated       -   if True, a random rotation is applied to variables
    RS              -   RNG, used for random rotations

  -- ALGLIB --
     Copyright 04.03.2023 by Bochkanov Sergey
*************************************************************************/
void motfcreatemetaheuristicu1(ae_int_t problemidx,
     ae_bool isrotated,
     hqrndstate* rs,
     multiobjectivetestfunction* problem,
     ae_state *_state)
{
    ae_bool processed;
    ae_int_t i;
    ae_int_t j;

    _multiobjectivetestfunction_clear(problem);

    
    /*
     * Set problem metrics
     */
    problem->problemtype = 1;
    problem->problemsubtype = problemidx;
    problem->isrotated = isrotated;
    problem->m = 1;
    problem->nlinear = 0;
    problem->nnonlinear = 0;
    ae_matrix_set_length(&problem->x0, 0, 0, _state);
    problem->k0 = 0;
    problem->ksol = 0;
    ae_matrix_set_length(&problem->xsol, 0, 0, _state);
    ae_matrix_set_length(&problem->fsol, 0, 0, _state);
    ae_vector_set_length(&problem->lagmultbc, 0, _state);
    ae_vector_set_length(&problem->lagmultlc, 0, _state);
    ae_vector_set_length(&problem->lagmultnlc, 0, _state);
    
    /*
     * Handle various problems from the collection
     */
    processed = ae_false;
    if( problemidx==0 )
    {
        
        /*
         * f_sp = sum(x^2)
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-100), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(100), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==1 )
    {
        
        /*
         * f_sch = sum(|x|) + prod(|x|)
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-10), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(10), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==2 )
    {
        
        /*
         * f_schDS
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-100), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(100), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==3 )
    {
        
        /*
         * f_sch
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-100), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(100), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==4 )
    {
        
        /*
         * f_ros
         */
        problem->n = 3;
        rsetallocv(problem->n, (double)(-30), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(30), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==5 )
    {
        
        /*
         * f = |x|^2 / (1+|x|^2)
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-30), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(30), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==6 )
    {
        
        /*
         * f = |x| / (1+|x|)
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-30), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(30), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==7 )
    {
        
        /*
         * f = |x| + |x|^2
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-30), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(30), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==8 )
    {
        
        /*
         * f_sp = sum(I*x^2)
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-100), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(100), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==9 )
    {
        
        /*
         * f_sp = sum(I*|x|)
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-100), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(100), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==10 )
    {
        
        /*
         * Brown function
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-1), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(4), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    ae_assert(processed, "MOTFCreateMetaheuristicU1: incorrect ProblemIdx", _state);
    
    /*
     * Generate random rotation
     */
    if( isrotated )
    {
        hqrndnormalm(rs, problem->n, problem->n, &problem->rotq, _state);
        for(i=0; i<=problem->n-1; i++)
        {
            rmulr(problem->n, (double)1/coalesce(ae_sqrt(rdotrr(problem->n, &problem->rotq, i, &problem->rotq, i, _state), _state), (double)(1), _state), &problem->rotq, i, _state);
            for(j=i+1; j<=problem->n-1; j++)
            {
                raddrr(problem->n, -rdotrr(problem->n, &problem->rotq, i, &problem->rotq, j, _state), &problem->rotq, i, &problem->rotq, j, _state);
            }
        }
    }
}


/*************************************************************************
Creates a single-objective test problem from the METAHEURISTIC-U2 collection

INPUT PARAMETERS:
    ProblemIdx      -   a value from [0,MOTFGetMetaheuristicU2Size)
    IsRotated       -   if True, a random rotation is applied to variables
    RS              -   RNG, used for random rotations

  -- ALGLIB --
     Copyright 04.03.2023 by Bochkanov Sergey
*************************************************************************/
void motfcreatemetaheuristicu2(ae_int_t problemidx,
     ae_bool isrotated,
     hqrndstate* rs,
     multiobjectivetestfunction* problem,
     ae_state *_state)
{
    ae_bool processed;
    ae_int_t i;
    ae_int_t j;

    _multiobjectivetestfunction_clear(problem);

    
    /*
     * Set problem metrics
     */
    problem->isrotated = isrotated;
    problem->problemtype = 2;
    problem->problemsubtype = problemidx;
    problem->m = 1;
    problem->nlinear = 0;
    problem->nnonlinear = 0;
    ae_matrix_set_length(&problem->x0, 0, 0, _state);
    problem->k0 = 0;
    problem->ksol = 0;
    ae_matrix_set_length(&problem->xsol, 0, 0, _state);
    ae_matrix_set_length(&problem->fsol, 0, 0, _state);
    ae_vector_set_length(&problem->lagmultbc, 0, _state);
    ae_vector_set_length(&problem->lagmultlc, 0, _state);
    ae_vector_set_length(&problem->lagmultnlc, 0, _state);
    
    /*
     * Handle various problems from the collection
     */
    processed = ae_false;
    if( problemidx==0 )
    {
        
        /*
         * Rastrigin: f = sum( x_i^2 - 10*cos(2pi*x_i)+10 )
         */
        problem->n = 30;
        rsetallocv(problem->n, -5.12, &problem->bndl, _state);
        rsetallocv(problem->n, 5.12, &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==1 )
    {
        
        /*
         * Ackley function
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-32), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(32), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==2 )
    {
        
        /*
         * Alphine 1 function
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-10), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(10), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==3 )
    {
        
        /*
         * Csendes function
         */
        problem->n = 50;
        rsetallocv(problem->n, (double)(-1), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(1), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==4 )
    {
        
        /*
         * Griewank function
         */
        problem->n = 30;
        rsetallocv(problem->n, (double)(-100), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(100), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==5 )
    {
        
        /*
         * Brown function
         */
        problem->n = 50;
        rsetallocv(problem->n, (double)(-1), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(4), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==6 )
    {
        
        /*
         * Pathological function
         */
        problem->n = 4;
        rsetallocv(problem->n, (double)(-100), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(100), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==7 )
    {
        
        /*
         * Pinter function
         */
        problem->n = 7;
        rsetallocv(problem->n, (double)(-10), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(10), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==8 )
    {
        
        /*
         * Streched V Sine Wave Function function
         */
        problem->n = 3;
        rsetallocv(problem->n, (double)(-10), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(10), &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==9 )
    {
        
        /*
         * Wavy function
         */
        problem->n = 20;
        rsetallocv(problem->n, -ae_pi, &problem->bndl, _state);
        rsetallocv(problem->n, ae_pi, &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    ae_assert(processed, "MOTFCreateMetaheuristicU2: incorrect ProblemIdx", _state);
    
    /*
     * Generate random rotation
     */
    if( isrotated )
    {
        hqrndnormalm(rs, problem->n, problem->n, &problem->rotq, _state);
        for(i=0; i<=problem->n-1; i++)
        {
            rmulr(problem->n, (double)1/coalesce(ae_sqrt(rdotrr(problem->n, &problem->rotq, i, &problem->rotq, i, _state), _state), (double)(1), _state), &problem->rotq, i, _state);
            for(j=i+1; j<=problem->n-1; j++)
            {
                raddrr(problem->n, -rdotrr(problem->n, &problem->rotq, i, &problem->rotq, j, _state), &problem->rotq, i, &problem->rotq, j, _state);
            }
        }
    }
}


/*************************************************************************
Creates a single-objective test problem from the METAHEURISTIC-U3 collection

INPUT PARAMETERS:
    ProblemIdx      -   a value from [0,MOTFGetMetaheuristicU3Size)
    RS              -   RNG, ignored in the current version because
                        all problems are deterministic, but may be used
                        later

  -- ALGLIB --
     Copyright 04.03.2024 by Bochkanov Sergey
*************************************************************************/
void motfcreatemetaheuristicu3(ae_int_t problemidx,
     hqrndstate* rs,
     multiobjectivetestfunction* problem,
     ae_state *_state)
{
    ae_bool processed;
    ae_int_t i;

    _multiobjectivetestfunction_clear(problem);

    
    /*
     * Set problem metrics
     */
    problem->isrotated = ae_false;
    problem->problemtype = 3;
    problem->problemsubtype = problemidx;
    problem->m = 1;
    problem->nlinear = 0;
    problem->nnonlinear = 0;
    problem->ksol = 0;
    problem->k0 = 0;
    ae_matrix_set_length(&problem->x0, 0, 0, _state);
    ae_matrix_set_length(&problem->xsol, 0, 0, _state);
    ae_matrix_set_length(&problem->fsol, 0, 0, _state);
    ae_vector_set_length(&problem->lagmultbc, 0, _state);
    ae_vector_set_length(&problem->lagmultlc, 0, _state);
    ae_vector_set_length(&problem->lagmultnlc, 0, _state);
    
    /*
     * Handle various problems from the collection
     */
    processed = ae_false;
    if( problemidx==0 )
    {
        
        /*
         * Trigonometric 1 Function
         */
        problem->n = 10;
        rsetallocv(problem->n, (double)(0), &problem->bndl, _state);
        rsetallocv(problem->n, ae_pi, &problem->bndu, _state);
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==1 )
    {
        
        /*
         * Xin-She Yang (Function 2)
         */
        problem->n = 2;
        rsetallocv(problem->n, -(double)2*ae_pi, &problem->bndl, _state);
        rsetallocv(problem->n, (double)2*ae_pi, &problem->bndu, _state);
        for(i=0; i<=problem->n-1; i++)
        {
            problem->bndl.ptr.p_double[i] = problem->bndl.ptr.p_double[i]*(0.5+0.5*ae_sqr(ae_sin((double)(117+89*i), _state), _state));
            problem->bndu.ptr.p_double[i] = problem->bndu.ptr.p_double[i]*(0.5+0.5*ae_sqr(ae_sin((double)(653+83*i), _state), _state));
        }
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==2 )
    {
        
        /*
         * Xin-She Yang (Function 4)
         */
        problem->n = 2;
        rsetallocv(problem->n, (double)(-10), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(10), &problem->bndu, _state);
        for(i=0; i<=problem->n-1; i++)
        {
            problem->bndl.ptr.p_double[i] = problem->bndl.ptr.p_double[i]*(0.1+0.9*ae_sqr(ae_sin((double)(117+89*i), _state), _state));
            problem->bndu.ptr.p_double[i] = problem->bndu.ptr.p_double[i]*(0.1+0.9*ae_sqr(ae_sin((double)(653+83*i), _state), _state));
        }
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, -1.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==3 )
    {
        
        /*
         * Big Rosenbrock
         */
        problem->n = 5;
        rsetallocv(problem->n, (double)(-30), &problem->bndl, _state);
        rsetallocv(problem->n, (double)(30), &problem->bndu, _state);
        for(i=0; i<=problem->n-1; i++)
        {
            problem->bndl.ptr.p_double[i] = problem->bndl.ptr.p_double[i]*(0.5+0.5*ae_sqr(ae_sin((double)(117+89*i), _state), _state));
            problem->bndu.ptr.p_double[i] = problem->bndu.ptr.p_double[i]*(0.5+0.5*ae_sqr(ae_sin((double)(653+83*i), _state), _state));
        }
        problem->ksol = 1;
        rsetallocm(1, problem->n, 0.0, &problem->xsol, _state);
        rsetallocm(1, 1, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    ae_assert(processed, "MOTFCreateMetaheuristicU3: incorrect ProblemIdx", _state);
}


/*************************************************************************
Creates a nonlinear least squares test problem from the NLS1 collection.
Usually comes with initial points.

INPUT PARAMETERS:
    ProblemIdx      -   a value from [0,MOTFGetNLS1Size)
    RS              -   RNG, ignored in the current version because
                        all problems are deterministic, but may be used
                        later

  -- ALGLIB --
     Copyright 04.04.2023 by Bochkanov Sergey
*************************************************************************/
void motfcreatenls1(ae_int_t problemidx,
     hqrndstate* rs,
     multiobjectivetestfunction* problem,
     ae_state *_state)
{
    ae_bool processed;

    _multiobjectivetestfunction_clear(problem);

    
    /*
     * Set problem metrics
     */
    problem->problemtype = 50;
    problem->problemsubtype = problemidx;
    problem->n = 0;
    problem->m = 0;
    problem->nlinear = 0;
    problem->nnonlinear = 0;
    ae_matrix_set_length(&problem->x0, 0, 0, _state);
    problem->k0 = 0;
    problem->ksol = 0;
    ae_matrix_set_length(&problem->xsol, 0, 0, _state);
    ae_matrix_set_length(&problem->fsol, 0, 0, _state);
    ae_vector_set_length(&problem->lagmultbc, 0, _state);
    ae_vector_set_length(&problem->lagmultlc, 0, _state);
    ae_vector_set_length(&problem->lagmultnlc, 0, _state);
    
    /*
     * Handle various problems from the collection
     */
    processed = ae_false;
    if( problemidx==0 )
    {
        
        /*
         * NF1
         */
        problem->n = 2;
        problem->m = 3;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        problem->ksol = 1;
        rallocm(1, problem->n, &problem->xsol, _state);
        problem->xsol.ptr.pp_double[0][0] = (double)(0);
        problem->xsol.ptr.pp_double[0][1] = (double)(1);
        rsetallocm(1, problem->m, 0.0, &problem->fsol, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==1 )
    {
        
        /*
         * NF2
         */
        problem->n = 2;
        problem->m = 4;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==2 )
    {
        
        /*
         * NF3
         */
        problem->n = 2;
        problem->m = 3;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        processed = ae_true;
    }
    if( problemidx==3 )
    {
        
        /*
         * Brown badly scaled function
         */
        problem->n = 2;
        problem->m = 3;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rsetallocm(1, problem->n, 1.0, &problem->x0, _state);
        problem->ksol = 1;
        rallocm(1, problem->n, &problem->xsol, _state);
        problem->xsol.ptr.pp_double[0][0] = 1.0E6;
        problem->xsol.ptr.pp_double[0][1] = 2.0E-6;
        rsetallocm(1, problem->m, 0.0, &problem->fsol, _state);
        processed = ae_true;
    }
    if( problemidx==4 )
    {
        
        /*
         * Mod Barbashin and Krasovskii function 1
         */
        problem->n = 2;
        problem->m = 3;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rallocm(1, problem->n, &problem->x0, _state);
        problem->x0.ptr.pp_double[0][0] = (double)(5);
        problem->x0.ptr.pp_double[0][1] = (double)(-6);
        problem->ksol = 1;
        rallocm(1, problem->n, &problem->xsol, _state);
        problem->xsol.ptr.pp_double[0][0] = (double)(0);
        problem->xsol.ptr.pp_double[0][1] = (double)(0);
        rsetallocm(1, problem->m, 0.0, &problem->fsol, _state);
        processed = ae_true;
    }
    if( problemidx==5 )
    {
        
        /*
         * Mod Barbashin and Krasovskii function 2
         */
        problem->n = 2;
        problem->m = 3;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rallocm(1, problem->n, &problem->x0, _state);
        problem->x0.ptr.pp_double[0][0] = (double)(5);
        problem->x0.ptr.pp_double[0][1] = (double)(-6);
        problem->ksol = 1;
        rallocm(1, problem->n, &problem->xsol, _state);
        problem->xsol.ptr.pp_double[0][0] = (double)(0);
        problem->xsol.ptr.pp_double[0][1] = (double)(0);
        rsetallocm(1, problem->m, 0.0, &problem->fsol, _state);
        problem->fsol.ptr.pp_double[0][2] = (double)(1);
        processed = ae_true;
    }
    if( problemidx==6 )
    {
        
        /*
         * Mod Rosenbrock 3
         */
        problem->n = 2;
        problem->m = 3;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rallocm(1, problem->n, &problem->x0, _state);
        problem->x0.ptr.pp_double[0][0] = -1.2;
        problem->x0.ptr.pp_double[0][1] = 1.0;
        processed = ae_true;
    }
    if( problemidx==7 )
    {
        
        /*
         * Beale function
         */
        problem->n = 2;
        problem->m = 3;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rallocm(1, problem->n, &problem->x0, _state);
        problem->x0.ptr.pp_double[0][0] = (double)(-5);
        problem->x0.ptr.pp_double[0][1] = (double)(-2);
        problem->ksol = 1;
        rallocm(1, problem->n, &problem->xsol, _state);
        problem->xsol.ptr.pp_double[0][0] = (double)(3);
        problem->xsol.ptr.pp_double[0][1] = 0.5;
        rsetallocm(1, problem->m, 0.0, &problem->fsol, _state);
        processed = ae_true;
    }
    if( problemidx==8 )
    {
        
        /*
         * B3DF function
         */
        problem->n = 3;
        problem->m = 10;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rallocm(1, problem->n, &problem->x0, _state);
        problem->x0.ptr.pp_double[0][0] = (double)(0);
        problem->x0.ptr.pp_double[0][1] = (double)(10);
        problem->x0.ptr.pp_double[0][2] = (double)(20);
        processed = ae_true;
    }
    if( problemidx==9 )
    {
        
        /*
         * GRDF function
         */
        problem->n = 3;
        problem->m = 10;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        problem->bndl.ptr.p_double[0] = 1.0E-5;
        problem->bndl.ptr.p_double[1] = 1.0E-5;
        problem->bndl.ptr.p_double[2] = 1.0E-1;
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rallocm(1, problem->n, &problem->x0, _state);
        problem->x0.ptr.pp_double[0][0] = (double)(5);
        problem->x0.ptr.pp_double[0][1] = 2.5;
        problem->x0.ptr.pp_double[0][2] = 0.15;
        processed = ae_true;
    }
    if( problemidx==10 )
    {
        
        /*
         * BARD function
         */
        problem->n = 3;
        problem->m = 15;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rsetallocm(1, problem->n, 1.0, &problem->x0, _state);
        processed = ae_true;
    }
    if( problemidx==11 )
    {
        
        /*
         * Colville function
         */
        problem->n = 4;
        problem->m = 7;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        problem->bndl.ptr.p_double[1] = 1.0;
        problem->bndl.ptr.p_double[3] = 1.0;
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rallocm(1, problem->n, &problem->x0, _state);
        problem->x0.ptr.pp_double[0][0] = (double)(10);
        problem->x0.ptr.pp_double[0][1] = (double)(10);
        problem->x0.ptr.pp_double[0][2] = (double)(10);
        problem->x0.ptr.pp_double[0][3] = (double)(10);
        processed = ae_true;
    }
    if( problemidx==12 )
    {
        
        /*
         * Hyperellipsoid function
         */
        problem->n = 8;
        problem->m = 9;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rallocm(1, problem->n, &problem->x0, _state);
        problem->x0.ptr.pp_double[0][0] = (double)(-1);
        problem->x0.ptr.pp_double[0][1] = (double)(2);
        problem->x0.ptr.pp_double[0][2] = (double)(-3);
        problem->x0.ptr.pp_double[0][3] = (double)(4);
        problem->x0.ptr.pp_double[0][4] = (double)(-5);
        problem->x0.ptr.pp_double[0][5] = (double)(6);
        problem->x0.ptr.pp_double[0][6] = (double)(-7);
        problem->x0.ptr.pp_double[0][7] = (double)(8);
        processed = ae_true;
    }
    if( problemidx==13 )
    {
        
        /*
         * Kowalik and Osborne Function
         */
        problem->n = 4;
        problem->m = 11;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rallocm(1, problem->n, &problem->x0, _state);
        problem->x0.ptr.pp_double[0][0] = 0.25;
        problem->x0.ptr.pp_double[0][1] = 0.39;
        problem->x0.ptr.pp_double[0][2] = 0.415;
        problem->x0.ptr.pp_double[0][3] = 0.39;
        processed = ae_true;
    }
    if( problemidx==14 )
    {
        
        /*
         * Biggs EXP6 Function
         */
        problem->n = 6;
        problem->m = 13;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rallocm(1, problem->n, &problem->x0, _state);
        problem->x0.ptr.pp_double[0][0] = (double)(1);
        problem->x0.ptr.pp_double[0][1] = (double)(2);
        problem->x0.ptr.pp_double[0][2] = (double)(1);
        problem->x0.ptr.pp_double[0][3] = (double)(1);
        problem->x0.ptr.pp_double[0][4] = (double)(1);
        problem->x0.ptr.pp_double[0][5] = (double)(1);
        processed = ae_true;
    }
    if( problemidx==15 )
    {
        
        /*
         * Highly ill-conditioned Rosenbrock
         */
        problem->n = 2;
        problem->m = 2;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rallocm(1, problem->n, &problem->x0, _state);
        problem->x0.ptr.pp_double[0][0] = (double)(-1);
        problem->x0.ptr.pp_double[0][1] = (double)(1);
        processed = ae_true;
    }
    ae_assert(processed, "MOTFCreateNLS1: incorrect ProblemIdx", _state);
}


/*************************************************************************
Creates a nonlinear least squares test problem from the NLS2 collection.
Usually comes with initial points.

INPUT PARAMETERS:
    ProblemIdx      -   a value from [0,MOTFGetNLS1Size)
    RS              -   RNG, ignored in the current version because
                        all problems are deterministic, but may be used
                        later

  -- ALGLIB --
     Copyright 04.04.2023 by Bochkanov Sergey
*************************************************************************/
void motfcreatenls2(ae_int_t problemidx,
     hqrndstate* rs,
     multiobjectivetestfunction* problem,
     ae_state *_state)
{
    ae_bool processed;

    _multiobjectivetestfunction_clear(problem);

    
    /*
     * Set problem metrics
     */
    problem->problemtype = 51;
    problem->problemsubtype = problemidx;
    problem->n = 0;
    problem->m = 0;
    problem->nlinear = 0;
    problem->nnonlinear = 0;
    ae_matrix_set_length(&problem->x0, 0, 0, _state);
    problem->k0 = 0;
    problem->ksol = 0;
    ae_matrix_set_length(&problem->xsol, 0, 0, _state);
    ae_matrix_set_length(&problem->fsol, 0, 0, _state);
    ae_vector_set_length(&problem->lagmultbc, 0, _state);
    ae_vector_set_length(&problem->lagmultlc, 0, _state);
    ae_vector_set_length(&problem->lagmultnlc, 0, _state);
    
    /*
     * Handle various problems from the collection
     */
    processed = ae_false;
    if( problemidx==0 )
    {
        
        /*
         * Meyer function
         */
        problem->n = 3;
        problem->m = 16;
        rsetallocv(problem->n, _state->v_neginf, &problem->bndl, _state);
        rsetallocv(problem->n, _state->v_posinf, &problem->bndu, _state);
        rsetallocv(problem->n, 0.0, &problem->lagmultbc, _state);
        problem->k0 = 1;
        rallocm(1, problem->n, &problem->x0, _state);
        problem->x0.ptr.pp_double[0][0] = 0.02;
        problem->x0.ptr.pp_double[0][1] = (double)(4000);
        problem->x0.ptr.pp_double[0][2] = (double)(250);
        processed = ae_true;
    }
    ae_assert(processed, "MOTFCreateNLS2: incorrect ProblemIdx", _state);
}


/*************************************************************************
Evaluate multiobjective test problem and its nonlinear constraints.

INPUT PARAMETERS:
    Problem             -   problem structure
    X                   -   trial point
    Fi                  -   preallocated array for M objectives and 
                            Problem.NNonLinear constraints
    NeedFi              -   whether Fi is needed or not
    Jac                 -   preallocated Jacobian array
    NeedJac             -   whether Jac is needed or not

  -- ALGLIB --
     Copyright 04.03.2023 by Bochkanov Sergey
*************************************************************************/
void motfeval(const multiobjectivetestfunction* problem,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* fi,
     ae_bool needfi,
     /* Real    */ ae_matrix* jac,
     ae_bool needjac,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t m;
    ae_int_t dst;
    ae_vector ax;
    ae_vector y;
    double vsum;
    double vprod;
    double v;
    double ui;
    double vi;
    double wi;
    double yi;
    double ti;
    double thetai;
    double ai;
    double bi;
    double ci;

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

    ae_assert(((((problem->problemtype==0||problem->problemtype==1)||problem->problemtype==2)||problem->problemtype==3)||problem->problemtype==50)||problem->problemtype==51, "MOTFEval: unknown problem type", _state);
    ae_assert(!needfi||fi->cnt>=problem->m+problem->nnonlinear, "MOTFEval: Fi is too short", _state);
    ae_assert(!needjac||jac->rows>=problem->m+problem->nnonlinear, "MOTFEval: Jac is too short", _state);
    ae_assert(!needjac||jac->cols>=problem->n, "MOTFEval: Jac is too short", _state);
    n = problem->n;
    m = problem->m;
    
    /*
     * Problem type #0
     */
    if( problem->problemtype==0 )
    {
        ae_assert(!problem->isrotated, "OPTSERV: integrity check 0009 failed", _state);
        
        /*
         * Evaluate objectives
         */
        for(k=0; k<=m-1; k++)
        {
            dst = k;
            
            /*
             * Initial values (constant term)
             */
            if( needfi )
            {
                fi->ptr.p_double[dst] = problem->tgtc.ptr.p_double[k];
            }
            if( needjac )
            {
                rsetr(n, 0.0, jac, dst, _state);
            }
            
            /*
             * Linear term
             */
            if( needfi )
            {
                fi->ptr.p_double[dst] = fi->ptr.p_double[dst]+rdotvr(n, x, &problem->tgtb, k, _state);
            }
            if( needjac )
            {
                raddrr(n, 1.0, &problem->tgtb, k, jac, dst, _state);
            }
            
            /*
             * Quadratic term
             */
            rallocv(n, &ax, _state);
            rmatrixgemv(n, n, 1.0, &problem->tgta, k*n, 0, 0, x, 0, 0.0, &ax, 0, _state);
            if( needfi )
            {
                fi->ptr.p_double[dst] = fi->ptr.p_double[dst]+0.5*rdotv(n, x, &ax, _state);
            }
            if( needjac )
            {
                raddvr(n, 1.0, &ax, jac, dst, _state);
            }
            
            /*
             * Quartic term
             */
            for(i=0; i<=n-1; i++)
            {
                if( needfi )
                {
                    fi->ptr.p_double[dst] = fi->ptr.p_double[dst]+problem->tgtd.ptr.pp_double[k][i]*ae_pow(x->ptr.p_double[i], (double)(4), _state);
                }
                if( needjac )
                {
                    jac->ptr.pp_double[dst][i] = jac->ptr.pp_double[dst][i]+problem->tgtd.ptr.pp_double[k][i]*(double)4*ae_pow(x->ptr.p_double[i], (double)(3), _state);
                }
            }
        }
        
        /*
         * Nonlinear constraints
         */
        for(k=0; k<=problem->nnonlinear-1; k++)
        {
            dst = m+k;
            
            /*
             * Initial values (constant term)
             */
            if( needfi )
            {
                fi->ptr.p_double[dst] = problem->nlc.ptr.p_double[k];
            }
            if( needjac )
            {
                rsetr(n, 0.0, jac, dst, _state);
            }
            
            /*
             * Linear term
             */
            if( needfi )
            {
                fi->ptr.p_double[dst] = fi->ptr.p_double[dst]+rdotvr(n, x, &problem->nlb, k, _state);
            }
            if( needjac )
            {
                raddrr(n, 1.0, &problem->nlb, k, jac, dst, _state);
            }
            
            /*
             * Quadratic term
             */
            rallocv(n, &ax, _state);
            rmatrixgemv(n, n, 1.0, &problem->nla, k*n, 0, 0, x, 0, 0.0, &ax, 0, _state);
            if( needfi )
            {
                fi->ptr.p_double[dst] = fi->ptr.p_double[dst]+0.5*rdotv(n, x, &ax, _state);
            }
            if( needjac )
            {
                raddvr(n, 1.0, &ax, jac, dst, _state);
            }
            
            /*
             * Quartic term
             */
            for(i=0; i<=n-1; i++)
            {
                if( needfi )
                {
                    fi->ptr.p_double[dst] = fi->ptr.p_double[dst]+problem->nld.ptr.pp_double[k][i]*ae_pow(x->ptr.p_double[i], (double)(4), _state);
                }
                if( needjac )
                {
                    jac->ptr.pp_double[dst][i] = jac->ptr.pp_double[dst][i]+problem->nld.ptr.pp_double[k][i]*(double)4*ae_pow(x->ptr.p_double[i], (double)(3), _state);
                }
            }
        }
        
        /*
         * Done
         */
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * METAHEURISTICS-U1 collection
     */
    if( problem->problemtype==1 )
    {
        ae_assert(!needjac, "OPTSERV: the METAHEURISTICS-U1 collection does not support Jacobians", _state);
        if( !needfi )
        {
            ae_frame_leave(_state);
            return;
        }
        rallocv(problem->n, &y, _state);
        if( problem->isrotated )
        {
            rgemv(problem->n, problem->n, 1.0, &problem->rotq, 0, x, 0.0, &y, _state);
        }
        else
        {
            rcopyv(problem->n, x, &y, _state);
        }
        fi->ptr.p_double[0] = (double)(0);
        if( problem->problemsubtype==0 )
        {
            for(i=0; i<=problem->n-1; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+ae_sqr(y.ptr.p_double[i], _state);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==1 )
        {
            vsum = (double)(0);
            vprod = (double)(1);
            for(i=0; i<=problem->n-1; i++)
            {
                vsum = vsum+ae_fabs(y.ptr.p_double[i], _state);
                vprod = vprod*ae_fabs(y.ptr.p_double[i], _state);
            }
            fi->ptr.p_double[0] = vsum+vprod;
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==2 )
        {
            for(i=0; i<=problem->n-1; i++)
            {
                v = (double)(0);
                for(j=0; j<=i; j++)
                {
                    v = v+y.ptr.p_double[i];
                }
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+ae_sqr(v, _state);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==3 )
        {
            for(i=0; i<=problem->n-1; i++)
            {
                fi->ptr.p_double[0] = ae_maxreal(fi->ptr.p_double[0], ae_fabs(y.ptr.p_double[i], _state), _state);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==4 )
        {
            for(i=0; i<=problem->n-2; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+((double)100*ae_sqr(y.ptr.p_double[i+1]-y.ptr.p_double[i]*y.ptr.p_double[i], _state)+ae_sqr(y.ptr.p_double[i]-(double)1, _state));
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==5 )
        {
            v = rdotv2(problem->n, &y, _state);
            fi->ptr.p_double[0] = v/(v+(double)1);
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==6 )
        {
            v = ae_sqrt(rdotv2(problem->n, &y, _state), _state);
            fi->ptr.p_double[0] = v/(v+(double)1);
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==7 )
        {
            v = rdotv2(problem->n, &y, _state);
            fi->ptr.p_double[0] = v+ae_sqrt(v, _state);
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==8 )
        {
            for(i=0; i<=problem->n-1; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+(double)(i+1)*ae_sqr(y.ptr.p_double[i], _state);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==9 )
        {
            for(i=0; i<=problem->n-1; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+(double)(i+1)*ae_fabs(y.ptr.p_double[i], _state);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==10 )
        {
            for(i=0; i<=problem->n-2; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+ae_pow(ae_sqr(y.ptr.p_double[i], _state), ae_sqr(y.ptr.p_double[i+1], _state)+(double)1, _state)+ae_pow(ae_sqr(y.ptr.p_double[i+1], _state), ae_sqr(y.ptr.p_double[i], _state)+(double)1, _state);
            }
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Error
         */
        ae_assert(ae_false, "OPTSERV: integrity check 9909 failed", _state);
    }
    
    /*
     * METAHEURISTICS-U2 collection
     */
    if( problem->problemtype==2 )
    {
        ae_assert(!needjac, "OPTSERV: the METAHEURISTICS-U2 collection does not support Jacobians", _state);
        if( !needfi )
        {
            ae_frame_leave(_state);
            return;
        }
        rallocv(problem->n, &y, _state);
        if( problem->isrotated )
        {
            rgemv(problem->n, problem->n, 1.0, &problem->rotq, 0, x, 0.0, &y, _state);
        }
        else
        {
            rcopyv(problem->n, x, &y, _state);
        }
        fi->ptr.p_double[0] = (double)(0);
        if( problem->problemsubtype==0 )
        {
            for(i=0; i<=problem->n-1; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+ae_sqr(y.ptr.p_double[i], _state)-(double)10*ae_cos((double)2*ae_pi*y.ptr.p_double[i], _state)+(double)10;
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==1 )
        {
            v = (double)(0);
            for(i=0; i<=problem->n-1; i++)
            {
                v = v+ae_sqr(y.ptr.p_double[i], _state);
            }
            fi->ptr.p_double[0] = fi->ptr.p_double[0]-(double)20*ae_exp(-0.2*ae_sqrt(v/(double)problem->n, _state), _state);
            v = (double)(0);
            for(i=0; i<=problem->n-1; i++)
            {
                v = v+ae_cos((double)2*ae_pi*y.ptr.p_double[i], _state);
            }
            fi->ptr.p_double[0] = fi->ptr.p_double[0]-ae_exp(v/(double)problem->n, _state);
            fi->ptr.p_double[0] = fi->ptr.p_double[0]+(double)20+ae_exp((double)(1), _state);
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==2 )
        {
            for(i=0; i<=problem->n-1; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+ae_fabs(y.ptr.p_double[i]*(0.1+ae_sin(y.ptr.p_double[i], _state)), _state);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==3 )
        {
            for(i=0; i<=problem->n-1; i++)
            {
                if( ae_fp_neq(y.ptr.p_double[i],(double)(0)) )
                {
                    fi->ptr.p_double[0] = fi->ptr.p_double[0]+ae_pow(y.ptr.p_double[i], (double)(6), _state)*((double)2+ae_sin((double)1/y.ptr.p_double[i], _state));
                }
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==4 )
        {
            for(i=0; i<=problem->n-1; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+ae_sqr(y.ptr.p_double[i], _state)/(double)4000;
            }
            v = (double)(1);
            for(i=0; i<=problem->n-1; i++)
            {
                v = v*ae_cos(y.ptr.p_double[i]/ae_sqrt((double)(i+1), _state), _state);
            }
            fi->ptr.p_double[0] = fi->ptr.p_double[0]+((double)1-v);
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==5 )
        {
            fi->ptr.p_double[0] = (double)(0);
            for(i=0; i<=problem->n-2; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+ae_pow(ae_sqr(y.ptr.p_double[i], _state), ae_sqr(y.ptr.p_double[i+1], _state)+(double)1, _state)+ae_pow(ae_sqr(y.ptr.p_double[i+1], _state), ae_sqr(y.ptr.p_double[i], _state)+(double)1, _state);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==6 )
        {
            fi->ptr.p_double[0] = (double)(0);
            for(i=0; i<=problem->n-2; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+0.5+(ae_sqr(ae_sin(ae_sqrt((double)100*y.ptr.p_double[i]*y.ptr.p_double[i]+y.ptr.p_double[i+1]*y.ptr.p_double[i+1], _state), _state), _state)-0.5)/((double)1+0.001*ae_sqr(ae_sqr(y.ptr.p_double[i]-y.ptr.p_double[i+1], _state), _state));
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==7 )
        {
            fi->ptr.p_double[0] = (double)(0);
            for(i=0; i<=problem->n-1; i++)
            {
                ai = y.ptr.p_double[(i-1+problem->n)%problem->n]*ae_sin(y.ptr.p_double[i], _state)+ae_sin(y.ptr.p_double[(i+1)%problem->n], _state);
                bi = ae_sqr(y.ptr.p_double[(i-1+problem->n)%problem->n], _state)-(double)2*y.ptr.p_double[i]+(double)3*y.ptr.p_double[(i+1)%problem->n]-ae_cos(y.ptr.p_double[i], _state)+(double)1;
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+(double)(i+1)*(ae_sqr(y.ptr.p_double[i], _state)+(double)20*ae_sqr(ae_sin(ai, _state), _state)+ae_log((double)1+(double)(i+1)*bi*bi, _state));
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==8 )
        {
            fi->ptr.p_double[0] = (double)(0);
            for(i=0; i<=problem->n-2; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+ae_pow(y.ptr.p_double[i+1]*y.ptr.p_double[i+1]+y.ptr.p_double[i]*y.ptr.p_double[i], 0.25, _state)*(ae_sqr(ae_sin((double)50*ae_pow(y.ptr.p_double[i+1]*y.ptr.p_double[i+1]+y.ptr.p_double[i]*y.ptr.p_double[i], 0.1, _state), _state), _state)+0.1);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==9 )
        {
            fi->ptr.p_double[0] = (double)(1);
            for(i=0; i<=problem->n-1; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]-ae_cos((double)10*y.ptr.p_double[i], _state)*ae_exp(-ae_sqr(y.ptr.p_double[i], _state)/(double)2, _state)/(double)problem->n;
            }
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Error
         */
        ae_assert(ae_false, "OPTSERV: integrity check 9919 failed", _state);
        
        /*
         * Error
         */
        ae_assert(ae_false, "OPTSERV: integrity check 9919 failed", _state);
    }
    
    /*
     * NLS1 collection
     */
    if( problem->problemtype==50 )
    {
        if( !needfi&&!needjac )
        {
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==0 )
        {
            if( needfi )
            {
                fi->ptr.p_double[0] = x->ptr.p_double[1]-ae_cosh(x->ptr.p_double[0], _state);
                fi->ptr.p_double[1] = x->ptr.p_double[1]-ae_cos(x->ptr.p_double[0], _state);
                fi->ptr.p_double[2] = x->ptr.p_double[1]-ae_sqr(x->ptr.p_double[0], _state)-(double)1;
            }
            if( needjac )
            {
                jac->ptr.pp_double[0][0] = -ae_sinh(x->ptr.p_double[0], _state);
                jac->ptr.pp_double[0][1] = (double)(1);
                jac->ptr.pp_double[1][0] = ae_sin(x->ptr.p_double[0], _state);
                jac->ptr.pp_double[1][1] = (double)(1);
                jac->ptr.pp_double[2][0] = -(double)2*x->ptr.p_double[0];
                jac->ptr.pp_double[2][1] = (double)(1);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==1 )
        {
            if( needfi )
            {
                fi->ptr.p_double[0] = x->ptr.p_double[1]-ae_cosh(x->ptr.p_double[0]+(double)1, _state)+(double)1;
                fi->ptr.p_double[1] = x->ptr.p_double[1]-ae_sin(x->ptr.p_double[0], _state)-ae_cos(x->ptr.p_double[0], _state);
                fi->ptr.p_double[2] = ae_sqr(x->ptr.p_double[1], _state)-x->ptr.p_double[0]+(double)1;
                fi->ptr.p_double[3] = x->ptr.p_double[0]+(double)1;
            }
            if( needjac )
            {
                jac->ptr.pp_double[0][0] = -ae_sinh(x->ptr.p_double[0]+(double)1, _state);
                jac->ptr.pp_double[0][1] = (double)(1);
                jac->ptr.pp_double[1][0] = ae_sin(x->ptr.p_double[0], _state)-ae_cos(x->ptr.p_double[0], _state);
                jac->ptr.pp_double[1][1] = (double)(1);
                jac->ptr.pp_double[2][0] = (double)(-1);
                jac->ptr.pp_double[2][1] = (double)2*x->ptr.p_double[1];
                jac->ptr.pp_double[3][0] = (double)(1);
                jac->ptr.pp_double[3][1] = (double)(0);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==2 )
        {
            if( needfi )
            {
                fi->ptr.p_double[0] = x->ptr.p_double[0]+1.5;
                fi->ptr.p_double[1] = x->ptr.p_double[1];
                fi->ptr.p_double[2] = ae_sqrt((double)(10), _state)*(ae_sqr(x->ptr.p_double[0], _state)+ae_sqr(x->ptr.p_double[1], _state)-(double)1);
            }
            if( needjac )
            {
                jac->ptr.pp_double[0][0] = (double)(1);
                jac->ptr.pp_double[0][1] = (double)(0);
                jac->ptr.pp_double[1][0] = (double)(0);
                jac->ptr.pp_double[1][1] = (double)(1);
                jac->ptr.pp_double[2][0] = ae_sqrt((double)(10), _state)*(double)2*x->ptr.p_double[0];
                jac->ptr.pp_double[2][1] = ae_sqrt((double)(10), _state)*(double)2*x->ptr.p_double[1];
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==3 )
        {
            if( needfi )
            {
                fi->ptr.p_double[0] = x->ptr.p_double[0]-1.0E6;
                fi->ptr.p_double[1] = x->ptr.p_double[1]-2.0E-6;
                fi->ptr.p_double[2] = x->ptr.p_double[0]*x->ptr.p_double[1]-(double)2;
            }
            if( needjac )
            {
                jac->ptr.pp_double[0][0] = (double)(1);
                jac->ptr.pp_double[0][1] = (double)(0);
                jac->ptr.pp_double[1][0] = (double)(0);
                jac->ptr.pp_double[1][1] = (double)(1);
                jac->ptr.pp_double[2][0] = x->ptr.p_double[1];
                jac->ptr.pp_double[2][1] = x->ptr.p_double[0];
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==4 )
        {
            if( needfi )
            {
                fi->ptr.p_double[0] = (double)100*x->ptr.p_double[0]/ae_sqrt((double)1+x->ptr.p_double[0]*x->ptr.p_double[0], _state);
                fi->ptr.p_double[1] = (double)10*x->ptr.p_double[1];
                fi->ptr.p_double[2] = ae_sinh(x->ptr.p_double[0], _state);
            }
            if( needjac )
            {
                rsetm(3, 2, 0.0, jac, _state);
                jac->ptr.pp_double[0][0] = ((double)100*ae_sqrt((double)1+x->ptr.p_double[0]*x->ptr.p_double[0], _state)-(double)100*x->ptr.p_double[0]*0.5/ae_sqrt((double)1+x->ptr.p_double[0]*x->ptr.p_double[0], _state))/((double)1+x->ptr.p_double[0]*x->ptr.p_double[0]);
                jac->ptr.pp_double[1][1] = (double)(10);
                jac->ptr.pp_double[2][0] = ae_cosh(x->ptr.p_double[0], _state);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==5 )
        {
            if( needfi )
            {
                fi->ptr.p_double[0] = (double)100*x->ptr.p_double[0]/ae_sqrt((double)1+x->ptr.p_double[0]*x->ptr.p_double[0], _state);
                fi->ptr.p_double[1] = (double)10*x->ptr.p_double[1];
                fi->ptr.p_double[2] = ae_cosh(x->ptr.p_double[0], _state);
            }
            if( needjac )
            {
                rsetm(3, 2, 0.0, jac, _state);
                jac->ptr.pp_double[0][0] = ((double)100*ae_sqrt((double)1+x->ptr.p_double[0]*x->ptr.p_double[0], _state)-(double)100*x->ptr.p_double[0]*0.5/ae_sqrt((double)1+x->ptr.p_double[0]*x->ptr.p_double[0], _state))/((double)1+x->ptr.p_double[0]*x->ptr.p_double[0]);
                jac->ptr.pp_double[1][1] = (double)(10);
                jac->ptr.pp_double[2][0] = ae_sinh(x->ptr.p_double[0], _state);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==6 )
        {
            if( needfi )
            {
                fi->ptr.p_double[0] = (double)100*(x->ptr.p_double[1]-ae_sqr(x->ptr.p_double[0], _state));
                fi->ptr.p_double[1] = (double)10*(x->ptr.p_double[0]-(double)1);
                fi->ptr.p_double[2] = ae_sqr(x->ptr.p_double[1]+(double)1, _state)-x->ptr.p_double[0]+(double)1;
            }
            if( needjac )
            {
                rsetm(3, 2, 0.0, jac, _state);
                jac->ptr.pp_double[0][0] = -(double)200*x->ptr.p_double[0];
                jac->ptr.pp_double[0][1] = (double)(100);
                jac->ptr.pp_double[1][0] = (double)(10);
                jac->ptr.pp_double[2][0] = (double)(-1);
                jac->ptr.pp_double[2][1] = (double)2*(x->ptr.p_double[1]+(double)1);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==7 )
        {
            if( needfi )
            {
                fi->ptr.p_double[0] = 1.5+x->ptr.p_double[0]*(x->ptr.p_double[1]-(double)1);
                fi->ptr.p_double[1] = 2.25+x->ptr.p_double[0]*(x->ptr.p_double[1]*x->ptr.p_double[1]-(double)1);
                fi->ptr.p_double[2] = 2.625+x->ptr.p_double[0]*(x->ptr.p_double[1]*x->ptr.p_double[1]*x->ptr.p_double[1]-(double)1);
            }
            if( needjac )
            {
                jac->ptr.pp_double[0][0] = x->ptr.p_double[1]-(double)1;
                jac->ptr.pp_double[0][1] = x->ptr.p_double[0];
                jac->ptr.pp_double[1][0] = x->ptr.p_double[1]*x->ptr.p_double[1]-(double)1;
                jac->ptr.pp_double[1][1] = x->ptr.p_double[0]*(double)2*x->ptr.p_double[1];
                jac->ptr.pp_double[2][0] = x->ptr.p_double[1]*x->ptr.p_double[1]*x->ptr.p_double[1]-(double)1;
                jac->ptr.pp_double[2][1] = x->ptr.p_double[0]*(double)3*x->ptr.p_double[1]*x->ptr.p_double[1];
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==8 )
        {
            if( needfi )
            {
                for(i=0; i<=9; i++)
                {
                    v = 0.1*(double)(i+1);
                    fi->ptr.p_double[i] = ae_exp(-v*x->ptr.p_double[0], _state)-ae_exp(-v*x->ptr.p_double[1], _state)-x->ptr.p_double[2]*(ae_exp(-v, _state)-ae_exp(-(double)10*v, _state));
                }
            }
            if( needjac )
            {
                for(i=0; i<=9; i++)
                {
                    v = 0.1*(double)(i+1);
                    jac->ptr.pp_double[i][0] = ae_exp(-v*x->ptr.p_double[0], _state)*(-v);
                    jac->ptr.pp_double[i][1] = -ae_exp(-v*x->ptr.p_double[1], _state)*(-v);
                    jac->ptr.pp_double[i][2] = -(ae_exp(-v, _state)-ae_exp(-(double)10*v, _state));
                }
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==9 )
        {
            if( needfi )
            {
                for(i=0; i<=9; i++)
                {
                    thetai = 0.01*(double)(i+1);
                    yi = (double)25+ae_pow(-(double)50*ae_log(thetai, _state), (double)2/(double)3, _state);
                    fi->ptr.p_double[i] = ae_exp(-ae_pow(ae_fabs(yi*(double)problem->m*(double)(i+1)*x->ptr.p_double[1], _state), x->ptr.p_double[2], _state)/x->ptr.p_double[0], _state)-thetai;
                }
            }
            if( needjac )
            {
                for(i=0; i<=9; i++)
                {
                    thetai = 0.01*(double)(i+1);
                    yi = (double)25+ae_pow(-(double)50*ae_log(thetai, _state), (double)2/(double)3, _state);
                    wi = ae_fabs(yi*(double)problem->m*(double)(i+1), _state);
                    v = ae_exp(-ae_pow(wi*x->ptr.p_double[1], x->ptr.p_double[2], _state)/x->ptr.p_double[0], _state);
                    jac->ptr.pp_double[i][0] = v*ae_pow(wi*x->ptr.p_double[1], x->ptr.p_double[2], _state)/ae_sqr(x->ptr.p_double[0], _state);
                    jac->ptr.pp_double[i][1] = v*(-(double)1/x->ptr.p_double[0])*x->ptr.p_double[2]*ae_pow(wi*x->ptr.p_double[1], x->ptr.p_double[2]-(double)1, _state)*wi;
                    jac->ptr.pp_double[i][2] = v*(-(double)1/x->ptr.p_double[0])*ae_pow(wi*x->ptr.p_double[1], x->ptr.p_double[2], _state)*ae_log(wi*x->ptr.p_double[1], _state);
                }
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==10 )
        {
            for(i=0; i<=14; i++)
            {
                yi = (double)(0);
                if( i==0 )
                {
                    yi = 0.14;
                }
                if( i==1 )
                {
                    yi = 0.18;
                }
                if( i==2 )
                {
                    yi = 0.22;
                }
                if( i==3 )
                {
                    yi = 0.25;
                }
                if( i==4 )
                {
                    yi = 0.29;
                }
                if( i==5 )
                {
                    yi = 0.32;
                }
                if( i==6 )
                {
                    yi = 0.35;
                }
                if( i==7 )
                {
                    yi = 0.39;
                }
                if( i==8 )
                {
                    yi = 0.37;
                }
                if( i==9 )
                {
                    yi = 0.58;
                }
                if( i==10 )
                {
                    yi = 0.73;
                }
                if( i==11 )
                {
                    yi = 0.96;
                }
                if( i==12 )
                {
                    yi = 1.34;
                }
                if( i==13 )
                {
                    yi = 2.10;
                }
                if( i==14 )
                {
                    yi = 4.39;
                }
                ui = (double)(i+1);
                vi = (double)(15-i);
                wi = ae_minreal(ui, vi, _state);
                if( ae_fp_greater(vi*x->ptr.p_double[1]+wi*x->ptr.p_double[2],ae_minrealnumber) )
                {
                    if( needfi )
                    {
                        fi->ptr.p_double[i] = yi-(x->ptr.p_double[0]+ui/(vi*x->ptr.p_double[1]+wi*x->ptr.p_double[2]));
                    }
                    if( needjac )
                    {
                        jac->ptr.pp_double[i][0] = (double)(-1);
                        jac->ptr.pp_double[i][1] = ui/ae_sqr(vi*x->ptr.p_double[1]+wi*x->ptr.p_double[2], _state)*vi;
                        jac->ptr.pp_double[i][2] = ui/ae_sqr(vi*x->ptr.p_double[1]+wi*x->ptr.p_double[2], _state)*wi;
                    }
                }
                else
                {
                    if( needfi )
                    {
                        fi->ptr.p_double[i] = yi-(x->ptr.p_double[0]+ui/ae_minrealnumber);
                    }
                    if( needjac )
                    {
                        jac->ptr.pp_double[i][0] = (double)(0);
                        jac->ptr.pp_double[i][1] = (double)(0);
                        jac->ptr.pp_double[i][2] = (double)(0);
                    }
                }
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==11 )
        {
            if( needfi )
            {
                fi->ptr.p_double[0] = (double)10*(ae_sqr(x->ptr.p_double[0], _state)-x->ptr.p_double[1]);
                fi->ptr.p_double[1] = x->ptr.p_double[0]-(double)1;
                fi->ptr.p_double[2] = x->ptr.p_double[2]-(double)1;
                fi->ptr.p_double[3] = ae_sqrt((double)(90), _state)*(ae_sqr(x->ptr.p_double[2], _state)-x->ptr.p_double[3]);
                fi->ptr.p_double[4] = ae_sqrt(10.1, _state)*(x->ptr.p_double[1]-(double)1);
                fi->ptr.p_double[5] = ae_sqrt(10.1, _state)*(x->ptr.p_double[3]-(double)1);
                fi->ptr.p_double[6] = ae_sqrt(19.8*(x->ptr.p_double[1]-(double)1)*(x->ptr.p_double[3]-(double)1)+ae_machineepsilon, _state);
            }
            if( needjac )
            {
                rsetm(problem->m, problem->n, 0.0, jac, _state);
                jac->ptr.pp_double[0][0] = (double)20*x->ptr.p_double[0];
                jac->ptr.pp_double[0][1] = (double)(-10);
                jac->ptr.pp_double[1][0] = (double)(1);
                jac->ptr.pp_double[2][2] = (double)(1);
                jac->ptr.pp_double[3][2] = ae_sqrt((double)(90), _state)*(double)2*x->ptr.p_double[2];
                jac->ptr.pp_double[3][3] = -ae_sqrt((double)(90), _state);
                jac->ptr.pp_double[4][1] = ae_sqrt(10.1, _state);
                jac->ptr.pp_double[5][3] = ae_sqrt(10.1, _state);
                jac->ptr.pp_double[6][1] = 0.5/ae_sqrt(19.8*(x->ptr.p_double[1]-(double)1)*(x->ptr.p_double[3]-(double)1)+ae_machineepsilon, _state)*19.8*(x->ptr.p_double[3]-(double)1);
                jac->ptr.pp_double[6][3] = 0.5/ae_sqrt(19.8*(x->ptr.p_double[1]-(double)1)*(x->ptr.p_double[3]-(double)1)+ae_machineepsilon, _state)*19.8*(x->ptr.p_double[1]-(double)1);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==12 )
        {
            if( needjac )
            {
                rsetm(problem->m, problem->n, 0.0, jac, _state);
            }
            for(i=0; i<=7; i++)
            {
                if( needfi )
                {
                    fi->ptr.p_double[i] = ae_sqrt((double)(2), _state)*ae_sqr(x->ptr.p_double[i], _state);
                }
                if( needjac )
                {
                    jac->ptr.pp_double[i][i] = (double)2*ae_sqrt((double)(2), _state)*x->ptr.p_double[i];
                }
            }
            if( needfi )
            {
                fi->ptr.p_double[8] = (double)(0);
                for(i=0; i<=8; i++)
                {
                    fi->ptr.p_double[8] = fi->ptr.p_double[8]+ae_pow(2.2, (double)(i), _state);
                }
                fi->ptr.p_double[8] = ae_sqrt(fi->ptr.p_double[8], _state);
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==13 )
        {
            for(i=0; i<=10; i++)
            {
                yi = (double)(0);
                ti = (double)(0);
                if( i==0 )
                {
                    yi = 0.1957;
                    ti = 4.0000;
                }
                if( i==1 )
                {
                    yi = 0.1947;
                    ti = 2.0000;
                }
                if( i==2 )
                {
                    yi = 0.1735;
                    ti = 1.0000;
                }
                if( i==3 )
                {
                    yi = 0.1600;
                    ti = 0.5000;
                }
                if( i==4 )
                {
                    yi = 0.0844;
                    ti = 0.2500;
                }
                if( i==5 )
                {
                    yi = 0.0627;
                    ti = 0.1670;
                }
                if( i==6 )
                {
                    yi = 0.0456;
                    ti = 0.1250;
                }
                if( i==7 )
                {
                    yi = 0.0342;
                    ti = 0.1000;
                }
                if( i==8 )
                {
                    yi = 0.0323;
                    ti = 0.0833;
                }
                if( i==9 )
                {
                    yi = 0.0235;
                    ti = 0.0714;
                }
                if( i==10 )
                {
                    yi = 0.0246;
                    ti = 0.0625;
                }
                if( ae_fp_greater(ti*ti+ti*x->ptr.p_double[2]+x->ptr.p_double[3],1.0E-20) )
                {
                    if( needfi )
                    {
                        fi->ptr.p_double[i] = yi-x->ptr.p_double[0]*(ti*ti+ti*x->ptr.p_double[1])/(ti*ti+ti*x->ptr.p_double[2]+x->ptr.p_double[3]);
                    }
                    if( needjac )
                    {
                        jac->ptr.pp_double[i][0] = -(ti*ti+ti*x->ptr.p_double[1])/(ti*ti+ti*x->ptr.p_double[2]+x->ptr.p_double[3]);
                        jac->ptr.pp_double[i][1] = -x->ptr.p_double[0]*ti/(ti*ti+ti*x->ptr.p_double[2]+x->ptr.p_double[3]);
                        jac->ptr.pp_double[i][2] = (ti*ti+ti*x->ptr.p_double[1])/ae_sqr(ti*ti+ti*x->ptr.p_double[2]+x->ptr.p_double[3], _state)*ti;
                        jac->ptr.pp_double[i][3] = (ti*ti+ti*x->ptr.p_double[1])/ae_sqr(ti*ti+ti*x->ptr.p_double[2]+x->ptr.p_double[3], _state);
                    }
                }
                else
                {
                    if( needfi )
                    {
                        fi->ptr.p_double[i] = ae_sqrt(ae_maxrealnumber, _state);
                    }
                    if( needjac )
                    {
                        jac->ptr.pp_double[i][0] = (double)(0);
                        jac->ptr.pp_double[i][1] = (double)(0);
                        jac->ptr.pp_double[i][2] = (double)(0);
                        jac->ptr.pp_double[i][3] = (double)(0);
                    }
                }
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==14 )
        {
            for(i=0; i<=12; i++)
            {
                ti = 0.1*(double)(i+1);
                yi = ae_exp(-ti, _state)-(double)5*ae_exp(-(double)10*ti, _state)+(double)3*ae_exp(-(double)4*ti, _state);
                if( needfi )
                {
                    fi->ptr.p_double[i] = x->ptr.p_double[2]*ae_exp(-ti*x->ptr.p_double[0], _state)-x->ptr.p_double[3]*ae_exp(-ti*x->ptr.p_double[1], _state)+x->ptr.p_double[5]*ae_exp(-ti*x->ptr.p_double[4], _state)-yi;
                }
                if( needjac )
                {
                    jac->ptr.pp_double[i][0] = x->ptr.p_double[2]*ae_exp(-ti*x->ptr.p_double[0], _state)*(-ti);
                    jac->ptr.pp_double[i][1] = -x->ptr.p_double[3]*ae_exp(-ti*x->ptr.p_double[1], _state)*(-ti);
                    jac->ptr.pp_double[i][2] = ae_exp(-ti*x->ptr.p_double[0], _state);
                    jac->ptr.pp_double[i][3] = -ae_exp(-ti*x->ptr.p_double[1], _state);
                    jac->ptr.pp_double[i][4] = x->ptr.p_double[5]*ae_exp(-ti*x->ptr.p_double[4], _state)*(-ti);
                    jac->ptr.pp_double[i][5] = ae_exp(-ti*x->ptr.p_double[4], _state);
                }
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==15 )
        {
            if( needfi )
            {
                fi->ptr.p_double[0] = (double)1-x->ptr.p_double[0];
                fi->ptr.p_double[1] = (double)1000*(x->ptr.p_double[1]-ae_sqr(x->ptr.p_double[0], _state));
            }
            if( needjac )
            {
                jac->ptr.pp_double[0][0] = (double)(-1);
                jac->ptr.pp_double[0][1] = (double)(0);
                jac->ptr.pp_double[1][0] = -(double)2000*x->ptr.p_double[0];
                jac->ptr.pp_double[1][1] = (double)(1000);
            }
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Error
         */
        ae_assert(ae_false, "OPTSERV: integrity check 9919 failed", _state);
    }
    
    /*
     * NLS1 collection
     */
    if( problem->problemtype==51 )
    {
        if( !needfi&&!needjac )
        {
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==0 )
        {
            for(i=0; i<=15; i++)
            {
                yi = (double)(0);
                if( i==0 )
                {
                    yi = (double)(34780);
                }
                if( i==1 )
                {
                    yi = (double)(28610);
                }
                if( i==2 )
                {
                    yi = (double)(23650);
                }
                if( i==3 )
                {
                    yi = (double)(19630);
                }
                if( i==4 )
                {
                    yi = (double)(16370);
                }
                if( i==5 )
                {
                    yi = (double)(13720);
                }
                if( i==6 )
                {
                    yi = (double)(11540);
                }
                if( i==7 )
                {
                    yi = (double)(9744);
                }
                if( i==8 )
                {
                    yi = (double)(8261);
                }
                if( i==9 )
                {
                    yi = (double)(7030);
                }
                if( i==10 )
                {
                    yi = (double)(6005);
                }
                if( i==11 )
                {
                    yi = (double)(5147);
                }
                if( i==12 )
                {
                    yi = (double)(4427);
                }
                if( i==13 )
                {
                    yi = (double)(3820);
                }
                if( i==14 )
                {
                    yi = (double)(3307);
                }
                if( i==15 )
                {
                    yi = (double)(2872);
                }
                thetai = (double)(45+5*(i+1));
                if( ae_fp_greater(thetai+x->ptr.p_double[2],(double)(0))&&ae_fp_less(x->ptr.p_double[1]/(thetai+x->ptr.p_double[2]),ae_log(ae_maxrealnumber, _state)) )
                {
                    if( needfi )
                    {
                        fi->ptr.p_double[i] = x->ptr.p_double[0]*ae_exp(x->ptr.p_double[1]/(thetai+x->ptr.p_double[2]), _state)-yi;
                    }
                    if( needjac )
                    {
                        jac->ptr.pp_double[i][0] = ae_exp(x->ptr.p_double[1]/(thetai+x->ptr.p_double[2]), _state);
                        jac->ptr.pp_double[i][1] = x->ptr.p_double[0]*ae_exp(x->ptr.p_double[1]/(thetai+x->ptr.p_double[2]), _state)/(thetai+x->ptr.p_double[2]);
                        jac->ptr.pp_double[i][2] = x->ptr.p_double[0]*ae_exp(x->ptr.p_double[1]/(thetai+x->ptr.p_double[2]), _state)*x->ptr.p_double[1]*(double)(-1)/ae_sqr(thetai+x->ptr.p_double[2], _state);
                    }
                }
                else
                {
                    if( needfi )
                    {
                        fi->ptr.p_double[i] = ae_sqrt(ae_maxrealnumber, _state);
                    }
                    if( needjac )
                    {
                        jac->ptr.pp_double[i][0] = (double)(0);
                        jac->ptr.pp_double[i][1] = (double)(0);
                        jac->ptr.pp_double[i][2] = (double)(0);
                    }
                }
            }
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * METAHEURISTICS-U3 collection
     */
    if( problem->problemtype==3 )
    {
        ae_assert(!problem->isrotated, "OPTSERV: integrity check 9609 failed", _state);
        ae_assert(!needjac, "OPTSERV: the METAHEURISTICS-U3 collection does not support Jacobians", _state);
        if( !needfi )
        {
            ae_frame_leave(_state);
            return;
        }
        fi->ptr.p_double[0] = (double)(0);
        if( problem->problemsubtype==0 )
        {
            fi->ptr.p_double[0] = (double)(0);
            for(i=0; i<=problem->n-1; i++)
            {
                v = (double)(problem->n);
                for(j=0; j<=problem->n-1; j++)
                {
                    v = v-ae_cos(x->ptr.p_double[j], _state);
                }
                v = v+(double)(1+i)*((double)1-ae_cos(x->ptr.p_double[i], _state)-ae_sin(x->ptr.p_double[i], _state));
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+v*v;
            }
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==1 )
        {
            ai = (double)(0);
            bi = (double)(0);
            for(i=0; i<=problem->n-1; i++)
            {
                ai = ai+ae_fabs(x->ptr.p_double[i], _state);
                bi = bi+ae_sin(x->ptr.p_double[i]*x->ptr.p_double[i], _state);
            }
            fi->ptr.p_double[0] = ai*ae_exp(-bi, _state);
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==2 )
        {
            ai = (double)(0);
            bi = (double)(0);
            ci = (double)(0);
            for(i=0; i<=problem->n-1; i++)
            {
                ai = ai+ae_sqr(ae_sin(x->ptr.p_double[i], _state), _state);
                bi = bi+x->ptr.p_double[i]*x->ptr.p_double[i];
                ci = ci+ae_sqr(ae_sin(ae_sqrt(ae_fabs(x->ptr.p_double[i], _state), _state), _state), _state);
            }
            fi->ptr.p_double[0] = (ai-ae_exp(-bi, _state))*ae_exp(-ci, _state);
            ae_frame_leave(_state);
            return;
        }
        if( problem->problemsubtype==3 )
        {
            fi->ptr.p_double[0] = (double)(0);
            for(i=0; i<=problem->n-2; i++)
            {
                fi->ptr.p_double[0] = fi->ptr.p_double[0]+((double)100*ae_sqr(x->ptr.p_double[i+1]-x->ptr.p_double[i]*x->ptr.p_double[i], _state)+ae_sqr(x->ptr.p_double[i]-(double)1, _state));
            }
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Error
         */
        ae_assert(ae_false, "OPTSERV: integrity check 1559 failed", _state);
    }
    
    /*
     * Error
     */
    ae_assert(ae_false, "OPTSERV: integrity check 4407 failed", _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Converts modern two-sided mixed sparse/dense linear constraints to an  old
dense single-sided format.

NOTE: this function ignores inconsistent bounds, like CL>CU, assuming that
      either they were already noticed, or will be noticed later.
*************************************************************************/
void converttwosidedlctoonesidedold(const sparsematrix* sparsec,
     ae_int_t ksparse,
     /* Real    */ const ae_matrix* densec,
     ae_int_t kdense,
     ae_int_t n,
     /* Real    */ const ae_vector* cl,
     /* Real    */ const ae_vector* cu,
     /* Real    */ ae_matrix* olddensec,
     /* Integer */ ae_vector* olddensect,
     ae_int_t* olddensek,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t k;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t idxout;


    
    /*
     * Compute required amount of single-sided constraints
     */
    *olddensek = 0;
    for(i=0; i<=ksparse+kdense-1; i++)
    {
        ae_assert(ae_isfinite(cl->ptr.p_double[i], _state)||ae_isneginf(cl->ptr.p_double[i], _state), "OPTSERV: integrity check 7117 failed", _state);
        ae_assert(ae_isfinite(cu->ptr.p_double[i], _state)||ae_isposinf(cu->ptr.p_double[i], _state), "OPTSERV: integrity check 7118 failed", _state);
        if( (ae_isfinite(cl->ptr.p_double[i], _state)&&ae_isfinite(cu->ptr.p_double[i], _state))&&ae_fp_eq(cl->ptr.p_double[i],cu->ptr.p_double[i]) )
        {
            *olddensek = *olddensek+1;
            continue;
        }
        if( ae_isfinite(cl->ptr.p_double[i], _state) )
        {
            *olddensek = *olddensek+1;
        }
        if( ae_isfinite(cu->ptr.p_double[i], _state) )
        {
            *olddensek = *olddensek+1;
        }
    }
    if( *olddensek==0 )
    {
        return;
    }
    
    /*
     * Convert two-sided constraints into single-sided
     */
    idxout = 0;
    rallocm(*olddensek, n+1, olddensec, _state);
    iallocv(*olddensek, olddensect, _state);
    for(i=0; i<=ksparse+kdense-1; i++)
    {
        
        /*
         * Ignore non-binding rows
         */
        if( !ae_isfinite(cl->ptr.p_double[i], _state)&&!ae_isfinite(cu->ptr.p_double[i], _state) )
        {
            continue;
        }
        
        /*
         * Extract row
         */
        ae_assert(idxout<(*olddensek), "OPTSERV: integrity check 0324 failed", _state);
        if( i<ksparse )
        {
            
            /*
             * Extract from the sparse matrix
             */
            rsetr(n, 0.0, olddensec, idxout, _state);
            k0 = sparsec->ridx.ptr.p_int[i];
            k1 = sparsec->ridx.ptr.p_int[i+1]-1;
            for(k=k0; k<=k1; k++)
            {
                olddensec->ptr.pp_double[idxout][sparsec->idx.ptr.p_int[k]] = sparsec->vals.ptr.p_double[k];
            }
        }
        else
        {
            
            /*
             * Copy from the dense matrix
             */
            rcopyrr(n, densec, i-ksparse, olddensec, idxout, _state);
        }
        
        /*
         * Two bounds are present
         */
        if( ae_isfinite(cl->ptr.p_double[i], _state)&&ae_isfinite(cu->ptr.p_double[i], _state) )
        {
            if( ae_fp_eq(cl->ptr.p_double[i],cu->ptr.p_double[i]) )
            {
                
                /*
                 * Two bounds = equality constraints, one row is output
                 */
                olddensect->ptr.p_int[idxout] = 0;
                olddensec->ptr.pp_double[idxout][n] = cl->ptr.p_double[i];
                idxout = idxout+1;
                continue;
            }
            else
            {
                
                /*
                 * Two bounds = range constraint, two rows are output
                 */
                rcopyrr(n, olddensec, idxout, olddensec, idxout+1, _state);
                olddensect->ptr.p_int[idxout+0] = 1;
                olddensect->ptr.p_int[idxout+1] = -1;
                olddensec->ptr.pp_double[idxout+0][n] = cl->ptr.p_double[i];
                olddensec->ptr.pp_double[idxout+1][n] = cu->ptr.p_double[i];
                idxout = idxout+2;
                continue;
            }
        }
        
        /*
         * Only one bound is present
         */
        if( ae_isfinite(cl->ptr.p_double[i], _state) )
        {
            olddensect->ptr.p_int[idxout] = 1;
            olddensec->ptr.pp_double[idxout][n] = cl->ptr.p_double[i];
            idxout = idxout+1;
            continue;
        }
        if( ae_isfinite(cu->ptr.p_double[i], _state) )
        {
            olddensect->ptr.p_int[idxout] = -1;
            olddensec->ptr.pp_double[idxout][n] = cu->ptr.p_double[i];
            idxout = idxout+1;
            continue;
        }
        ae_assert(ae_false, "OPTSERV: integrity check 7025 failed", _state);
    }
    ae_assert(idxout==(*olddensek), "OPTSERV: integrity check 0214 failed", _state);
}


/*************************************************************************
Prepares conversion of the modern two-sided nonlinear constraints to an old
single-sided zero-bounded format.

NOTE: this function ignores inconsistent bounds, like NL>NU, assuming that
      either they were already noticed, or will be noticed later.
*************************************************************************/
void converttwosidednlctoonesidedold(/* Real    */ const ae_vector* nl,
     /* Real    */ const ae_vector* nu,
     ae_int_t nnlc,
     /* Integer */ ae_vector* nlcidx,
     /* Real    */ ae_vector* nlcmul,
     /* Real    */ ae_vector* nlcadd,
     ae_int_t* cntnlec,
     ae_int_t* cntnlic,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t idxoutnlec;
    ae_int_t idxoutnlic;


    
    /*
     * Compute required amount of single-sided constraints
     */
    *cntnlec = 0;
    *cntnlic = 0;
    for(i=0; i<=nnlc-1; i++)
    {
        ae_assert(ae_isfinite(nl->ptr.p_double[i], _state)||ae_isneginf(nl->ptr.p_double[i], _state), "OPTSERV: integrity check 0017 failed", _state);
        ae_assert(ae_isfinite(nu->ptr.p_double[i], _state)||ae_isposinf(nu->ptr.p_double[i], _state), "OPTSERV: integrity check 0018 failed", _state);
        if( (ae_isfinite(nl->ptr.p_double[i], _state)&&ae_isfinite(nu->ptr.p_double[i], _state))&&ae_fp_eq(nl->ptr.p_double[i],nu->ptr.p_double[i]) )
        {
            *cntnlec = *cntnlec+1;
            continue;
        }
        if( ae_isfinite(nl->ptr.p_double[i], _state) )
        {
            *cntnlic = *cntnlic+1;
        }
        if( ae_isfinite(nu->ptr.p_double[i], _state) )
        {
            *cntnlic = *cntnlic+1;
        }
    }
    if( *cntnlec+(*cntnlic)==0 )
    {
        return;
    }
    
    /*
     * Convert two-sided constraints into single-sided ones
     */
    idxoutnlec = 0;
    idxoutnlic = *cntnlec;
    iallocv(*cntnlec+(*cntnlic), nlcidx, _state);
    rallocv(*cntnlec+(*cntnlic), nlcmul, _state);
    rallocv(*cntnlec+(*cntnlic), nlcadd, _state);
    for(i=0; i<=nnlc-1; i++)
    {
        
        /*
         * Ignore non-binding rows
         */
        if( !ae_isfinite(nl->ptr.p_double[i], _state)&&!ae_isfinite(nu->ptr.p_double[i], _state) )
        {
            continue;
        }
        
        /*
         *
         * Two bounds are present
         */
        if( ae_isfinite(nl->ptr.p_double[i], _state)&&ae_isfinite(nu->ptr.p_double[i], _state) )
        {
            if( ae_fp_eq(nl->ptr.p_double[i],nu->ptr.p_double[i]) )
            {
                
                /*
                 * Two bounds = equality constraints, one row is output
                 */
                nlcidx->ptr.p_int[idxoutnlec] = i;
                nlcmul->ptr.p_double[idxoutnlec] = 1.0;
                nlcadd->ptr.p_double[idxoutnlec] = -nl->ptr.p_double[i];
                idxoutnlec = idxoutnlec+1;
                continue;
            }
            else
            {
                
                /*
                 * Two bounds = range constraint, two rows are output
                 */
                nlcidx->ptr.p_int[idxoutnlic+0] = i;
                nlcmul->ptr.p_double[idxoutnlic+0] = -1.0;
                nlcadd->ptr.p_double[idxoutnlic+0] = nl->ptr.p_double[i];
                nlcidx->ptr.p_int[idxoutnlic+1] = i;
                nlcmul->ptr.p_double[idxoutnlic+1] = 1.0;
                nlcadd->ptr.p_double[idxoutnlic+1] = -nu->ptr.p_double[i];
                idxoutnlic = idxoutnlic+2;
                continue;
            }
        }
        
        /*
         * Only one bound is present
         */
        if( ae_isfinite(nl->ptr.p_double[i], _state) )
        {
            nlcidx->ptr.p_int[idxoutnlic+0] = i;
            nlcmul->ptr.p_double[idxoutnlic+0] = -1.0;
            nlcadd->ptr.p_double[idxoutnlic+0] = nl->ptr.p_double[i];
            idxoutnlic = idxoutnlic+1;
            continue;
        }
        if( ae_isfinite(nu->ptr.p_double[i], _state) )
        {
            nlcidx->ptr.p_int[idxoutnlic+0] = i;
            nlcmul->ptr.p_double[idxoutnlic+0] = 1.0;
            nlcadd->ptr.p_double[idxoutnlic+0] = -nu->ptr.p_double[i];
            idxoutnlic = idxoutnlic+1;
            continue;
        }
        ae_assert(ae_false, "OPTSERV: integrity check 9041 failed", _state);
    }
    ae_assert(idxoutnlec==(*cntnlec), "OPTSERV: integrity check 9242 failed", _state);
    ae_assert(idxoutnlic==*cntnlec+(*cntnlic), "OPTSERV: integrity check 9243 failed", _state);
}


/*************************************************************************
Increases trust region growth factor until maximum growth reached.
*************************************************************************/
void trustradincreasemomentum(double* growthfactor,
     double growthincrease,
     double maxgrowthfactor,
     ae_state *_state)
{


    *growthfactor = ae_minreal(*growthfactor*growthincrease, maxgrowthfactor, _state);
}


/*************************************************************************
Resets trust region growth factor to its default state.
*************************************************************************/
void trustradresetmomentum(double* growthfactor,
     double mingrowthfactor,
     ae_state *_state)
{


    *growthfactor = mingrowthfactor;
}


/*************************************************************************
Initialize V-F-J structure by allocating uninitialized fields
*************************************************************************/
void vfjallocdense(ae_int_t n,
     ae_int_t m,
     varsfuncjac* s,
     ae_state *_state)
{


    ae_assert(n>=1, "vfjAllocDense: N<1", _state);
    ae_assert(m>=1, "vfjAllocDense: M<1", _state);
    s->n = n;
    s->m = m;
    s->isdense = ae_true;
    rallocv(n, &s->x, _state);
    rallocv(m, &s->fi, _state);
    rallocm(m, n, &s->jac, _state);
}


/*************************************************************************
Initialize V-F-J structure by allocating uninitialized fields
*************************************************************************/
void vfjallocsparse(ae_int_t n,
     ae_int_t m,
     varsfuncjac* s,
     ae_state *_state)
{


    ae_assert(n>=1, "vfjAllocSparse: N<1", _state);
    ae_assert(m>=1, "vfjAllocSparse: M<1", _state);
    s->n = n;
    s->m = m;
    s->isdense = ae_false;
    rallocv(n, &s->x, _state);
    rallocv(m, &s->fi, _state);
    s->sj.matrixtype = -1;
    s->sj.m = -1;
    s->sj.n = -1;
}


/*************************************************************************
Initialize V-F-J structure from user-supplied data
*************************************************************************/
void vfjinitfromdense(/* Real    */ const ae_vector* x,
     ae_int_t n,
     /* Real    */ const ae_vector* fi,
     ae_int_t m,
     /* Real    */ const ae_matrix* jac,
     varsfuncjac* s,
     ae_state *_state)
{


    ae_assert(n>=1, "vfjInitFromDense: N<1", _state);
    ae_assert(m>=1, "vfjInitFromDense: M<1", _state);
    s->n = n;
    s->m = m;
    s->isdense = ae_true;
    rcopyallocv(n, x, &s->x, _state);
    rcopyallocv(m, fi, &s->fi, _state);
    rcopyallocm(m, n, jac, &s->jac, _state);
}


/*************************************************************************
Initialize V-F-J structure from user-supplied data
*************************************************************************/
void vfjinitfromsparse(/* Real    */ const ae_vector* x,
     ae_int_t n,
     /* Real    */ const ae_vector* fi,
     ae_int_t m,
     const sparsematrix* jac,
     varsfuncjac* s,
     ae_state *_state)
{


    ae_assert(n>=1, "vfjInitFromSparse: N<1", _state);
    ae_assert(m>=1, "vfjInitFromSparse: M<1", _state);
    ae_assert(sparsegetnrows(jac, _state)==m, "vfjInitFromSparse: Jac has incorrect rows count", _state);
    ae_assert(sparsegetncols(jac, _state)==n, "vfjInitFromSparse: Jac has incorrect cols count", _state);
    s->n = n;
    s->m = m;
    s->isdense = ae_false;
    rcopyallocv(n, x, &s->x, _state);
    rcopyallocv(m, fi, &s->fi, _state);
    sparsecopytocrsbuf(jac, &s->sj, _state);
}


/*************************************************************************
Copy V-F-J structure
*************************************************************************/
void vfjcopy(const varsfuncjac* src, varsfuncjac* dst, ae_state *_state)
{


    dst->n = src->n;
    dst->m = src->m;
    dst->isdense = src->isdense;
    rcopyallocv(src->n, &src->x, &dst->x, _state);
    rcopyallocv(src->m, &src->fi, &dst->fi, _state);
    if( src->isdense )
    {
        rcopyallocm(src->m, src->n, &src->jac, &dst->jac, _state);
    }
    else
    {
        sparsecopybuf(&src->sj, &dst->sj, _state);
    }
}


/*************************************************************************
Initialize criteria with default settings
*************************************************************************/
void critinitdefault(nlpstoppingcriteria* crit, ae_state *_state)
{


    crit->epsf = (double)(0);
    crit->eps = (double)(0);
    crit->maxits = 0;
}


/*************************************************************************
Copy criteria
*************************************************************************/
void critcopy(const nlpstoppingcriteria* src,
     nlpstoppingcriteria* dst,
     ae_state *_state)
{


    dst->epsf = src->epsf;
    dst->eps = src->eps;
    dst->maxits = src->maxits;
}


/*************************************************************************
Overwrites stopping CONDITIONS (not TOLERANCES) by values  passed  by  the
user.

Future versions of the structure may introduce additional conditions, these
condifitons will be set to zero by this version-1 function.
*************************************************************************/
void critsetcondv1(nlpstoppingcriteria* crit,
     double epsf,
     double eps,
     ae_int_t maxits,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsf, _state)&&ae_fp_greater_eq(epsf,(double)(0)), "critSetCondV1: EpsF is infinite or negative", _state);
    ae_assert(ae_isfinite(eps, _state)&&ae_fp_greater_eq(eps,(double)(0)), "critSetCondV1: Eps is infinite or negative", _state);
    ae_assert(maxits>=0, "critSetCondV1: MaxIts is negative", _state);
    crit->epsf = epsf;
    crit->eps = eps;
    crit->maxits = maxits;
}


/*************************************************************************
Returns the value of the EpsF stopping condition:
* if user specified nonzero value, it will be returned
* if user specified zero value of EpsF, but non-zero value for  any  other
  condition or tolerance, zero will be returned
* if user specified zero value of EpsF and zero values for all  other
  conditions and tolerances, a default value will be returned
*************************************************************************/
double critgetepsf(const nlpstoppingcriteria* crit, ae_state *_state)
{
    double result;


    if( rmax3(crit->epsf, crit->eps, (double)(crit->maxits), _state)==0.0 )
    {
        result = 1.0E-8;
    }
    else
    {
        result = crit->epsf;
    }
    return result;
}


/*************************************************************************
Returns the value of the EpsX stopping condition:
* if user specified nonzero value, it will be returned
* if user specified zero value of EpsX, but non-zero value for  any  other
  condition or tolerance, zero will be returned
* if user specified zero value of EpsX and zero values for all  other
  conditions and tolerances, a default value will be returned
*************************************************************************/
double critgeteps(const nlpstoppingcriteria* crit, ae_state *_state)
{
    double result;


    result = critgetepswithdefault(crit, 1.0E-7, _state);
    return result;
}


/*************************************************************************
Returns the value of the EpsX stopping condition:
* if user specified nonzero value, it will be returned
* if user specified zero value of EpsX, but non-zero value for  any  other
  condition or tolerance, zero will be returned
* if user specified zero value of EpsX and zero values for all  other
  conditions and tolerances, a default value will be returned
*************************************************************************/
double critgetepswithdefault(const nlpstoppingcriteria* crit,
     double defval,
     ae_state *_state)
{
    double result;


    if( rmax3(crit->epsf, crit->eps, (double)(crit->maxits), _state)==0.0 )
    {
        result = defval;
    }
    else
    {
        result = crit->eps;
    }
    return result;
}


/*************************************************************************
Returns the value of the MaxIts stopping condition:
* the value specified by the user (either zero or nonzero) will be returned
*************************************************************************/
ae_int_t critgetmaxits(const nlpstoppingcriteria* crit, ae_state *_state)
{
    ae_int_t result;


    result = crit->maxits;
    return result;
}


/*************************************************************************
Initializes line search using bisection

NOTES:
* MaxIts>=2 is required
*************************************************************************/
void linesearchinitbisect(double f0,
     double g0,
     double alpha1,
     double alphamax,
     double c1,
     double c2,
     ae_bool strongwolfecond,
     ae_int_t maxits,
     ae_bool dotrace,
     ae_int_t tracelevel,
     linesearchstate* state,
     ae_state *_state)
{


    ae_assert(ae_isfinite(f0, _state), "OPTSERV: integrity check 9420 failed", _state);
    ae_assert(ae_isfinite(g0, _state), "OPTSERV: integrity check 9421 failed", _state);
    ae_assert(ae_isfinite(alpha1, _state), "OPTSERV: integrity check 9422 failed", _state);
    ae_assert(ae_isfinite(alphamax, _state), "OPTSERV: integrity check 9422A failed", _state);
    ae_assert(ae_fp_greater(alpha1,(double)(0)), "OPTSERV: integrity check 9423 failed", _state);
    ae_assert(ae_fp_greater_eq(alphamax,alpha1), "OPTSERV: integrity check 9423A failed", _state);
    ae_assert(maxits>=2, "OPTSERV: integrity check 9424 failed", _state);
    ae_assert(ae_isfinite(c1, _state), "OPTSERV: integrity check 9425 failed", _state);
    ae_assert(ae_isfinite(c2, _state), "OPTSERV: integrity check 9426 failed", _state);
    ae_assert(ae_fp_greater(c1,(double)(0))&&ae_fp_less(c1,(double)(1)), "OPTSERV: integrity check 9427 failed", _state);
    ae_assert(ae_fp_greater(c2,(double)(0))&&ae_fp_less(c2,(double)(1)), "OPTSERV: integrity check 9428 failed", _state);
    state->f0 = f0;
    state->g0 = g0;
    state->alpha1 = alpha1;
    state->alphamax = alphamax;
    state->c1 = c1;
    state->c2 = c2;
    state->strongwolfecond = strongwolfecond;
    state->maxits = maxits;
    state->dotrace = dotrace;
    state->tracelevel = tracelevel;
    state->rstate.stage = -1;
}


/*************************************************************************
Performs iteration.

After the end it stores the best  step  to  State.AlphaSol.  The  function
warrants that either:
* AlphaSol is the last Alpha tried
* AlphaSol=0, which means that a non-descent direction was specified.
  In the latter case no search was performed.

The function also sets TerminationType field  to  a  value  which  roughly
corresponds to ones returned by MCSRCH:
* 0 for non-descent direction
* 1 for search ended with the sufficient decrease condition and the
    curvature condition satisfied
* 3 for iteration budget being exhausted, stopping at the best point so far
* 5 for a step being at the upper bound
*************************************************************************/
ae_bool linesearchiteration(linesearchstate* state, ae_state *_state)
{
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
    }
    else
    {
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    
    /*
     * Routine body
     */
    
    /*
     * Initial checks
     */
    if( state->dotrace )
    {
        traceangles(state->tracelevel, _state);
        ae_trace(" starting line search\n");
    }
    if( ae_fp_greater_eq(state->g0,(double)(0)) )
    {
        if( state->dotrace )
        {
            traceangles(state->tracelevel, _state);
            ae_trace(" directional derivative non-negative (%0.2e), stopping\n",
                (double)(state->g0));
        }
        state->alphasol = 0.0;
        state->terminationtype = 0;
        result = ae_false;
        return result;
    }
    state->nfev = 0;
    
    /*
     * Phase 1: bracket step
     */
    state->fprev = state->f0;
    state->gprev = state->g0;
    state->alphaprev = (double)(0);
    state->alphacur = state->alpha1;
    state->bestalphasofar = (double)(0);
    state->bestfsofar = state->f0;
lbl_5:
    if( ae_false )
    {
        goto lbl_6;
    }
    
    /*
     * RComm
     */
    state->alphatrial = state->alphacur;
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    state->fcur = state->ftrial;
    state->gcur = state->gtrial;
    state->nfev = state->nfev+1;
    if( state->dotrace )
    {
        traceangles(state->tracelevel, _state);
        ae_trace(" target evaluated at stp=%0.3e, deltaF=%0.3e, dg=%0.3e\n",
            (double)(state->alphacur),
            (double)(state->fcur-state->f0),
            (double)(state->gcur));
    }
    
    /*
     * Update best point so far
     *
     * NOTE: strict inequality is essential
     */
    if( ae_fp_eq(state->bestalphasofar,(double)(0))||ae_fp_less(state->fcur,state->bestfsofar) )
    {
        state->bestalphasofar = state->alphacur;
        state->bestfsofar = state->fcur;
    }
    
    /*
     * Tests
     */
    if( state->nfev<state->maxits-1 )
    {
        goto lbl_7;
    }
    
    /*
     * Function budget is almost exhausted. We may have to terminate before exhausting it completely
     * in order to be able to evaluate the target at the last point known.
     */
    ae_assert(ae_fp_greater(state->bestalphasofar,(double)(0)), "OPTSERV: integrity check 1224 failed", _state);
    if( ae_fp_eq(state->alphacur,state->bestalphasofar) )
    {
        goto lbl_9;
    }
    
    /*
     * Make one reverse communication request.
     *
     * We do not use its results, it is here merely for caller to evaluate the target at the point where we stopped
     */
    state->alphatrial = state->bestalphasofar;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->nfev = state->nfev+1;
lbl_9:
    state->alphasol = state->bestalphasofar;
    state->terminationtype = 3;
    if( state->dotrace )
    {
        traceangles(state->tracelevel, _state);
        ae_trace(" iteration budget of %0d evaluations is used, stopping\n",
            (int)(state->maxits));
    }
    result = ae_false;
    return result;
lbl_7:
    if( ae_fp_less_eq(state->fcur,state->f0+state->c1*state->alphacur*state->g0)&&(ae_fp_less_eq(ae_fabs(state->gcur, _state),-state->c2*state->g0)||(!state->strongwolfecond&&ae_fp_greater_eq(state->gcur,state->c2*state->g0))) )
    {
        
        /*
         * Found
         */
        state->alphasol = state->alphacur;
        state->terminationtype = 1;
        if( state->dotrace )
        {
            traceangles(state->tracelevel, _state);
            ae_trace(" sufficient decrease and curvature conditions hold, stopping\n");
        }
        result = ae_false;
        return result;
    }
    if( ae_fp_greater_eq(state->fcur,state->f0+state->c1*state->alphacur*state->g0)||ae_fp_greater_eq(state->fcur,state->fprev) )
    {
        
        /*
         * Bracketed by target value
         */
        state->alphalo = state->alphaprev;
        state->alphahi = state->alphacur;
        state->flo = state->fprev;
        state->fhi = state->fcur;
        state->glo = state->gprev;
        state->ghi = state->gcur;
        if( state->dotrace )
        {
            traceangles(state->tracelevel, _state);
            ae_trace(" the minimum is bracketed by the target, stp=[%0.3e,%0.3e], proceeding to phase 2\n",
                (double)(state->alphalo),
                (double)(state->alphahi));
        }
        goto lbl_6;
    }
    if( ae_fp_greater_eq(state->gcur,(double)(0)) )
    {
        
        /*
         * Bracketed by gradient value
         */
        state->alphahi = state->alphaprev;
        state->alphalo = state->alphacur;
        state->fhi = state->fprev;
        state->flo = state->fcur;
        state->ghi = state->gprev;
        state->glo = state->gcur;
        if( state->dotrace )
        {
            traceangles(state->tracelevel, _state);
            ae_trace(" the minimum is bracketed by the gradient, stp=[%0.3e,%0.3e], proceeding to phase 2\n",
                (double)(state->alphahi),
                (double)(state->alphalo));
        }
        goto lbl_6;
    }
    if( !(ae_fp_greater_eq(state->alphacur,0.999*state->alphamax)||ae_fp_eq(state->alphacur,state->alphaprev)) )
    {
        goto lbl_11;
    }
    
    /*
     * We reached upper bound on the step length (or the step stagnated due to numerical errors)
     */
    ae_assert(ae_fp_greater(state->bestalphasofar,(double)(0)), "OPTSERV: integrity check 1224 failed", _state);
    if( ae_fp_eq(state->alphacur,state->bestalphasofar) )
    {
        goto lbl_13;
    }
    
    /*
     * Make one reverse communication request.
     *
     * We do not use its results, it is here merely for caller to evaluate the target at the point where we stopped
     */
    state->alphatrial = state->bestalphasofar;
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->nfev = state->nfev+1;
lbl_13:
    if( state->dotrace )
    {
        traceangles(state->tracelevel, _state);
        ae_trace(" reached an upper bound on the step length, stopping at the best point so far (stp=%0.3e)\n",
            (double)(state->bestalphasofar));
    }
    state->alphasol = state->bestalphasofar;
    state->terminationtype = 5;
    result = ae_false;
    return result;
lbl_11:
    
    /*
     * Next step
     */
    state->fprev = state->fcur;
    state->gprev = state->gcur;
    state->alphaprev = state->alphacur;
    state->alphacur = ae_minreal((double)2*state->alphacur, state->alphamax, _state);
    goto lbl_5;
lbl_6:
    
    /*
     * Phase 2: perform bisection on bracketed step
     */
lbl_15:
    if( ae_false )
    {
        goto lbl_16;
    }
    
    /*
     * Compute AlphaTrial and evaluate target
     */
    state->alphacur = 0.5*(state->alphalo+state->alphahi);
    state->alphatrial = state->alphacur;
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    state->fcur = state->ftrial;
    state->gcur = state->gtrial;
    state->nfev = state->nfev+1;
    if( state->dotrace )
    {
        traceangles(state->tracelevel, _state);
        ae_trace(" target evaluated at stp=%0.3e, deltaF=%0.3e, dg=%0.3e, interval of uncertainty is [%0.3e,%0.3e]\n",
            (double)(state->alphacur),
            (double)(state->fcur-state->f0),
            (double)(state->gcur),
            (double)(ae_minreal(state->alphalo, state->alphahi, _state)),
            (double)(ae_maxreal(state->alphalo, state->alphahi, _state)));
    }
    
    /*
     * Update best point so far
     *
     * NOTE: strict inequality is essential
     */
    ae_assert(ae_fp_neq(state->bestalphasofar,(double)(0)), "OPTSERV: integrity check 1130 failed", _state);
    if( ae_fp_less(state->fcur,state->bestfsofar) )
    {
        state->bestalphasofar = state->alphacur;
        state->bestfsofar = state->fcur;
    }
    
    /*
     * Check iteration budget
     */
    if( state->nfev<state->maxits-1 )
    {
        goto lbl_17;
    }
    
    /*
     * Function budget is almost exhausted. We may have to terminate before exhausting it completely
     * in order to be able to evaluate the target at the last point known.
     */
    ae_assert(ae_fp_greater(state->bestalphasofar,(double)(0)), "OPTSERV: integrity check 1224 failed", _state);
    if( ae_fp_eq(state->alphacur,state->bestalphasofar) )
    {
        goto lbl_19;
    }
    
    /*
     * Make one reverse communication request.
     *
     * We do not use its results, it is here merely for caller to evaluate the target at the point where we stopped
     */
    state->alphatrial = state->bestalphasofar;
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    state->nfev = state->nfev+1;
lbl_19:
    state->alphasol = state->bestalphasofar;
    state->terminationtype = 3;
    if( state->dotrace )
    {
        traceangles(state->tracelevel, _state);
        ae_trace(" iteration budget of %0d evaluations is used, stopping\n",
            (int)(state->maxits));
    }
    result = ae_false;
    return result;
lbl_17:
    
    /*
     * Check stopping criteria
     */
    if( ae_fp_less_eq(state->fcur,state->f0+state->c1*state->alphacur*state->g0)&&(ae_fp_less_eq(ae_fabs(state->gcur, _state),-state->c2*state->g0)||(!state->strongwolfecond&&ae_fp_greater_eq(state->gcur,state->c2*state->g0))) )
    {
        
        /*
         * Found
         */
        state->alphasol = state->alphacur;
        state->terminationtype = 1;
        if( state->dotrace )
        {
            traceangles(state->tracelevel, _state);
            ae_trace(" sufficient decrease and curvature conditions hold, stopping\n");
        }
        result = ae_false;
        return result;
    }
    
    /*
     * Analyze
     */
    if( ae_fp_greater_eq(state->fcur,state->f0+state->c1*state->alphatrial*state->g0)||ae_fp_greater_eq(state->fcur,state->flo) )
    {
        state->alphahi = state->alphacur;
        state->fhi = state->fcur;
        state->ghi = state->gcur;
    }
    else
    {
        if( ae_fp_greater_eq(state->gcur*(state->alphahi-state->alphalo),(double)(0)) )
        {
            state->alphahi = state->alphalo;
            state->fhi = state->flo;
            state->ghi = state->glo;
        }
        state->alphalo = state->alphacur;
        state->flo = state->fcur;
        state->glo = state->gcur;
    }
    goto lbl_15;
lbl_16:
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    return result;
}


/*************************************************************************
Create a filter for NLP solver
*************************************************************************/
void nlpfinit(double maxh, nlpfilter* s, ae_state *_state)
{


    s->maxh = maxh;
    s->filtersize = 0;
    s->gammaf = 0.0;
    s->gammah = 1.0;
    s->maxdominating = 0;
}


/*************************************************************************
Copy filter
*************************************************************************/
void nlpfinitfrom(const nlpfilter* src, nlpfilter* dst, ae_state *_state)
{


    dst->maxh = src->maxh;
    dst->filtersize = src->filtersize;
    dst->maxdominating = src->maxdominating;
    dst->gammaf = src->gammaf;
    dst->gammah = src->gammah;
    dst->violationistoohigh = src->violationistoohigh;
    rcopyallocv(dst->filtersize, &src->filterf, &dst->filterf, _state);
    rcopyallocv(dst->filtersize, &src->filterh, &dst->filterh, _state);
}


/*************************************************************************
Create a filter for NLP solver with the possibility of points being accepted
when dominated by no more than MD>=0 ones.

MD=0 corresponds to the traditional filter.
*************************************************************************/
void nlpfinitx(double maxh, ae_int_t md, nlpfilter* s, ae_state *_state)
{


    ae_assert(md>=0, "nlpfInitX: MD<0", _state);
    s->maxh = maxh;
    s->filtersize = 0;
    s->gammaf = 0.0;
    s->gammah = 1.0;
    s->maxdominating = md;
}


/*************************************************************************
Check point for acceptance.

On failure sets S.ViolationIsTooHigh.
*************************************************************************/
ae_bool nlpfisacceptable(nlpfilter* s,
     double f0,
     double h0,
     double f1,
     double h1,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t dcnt;
    ae_bool result;


    if( h1>s->maxh )
    {
        s->violationistoohigh = ae_true;
        result = ae_false;
        return result;
    }
    s->violationistoohigh = ae_false;
    if( f1>=f0-s->gammaf*h0&&h1>=h0*s->gammah )
    {
        result = ae_false;
        return result;
    }
    dcnt = 0;
    for(i=0; i<=s->filtersize-1; i++)
    {
        if( f1>=s->filterf.ptr.p_double[i]-s->gammaf*h1&&h1>=s->filterh.ptr.p_double[i]*s->gammah )
        {
            dcnt = dcnt+1;
        }
        if( dcnt>s->maxdominating )
        {
            result = ae_false;
            return result;
        }
    }
    result = ae_true;
    return result;
}


/*************************************************************************
Check point for acceptance.

On failure sets S.ViolationIsTooHigh.
*************************************************************************/
ae_bool nlpfisacceptable1(nlpfilter* s,
     double f,
     double h,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t dcnt;
    ae_bool result;


    if( h>=s->maxh )
    {
        s->violationistoohigh = ae_true;
        result = ae_false;
        return result;
    }
    s->violationistoohigh = ae_false;
    dcnt = 0;
    for(i=0; i<=s->filtersize-1; i++)
    {
        if( f>=s->filterf.ptr.p_double[i]-s->gammaf*h&&h>=s->filterh.ptr.p_double[i]*s->gammah )
        {
            dcnt = dcnt+1;
        }
        if( dcnt>s->maxdominating )
        {
            result = ae_false;
            return result;
        }
    }
    result = ae_true;
    return result;
}


/*************************************************************************
Append point, if acceptable. Behavior is undefined for unacceptable points.
*************************************************************************/
void nlpfappend(nlpfilter* s, double f, double h, ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t dcnt;
    ae_int_t newsize;
    double fcand;
    double hcand;


    if( s->maxdominating==0 )
    {
        
        /*
         * Quick O(FilterSize) code for the traditional filter
         */
        i = 0;
        while(i<s->filtersize)
        {
            if( f<=s->filterf.ptr.p_double[i]&&h<=s->filterh.ptr.p_double[i] )
            {
                
                /*
                 * The point being added dominates the I-th one, remove it
                 */
                s->filterf.ptr.p_double[i] = s->filterf.ptr.p_double[s->filtersize-1];
                s->filterh.ptr.p_double[i] = s->filterh.ptr.p_double[s->filtersize-1];
                s->filtersize = s->filtersize-1;
                continue;
            }
            i = i+1;
        }
        rgrowv(s->filtersize+1, &s->filterf, _state);
        rgrowv(s->filtersize+1, &s->filterh, _state);
        s->filterf.ptr.p_double[s->filtersize] = f;
        s->filterh.ptr.p_double[s->filtersize] = h;
        s->filtersize = s->filtersize+1;
    }
    else
    {
        
        /*
         * Slower O(FilterSize^2) code for the D-dominating filter.
         *
         * First, insert new point to the beginning. This way we make sure
         * that it is always retained, even under rounding errors.
         */
        rgrowv(s->filtersize+1, &s->filterf, _state);
        rgrowv(s->filtersize+1, &s->filterh, _state);
        s->filterf.ptr.p_double[s->filtersize] = s->filterf.ptr.p_double[0];
        s->filterh.ptr.p_double[s->filtersize] = s->filterh.ptr.p_double[0];
        s->filterf.ptr.p_double[0] = f;
        s->filterh.ptr.p_double[0] = h;
        s->filtersize = s->filtersize+1;
        
        /*
         * Then, for each point compute how many times it is dominated
         */
        newsize = 1;
        for(i=1; i<=s->filtersize-1; i++)
        {
            dcnt = 0;
            fcand = s->filterf.ptr.p_double[i];
            hcand = s->filterh.ptr.p_double[i];
            for(j=0; j<=newsize-1; j++)
            {
                if( fcand>=s->filterf.ptr.p_double[j]&&hcand>=s->filterh.ptr.p_double[j] )
                {
                    dcnt = dcnt+1;
                }
            }
            if( dcnt<=s->maxdominating )
            {
                s->filterf.ptr.p_double[newsize] = fcand;
                s->filterh.ptr.p_double[newsize] = hcand;
                newsize = newsize+1;
            }
        }
        
        /*
         * Done
         */
        s->filtersize = newsize;
    }
}


/*************************************************************************
Drop all points stored in the filter
*************************************************************************/
void nlpfclear(nlpfilter* s, ae_state *_state)
{


    s->filtersize = 0;
}


/*************************************************************************
Updates maximum violation
*************************************************************************/
void nlpfsetmaxh(nlpfilter* s, double maxh, ae_state *_state)
{


    s->maxh = maxh;
}


/*************************************************************************
Initialize linear constraints structure
*************************************************************************/
void xlcinit(ae_int_t n, xlinearconstraints* state, ae_state *_state)
{


    ae_assert(n>=1, "xlcInit: N<1", _state);
    state->n = n;
    state->ndense = 0;
    state->nsparse = 0;
}


/*************************************************************************
SINGLE-SIDED CONSTRANTS USING LEGACY FORMAT

This function sets mixed linear constraints, which include a set of  dense
rows, and a set of sparse rows.

This function overrides results of previous constraint modifications.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinQPCreate call.
    SparseC -   linear constraints, sparse  matrix with dimensions EXACTLY
                EQUAL TO [SparseK,N+1].  Each  row  of  C  represents  one
                constraint, either equality or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    SparseCT-   type of sparse constraints, array[K]:
                * if SparseCT[i]>0, then I-th constraint is SparseC[i,*]*x >= SparseC[i,n+1]
                * if SparseCT[i]=0, then I-th constraint is SparseC[i,*]*x  = SparseC[i,n+1]
                * if SparseCT[i]<0, then I-th constraint is SparseC[i,*]*x <= SparseC[i,n+1]
    SparseK -   number of sparse equality/inequality constraints, K>=0
    DenseC  -   dense linear constraints, array[K,N+1].
                Each row of DenseC represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of DenseC (including right part) must be finite.
    DenseCT -   type of constraints, array[K]:
                * if DenseCT[i]>0, then I-th constraint is DenseC[i,*]*x >= DenseC[i,n+1]
                * if DenseCT[i]=0, then I-th constraint is DenseC[i,*]*x  = DenseC[i,n+1]
                * if DenseCT[i]<0, then I-th constraint is DenseC[i,*]*x <= DenseC[i,n+1]
    DenseK  -   number of equality/inequality constraints, DenseK>=0

  -- ALGLIB --
     Copyright 13.04.2024 by Bochkanov Sergey
*************************************************************************/
void xlcsetlcmixed(xlinearconstraints* state,
     const sparsematrix* sparsec,
     /* Integer */ const ae_vector* sparsect,
     ae_int_t sparsek,
     /* Real    */ const ae_matrix* densec,
     /* Integer */ const ae_vector* densect,
     ae_int_t densek,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    double v;
    ae_vector srcidx;
    ae_vector dstidx;
    ae_vector s;
    ae_vector rs;
    ae_vector eoffs;
    ae_vector roffs;
    ae_vector v2;
    ae_vector eidx;
    ae_vector eval;
    ae_int_t t0;
    ae_int_t t1;
    ae_int_t nnz;

    ae_frame_make(_state, &_frame_block);
    memset(&srcidx, 0, sizeof(srcidx));
    memset(&dstidx, 0, sizeof(dstidx));
    memset(&s, 0, sizeof(s));
    memset(&rs, 0, sizeof(rs));
    memset(&eoffs, 0, sizeof(eoffs));
    memset(&roffs, 0, sizeof(roffs));
    memset(&v2, 0, sizeof(v2));
    memset(&eidx, 0, sizeof(eidx));
    memset(&eval, 0, sizeof(eval));
    ae_vector_init(&srcidx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&dstidx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rs, 0, DT_INT, _state, ae_true);
    ae_vector_init(&eoffs, 0, DT_INT, _state, ae_true);
    ae_vector_init(&roffs, 0, DT_INT, _state, ae_true);
    ae_vector_init(&v2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&eidx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&eval, 0, DT_REAL, _state, ae_true);

    n = state->n;
    
    /*
     * First, check for errors in the inputs
     */
    ae_assert(densek>=0, "xlcSetLCMixed: K<0", _state);
    ae_assert(densek==0||densec->cols>=n+1, "xlcSetLCMixed: Cols(C)<N+1", _state);
    ae_assert(densec->rows>=densek, "xlcSetLCMixed: Rows(DenseC)<DenseK", _state);
    ae_assert(densect->cnt>=densek, "xlcSetLCMixed: Length(DenseCT)<DenseK", _state);
    ae_assert(apservisfinitematrix(densec, densek, n+1, _state), "xlcSetLCMixed: C contains infinite or NaN values!", _state);
    ae_assert(sparsek>=0, "xlcSetLCMixed: SparseK<0", _state);
    ae_assert(sparsek==0||sparsegetncols(sparsec, _state)>=n+1, "xlcSetLCMixed: Cols(SparseC)<N+1", _state);
    ae_assert(sparsek==0||sparsegetnrows(sparsec, _state)>=sparsek, "xlcSetLCMixed: Rows(SparseC)<SparseK", _state);
    ae_assert(sparsect->cnt>=sparsek, "xlcSetLCMixed: Length(SparseCT)<SparseK", _state);
    
    /*
     * Init
     */
    state->ndense = densek;
    state->nsparse = sparsek;
    if( sparsek>0 )
    {
        rallocv(sparsek, &state->scl, _state);
        rallocv(sparsek, &state->scu, _state);
        
        /*
         * Evaluate row sizes for new storage
         */
        ae_vector_set_length(&rs, sparsek, _state);
        for(i=0; i<=sparsek-1; i++)
        {
            rs.ptr.p_int[i] = 0;
        }
        t0 = 0;
        t1 = 0;
        nnz = 0;
        while(sparseenumerate(sparsec, &t0, &t1, &i, &j, &v, _state))
        {
            if( i>sparsek-1||j>n-1 )
            {
                continue;
            }
            ae_assert(ae_isfinite(v, _state), "xlcSetLCSparse: C contains infinite or NAN values", _state);
            nnz = nnz+1;
            rs.ptr.p_int[i] = rs.ptr.p_int[i]+1;
        }
        
        /*
         * Prepare new sparse CRS storage, copy leading SparseK*N submatrix into the storage
         */
        for(i=0; i<=sparsek-1; i++)
        {
            state->scl.ptr.p_double[i] = (double)(0);
            state->scu.ptr.p_double[i] = (double)(0);
        }
        state->sparsec.m = sparsek;
        state->sparsec.n = n;
        ivectorsetlengthatleast(&state->sparsec.ridx, sparsek+1, _state);
        ivectorsetlengthatleast(&state->sparsec.idx, nnz, _state);
        rvectorsetlengthatleast(&state->sparsec.vals, nnz, _state);
        ae_vector_set_length(&eoffs, sparsek+1, _state);
        state->sparsec.ridx.ptr.p_int[0] = 0;
        eoffs.ptr.p_int[0] = 0;
        for(i=1; i<=sparsek; i++)
        {
            state->sparsec.ridx.ptr.p_int[i] = state->sparsec.ridx.ptr.p_int[i-1]+rs.ptr.p_int[i-1];
            eoffs.ptr.p_int[i] = state->sparsec.ridx.ptr.p_int[i];
        }
        t0 = 0;
        t1 = 0;
        while(sparseenumerate(sparsec, &t0, &t1, &i, &j, &v, _state))
        {
            if( i>sparsek-1||j>n )
            {
                continue;
            }
            if( j<n )
            {
                
                /*
                 * Copy left part of constraint
                 */
                j0 = eoffs.ptr.p_int[i];
                state->sparsec.idx.ptr.p_int[j0] = j;
                state->sparsec.vals.ptr.p_double[j0] = v;
                eoffs.ptr.p_int[i] = j0+1;
            }
            else
            {
                
                /*
                 * Handle right part of the constraint
                 */
                state->scl.ptr.p_double[i] = v;
                state->scu.ptr.p_double[i] = v;
            }
        }
        for(i=0; i<=sparsek-1; i++)
        {
            ae_assert(eoffs.ptr.p_int[i]==state->sparsec.ridx.ptr.p_int[i+1], "xlc: critical integrity check failed (sparse copying)", _state);
        }
        sparsecreatecrsinplace(&state->sparsec, _state);
        for(i=0; i<=sparsek-1; i++)
        {
            if( sparsect->ptr.p_int[i]>0 )
            {
                state->scu.ptr.p_double[i] = _state->v_posinf;
            }
            if( sparsect->ptr.p_int[i]<0 )
            {
                state->scl.ptr.p_double[i] = _state->v_neginf;
            }
        }
    }
    if( densek>0 )
    {
        
        /*
         * Copy dense constraints
         */
        rallocv(densek, &state->dcl, _state);
        rallocv(densek, &state->dcu, _state);
        rmatrixsetlengthatleast(&state->densec, densek, n, _state);
        for(i=0; i<=densek-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                state->densec.ptr.pp_double[i][j] = densec->ptr.pp_double[i][j];
            }
            if( densect->ptr.p_int[i]>0 )
            {
                state->dcl.ptr.p_double[i] = densec->ptr.pp_double[i][n];
                state->dcu.ptr.p_double[i] = _state->v_posinf;
                continue;
            }
            if( densect->ptr.p_int[i]<0 )
            {
                state->dcl.ptr.p_double[i] = _state->v_neginf;
                state->dcu.ptr.p_double[i] = densec->ptr.pp_double[i][n];
                continue;
            }
            state->dcl.ptr.p_double[i] = densec->ptr.pp_double[i][n];
            state->dcu.ptr.p_double[i] = densec->ptr.pp_double[i][n];
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
mixed constraining matrix A including sparse part (first SparseK rows) and
dense part (last DenseK rows). Recommended for large-scale problems.

This function overwrites modifications set  by  previous  calls  (if  such
calls were made).

This function may be useful if constraint matrix includes large number  of
both types of rows - dense and sparse. If you have just a few sparse rows,
you  may  represent  them  in  dense  format  without losing  performance.
Similarly, if you have just a few dense rows, you may store them in sparse
format with almost same performance.

INPUT PARAMETERS:
    State   -   linear constraints structure
    SparseA -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    SparseK -   number of sparse constraints, SparseK>=0
    DenseA  -   linear constraints, array[K,N], set of dense constraints.
                Each row of A represents one general linear constraint.
    DenseK  -   number of dense constraints, DenseK>=0
    AL, AU  -   lower and upper bounds, array[SparseK+DenseK], with former
                SparseK elements corresponding to sparse constraints,  and
                latter DenseK elements corresponding to dense constraints;
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 13.04.2024 by Bochkanov Sergey
*************************************************************************/
void xlcsetlc2mixed(xlinearconstraints* state,
     const sparsematrix* sparsea,
     ae_int_t ksparse,
     /* Real    */ const ae_matrix* densea,
     ae_int_t kdense,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;


    n = state->n;
    m = kdense+ksparse;
    
    /*
     * Check input arguments
     */
    ae_assert(ksparse>=0, "xlcSetLC2Mixed: KSparse<0", _state);
    ae_assert(ksparse==0||sparsegetncols(sparsea, _state)==n, "xlcSetLC2: Cols(SparseA)<>N", _state);
    ae_assert(ksparse==0||sparsegetnrows(sparsea, _state)==ksparse, "xlcSetLC2: Rows(SparseA)<>K", _state);
    ae_assert(kdense>=0, "xlcSetLC2Mixed: KDense<0", _state);
    ae_assert(kdense==0||densea->cols>=n, "xlcSetLC2Mixed: Cols(DenseA)<N", _state);
    ae_assert(kdense==0||densea->rows>=kdense, "xlcSetLC2Mixed: Rows(DenseA)<K", _state);
    ae_assert(apservisfinitematrix(densea, kdense, n, _state), "xlcSetLC2Mixed: DenseA contains infinite or NaN values!", _state);
    ae_assert(al->cnt>=kdense+ksparse, "xlcSetLC2Mixed: Length(AL)<K", _state);
    ae_assert(au->cnt>=kdense+ksparse, "xlcSetLC2Mixed: Length(AU)<K", _state);
    for(i=0; i<=m-1; i++)
    {
        ae_assert(ae_isfinite(al->ptr.p_double[i], _state)||ae_isneginf(al->ptr.p_double[i], _state), "xlcSetLC2Mixed: AL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(au->ptr.p_double[i], _state)||ae_isposinf(au->ptr.p_double[i], _state), "xlcSetLC2Mixed: AU contains NAN or -INF", _state);
    }
    
    /*
     * Quick exit if needed
     */
    if( m==0 )
    {
        state->ndense = 0;
        state->nsparse = 0;
        return;
    }
    
    /*
     * Prepare
     */
    rallocv(ksparse, &state->scl, _state);
    rallocv(ksparse, &state->scu, _state);
    rallocv(kdense, &state->dcl, _state);
    rallocv(kdense, &state->dcu, _state);
    rcopyvx(ksparse, al, 0, &state->scl, 0, _state);
    rcopyvx(ksparse, au, 0, &state->scu, 0, _state);
    rcopyvx(kdense, al, ksparse, &state->dcl, 0, _state);
    rcopyvx(kdense, au, ksparse, &state->dcu, 0, _state);
    state->ndense = kdense;
    state->nsparse = ksparse;
    
    /*
     * Copy dense and sparse terms
     */
    if( ksparse>0 )
    {
        sparsecopytocrsbuf(sparsea, &state->sparsec, _state);
    }
    if( kdense>0 )
    {
        rcopyallocm(kdense, n, densea, &state->densec, _state);
    }
}


/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
matrix of currently present dense constraints.

INPUT PARAMETERS:
    State   -   current constraints
    A       -   linear constraint coefficient, array[N], right side is NOT
                included.
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint Ai*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint Ai*x<=AU
                * AU=+INF  => one-sided constraint AL<=Ai*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void xlcaddlc2dense(xlinearconstraints* state,
     /* Real    */ const ae_vector* a,
     double al,
     double au,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(a->cnt>=n, "xlcAddLC2Dense: Length(A)<N", _state);
    ae_assert(isfinitevector(a, n, _state), "xlcAddLC2Dense: A contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(al, _state)||ae_isneginf(al, _state), "xlcAddLC2Dense: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(au, _state)||ae_isposinf(au, _state), "xlcAddLC2Dense: AU is NAN or -INF", _state);
    rgrowv(state->ndense+1, &state->dcl, _state);
    rgrowv(state->ndense+1, &state->dcu, _state);
    rmatrixgrowrowsto(&state->densec, state->ndense+1, n, _state);
    rcopyvr(n, a, &state->densec, state->ndense, _state);
    state->dcl.ptr.p_double[state->ndense] = al;
    state->dcu.ptr.p_double[state->ndense] = au;
    state->ndense = state->ndense+1;
}


/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present sparse constraints.

Constraint is passed in compressed format: as list of non-zero entries  of
coefficient vector A. Such approach is more efficient than  dense  storage
for highly sparse constraint vectors.

INPUT PARAMETERS:
    State   -   structure
    IdxA    -   array[NNZ], indexes of non-zero elements of A:
                * can be unsorted
                * can include duplicate indexes (corresponding entries  of
                  ValA[] will be summed)
    ValA    -   array[NNZ], values of non-zero elements of A
    NNZ     -   number of non-zero coefficients in A
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void xlcaddlc2(xlinearconstraints* state,
     /* Integer */ const ae_vector* idxa,
     /* Real    */ const ae_vector* vala,
     ae_int_t nnz,
     double al,
     double au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t offs;
    ae_int_t offsdst;
    ae_int_t n;
    ae_int_t didx;
    ae_int_t uidx;


    n = state->n;
    
    /*
     * Check inputs
     */
    ae_assert(nnz>=0, "xlcAddLC2: NNZ<0", _state);
    ae_assert(idxa->cnt>=nnz, "xlcAddLC2: Length(IdxA)<NNZ", _state);
    ae_assert(vala->cnt>=nnz, "xlcAddLC2: Length(ValA)<NNZ", _state);
    for(i=0; i<=nnz-1; i++)
    {
        ae_assert(idxa->ptr.p_int[i]>=0&&idxa->ptr.p_int[i]<n, "xlcAddLC2: IdxA contains indexes outside of [0,N) range", _state);
    }
    ae_assert(isfinitevector(vala, nnz, _state), "xlcAddLC2: ValA contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(al, _state)||ae_isneginf(al, _state), "xlcAddLC2: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(au, _state)||ae_isposinf(au, _state), "xlcAddLC2: AU is NAN or -INF", _state);
    
    /*
     * If M=0, it means that A is uninitialized.
     * Prepare sparse matrix structure
     */
    if( state->nsparse==0 )
    {
        state->sparsec.matrixtype = 1;
        state->sparsec.m = 0;
        state->sparsec.n = n;
        state->sparsec.ninitialized = 0;
        ivectorsetlengthatleast(&state->sparsec.ridx, 1, _state);
        state->sparsec.ridx.ptr.p_int[0] = 0;
    }
    ae_assert(state->sparsec.matrixtype==1&&state->sparsec.m==state->nsparse, "xlcAddLC2: integrity check failed!", _state);
    
    /*
     * Reallocate inequality bounds
     */
    rgrowv(state->nsparse+1, &state->scl, _state);
    rgrowv(state->nsparse+1, &state->scu, _state);
    state->scl.ptr.p_double[state->nsparse] = al;
    state->scu.ptr.p_double[state->nsparse] = au;
    
    /*
     * Reallocate sparse storage
     */
    offs = state->sparsec.ridx.ptr.p_int[state->nsparse];
    ivectorgrowto(&state->sparsec.idx, offs+nnz, _state);
    rvectorgrowto(&state->sparsec.vals, offs+nnz, _state);
    ivectorgrowto(&state->sparsec.didx, state->nsparse+1, _state);
    ivectorgrowto(&state->sparsec.uidx, state->nsparse+1, _state);
    ivectorgrowto(&state->sparsec.ridx, state->nsparse+2, _state);
    
    /*
     * If NNZ=0, perform quick and simple row append. 
     */
    if( nnz==0 )
    {
        state->sparsec.didx.ptr.p_int[state->nsparse] = state->sparsec.ridx.ptr.p_int[state->nsparse];
        state->sparsec.uidx.ptr.p_int[state->nsparse] = state->sparsec.ridx.ptr.p_int[state->nsparse];
        state->sparsec.ridx.ptr.p_int[state->nsparse+1] = state->sparsec.ridx.ptr.p_int[state->nsparse];
        inc(&state->sparsec.m, _state);
        inc(&state->nsparse, _state);
        return;
    }
    
    /*
     * Now we are sure that SparseC contains properly initialized sparse
     * matrix (or some appropriate dummy for M=0) and we have NNZ>0
     * (no need to care about degenerate cases).
     *
     * Append rows to SparseC:
     * * append data
     * * sort in place
     * * merge duplicate indexes
     * * compute DIdx and UIdx
     *
     */
    for(i=0; i<=nnz-1; i++)
    {
        state->sparsec.idx.ptr.p_int[offs+i] = idxa->ptr.p_int[i];
        state->sparsec.vals.ptr.p_double[offs+i] = vala->ptr.p_double[i];
    }
    tagsortmiddleir(&state->sparsec.idx, &state->sparsec.vals, offs, nnz, _state);
    offsdst = offs;
    for(i=1; i<=nnz-1; i++)
    {
        if( state->sparsec.idx.ptr.p_int[offsdst]!=state->sparsec.idx.ptr.p_int[offs+i] )
        {
            offsdst = offsdst+1;
            state->sparsec.idx.ptr.p_int[offsdst] = state->sparsec.idx.ptr.p_int[offs+i];
            state->sparsec.vals.ptr.p_double[offsdst] = state->sparsec.vals.ptr.p_double[offs+i];
        }
        else
        {
            state->sparsec.vals.ptr.p_double[offsdst] = state->sparsec.vals.ptr.p_double[offsdst]+state->sparsec.vals.ptr.p_double[offs+i];
        }
    }
    nnz = offsdst-offs+1;
    uidx = -1;
    didx = -1;
    for(j=offs; j<=offsdst; j++)
    {
        k = state->sparsec.idx.ptr.p_int[j];
        if( k==state->nsparse )
        {
            didx = j;
        }
        else
        {
            if( k>state->nsparse&&uidx==-1 )
            {
                uidx = j;
                break;
            }
        }
    }
    if( uidx==-1 )
    {
        uidx = offsdst+1;
    }
    if( didx==-1 )
    {
        didx = uidx;
    }
    state->sparsec.didx.ptr.p_int[state->nsparse] = didx;
    state->sparsec.uidx.ptr.p_int[state->nsparse] = uidx;
    state->sparsec.ridx.ptr.p_int[state->nsparse+1] = offsdst+1;
    state->sparsec.ninitialized = state->sparsec.ridx.ptr.p_int[state->nsparse+1];
    inc(&state->sparsec.m, _state);
    inc(&state->nsparse, _state);
}


/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present sparse constraints.

Constraint vector A is  passed  as  a  dense  array  which  is  internally
sparsified by this function.

INPUT PARAMETERS:
    State   -   structure
    DA      -   array[N], constraint vector
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void xlcaddlc2sparsefromdense(xlinearconstraints* state,
     /* Real    */ const ae_vector* da,
     double al,
     double au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t nzi;
    ae_int_t offs;
    ae_int_t n;
    ae_int_t nnz;
    ae_int_t didx;
    ae_int_t uidx;


    n = state->n;
    
    /*
     * Check inputs
     */
    ae_assert(da->cnt>=n, "xlcAddLC2SparseFromDense: Length(DA)<N", _state);
    ae_assert(isfinitevector(da, n, _state), "xlcAddLC2SparseFromDense: DA contains infinities/NANs", _state);
    ae_assert(ae_isfinite(al, _state)||ae_isneginf(al, _state), "xlcAddLC2SparseFromDense: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(au, _state)||ae_isposinf(au, _state), "xlcAddLC2SparseFromDense: AU is NAN or -INF", _state);
    
    /*
     * If M=0, it means that A is uninitialized.
     * Prepare sparse matrix structure
     */
    if( state->nsparse==0 )
    {
        state->sparsec.matrixtype = 1;
        state->sparsec.m = 0;
        state->sparsec.n = n;
        state->sparsec.ninitialized = 0;
        ivectorsetlengthatleast(&state->sparsec.ridx, 1, _state);
        state->sparsec.ridx.ptr.p_int[0] = 0;
    }
    ae_assert(state->sparsec.matrixtype==1&&state->sparsec.m==state->nsparse, "xlcAddLC2SparseFromDense: integrity check failed!", _state);
    
    /*
     * Reallocate inequality bounds
     */
    rvectorgrowto(&state->scl, state->nsparse+1, _state);
    rvectorgrowto(&state->scu, state->nsparse+1, _state);
    state->scl.ptr.p_double[state->nsparse] = al;
    state->scu.ptr.p_double[state->nsparse] = au;
    
    /*
     * Determine nonzeros count.
     * Reallocate sparse storage.
     */
    nnz = 0;
    for(i=0; i<=n-1; i++)
    {
        if( !(da->ptr.p_double[i]==0.0) )
        {
            nnz = nnz+1;
        }
    }
    offs = state->sparsec.ridx.ptr.p_int[state->nsparse];
    ivectorgrowto(&state->sparsec.idx, offs+nnz, _state);
    rvectorgrowto(&state->sparsec.vals, offs+nnz, _state);
    ivectorgrowto(&state->sparsec.didx, state->nsparse+1, _state);
    ivectorgrowto(&state->sparsec.uidx, state->nsparse+1, _state);
    ivectorgrowto(&state->sparsec.ridx, state->nsparse+2, _state);
    
    /*
     * If NNZ=0, perform quick and simple row append. 
     */
    if( nnz==0 )
    {
        state->sparsec.didx.ptr.p_int[state->nsparse] = state->sparsec.ridx.ptr.p_int[state->nsparse];
        state->sparsec.uidx.ptr.p_int[state->nsparse] = state->sparsec.ridx.ptr.p_int[state->nsparse];
        state->sparsec.ridx.ptr.p_int[state->nsparse+1] = state->sparsec.ridx.ptr.p_int[state->nsparse];
        inc(&state->sparsec.m, _state);
        inc(&state->nsparse, _state);
        return;
    }
    
    /*
     * Now we are sure that SparseC contains properly initialized sparse
     * matrix (or some appropriate dummy for M=0) and we have NNZ>0
     * (no need to care about degenerate cases).
     *
     * Append rows to SparseC:
     * * append data
     * * compute DIdx and UIdx
     *
     */
    nzi = 0;
    for(i=0; i<=n-1; i++)
    {
        if( !(da->ptr.p_double[i]==0.0) )
        {
            state->sparsec.idx.ptr.p_int[offs+nzi] = i;
            state->sparsec.vals.ptr.p_double[offs+nzi] = da->ptr.p_double[i];
            nzi = nzi+1;
        }
    }
    uidx = -1;
    didx = -1;
    for(j=offs; j<=offs+nnz-1; j++)
    {
        k = state->sparsec.idx.ptr.p_int[j];
        if( k==state->nsparse )
        {
            didx = j;
        }
        else
        {
            if( k>state->nsparse&&uidx==-1 )
            {
                uidx = j;
                break;
            }
        }
    }
    if( uidx==-1 )
    {
        uidx = offs+nnz;
    }
    if( didx==-1 )
    {
        didx = uidx;
    }
    state->sparsec.didx.ptr.p_int[state->nsparse] = didx;
    state->sparsec.uidx.ptr.p_int[state->nsparse] = uidx;
    state->sparsec.ridx.ptr.p_int[state->nsparse+1] = offs+nnz;
    state->sparsec.ninitialized = state->sparsec.ridx.ptr.p_int[state->nsparse+1];
    inc(&state->sparsec.m, _state);
    inc(&state->nsparse, _state);
}


/*************************************************************************
Converts linear constraints to the legacy dense single-sided format. Sets
State.NEC, State.NIC, State.CLEIC, State.LCSrcIdx, State.LCSrcMult

NOTE: this function ignores inconsistent bounds, like CL>CU, assuming that
      either they were already noticed, or will be noticed later.
*************************************************************************/
void xlcconverttoold(xlinearconstraints* state, ae_state *_state)
{
    ae_int_t i;
    ae_int_t k;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t idxoutec;
    ae_int_t idxoutic;
    ae_int_t dstrow0;
    ae_int_t dstrow1;
    double vdst0;
    double vdst1;
    double rhsdst0;
    double rhsdst1;
    ae_int_t n;
    double vl;
    double vu;


    n = state->n;
    state->nec = 0;
    state->nic = 0;
    
    /*
     * Compute required amount of single-sided constraints
     */
    for(i=0; i<=state->nsparse+state->ndense-1; i++)
    {
        if( i<state->nsparse )
        {
            vl = state->scl.ptr.p_double[i];
            vu = state->scu.ptr.p_double[i];
        }
        else
        {
            vl = state->dcl.ptr.p_double[i-state->nsparse];
            vu = state->dcu.ptr.p_double[i-state->nsparse];
        }
        ae_assert(ae_isfinite(vl, _state)||ae_isneginf(vl, _state), "OPTSERV: integrity check 0354 failed", _state);
        ae_assert(ae_isfinite(vu, _state)||ae_isposinf(vu, _state), "OPTSERV: integrity check 0355 failed", _state);
        if( (ae_isfinite(vl, _state)&&ae_isfinite(vu, _state))&&ae_fp_eq(vl,vu) )
        {
            state->nec = state->nec+1;
            continue;
        }
        if( ae_isfinite(vl, _state) )
        {
            state->nic = state->nic+1;
        }
        if( ae_isfinite(vu, _state) )
        {
            state->nic = state->nic+1;
        }
    }
    if( state->nec+state->nic==0 )
    {
        return;
    }
    
    /*
     * Convert two-sided constraints into single-sided
     */
    idxoutec = 0;
    idxoutic = state->nec;
    rallocm(state->nec+state->nic, n+1, &state->cleic, _state);
    isetallocv(state->nec+state->nic, -1, &state->lcsrcidx, _state);
    rsetallocv(state->nec+state->nic, 0.0, &state->lcsrcmult, _state);
    for(i=0; i<=state->nsparse+state->ndense-1; i++)
    {
        
        /*
         * Extract bounds
         */
        if( i<state->nsparse )
        {
            vl = state->scl.ptr.p_double[i];
            vu = state->scu.ptr.p_double[i];
        }
        else
        {
            vl = state->dcl.ptr.p_double[i-state->nsparse];
            vu = state->dcu.ptr.p_double[i-state->nsparse];
        }
        
        /*
         * Ignore non-binding rows
         */
        if( !ae_isfinite(vl, _state)&&!ae_isfinite(vu, _state) )
        {
            continue;
        }
        
        /*
         * Analyze bounds
         */
        dstrow0 = -1;
        dstrow1 = -1;
        vdst0 = (double)(0);
        vdst1 = (double)(0);
        rhsdst0 = (double)(0);
        rhsdst1 = (double)(0);
        if( ae_isfinite(vl, _state)&&ae_isfinite(vu, _state) )
        {
            if( ae_fp_eq(vl,vu) )
            {
                
                /*
                 * Two bounds = equality constraints, one row is output
                 */
                dstrow0 = idxoutec;
                vdst0 = 1.0;
                rhsdst0 = vl;
                idxoutec = idxoutec+1;
            }
            else
            {
                
                /*
                 * Two bounds = range constraint, two rows are output
                 */
                dstrow0 = idxoutic;
                dstrow1 = idxoutic+1;
                vdst0 = -1.0;
                vdst1 = 1.0;
                rhsdst0 = -vl;
                rhsdst1 = vu;
                idxoutic = idxoutic+2;
            }
        }
        else
        {
            
            /*
             * Only one bound is present
             */
            if( ae_isfinite(vl, _state) )
            {
                dstrow0 = idxoutic;
                vdst0 = -1.0;
                rhsdst0 = -vl;
                idxoutic = idxoutic+1;
            }
            if( ae_isfinite(vu, _state) )
            {
                dstrow0 = idxoutic;
                vdst0 = 1.0;
                rhsdst0 = vu;
                idxoutic = idxoutic+1;
            }
        }
        
        /*
         * Extract row
         */
        ae_assert(idxoutec<=state->nec, "OPTSERV: integrity check 2607 failed", _state);
        ae_assert(idxoutic<=state->nec+state->nic, "OPTSERV: integrity check 2608 failed", _state);
        if( i<state->nsparse )
        {
            
            /*
             * Extract from the sparse matrix
             */
            if( dstrow0>=0 )
            {
                rsetr(n, 0.0, &state->cleic, dstrow0, _state);
                k0 = state->sparsec.ridx.ptr.p_int[i];
                k1 = state->sparsec.ridx.ptr.p_int[i+1]-1;
                for(k=k0; k<=k1; k++)
                {
                    state->cleic.ptr.pp_double[dstrow0][state->sparsec.idx.ptr.p_int[k]] = vdst0*state->sparsec.vals.ptr.p_double[k];
                }
                state->cleic.ptr.pp_double[dstrow0][n] = rhsdst0;
            }
            if( dstrow1>=0 )
            {
                rsetr(n, 0.0, &state->cleic, dstrow1, _state);
                k0 = state->sparsec.ridx.ptr.p_int[i];
                k1 = state->sparsec.ridx.ptr.p_int[i+1]-1;
                for(k=k0; k<=k1; k++)
                {
                    state->cleic.ptr.pp_double[dstrow1][state->sparsec.idx.ptr.p_int[k]] = vdst1*state->sparsec.vals.ptr.p_double[k];
                }
                state->cleic.ptr.pp_double[dstrow1][n] = rhsdst1;
            }
        }
        else
        {
            
            /*
             * Copy from the dense matrix
             */
            if( dstrow0>=0 )
            {
                rcopyrr(n, &state->densec, i-state->nsparse, &state->cleic, dstrow0, _state);
                rmulr(n, vdst0, &state->cleic, dstrow0, _state);
                state->cleic.ptr.pp_double[dstrow0][n] = rhsdst0;
            }
            if( dstrow1>=0 )
            {
                rcopyrr(n, &state->densec, i-state->nsparse, &state->cleic, dstrow1, _state);
                rmulr(n, vdst1, &state->cleic, dstrow1, _state);
                state->cleic.ptr.pp_double[dstrow1][n] = rhsdst1;
            }
        }
        if( dstrow0>=0 )
        {
            state->lcsrcidx.ptr.p_int[dstrow0] = i;
            state->lcsrcmult.ptr.p_double[dstrow0] = vdst0;
        }
        if( dstrow1>=0 )
        {
            state->lcsrcidx.ptr.p_int[dstrow1] = i;
            state->lcsrcmult.ptr.p_double[dstrow1] = vdst1;
        }
    }
}


/*************************************************************************
Converts linear constraints to the modern 2-sided sparse-only format.

Sets State.EffSparseA, State.EffAL, State.EffAU. After  these  fields  are
set, they can be modified by the user (e.g. by adding rows and/or slack
variables).

Additionally sets State.LCSrcIdx to an identity map.

NOTE: this function ignores inconsistent bounds, like CL>CU, assuming that
      either they were already noticed, or will be noticed later.
*************************************************************************/
void xlcconverttosparse(xlinearconstraints* state, ae_state *_state)
{
    ae_int_t i;


    if( state->nsparse+state->ndense==0 )
    {
        return;
    }
    rallocv(state->nsparse+state->ndense, &state->effal, _state);
    rallocv(state->nsparse+state->ndense, &state->effau, _state);
    if( state->nsparse>0 )
    {
        sparsecopytocrsbuf(&state->sparsec, &state->effsparsea, _state);
        rcopyvx(state->nsparse, &state->scl, 0, &state->effal, 0, _state);
        rcopyvx(state->nsparse, &state->scu, 0, &state->effau, 0, _state);
    }
    else
    {
        sparsecreatecrsemptybuf(state->n, &state->effsparsea, _state);
    }
    if( state->ndense>0 )
    {
        sparsecreatecrsfromdensebuf(&state->densec, state->ndense, state->n, &state->tmps, _state);
        sparseappendmatrix(&state->effsparsea, &state->tmps, _state);
        rcopyvx(state->ndense, &state->dcl, 0, &state->effal, state->nsparse, _state);
        rcopyvx(state->ndense, &state->dcu, 0, &state->effau, state->nsparse, _state);
    }
    iallocv(state->nsparse+state->ndense, &state->lcsrcidx, _state);
    for(i=0; i<=state->nsparse+state->ndense-1; i++)
    {
        state->lcsrcidx.ptr.p_int[i] = i;
    }
}


/*************************************************************************
Append quadratic constraint given by a list of nonzeros
*************************************************************************/
void xqcaddqc2list(xquadraticconstraints* xqc,
     /* Integer */ const ae_vector* qridx,
     /* Integer */ const ae_vector* qcidx,
     /* Real    */ const ae_vector* qvals,
     ae_int_t qnnz,
     ae_bool isupper,
     /* Integer */ const ae_vector* bidx,
     /* Real    */ const ae_vector* bvals,
     ae_int_t bnnz,
     double cl,
     double cu,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector allitems;
    xquadraticconstraint *c;
    ae_smart_ptr _c;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t offs;
    ae_vector b;
    sparsematrix q;

    ae_frame_make(_state, &_frame_block);
    memset(&allitems, 0, sizeof(allitems));
    memset(&_c, 0, sizeof(_c));
    memset(&b, 0, sizeof(b));
    memset(&q, 0, sizeof(q));
    ae_vector_init(&allitems, 0, DT_INT, _state, ae_true);
    ae_smart_ptr_init(&_c, (void**)&c, ae_false, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&q, _state, ae_true);

    n = xqc->n;
    c = (xquadraticconstraint*)ae_malloc(sizeof(xquadraticconstraint), _state); /* note: using c as a temporary prior to assigning its value to _c */
    memset(c, 0, sizeof(xquadraticconstraint));
    _xquadraticconstraint_init(c, _state, ae_false);
    ae_smart_ptr_assign(&_c, c, ae_true, ae_true, (ae_int_t)sizeof(xquadraticconstraint), _xquadraticconstraint_init_copy, _xquadraticconstraint_destroy);
    
    /*
     * Determine unique variable indexes
     */
    iallocv(2*qnnz+bnnz, &allitems, _state);
    offs = 0;
    for(i=0; i<=qnnz-1; i++)
    {
        if( (isupper&&qcidx->ptr.p_int[i]>=qridx->ptr.p_int[i])||(!isupper&&qridx->ptr.p_int[i]>=qcidx->ptr.p_int[i]) )
        {
            if( qvals->ptr.p_double[i]!=(double)0 )
            {
                allitems.ptr.p_int[offs+0] = qridx->ptr.p_int[i];
                allitems.ptr.p_int[offs+1] = qcidx->ptr.p_int[i];
                offs = offs+2;
            }
        }
    }
    for(i=0; i<=bnnz-1; i++)
    {
        if( bvals->ptr.p_double[i]!=(double)0 )
        {
            allitems.ptr.p_int[offs] = bidx->ptr.p_int[i];
            offs = offs+1;
        }
    }
    if( offs==0 )
    {
        
        /*
         * Quick exit
         */
        c->nvars = 0;
        c->applyorigin = applyorigin;
        c->cl = cl;
        c->cu = cu;
        ae_obj_array_append_transfer(&xqc->constraints, &_c, _state);
        ae_frame_leave(_state);
        return;
    }
    tagsortmiddlei(&allitems, 0, offs, _state);
    ae_assert(allitems.ptr.p_int[0]>=0&&allitems.ptr.p_int[offs-1]<n, "xqcAddQC2List: one of nonzero indexes is outside of [0,N) range", _state);
    c->nvars = 0;
    for(i=0; i<=offs-1; i++)
    {
        if( c->nvars==0||c->varidx.ptr.p_int[c->nvars-1]!=allitems.ptr.p_int[i] )
        {
            igrowv(c->nvars+1, &c->varidx, _state);
            c->varidx.ptr.p_int[c->nvars] = allitems.ptr.p_int[i];
            c->nvars = c->nvars+1;
        }
    }
    
    /*
     * Save constraints
     */
    c->applyorigin = applyorigin;
    c->cl = cl;
    c->cu = cu;
    if( c->nvars>0 )
    {
        
        /*
         * Compress B
         */
        rsetallocv(c->nvars, 0.0, &c->b, _state);
        for(i=0; i<=bnnz-1; i++)
        {
            c->b.ptr.p_double[ibinarysearchexisting(&c->varidx, 0, c->nvars, bidx->ptr.p_int[i], _state)] = bvals->ptr.p_double[i];
        }
        
        /*
         * Compress Q
         */
        c->lowerq.m = c->nvars;
        c->lowerq.n = c->nvars;
        isetallocv(c->nvars, 0, &c->lowerq.didx, _state);
        for(k=0; k<=qnnz-1; k++)
        {
            if( (isupper&&qcidx->ptr.p_int[k]>=qridx->ptr.p_int[k])||(!isupper&&qridx->ptr.p_int[k]>=qcidx->ptr.p_int[k]) )
            {
                if( qvals->ptr.p_double[k]!=0.0 )
                {
                    if( isupper )
                    {
                        i = ibinarysearchexisting(&c->varidx, 0, c->nvars, qcidx->ptr.p_int[k], _state);
                    }
                    else
                    {
                        i = ibinarysearchexisting(&c->varidx, 0, c->nvars, qridx->ptr.p_int[k], _state);
                    }
                    c->lowerq.didx.ptr.p_int[i] = c->lowerq.didx.ptr.p_int[i]+1;
                }
            }
        }
        iallocv(c->nvars+1, &c->lowerq.ridx, _state);
        c->lowerq.ridx.ptr.p_int[0] = 0;
        for(i=0; i<=c->nvars-1; i++)
        {
            c->lowerq.ridx.ptr.p_int[i+1] = c->lowerq.ridx.ptr.p_int[i]+c->lowerq.didx.ptr.p_int[i];
        }
        iallocv(c->lowerq.ridx.ptr.p_int[c->nvars], &c->lowerq.idx, _state);
        rallocv(c->lowerq.ridx.ptr.p_int[c->nvars], &c->lowerq.vals, _state);
        icopyv(c->nvars, &c->lowerq.ridx, &c->lowerq.didx, _state);
        for(k=0; k<=qnnz-1; k++)
        {
            if( (isupper&&qcidx->ptr.p_int[k]>=qridx->ptr.p_int[k])||(!isupper&&qridx->ptr.p_int[k]>=qcidx->ptr.p_int[k]) )
            {
                if( qvals->ptr.p_double[k]!=0.0 )
                {
                    if( isupper )
                    {
                        i = ibinarysearchexisting(&c->varidx, 0, c->nvars, qcidx->ptr.p_int[k], _state);
                        j = ibinarysearchexisting(&c->varidx, 0, c->nvars, qridx->ptr.p_int[k], _state);
                    }
                    else
                    {
                        i = ibinarysearchexisting(&c->varidx, 0, c->nvars, qridx->ptr.p_int[k], _state);
                        j = ibinarysearchexisting(&c->varidx, 0, c->nvars, qcidx->ptr.p_int[k], _state);
                    }
                    offs = c->lowerq.didx.ptr.p_int[i];
                    c->lowerq.idx.ptr.p_int[offs] = j;
                    c->lowerq.vals.ptr.p_double[offs] = qvals->ptr.p_double[k];
                    c->lowerq.didx.ptr.p_int[i] = offs+1;
                }
            }
        }
        for(i=0; i<=c->nvars-1; i++)
        {
            if( c->lowerq.didx.ptr.p_int[i]!=c->lowerq.ridx.ptr.p_int[i+1] )
            {
                ae_assert(ae_false, "OPTSERV: integrity check 4227 failed", _state);
            }
        }
        sparsecreatecrsinplace(&c->lowerq, _state);
    }
    ae_obj_array_append_transfer(&xqc->constraints, &_c, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Append quadratic constraint given by dense quadratic matrix
*************************************************************************/
void xqcaddqc2dense(xquadraticconstraints* xqc,
     /* Real    */ const ae_matrix* q,
     ae_bool isupper,
     /* Real    */ const ae_vector* b,
     double cl,
     double cu,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t offs;
    ae_int_t n;
    double vq;
    xquadraticconstraint *c;
    ae_smart_ptr _c;

    ae_frame_make(_state, &_frame_block);
    memset(&_c, 0, sizeof(_c));
    ae_smart_ptr_init(&_c, (void**)&c, ae_false, _state, ae_true);

    ae_assert(ae_isfinite(cl, _state)||ae_isneginf(cl, _state), "xqcAppendDense: CL is not finite or -INF", _state);
    ae_assert(ae_isfinite(cu, _state)||ae_isposinf(cu, _state), "xqcAppendDense: CU is not finite or +INF", _state);
    n = xqc->n;
    c = (xquadraticconstraint*)ae_malloc(sizeof(xquadraticconstraint), _state); /* note: using c as a temporary prior to assigning its value to _c */
    memset(c, 0, sizeof(xquadraticconstraint));
    _xquadraticconstraint_init(c, _state, ae_false);
    ae_smart_ptr_assign(&_c, c, ae_true, ae_true, (ae_int_t)sizeof(xquadraticconstraint), _xquadraticconstraint_init_copy, _xquadraticconstraint_destroy);
    
    /*
     * Determine working variables
     */
    isetallocv(n, 0, &xqc->tmpi, _state);
    for(i=0; i<=n-1; i++)
    {
        if( b->ptr.p_double[i]!=0.0 )
        {
            xqc->tmpi.ptr.p_int[i] = 1;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( isupper )
        {
            j0 = i;
            j1 = n-1;
        }
        else
        {
            j0 = 0;
            j1 = i;
        }
        for(j=j0; j<=j1; j++)
        {
            if( q->ptr.pp_double[i][j]!=0.0 )
            {
                xqc->tmpi.ptr.p_int[i] = 1;
                xqc->tmpi.ptr.p_int[j] = 1;
            }
        }
    }
    c->nvars = 0;
    for(i=0; i<=n-1; i++)
    {
        if( xqc->tmpi.ptr.p_int[i]!=0 )
        {
            igrowv(c->nvars+1, &c->varidx, _state);
            c->varidx.ptr.p_int[c->nvars] = i;
            c->nvars = c->nvars+1;
        }
    }
    
    /*
     * Save constraints
     */
    c->applyorigin = applyorigin;
    c->cl = cl;
    c->cu = cu;
    if( c->nvars>0 )
    {
        rallocv(c->nvars, &c->b, _state);
        for(i=0; i<=c->nvars-1; i++)
        {
            c->b.ptr.p_double[i] = b->ptr.p_double[c->varidx.ptr.p_int[i]];
        }
        c->lowerq.m = c->nvars;
        c->lowerq.n = c->nvars;
        iallocv(c->nvars+1, &c->lowerq.ridx, _state);
        c->lowerq.ridx.ptr.p_int[0] = 0;
        for(i=0; i<=c->nvars-1; i++)
        {
            offs = c->lowerq.ridx.ptr.p_int[i];
            igrowv(offs+c->nvars, &c->lowerq.idx, _state);
            rgrowv(offs+c->nvars, &c->lowerq.vals, _state);
            for(j=0; j<=i; j++)
            {
                if( isupper )
                {
                    vq = q->ptr.pp_double[c->varidx.ptr.p_int[j]][c->varidx.ptr.p_int[i]];
                }
                else
                {
                    vq = q->ptr.pp_double[c->varidx.ptr.p_int[i]][c->varidx.ptr.p_int[j]];
                }
                if( vq!=0.0||j==i )
                {
                    c->lowerq.idx.ptr.p_int[offs] = j;
                    c->lowerq.vals.ptr.p_double[offs] = vq;
                    offs = offs+1;
                }
            }
            c->lowerq.ridx.ptr.p_int[i+1] = offs;
        }
        sparsecreatecrsinplace(&c->lowerq, _state);
    }
    ae_obj_array_append_transfer(&xqc->constraints, &_c, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Slightly denormalized primitive power cone
*************************************************************************/
ae_int_t xccdenormalizedprimitivepowerconetype(ae_state *_state)
{
    ae_int_t result;


    result = -4;
    return result;
}


/*************************************************************************
Generic orthogonal power cone
*************************************************************************/
ae_int_t xccgenericorthogonalpowerconetype(ae_state *_state)
{
    ae_int_t result;


    result = -3;
    return result;
}


/*************************************************************************
Slightly denormalized primitive SOC
*************************************************************************/
ae_int_t xccdenormalizedprimitiveconetype(ae_state *_state)
{
    ae_int_t result;


    result = -2;
    return result;
}


/*************************************************************************
Slightly denormalized primitive SOC
*************************************************************************/
ae_int_t xccgenericorthogonalconetype(ae_state *_state)
{
    ae_int_t result;


    result = -1;
    return result;
}


/*************************************************************************
Primitive SOC
*************************************************************************/
ae_int_t xccprimitiveconetype(ae_state *_state)
{
    ae_int_t result;


    result = 1;
    return result;
}


/*************************************************************************
Primitive SOC
*************************************************************************/
ae_int_t xccprimitivepowerconetype(ae_state *_state)
{
    ae_int_t result;


    result = 2;
    return result;
}


/*************************************************************************
Clear quadratic constraints structure
*************************************************************************/
void xccclear(xconicconstraints* state, ae_state *_state)
{


    ae_obj_array_clear(&state->constraints);
}


/*************************************************************************
Copies conic constraints structure, target structure is cleared,
previously allocated memory is ignored.
*************************************************************************/
void xcccopy(xconicconstraints* src,
     xconicconstraints* dst,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t m;
    xconicconstraint *c;
    ae_smart_ptr _c;
    xconicconstraint *c1;
    ae_smart_ptr _c1;
    ae_bool donecc;

    ae_frame_make(_state, &_frame_block);
    memset(&_c, 0, sizeof(_c));
    memset(&_c1, 0, sizeof(_c1));
    ae_smart_ptr_init(&_c, (void**)&c, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_c1, (void**)&c1, ae_false, _state, ae_true);

    dst->n = src->n;
    m = ae_obj_array_get_length(&src->constraints);
    ae_obj_array_clear(&dst->constraints);
    for(i=0; i<=m-1; i++)
    {
        ae_obj_array_get(&src->constraints, i, &_c, _state);
        c1 = (xconicconstraint*)ae_malloc(sizeof(xconicconstraint), _state); /* note: using c1 as a temporary prior to assigning its value to _c1 */
        memset(c1, 0, sizeof(xconicconstraint));
        _xconicconstraint_init(c1, _state, ae_false);
        ae_smart_ptr_assign(&_c1, c1, ae_true, ae_true, (ae_int_t)sizeof(xconicconstraint), _xconicconstraint_init_copy, _xconicconstraint_destroy);
        donecc = ae_false;
        c1->conetype = c->conetype;
        c1->nvars = c->nvars;
        c1->applyorigin = c->applyorigin;
        if( c->conetype==-1||c->conetype==1 )
        {
            
            /*
             * Canonic/noncanonic orthogonal cones
             */
            if( c->nvars>0 )
            {
                icopyallocv(c->nvars, &c->varidx, &c1->varidx, _state);
                rcopyallocv(c->nvars, &c->diaga, &c1->diaga, _state);
                rcopyallocv(c->nvars+1, &c->shftc, &c1->shftc, _state);
            }
            donecc = ae_true;
        }
        if( c->conetype==xccgenericorthogonalpowerconetype(_state)||c->conetype==xccprimitivepowerconetype(_state) )
        {
            
            /*
             * Power cones
             */
            ae_assert(c->nvars>0, "xccCopy: integrity check 561151 failed", _state);
            icopyallocv(c->nvars, &c->varidx, &c1->varidx, _state);
            rcopyallocv(c->nvars, &c->diaga, &c1->diaga, _state);
            rcopyallocv(c->nvars+1, &c->shftc, &c1->shftc, _state);
            c1->kpow = c->kpow;
            rcopyallocv(c->kpow, &c->alphapow, &c1->alphapow, _state);
            donecc = ae_true;
        }
        ae_assert(donecc, "xccCopy: unexpected cone type", _state);
        ae_obj_array_append_transfer(&dst->constraints, &_c1, _state);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Copies conic constraints structure, skipping some constraints and permuting
variables using so-called 'pack' permutation that monotonically maps increasing
indexes into increasing indexes.

The target structure is cleared, previously allocated memory is ignored.
*************************************************************************/
void xcccopywithskipandpack(xconicconstraints* src,
     /* Boolean */ const ae_vector* skipflags,
     /* Integer */ const ae_vector* packxperm,
     xconicconstraints* dst,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t srcm;
    xconicconstraint *c;
    ae_smart_ptr _c;
    xconicconstraint *c1;
    ae_smart_ptr _c1;
    ae_bool donecc;

    ae_frame_make(_state, &_frame_block);
    memset(&_c, 0, sizeof(_c));
    memset(&_c1, 0, sizeof(_c1));
    ae_smart_ptr_init(&_c, (void**)&c, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_c1, (void**)&c1, ae_false, _state, ae_true);

    dst->n = src->n;
    srcm = ae_obj_array_get_length(&src->constraints);
    ae_obj_array_clear(&dst->constraints);
    for(i=0; i<=srcm-1; i++)
    {
        if( !skipflags->ptr.p_bool[i] )
        {
            ae_obj_array_get(&src->constraints, i, &_c, _state);
            c1 = (xconicconstraint*)ae_malloc(sizeof(xconicconstraint), _state); /* note: using c1 as a temporary prior to assigning its value to _c1 */
            memset(c1, 0, sizeof(xconicconstraint));
            _xconicconstraint_init(c1, _state, ae_false);
            ae_smart_ptr_assign(&_c1, c1, ae_true, ae_true, (ae_int_t)sizeof(xconicconstraint), _xconicconstraint_init_copy, _xconicconstraint_destroy);
            donecc = ae_false;
            c1->conetype = c->conetype;
            c1->nvars = c->nvars;
            c1->applyorigin = c->applyorigin;
            if( c->conetype==-1||c->conetype==1 )
            {
                
                /*
                 * Canonic/noncanonic orthogonal cones
                 */
                if( c->nvars>0 )
                {
                    rcopyallocv(c->nvars, &c->diaga, &c1->diaga, _state);
                    rcopyallocv(c->nvars+1, &c->shftc, &c1->shftc, _state);
                    iallocv(c->nvars, &c1->varidx, _state);
                    for(j=0; j<=c->nvars-1; j++)
                    {
                        c1->varidx.ptr.p_int[j] = packxperm->ptr.p_int[c->varidx.ptr.p_int[j]];
                    }
                }
                donecc = ae_true;
            }
            if( c->conetype==xccprimitivepowerconetype(_state) )
            {
                
                /*
                 * Power cones
                 */
                ae_assert(c->nvars>0, "OPTSERV: integrity check 625237 failed", _state);
                rcopyallocv(c->nvars, &c->diaga, &c1->diaga, _state);
                rcopyallocv(c->nvars+1, &c->shftc, &c1->shftc, _state);
                iallocv(c->nvars, &c1->varidx, _state);
                for(j=0; j<=c->nvars-1; j++)
                {
                    c1->varidx.ptr.p_int[j] = packxperm->ptr.p_int[c->varidx.ptr.p_int[j]];
                }
                c1->kpow = c->kpow;
                rcopyallocv(c->kpow, &c->alphapow, &c1->alphapow, _state);
                donecc = ae_true;
            }
            ae_assert(donecc, "xccCopyWithSkipAndPack: unexpected cone type", _state);
            ae_obj_array_append_transfer(&dst->constraints, &_c1, _state);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Get constraints count
*************************************************************************/
ae_int_t xccgetcount(const xconicconstraints* xcc, ae_state *_state)
{
    ae_int_t result;


    result = ae_obj_array_get_length(&xcc->constraints);
    return result;
}


/*************************************************************************
Adds noncanonic orthogonal second order cone.

INPUT PARAMETERS:
    XCC         -   a set of conic constraints
    VarIdx      -   array[NVars], can be unsorted and non-distinct
    Diag        -   array[NVars], diagonal multipliers before variables
    Shft        -   array[NVars], shifts applied to variables
    NVars       -   1<=NVars, variables count (can be larger than N).
                    The last variable is the cone axis.
    Theta       -   theta^2 is added to the term under the square root
    ApplyOrigin -   whether conic constraint applies as f(x-origin)<=0
                    (ApplyOrigin=True) or as f(x)<=0.
*************************************************************************/
void xccaddsoccorthogonalnoncanonic(xconicconstraints* xcc,
     /* Integer */ const ae_vector* varidx,
     /* Real    */ const ae_vector* diag,
     /* Real    */ const ae_vector* shft,
     ae_int_t nvars,
     double theta,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    xconicconstraint *c;
    ae_smart_ptr _c;

    ae_frame_make(_state, &_frame_block);
    memset(&_c, 0, sizeof(_c));
    ae_smart_ptr_init(&_c, (void**)&c, ae_false, _state, ae_true);

    n = xcc->n;
    ae_assert(nvars>=1, "xccAddSOCCOrthogonalNoncanonic: NVars<1", _state);
    ae_assert(varidx->cnt>=nvars, "xccAddSOCCOrthogonalNoncanonic: len(VarIdx)<NVars", _state);
    ae_assert(diag->cnt>=nvars, "xccAddSOCCOrthogonalNoncanonic: len(Diag)<NVars", _state);
    ae_assert(shft->cnt>=nvars, "xccAddSOCCOrthogonalNoncanonic: len(Shft)<NVars", _state);
    for(i=0; i<=nvars-1; i++)
    {
        ae_assert(varidx->ptr.p_int[i]>=0&&varidx->ptr.p_int[i]<n, "xccAddSOCCOrthogonalNoncanonic: VarIdx[] contains values outside of [0,N)", _state);
        ae_assert(ae_isfinite(diag->ptr.p_double[i], _state), "xccAddSOCCOrthogonalNoncanonic: Diaga[] contains infinite values", _state);
        ae_assert(ae_isfinite(shft->ptr.p_double[i], _state), "xccAddSOCCOrthogonalNoncanonic: Shft[] contains infinite values", _state);
    }
    ae_assert(ae_isfinite(theta, _state), "xccAddSOCCOrthogonalNoncanonic: theta is not a finite number", _state);
    c = (xconicconstraint*)ae_malloc(sizeof(xconicconstraint), _state); /* note: using c as a temporary prior to assigning its value to _c */
    memset(c, 0, sizeof(xconicconstraint));
    _xconicconstraint_init(c, _state, ae_false);
    ae_smart_ptr_assign(&_c, c, ae_true, ae_true, (ae_int_t)sizeof(xconicconstraint), _xconicconstraint_init_copy, _xconicconstraint_destroy);
    c->conetype = -1;
    c->nvars = nvars;
    c->applyorigin = applyorigin;
    icopyallocv(nvars, varidx, &c->varidx, _state);
    rcopyallocv(nvars, diag, &c->diaga, _state);
    rallocv(nvars+1, &c->shftc, _state);
    rcopyv(nvars, shft, &c->shftc, _state);
    c->shftc.ptr.p_double[nvars] = theta*theta;
    ae_obj_array_append_transfer(&xcc->constraints, &_c, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Adds canonic primitive second order cone. The canonic primitive  cone  has
requirement that all variables are distinct.

INPUT PARAMETERS:
    XCC         -   a set of conic constraints
    VarIdx      -   array[NVars], sorted by ascending (except for the
                    last element)
    Diag        -   array[NVars], diagonal multipliers before variables
    Shft        -   array[NVars], shifts applied to variables
    NVars       -   1<=NVars<=N, variables count. The last variable is the
                    cone axis.
    ApplyOrigin -   whether conic constraint applies as f(x-origin)<=0
                    (ApplyOrigin=True) or as f(x)<=0.
*************************************************************************/
void xccaddsoccprimitivecanonic(xconicconstraints* xcc,
     /* Integer */ const ae_vector* varidx,
     /* Real    */ const ae_vector* diag,
     /* Real    */ const ae_vector* shft,
     ae_int_t nvars,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    xconicconstraint *c;
    ae_smart_ptr _c;

    ae_frame_make(_state, &_frame_block);
    memset(&_c, 0, sizeof(_c));
    ae_smart_ptr_init(&_c, (void**)&c, ae_false, _state, ae_true);

    n = xcc->n;
    ae_assert(nvars>=1&&nvars<=xcc->n, "xccAddSOCCPrimitiveCanonic: NVars<1 or NVars>N", _state);
    ae_assert(varidx->ptr.p_int[0]>=0&&varidx->ptr.p_int[0]<n, "xccAddSOCCPrimitiveCanonic: VarIdx[] contains values outside of [0,N)", _state);
    ae_assert(varidx->ptr.p_int[nvars-1]>=0&&varidx->ptr.p_int[nvars-1]<n, "xccAddSOCCPrimitiveCanonic: VarIdx[] contains values outside of [0,N)", _state);
    for(i=1; i<=nvars-2; i++)
    {
        ae_assert(varidx->ptr.p_int[i]>varidx->ptr.p_int[i-1], "xccAddSOCCPrimitiveCanonic: VarIdx[] is unsorted and/or has nondistinct values", _state);
        ae_assert(varidx->ptr.p_int[i]<n, "xccAddSOCCPrimitiveCanonic: VarIdx[] contains values outside of [0,N)", _state);
    }
    c = (xconicconstraint*)ae_malloc(sizeof(xconicconstraint), _state); /* note: using c as a temporary prior to assigning its value to _c */
    memset(c, 0, sizeof(xconicconstraint));
    _xconicconstraint_init(c, _state, ae_false);
    ae_smart_ptr_assign(&_c, c, ae_true, ae_true, (ae_int_t)sizeof(xconicconstraint), _xconicconstraint_init_copy, _xconicconstraint_destroy);
    c->conetype = 1;
    c->nvars = nvars;
    c->applyorigin = applyorigin;
    icopyallocv(nvars, varidx, &c->varidx, _state);
    rcopyallocv(nvars, diag, &c->diaga, _state);
    rallocv(nvars+1, &c->shftc, _state);
    rcopyv(nvars, shft, &c->shftc, _state);
    c->shftc.ptr.p_double[nvars] = 0.0;
    ae_obj_array_append_transfer(&xcc->constraints, &_c, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Adds canonic primitive power cone. The canonic primitive  cone  has
requirement that all variables are distinct.

INPUT PARAMETERS:
    XCC         -   a set of conic constraints
    VarIdx      -   array[NVars]:
                    * first NVars-KPow elements are sorted by ascending,
                      all elements within the subsequence are distinct
                    * last KPow elements are sorted by ascending,
                      all elements within the subsequence are distinct
    Diag        -   array[NVars], diagonal multipliers before variables;
                    Diag[i] is nonzero for NVars-KPow <= i < NVars.
    Shft        -   array[NVars], shifts applied to variables
    NVars       -   1<=NVars<=N, variables count. The last KPow variables
                    are the cone axis.
    AlphaPow    -   array[KPow], powers:
                    * AlphaPow[I]>0
                    * 0 < SUM(AlphaPow[]) <= 1
    KPow        -   1<=NPow<=NVars
    ApplyOrigin -   whether conic constraint applies as f(x-origin)<=0
                    (ApplyOrigin=True) or as f(x)<=0.
*************************************************************************/
void xccaddpowccprimitivecanonic(xconicconstraints* xcc,
     /* Integer */ const ae_vector* varidx,
     /* Real    */ const ae_vector* diag,
     /* Real    */ const ae_vector* shft,
     ae_int_t nvars,
     /* Real    */ const ae_vector* alphapow,
     ae_int_t kpow,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    xconicconstraint *c;
    ae_smart_ptr _c;
    double vs;

    ae_frame_make(_state, &_frame_block);
    memset(&_c, 0, sizeof(_c));
    ae_smart_ptr_init(&_c, (void**)&c, ae_false, _state, ae_true);

    n = xcc->n;
    ae_assert(nvars>=1&&nvars<=xcc->n, "xccAddPOWCCPrimitiveCanonic: NVars<1 or NVars>N", _state);
    ae_assert(kpow>=1&&kpow<=nvars, "xccAddPOWCCPrimitiveCanonic: KPow<1 or KPow>NVars", _state);
    if( kpow<nvars )
    {
        ae_assert(varidx->ptr.p_int[0]>=0&&varidx->ptr.p_int[0]<n, "xccAddPOWCCPrimitiveCanonic: VarIdx[] contains values outside of [0,N)", _state);
        ae_assert(varidx->ptr.p_int[nvars-kpow-1]>=0&&varidx->ptr.p_int[nvars-kpow-1]<n, "xccAddPOWCCPrimitiveCanonic: VarIdx[] contains values outside of [0,N)", _state);
        for(i=1; i<=nvars-kpow-2; i++)
        {
            ae_assert(varidx->ptr.p_int[i]>varidx->ptr.p_int[i-1], "xccAddPOWCCPrimitiveCanonic: VarIdx[] is unsorted and/or has nondistinct values", _state);
            ae_assert(varidx->ptr.p_int[i]<n, "xccAddPOWCCPrimitiveCanonic: VarIdx[] contains values outside of [0,N)", _state);
        }
    }
    ae_assert(varidx->ptr.p_int[nvars-kpow]>=0&&varidx->ptr.p_int[nvars-kpow]<n, "xccAddPOWCCPrimitiveCanonic: VarIdx[] contains values outside of [0,N)", _state);
    ae_assert(varidx->ptr.p_int[nvars-1]>=0&&varidx->ptr.p_int[nvars-1]<n, "xccAddPOWCCPrimitiveCanonic: VarIdx[] contains values outside of [0,N)", _state);
    for(i=nvars-kpow+1; i<=nvars-1; i++)
    {
        ae_assert(varidx->ptr.p_int[i]>varidx->ptr.p_int[i-1], "xccAddPOWCCPrimitiveCanonic: VarIdx[] is unsorted and/or has nondistinct values", _state);
        ae_assert(varidx->ptr.p_int[i]<n, "xccAddPOWCCPrimitiveCanonic: VarIdx[] contains values outside of [0,N)", _state);
    }
    vs = (double)(0);
    for(i=0; i<=kpow-1; i++)
    {
        ae_assert(ae_isfinite(alphapow->ptr.p_double[i], _state), "xccAddPOWCCPrimitiveCanonic: AlphaPow[] contains INF/NAN values", _state);
        ae_assert(ae_fp_greater(alphapow->ptr.p_double[i],(double)(0))&&ae_fp_less_eq(alphapow->ptr.p_double[i],(double)(1)), "xccAddPOWCCPrimitiveCanonic: AlphaPow[] is outside of (0,1]", _state);
        ae_assert(ae_fp_neq(diag->ptr.p_double[nvars-kpow+i],(double)(0)), "xccAddPOWCCPrimitiveCanonic: scaling coefficient for power terms is zero", _state);
        vs = vs+alphapow->ptr.p_double[i];
    }
    ae_assert(ae_fp_greater(vs,(double)(0))&&ae_fp_less_eq(vs,(double)1+(double)(10*kpow)*ae_machineepsilon), "xccAddPOWCCPrimitiveCanonic: AlphaPow[] sum is is outside of (0,1]", _state);
    c = (xconicconstraint*)ae_malloc(sizeof(xconicconstraint), _state); /* note: using c as a temporary prior to assigning its value to _c */
    memset(c, 0, sizeof(xconicconstraint));
    _xconicconstraint_init(c, _state, ae_false);
    ae_smart_ptr_assign(&_c, c, ae_true, ae_true, (ae_int_t)sizeof(xconicconstraint), _xconicconstraint_init_copy, _xconicconstraint_destroy);
    c->conetype = xccprimitivepowerconetype(_state);
    c->nvars = nvars;
    c->kpow = kpow;
    c->applyorigin = applyorigin;
    icopyallocv(nvars, varidx, &c->varidx, _state);
    rcopyallocv(nvars, diag, &c->diaga, _state);
    rallocv(nvars+1, &c->shftc, _state);
    rcopyv(nvars, shft, &c->shftc, _state);
    c->shftc.ptr.p_double[nvars] = 0.0;
    rcopyallocv(kpow, alphapow, &c->alphapow, _state);
    ae_obj_array_append_transfer(&xcc->constraints, &_c, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Adds noncanonic orthogonal second order cone.

INPUT PARAMETERS:
    XCC         -   a set of conic constraints
    VarIdx      -   array[NVars], can be unsorted and non-distinct
    Diag        -   array[NVars], diagonal multipliers before variables
    Shft        -   array[NVars], shifts applied to variables
    NVars       -   1<=NVars, variables count (can be larger than N).
                    The last variable is the cone axis.
    Theta       -   theta^2 is added to the term under the square root
    AlphaPow    -   array[KPow], power coeffs
    KPow        -   1<=KPow<=NVars, number of power terms
    ApplyOrigin -   whether conic constraint applies as f(x-origin)<=0
                    (ApplyOrigin=True) or as f(x)<=0.
*************************************************************************/
void xccaddpowccorthogonalnoncanonic(xconicconstraints* xcc,
     /* Integer */ const ae_vector* varidx,
     /* Real    */ const ae_vector* diag,
     /* Real    */ const ae_vector* shft,
     ae_int_t nvars,
     double theta,
     /* Real    */ const ae_vector* alphapow,
     ae_int_t kpow,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    double vs;
    xconicconstraint *c;
    ae_smart_ptr _c;

    ae_frame_make(_state, &_frame_block);
    memset(&_c, 0, sizeof(_c));
    ae_smart_ptr_init(&_c, (void**)&c, ae_false, _state, ae_true);

    n = xcc->n;
    ae_assert(nvars>=1, "xccAddPOWCCOrthogonalNoncanonic: NVars<1", _state);
    ae_assert(varidx->cnt>=nvars, "xccAddPOWCCOrthogonalNoncanonic: len(VarIdx)<NVars", _state);
    ae_assert(diag->cnt>=nvars, "xccAddPOWCCOrthogonalNoncanonic: len(Diag)<NVars", _state);
    ae_assert(shft->cnt>=nvars, "xccAddPOWCCOrthogonalNoncanonic: len(Shft)<NVars", _state);
    for(i=0; i<=nvars-1; i++)
    {
        ae_assert(varidx->ptr.p_int[i]>=0&&varidx->ptr.p_int[i]<n, "xccAddPOWCCOrthogonalNoncanonic: VarIdx[] contains values outside of [0,N)", _state);
        ae_assert(ae_isfinite(diag->ptr.p_double[i], _state), "xccAddPOWCCOrthogonalNoncanonic: Diaga[] contains infinite values", _state);
        ae_assert(ae_isfinite(shft->ptr.p_double[i], _state), "xccAddPOWCCOrthogonalNoncanonic: Shft[] contains infinite values", _state);
    }
    ae_assert(ae_isfinite(theta, _state), "xccAddPOWCCOrthogonalNoncanonic: theta is not a finite number", _state);
    vs = (double)(0);
    for(i=0; i<=kpow-1; i++)
    {
        ae_assert(ae_isfinite(alphapow->ptr.p_double[i], _state), "xccAddPOWCCOrthogonalNoncanonic: AlphaPow[] contains INF/NAN values", _state);
        ae_assert(ae_fp_greater(alphapow->ptr.p_double[i],(double)(0))&&ae_fp_less_eq(alphapow->ptr.p_double[i],(double)(1)), "xccAddPOWCCOrthogonalNoncanonic: AlphaPow[] is outside of (0,1]", _state);
        vs = vs+alphapow->ptr.p_double[i];
    }
    ae_assert(ae_fp_greater(vs,(double)(0))&&ae_fp_less_eq(vs,(double)1+(double)(10*kpow)*ae_machineepsilon), "xccAddPOWCCOrthogonalNoncanonic: AlphaPow[] sum is is outside of (0,1]", _state);
    c = (xconicconstraint*)ae_malloc(sizeof(xconicconstraint), _state); /* note: using c as a temporary prior to assigning its value to _c */
    memset(c, 0, sizeof(xconicconstraint));
    _xconicconstraint_init(c, _state, ae_false);
    ae_smart_ptr_assign(&_c, c, ae_true, ae_true, (ae_int_t)sizeof(xconicconstraint), _xconicconstraint_init_copy, _xconicconstraint_destroy);
    c->conetype = xccgenericorthogonalpowerconetype(_state);
    c->nvars = nvars;
    c->kpow = kpow;
    c->applyorigin = applyorigin;
    icopyallocv(nvars, varidx, &c->varidx, _state);
    rcopyallocv(nvars, diag, &c->diaga, _state);
    rallocv(nvars+1, &c->shftc, _state);
    rcopyv(nvars, shft, &c->shftc, _state);
    c->shftc.ptr.p_double[nvars] = theta*theta;
    rcopyallocv(kpow, alphapow, &c->alphapow, _state);
    ae_obj_array_append_transfer(&xcc->constraints, &_c, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Factors out non-axial variable K0 from a conic constraint by making a
substitution X[k0]:=alpha0 + alpha1*X[k1]. Both K0 and K1 must be non-axial
variables. If Alpha1 is zero, then K1 must be equal to -1.

Returns:
* HadK0     -   True if K0 was found in the original (unmodified) VarIdx[].
                If HadK0=False it means that no modification was applied.
* HadK1     -   True if K1 was found in the original (unmodified) VarIdx[]

Works with the following cones:
* xccPrimitiveConeType()

  -- ALGLIB --
     Copyright 01.11.2024 by Bochkanov Sergey
*************************************************************************/
void xccfactoroutnonaxial(xconicconstraint* c,
     ae_int_t k0,
     double alpha0,
     double alpha1,
     ae_int_t k1,
     ae_bool* hadk0,
     ae_bool* hadk1,
     ae_state *_state)
{
    ae_int_t nv;
    ae_int_t nradial;
    ae_int_t localk0;
    ae_int_t localk1;
    ae_int_t j;
    double a0;
    double a1;
    double c0;
    double c1;
    double f;
    double g;
    double h;

    *hadk0 = ae_false;
    *hadk1 = ae_false;

    nv = c->nvars;
    *hadk0 = ae_false;
    *hadk1 = ae_false;
    if( nv==0 )
    {
        return;
    }
    if( c->conetype==xccprimitiveconetype(_state)||c->conetype==xccprimitivepowerconetype(_state) )
    {
        
        /*
         * Analyze cone
         */
        nradial = -1;
        if( c->conetype==xccprimitiveconetype(_state) )
        {
            nradial = nv-1;
            ae_assert(c->varidx.ptr.p_int[nv-1]!=k0&&c->varidx.ptr.p_int[nv-1]!=k1, "xccFactorOutNonAxial: K0 or K1 is an axial variable for the cone", _state);
        }
        if( c->conetype==xccprimitivepowerconetype(_state) )
        {
            nradial = nv-c->kpow;
            ae_assert(!ilinearsearchispresent(&c->varidx, nradial, nv, k0, _state)&&!ilinearsearchispresent(&c->varidx, nradial, nv, k1, _state), "xccFactorOutNonAxial: K0 or K1 is an axial variable for the power cone", _state);
        }
        ae_assert(nradial>=1, "xccFactorOutNonAxial: unrecognized cone type", _state);
        
        /*
         * Determine variable locations in the array
         * Quick exit if variable K0 is not found
         */
        localk0 = -1;
        localk1 = -1;
        for(j=0; j<=nradial-1; j++)
        {
            if( c->varidx.ptr.p_int[j]==k0 )
            {
                localk0 = j;
            }
            if( c->varidx.ptr.p_int[j]==k1 )
            {
                localk1 = j;
            }
        }
        *hadk0 = localk0>=0;
        *hadk1 = localk1>=0;
        if( localk0<0 )
        {
            return;
        }
        if( ae_fp_eq(alpha1,(double)(0)) )
        {
            ae_assert(k1==-1, "xccFactorOutNonAxial: alpha1=0, but K1 is different from -1", _state);
            c->shftc.ptr.p_double[nv] = c->shftc.ptr.p_double[nv]+ae_sqr(c->diaga.ptr.p_double[localk0]*alpha0+c->shftc.ptr.p_double[localk0], _state);
            for(j=localk0; j<=nv-2; j++)
            {
                c->varidx.ptr.p_int[j] = c->varidx.ptr.p_int[j+1];
                c->diaga.ptr.p_double[j] = c->diaga.ptr.p_double[j+1];
                c->shftc.ptr.p_double[j] = c->shftc.ptr.p_double[j+1];
            }
            c->shftc.ptr.p_double[nv-1] = c->shftc.ptr.p_double[nv];
            c->nvars = nv-1;
            return;
        }
        ae_assert(k1>=0, "xccFactorOutNonAxial: alpha1<>0, but K1 negative", _state);
        
        /*
         * Different algorithms for K1 being present or absent
         */
        if( localk1<0 )
        {
            
            /*
             * The element K1 is not present, we can simply replace K0 by K1 as follows. Let A0 and
             * C0 are multiplier and shift for K0. Substituting Xk0:=Alpha0+Xk1*Alpha1 gives us
             *
             *     (A0*Xk0+C0)^2 =
             *     = (A0*(Alpha0+Xk1*Alpha1)+C0)^2 =
             *     = (A0*Alpha1*Xk1+(C0+A0*Alpha0))^2
             */
            a0 = c->diaga.ptr.p_double[localk0];
            c0 = c->shftc.ptr.p_double[localk0];
            c->varidx.ptr.p_int[localk0] = k1;
            c->diaga.ptr.p_double[localk0] = a0*alpha1;
            c->shftc.ptr.p_double[localk0] = c0+a0*alpha0;
            tagsortmiddleirr(&c->varidx, &c->diaga, &c->shftc, 0, nradial, _state);
        }
        else
        {
            
            /*
             * The element K1 is present.
             *
             * Let A0 and C0 are multiplier and shift for K0, and similarly A1 and C1 are multiplier
             * and shift for K1. Substituting Xk0:=Alpha0+Xk1*Alpha1 gives us
             *
             *     (A0*Xk0+C0)^2 + (A1*Xk1+C1)^2 =
             *     = (A0*(Alpha0+Xk1*Alpha1)+C0)^2 + (A1*Xk1+C1)^2 =
             *     = (A0*Alpha1*Xk1+C0+A0*Alpha0)^2 + (A1*Xk1+C1)^2 =
             *     = (A0*Alpha1)^2*Xk1^2 + 2*(A0*Alpha1)*(C0+A0*Alpha0)*Xk1 + (C0+A0*Alpha0)^2 + A1^2*Xk1^2 + 2*A1*C1*Xk1 + C1^2 =
             *     = ((A0*Alpha1)^2+A1^2)*Xk1^2 + 2*((A0*Alpha1)*(C0+A0*Alpha0)+A1*C1)*Xk1 + ((C0+A0*Alpha0)^2+C1^2) =
             *     = f^2*Xk1^2 + 2*g*Xk1 + h =
             *     = (f*Xk1)^2 + 2*(g/f)*(f*Xk1) + (g/f)^2 + h-(g/f)^2 =
             *     = (f*Xk1 + g/f)^2 + h-(g/f)^2
             *
             * Modify coefficients for K1.
             */
            a0 = c->diaga.ptr.p_double[localk0];
            a1 = c->diaga.ptr.p_double[localk1];
            c0 = c->shftc.ptr.p_double[localk0];
            c1 = c->shftc.ptr.p_double[localk1];
            f = ae_sqrt(a0*alpha1*(a0*alpha1)+a1*a1, _state);
            g = a0*alpha1*(c0+a0*alpha0)+a1*c1;
            h = (c0+a0*alpha0)*(c0+a0*alpha0)+c1*c1;
            ae_assert(ae_fp_greater(f,(double)(0)), "xccFactorOutNonAxial: diagonal term is zero", _state);
            c->diaga.ptr.p_double[localk1] = f;
            c->shftc.ptr.p_double[localk1] = g/f;
            c->shftc.ptr.p_double[nv] = c->shftc.ptr.p_double[nv]+ae_maxreal(h-g/f*(g/f), 0.0, _state);
            
            /*
             * Remove K0 
             */
            for(j=localk0; j<=nv-2; j++)
            {
                c->varidx.ptr.p_int[j] = c->varidx.ptr.p_int[j+1];
                c->diaga.ptr.p_double[j] = c->diaga.ptr.p_double[j+1];
                c->shftc.ptr.p_double[j] = c->shftc.ptr.p_double[j+1];
            }
            c->shftc.ptr.p_double[nv-1] = c->shftc.ptr.p_double[nv];
            c->nvars = nv-1;
        }
        return;
    }
    ae_assert(ae_false, "xccFactorOutNonAxial: unexpected cone type", _state);
}


/*************************************************************************
Unscales X (converts from scaled variables to original ones), paying special
attention to box constraints (output is always feasible; active constraints
are mapped to active ones).

Can handle batch requests.

Parameters:
    XS              array[N*BatchSize] - scaled variables. The function
                    assumes that vars are feasible with respect to SCALED
                    box constraints.
    SclFiniteBndL,
    SclFiniteBndU   array[N], lower/upper bounds for SCALED variables with
                    infinities being replaced by big numbers
    RawFiniteBndL,
    RawFiniteBndU   array[N], lower/upper bounds for ORIGINAL variables with
                    infinities being replaced by big numbers
                    
output:
    XU              array[N*BatchSize], vars after unscaling
*************************************************************************/
void unscalexbatchfinitebnd(/* Real    */ const ae_vector* xs,
     ae_int_t batchsize,
     /* Real    */ const ae_vector* s,
     ae_int_t n,
     /* Real    */ const ae_vector* sclfinitebndl,
     /* Real    */ const ae_vector* sclfinitebndu,
     /* Real    */ const ae_vector* rawfinitebndl,
     /* Real    */ const ae_vector* rawfinitebndu,
     /* Real    */ ae_vector* xu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double vs;


    if( optserv_unscalexbatchfinitebndkernel(xs, batchsize, s, n, sclfinitebndl, sclfinitebndu, rawfinitebndl, rawfinitebndu, xu, _state) )
    {
        return;
    }
    for(k=0; k<=batchsize-1; k++)
    {
        for(j=0; j<=n-1; j++)
        {
            i = k*n+j;
            vs = xs->ptr.p_double[i];
            if( vs<=sclfinitebndl->ptr.p_double[j] )
            {
                xu->ptr.p_double[i] = rawfinitebndl->ptr.p_double[j];
                continue;
            }
            if( vs>=sclfinitebndu->ptr.p_double[j] )
            {
                xu->ptr.p_double[i] = rawfinitebndu->ptr.p_double[j];
                continue;
            }
            xu->ptr.p_double[i] = vs*s->ptr.p_double[j];
            if( xu->ptr.p_double[i]<rawfinitebndl->ptr.p_double[j] )
            {
                xu->ptr.p_double[i] = rawfinitebndl->ptr.p_double[j];
            }
            if( xu->ptr.p_double[i]>rawfinitebndu->ptr.p_double[j] )
            {
                xu->ptr.p_double[i] = rawfinitebndu->ptr.p_double[j];
            }
        }
    }
}


/*************************************************************************
This function calculates feasibility error (square root of sum of  squared
errors) for a Kx(NMain+NSlack) system of linear equalities.
    
INPUT PARAMETERS:
    CE      -   set of K equality constraints, array[K,NMain+NSlack+1]
    X       -   candidate point, array [NMain+NSlack]
    NMain   -   number of primary variables
    NSlack  -   number of slack variables
    K       -   number of constraints
    Tmp0    -   possible preallocated buffer, automatically resized

RESULT:
    Sqrt(SUM(Err^2))
    
  -- ALGLIB --
     Copyright 17.09.2015 by Bochkanov Sergey
*************************************************************************/
static double optserv_feasibilityerror(/* Real    */ const ae_matrix* ce,
     /* Real    */ const ae_vector* x,
     ae_int_t nmain,
     ae_int_t nslack,
     ae_int_t k,
     /* Real    */ ae_vector* tmp0,
     ae_state *_state)
{
    ae_int_t i;
    double result;


    rvectorsetlengthatleast(tmp0, k, _state);
    for(i=0; i<=k-1; i++)
    {
        tmp0->ptr.p_double[i] = -ce->ptr.pp_double[i][nmain+nslack];
    }
    rmatrixgemv(k, nmain+nslack, 1.0, ce, 0, 0, 0, x, 0, 1.0, tmp0, 0, _state);
    result = 0.0;
    for(i=0; i<=k-1; i++)
    {
        result = result+tmp0->ptr.p_double[i]*tmp0->ptr.p_double[i];
    }
    result = ae_sqrt(result, _state);
    return result;
}


/*************************************************************************
This function calculates feasibility error (square root of sum of  squared
errors) for a Kx(NMain+NSlack)  system  of  linear  equalities  and  error
gradient (with respect to x)
    
INPUT PARAMETERS:
    CE      -   set of K equality constraints, array[K,NMain+NSlack+1]
    X       -   candidate point, array [NMain+NSlack]
    NMain   -   number of primary variables
    NSlack  -   number of slack variables
    K       -   number of constraints
    Grad    -   preallocated array[NMain+NSlack]
    Tmp0    -   possible preallocated buffer, automatically resized

RESULT:
    Err     -   Sqrt(SUM(Err^2))
    Grad    -   error gradient with respect to X, array[NMain+NSlack]
    
  -- ALGLIB --
     Copyright 17.09.2015 by Bochkanov Sergey
*************************************************************************/
static void optserv_feasibilityerrorgrad(/* Real    */ const ae_matrix* ce,
     /* Real    */ const ae_vector* x,
     ae_int_t nmain,
     ae_int_t nslack,
     ae_int_t k,
     double* err,
     /* Real    */ ae_vector* grad,
     /* Real    */ ae_vector* tmp0,
     ae_state *_state)
{
    ae_int_t i;
    double v;

    *err = 0.0;

    ae_assert(grad->cnt>=nmain+nslack, "FeasibilityErrorGrad: integrity check failed", _state);
    rvectorsetlengthatleast(tmp0, k, _state);
    rmatrixgemv(k, nmain+nslack, 1.0, ce, 0, 0, 0, x, 0, 0.0, tmp0, 0, _state);
    *err = 0.0;
    for(i=0; i<=k-1; i++)
    {
        v = tmp0->ptr.p_double[i]-ce->ptr.pp_double[i][nmain+nslack];
        tmp0->ptr.p_double[i] = v;
        *err = *err+v*v;
    }
    *err = ae_sqrt(*err, _state);
    rmatrixgemv(nmain+nslack, k, 1.0, ce, 0, 0, 1, tmp0, 0, 0.0, grad, 0, _state);
}


/*************************************************************************
This  subroutine  checks  C0  continuity  and  returns  continuity  rating
(normalized value, with values above 50-500 being good indication  of  the
discontinuity) and Lipschitz constant.

An interval between F1 and F2 is  tested  for  (dis)continuity.  Per-point
noise estimates are provided. Delta[i] is a step from F[i] to F[i+1].

ApplySpecialCorrection parameter should be set to True  if  you  use  this
function to estimate continuity of  the  model  around  minimum;  it  adds
special correction which helps to detect "max(0,1/x)"-like discontinuities.
Without this correction algorithm will still work, but will be a bit  less
powerful. Do not use this correction  for  situations  when  you  want  to
estimate continuity around some non-extremal point  -  it  may  result  in
spurious discontinuities being reported.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
static void optserv_testc0continuity(double f0,
     double f1,
     double f2,
     double f3,
     double noise0,
     double noise1,
     double noise2,
     double noise3,
     double delta0,
     double delta1,
     double delta2,
     ae_bool applyspecialcorrection,
     double* rating,
     double* lipschitz,
     ae_state *_state)
{
    double lipschitz01;
    double lipschitz12;
    double lipschitz23;

    *rating = 0.0;
    *lipschitz = 0.0;

    
    /*
     * Compute Lipschitz constant for the interval [0,1],
     * add noise correction in order to get increased estimate (makes
     * comparison below more conservative).
     */
    lipschitz01 = (ae_fabs(f1-f0, _state)+(noise0+noise1))/delta0;
    
    /*
     * Compute Lipschitz constant for the interval [StpIdx+1,StpIdx+2],
     * SUBTRACT noise correction in order to get decreased estimate (makes
     * comparison below more conservative).
     */
    lipschitz12 = ae_maxreal(ae_fabs(f2-f1, _state)-(noise1+noise2), 0.0, _state)/delta1;
    
    /*
     * Compute Lipschitz constant for the interval [StpIdx+2,StpIdx+3]
     * using special algorithm:
     * a) if F3<F2-Noise23, Lipschitz constant is assumed to be zero
     * b) otherwise, we compute Lipschitz constant as usual,
     *    with noise correction term being added
     *
     * We need case (a) because some kinds of discontinuities
     * (like one introduced by max(1/x,0)) manifest themselves
     * in a quite special way.
     */
    if( applyspecialcorrection&&f3<f2-(noise2+noise3) )
    {
        lipschitz23 = (double)(0);
    }
    else
    {
        lipschitz23 = (ae_fabs(f3-f2, _state)+(noise2+noise3))/delta2;
    }
    
    /*
     * Compute rating (ratio of two Lipschitz constants)
     */
    ae_assert(ae_maxreal(lipschitz01, lipschitz23, _state)>(double)0, "OptGuard: integrity check failed", _state);
    *rating = lipschitz12/ae_maxreal(lipschitz01, lipschitz23, _state);
    *lipschitz = lipschitz12;
}


/*************************************************************************
This subroutine checks C1 continuity using test #0 (function  values  from
the line search log are studied, gradient is not used).

An interval between F[StpIdx+0] and F[StpIdx+5]is  tested for  continuity.
An normalized error metric (Lipschitz constant growth for the  derivative)
for the interval in question is calculated. Values above  50  are  a  good
indication of the discontinuity.

A six-point algorithm is used for testing, so we expect that Monitor.F and
Monitor.Stp have enough points for this test.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
static void optserv_c1continuitytest0(smoothnessmonitor* monitor,
     ae_int_t funcidx,
     ae_int_t stpidx,
     ae_int_t sortedcnt,
     ae_state *_state)
{
    double f0;
    double f1;
    double f2;
    double f3;
    double f4;
    double f5;
    double noise0;
    double noise1;
    double noise2;
    double noise3;
    double noise4;
    double noise5;
    double delta0;
    double delta1;
    double delta2;
    double delta3;
    double delta4;
    double d0;
    double d1;
    double d2;
    double d3;
    double newnoise0;
    double newnoise1;
    double newnoise2;
    double newnoise3;
    double newdelta0;
    double newdelta1;
    double newdelta2;
    double rating;
    double lipschitz;
    double lengthrating;
    ae_int_t i;
    ae_int_t n;
    double nrm;


    n = monitor->n;
    ae_assert(stpidx+5<sortedcnt, "C1ContinuityTest0: integrity check failed", _state);
    ae_assert(ae_fp_eq(monitor->sortedstp.ptr.p_double[0],(double)(0)), "C1ContinuityTest0: integrity check failed", _state);
    ae_assert(ae_fp_greater(monitor->sortedstp.ptr.p_double[sortedcnt-1],(double)(0)), "C1ContinuityTest0: integrity check failed", _state);
    
    /*
     * Fetch F, noise, Delta's
     */
    f0 = monitor->f.ptr.p_double[stpidx+0];
    f1 = monitor->f.ptr.p_double[stpidx+1];
    f2 = monitor->f.ptr.p_double[stpidx+2];
    f3 = monitor->f.ptr.p_double[stpidx+3];
    f4 = monitor->f.ptr.p_double[stpidx+4];
    f5 = monitor->f.ptr.p_double[stpidx+5];
    noise0 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f0, _state), 1.0, _state);
    noise1 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f1, _state), 1.0, _state);
    noise2 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f2, _state), 1.0, _state);
    noise3 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f3, _state), 1.0, _state);
    noise4 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f4, _state), 1.0, _state);
    noise5 = optserv_ognoiselevelf*ae_maxreal(ae_fabs(f5, _state), 1.0, _state);
    delta0 = monitor->sortedstp.ptr.p_double[stpidx+1]-monitor->sortedstp.ptr.p_double[stpidx+0];
    delta1 = monitor->sortedstp.ptr.p_double[stpidx+2]-monitor->sortedstp.ptr.p_double[stpidx+1];
    delta2 = monitor->sortedstp.ptr.p_double[stpidx+3]-monitor->sortedstp.ptr.p_double[stpidx+2];
    delta3 = monitor->sortedstp.ptr.p_double[stpidx+4]-monitor->sortedstp.ptr.p_double[stpidx+3];
    delta4 = monitor->sortedstp.ptr.p_double[stpidx+5]-monitor->sortedstp.ptr.p_double[stpidx+4];
    
    /*
     * Differentiate functions, get derivative values and noise
     * estimates at points (0+1)/2, (1+2)/2, (3+4)/2, (3+4)/2,
     * (4+5)/2. Compute new step values NewDelta[i] and new
     * noise estimates.
     */
    d0 = (f1-f0)/delta0;
    d1 = (f2-f1)/delta1;
    d2 = (f4-f3)/delta3;
    d3 = (f5-f4)/delta4;
    newnoise0 = (noise0+noise1)/delta0;
    newnoise1 = (noise1+noise2)/delta1;
    newnoise2 = (noise3+noise4)/delta3;
    newnoise3 = (noise4+noise5)/delta4;
    newdelta0 = 0.5*(delta0+delta1);
    newdelta1 = 0.5*delta1+delta2+0.5*delta3;
    newdelta2 = 0.5*(delta3+delta4);
    
    /*
     * Test with C0 continuity tester. "Special correction" is
     * turned off for this test.
     */
    optserv_testc0continuity(d0, d1, d2, d3, newnoise0, newnoise1, newnoise2, newnoise3, newdelta0, newdelta1, newdelta2, ae_false, &rating, &lipschitz, _state);
    
    /*
     * Store results
     */
    if( rating>optserv_ogminrating1 )
    {
        
        /*
         * Store to total report
         */
        monitor->rep.nonc1test0positive = ae_true;
        if( rating>monitor->nonc1currentrating )
        {
            monitor->nonc1currentrating = rating;
            monitor->rep.nonc1suspected = ae_true;
            monitor->rep.nonc1lipschitzc = lipschitz;
            monitor->rep.nonc1fidx = funcidx;
        }
        
        /*
         * Store to "strongest" report
         */
        if( rating>monitor->nonc1test0strrating )
        {
            monitor->nonc1test0strrating = rating;
            monitor->nonc1test0strrep.positive = ae_true;
            monitor->nonc1test0strrep.fidx = funcidx;
            monitor->nonc1test0strrep.n = n;
            monitor->nonc1test0strrep.cnt = sortedcnt;
            monitor->nonc1test0strrep.stpidxa = stpidx+1;
            monitor->nonc1test0strrep.stpidxb = stpidx+4;
            monitor->nonc1test0strrep.inneriter = monitor->linesearchinneridx;
            monitor->nonc1test0strrep.outeriter = monitor->linesearchouteridx;
            rvectorsetlengthatleast(&monitor->nonc1test0strrep.x0, n, _state);
            rvectorsetlengthatleast(&monitor->nonc1test0strrep.d, n, _state);
            for(i=0; i<=n-1; i++)
            {
                monitor->nonc1test0strrep.x0.ptr.p_double[i] = monitor->enqueuedx.ptr.p_double[monitor->sortedidx.ptr.p_int[0]*n+i];
                monitor->nonc1test0strrep.d.ptr.p_double[i] = monitor->dcur.ptr.p_double[i];
            }
            rvectorsetlengthatleast(&monitor->nonc1test0strrep.stp, sortedcnt, _state);
            rvectorsetlengthatleast(&monitor->nonc1test0strrep.f, sortedcnt, _state);
            for(i=0; i<=sortedcnt-1; i++)
            {
                monitor->nonc1test0strrep.stp.ptr.p_double[i] = monitor->sortedstp.ptr.p_double[i];
                monitor->nonc1test0strrep.f.ptr.p_double[i] = monitor->f.ptr.p_double[i];
            }
        }
        
        /*
         * Store to "longest" report
         */
        nrm = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            nrm = nrm+ae_sqr(monitor->enqueuedx.ptr.p_double[monitor->sortedidx.ptr.p_int[0]*n+i]-monitor->enqueuedx.ptr.p_double[monitor->sortedidx.ptr.p_int[sortedcnt-1]*n+i], _state);
        }
        nrm = ae_sqrt(nrm, _state);
        nrm = coalesce(nrm, ae_machineepsilon, _state);
        lengthrating = nrm;
        if( lengthrating>monitor->nonc1test0lngrating )
        {
            monitor->nonc1test0lngrating = lengthrating;
            monitor->nonc1test0lngrep.positive = ae_true;
            monitor->nonc1test0lngrep.fidx = funcidx;
            monitor->nonc1test0lngrep.n = n;
            monitor->nonc1test0lngrep.cnt = sortedcnt;
            monitor->nonc1test0lngrep.stpidxa = stpidx+1;
            monitor->nonc1test0lngrep.stpidxb = stpidx+4;
            monitor->nonc1test0lngrep.inneriter = monitor->linesearchinneridx;
            monitor->nonc1test0lngrep.outeriter = monitor->linesearchouteridx;
            rvectorsetlengthatleast(&monitor->nonc1test0lngrep.x0, n, _state);
            rvectorsetlengthatleast(&monitor->nonc1test0lngrep.d, n, _state);
            for(i=0; i<=n-1; i++)
            {
                monitor->nonc1test0lngrep.x0.ptr.p_double[i] = monitor->enqueuedx.ptr.p_double[monitor->sortedidx.ptr.p_int[0]*n+i];
                monitor->nonc1test0lngrep.d.ptr.p_double[i] = monitor->dcur.ptr.p_double[i];
            }
            rvectorsetlengthatleast(&monitor->nonc1test0lngrep.stp, sortedcnt, _state);
            rvectorsetlengthatleast(&monitor->nonc1test0lngrep.f, sortedcnt, _state);
            for(i=0; i<=sortedcnt-1; i++)
            {
                monitor->nonc1test0lngrep.stp.ptr.p_double[i] = monitor->sortedstp.ptr.p_double[i];
                monitor->nonc1test0lngrep.f.ptr.p_double[i] = monitor->f.ptr.p_double[i];
            }
        }
    }
}


/*************************************************************************
This  subroutine checks C1 continuity using test #1  (individual  gradient
components from the line search log are studied for continuity).

An interval between F[StpIdx+0] and F[StpIdx+3]is  tested for  continuity.
An normalized error metric (Lipschitz constant growth for the  derivative)
for the interval in question is calculated. Values above  50  are  a  good
indication of the discontinuity.

  -- ALGLIB --
     Copyright 19.11.2018 by Bochkanov Sergey
*************************************************************************/
static void optserv_c1continuitytest1(smoothnessmonitor* monitor,
     ae_int_t funcidx,
     ae_int_t stpidx,
     ae_int_t sortedcnt,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t varidx;
    ae_int_t n;
    double f0;
    double f1;
    double f2;
    double f3;
    double noise0;
    double noise1;
    double noise2;
    double noise3;
    double nrm;
    double rating;
    double lengthrating;
    double lipschitz;


    n = monitor->n;
    ae_assert(stpidx+3<sortedcnt, "C1ContinuityTest1: integrity check failed", _state);
    ae_assert(ae_fp_eq(monitor->sortedstp.ptr.p_double[0],(double)(0)), "C1ContinuityTest1: integrity check failed", _state);
    ae_assert(ae_fp_greater(monitor->sortedstp.ptr.p_double[sortedcnt-1],(double)(0)), "C1ContinuityTest1: integrity check failed", _state);
    
    /*
     * Study each component of the gradient in the interval in question
     */
    for(varidx=0; varidx<=n-1; varidx++)
    {
        f0 = monitor->g.ptr.p_double[(stpidx+0)*n+varidx];
        f1 = monitor->g.ptr.p_double[(stpidx+1)*n+varidx];
        f2 = monitor->g.ptr.p_double[(stpidx+2)*n+varidx];
        f3 = monitor->g.ptr.p_double[(stpidx+3)*n+varidx];
        noise0 = optserv_ognoiselevelg*ae_maxreal(ae_fabs(f0, _state), 1.0, _state);
        noise1 = optserv_ognoiselevelg*ae_maxreal(ae_fabs(f1, _state), 1.0, _state);
        noise2 = optserv_ognoiselevelg*ae_maxreal(ae_fabs(f2, _state), 1.0, _state);
        noise3 = optserv_ognoiselevelg*ae_maxreal(ae_fabs(f3, _state), 1.0, _state);
        optserv_testc0continuity(f0, f1, f2, f3, noise0, noise1, noise2, noise3, monitor->sortedstp.ptr.p_double[stpidx+1]-monitor->sortedstp.ptr.p_double[stpidx+0], monitor->sortedstp.ptr.p_double[stpidx+2]-monitor->sortedstp.ptr.p_double[stpidx+1], monitor->sortedstp.ptr.p_double[stpidx+3]-monitor->sortedstp.ptr.p_double[stpidx+2], ae_false, &rating, &lipschitz, _state);
        
        /*
         * Store results
         */
        if( rating>optserv_ogminrating1 )
        {
            
            /*
             * Store to total report
             */
            monitor->rep.nonc1test1positive = ae_true;
            if( rating>monitor->nonc1currentrating )
            {
                monitor->nonc1currentrating = rating;
                monitor->rep.nonc1suspected = ae_true;
                monitor->rep.nonc1lipschitzc = lipschitz;
                monitor->rep.nonc1fidx = funcidx;
            }
            
            /*
             * Store to "strongest" report
             */
            if( rating>monitor->nonc1test1strrating )
            {
                monitor->nonc1test1strrating = rating;
                monitor->nonc1test1strrep.positive = ae_true;
                monitor->nonc1test1strrep.fidx = funcidx;
                monitor->nonc1test1strrep.vidx = varidx;
                monitor->nonc1test1strrep.n = n;
                monitor->nonc1test1strrep.cnt = sortedcnt;
                monitor->nonc1test1strrep.stpidxa = stpidx+0;
                monitor->nonc1test1strrep.stpidxb = stpidx+3;
                monitor->nonc1test1strrep.inneriter = monitor->linesearchinneridx;
                monitor->nonc1test1strrep.outeriter = monitor->linesearchouteridx;
                rvectorsetlengthatleast(&monitor->nonc1test1strrep.x0, n, _state);
                rvectorsetlengthatleast(&monitor->nonc1test1strrep.d, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    monitor->nonc1test1strrep.x0.ptr.p_double[i] = monitor->enqueuedx.ptr.p_double[monitor->sortedidx.ptr.p_int[0]*n+i];
                    monitor->nonc1test1strrep.d.ptr.p_double[i] = monitor->dcur.ptr.p_double[i];
                }
                rvectorsetlengthatleast(&monitor->nonc1test1strrep.stp, sortedcnt, _state);
                rvectorsetlengthatleast(&monitor->nonc1test1strrep.g, sortedcnt, _state);
                for(i=0; i<=sortedcnt-1; i++)
                {
                    monitor->nonc1test1strrep.stp.ptr.p_double[i] = monitor->sortedstp.ptr.p_double[i];
                    monitor->nonc1test1strrep.g.ptr.p_double[i] = monitor->g.ptr.p_double[i*n+varidx];
                }
            }
            
            /*
             * Store to "longest" report
             */
            nrm = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                nrm = nrm+ae_sqr(monitor->enqueuedx.ptr.p_double[monitor->sortedidx.ptr.p_int[0]*n+i]-monitor->enqueuedx.ptr.p_double[monitor->sortedidx.ptr.p_int[sortedcnt-1]*n+i], _state);
            }
            nrm = ae_sqrt(nrm, _state);
            nrm = coalesce(nrm, ae_machineepsilon, _state);
            lengthrating = nrm;
            if( lengthrating>monitor->nonc1test1lngrating )
            {
                monitor->nonc1test1lngrating = lengthrating;
                monitor->nonc1test1lngrep.positive = ae_true;
                monitor->nonc1test1lngrep.fidx = funcidx;
                monitor->nonc1test1lngrep.vidx = varidx;
                monitor->nonc1test1lngrep.n = n;
                monitor->nonc1test1lngrep.cnt = sortedcnt;
                monitor->nonc1test1lngrep.stpidxa = stpidx+0;
                monitor->nonc1test1lngrep.stpidxb = stpidx+3;
                monitor->nonc1test1lngrep.inneriter = monitor->linesearchinneridx;
                monitor->nonc1test1lngrep.outeriter = monitor->linesearchouteridx;
                rvectorsetlengthatleast(&monitor->nonc1test1lngrep.x0, n, _state);
                rvectorsetlengthatleast(&monitor->nonc1test1lngrep.d, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    monitor->nonc1test1lngrep.x0.ptr.p_double[i] = monitor->enqueuedx.ptr.p_double[monitor->sortedidx.ptr.p_int[0]*n+i];
                    monitor->nonc1test1lngrep.d.ptr.p_double[i] = monitor->dcur.ptr.p_double[i];
                }
                rvectorsetlengthatleast(&monitor->nonc1test1lngrep.stp, sortedcnt, _state);
                rvectorsetlengthatleast(&monitor->nonc1test1lngrep.g, sortedcnt, _state);
                for(i=0; i<=sortedcnt-1; i++)
                {
                    monitor->nonc1test1lngrep.stp.ptr.p_double[i] = monitor->sortedstp.ptr.p_double[i];
                    monitor->nonc1test1lngrep.g.ptr.p_double[i] = monitor->g.ptr.p_double[i*n+varidx];
                }
            }
        }
    }
}


/*************************************************************************
Removes oldest update pair from the limited memory Hessian model and
invalidates Hessian model

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
static void optserv_popfrontxy(xbfgshessian* hess, ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    ae_assert(hess->htype==3||hess->htype==4, "PopFrontXY: Hessian mode is not supported", _state);
    if( hess->memlen==0 )
    {
        return;
    }
    for(i=0; i<=hess->memlen-2; i++)
    {
        rcopyrr(hess->n, &hess->s, i+1, &hess->s, i, _state);
        rcopyrr(hess->n, &hess->y, i+1, &hess->y, i, _state);
    }
    if( hess->htype==3 )
    {
        for(i=0; i<=hess->memlen-2; i++)
        {
            for(j=0; j<=hess->memlen-2; j++)
            {
                hess->lowranksst.ptr.pp_double[i][j] = hess->lowranksst.ptr.pp_double[i+1][j+1];
                hess->lowranksyt.ptr.pp_double[i][j] = hess->lowranksyt.ptr.pp_double[i+1][j+1];
            }
        }
    }
    hess->memlen = hess->memlen-1;
    optserv_resetlowrankmodel(hess, _state);
}


/*************************************************************************
Lowe-level Hessian update function, to be used by HessianUpdate()

INPUT PARAMETERS:
    Hess            -   Hessian state
    H               -   specific Hessian matrix to update, usually one
                        of the Hess fields
    X0, G0          -   point #0 and gradient at #0, array[N]
    X1, G1          -   point #1 and gradient at #1, array[N]

OUTPUT PARAMETERS:
    Status          -   sets update status (informative)

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
static void optserv_hessianupdatelowlevel(xbfgshessian* hess,
     /* Real    */ ae_matrix* h,
     /* Real    */ const ae_vector* sk,
     /* Real    */ const ae_vector* yk,
     ae_int_t* status,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    double shs;
    double sy;
    double snrm2;
    double hsnrm2;
    double ynrm2;
    double ski;
    double yki;
    double mxs;
    double mxy;
    double mxhs;
    double mxd;
    double big;

    *status = 0;

    n = hess->n;
    *status = 0;
    big = (double)1/hess->reg;
    
    /*
     * Perform preliminary analysis
     */
    rvectorsetlengthatleast(&hess->hsk, n, _state);
    rmatrixgemv(n, n, 1.0, h, 0, 0, 0, sk, 0, 0.0, &hess->hsk, 0, _state);
    shs = (double)(0);
    sy = (double)(0);
    snrm2 = (double)(0);
    ynrm2 = (double)(0);
    mxs = (double)(0);
    mxy = (double)(0);
    mxhs = (double)(0);
    hsnrm2 = (double)(0);
    mxd = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        ski = sk->ptr.p_double[i];
        yki = yk->ptr.p_double[i];
        shs = shs+ski*hess->hsk.ptr.p_double[i];
        sy = sy+ski*yki;
        snrm2 = snrm2+ski*ski;
        ynrm2 = ynrm2+yki*yki;
        mxs = ae_maxreal(mxs, ae_fabs(ski, _state), _state);
        mxy = ae_maxreal(mxy, ae_fabs(yki, _state), _state);
        mxhs = ae_maxreal(mxhs, ae_fabs(hess->hsk.ptr.p_double[i], _state), _state);
        hsnrm2 = hsnrm2+ae_sqr(hess->hsk.ptr.p_double[i], _state);
        mxd = ae_maxreal(mxd, ae_fabs(h->ptr.pp_double[i][i], _state), _state);
    }
    
    /*
     * Completely skip updates with too short steps and degenerate updates
     *
     * NOTE: may prevent us from updating Hessian near the solution
     */
    if( ae_fp_less_eq(mxs,hess->stpshort) )
    {
        
        /*
         * Sk is too small
         */
        return;
    }
    if( ae_fp_eq(hsnrm2,(double)(0)) )
    {
        
        /*
         * H*Sk is exactly zero, exit
         */
        return;
    }
    if( ae_fp_less_eq(shs,(double)(0))||ae_fp_less_eq(shs,mxs*mxd*mxs*hess->microreg) )
    {
        
        /*
         * Sk'*H*Sk is too small.
         *
         * Apply regularization to Hessian before exiting
         */
        ae_assert(ae_fp_greater(hsnrm2,(double)(0)), "UpdateHessian: integrity check failed", _state);
        rmatrixger(n, n, h, 0, 0, hess->reg/hsnrm2, &hess->hsk, 0, &hess->hsk, 0, _state);
        return;
    }
    
    /*
     * First, we discard Hessian components which give non-zero product with Sk.
     *
     * We apply some damping in order to avoid problems arising with very small
     * Sk'*H*Sk, and we apply some regularization term in order to have Sk'*Hnew*Sk
     * still slightly larger than zero.
     *
     * Traditional BFGS update adds -(Hk*Sk)*(Hk*Sk)'/(Sk'*H*Sk). We use
     * modified, more robust formula (below Z=(H*Sk)/|H*Sk|)
     *
     *     (                         (H*Sk,H*Sk)                               )
     *     ( - --------------------------------------------------------- + Reg ) * Z * Z'
     *     (    Sk'*H*Sk + Reg*(H*Sk,H*Sk) + MicroReg*(max(H)*max(S))^2        )
     */
    ae_assert(ae_fp_greater(hsnrm2,(double)(0)), "UpdateHessian: integrity check failed", _state);
    rmatrixger(n, n, h, 0, 0, -(double)1/(shs+hsnrm2*hess->reg+ae_sqr(mxd*mxs, _state)*hess->microreg)+hess->reg/hsnrm2, &hess->hsk, 0, &hess->hsk, 0, _state);
    *status = 1;
    
    /*
     * Before we update Hessian with Yk, decide whether we need this update - or
     * maybe it is better to leave Hessian as is, with small curvature along Sk
     * (in the latter case we still treat BFGS update as successful).
     *
     * Traditional BFGS update adds Yk*Yk'/(Sk,Yk) to the Hessian. Instead we
     * use modified update (below U=Yk/|Yk|)
     *
     *               (Yk,Yk)
     *     -------------------------------------- * U * U'
     *      (Sk,Yk)+Reg*(Yk,Yk)+MicroReg*(Sk,Sk)
     */
    if( ae_fp_eq(ynrm2,(double)(0)) )
    {
        return;
    }
    if( ae_fp_less_eq(sy,(double)(0)) )
    {
        return;
    }
    if( ae_fp_greater_eq(ae_sqr(mxy, _state)/sy,big) )
    {
        return;
    }
    ae_assert(ae_fp_greater(sy,(double)(0)), "UpdateHessian: integrity check failed", _state);
    rmatrixger(n, n, h, 0, 0, (double)1/(sy+hess->reg*ynrm2+hess->microreg*snrm2), yk, 0, yk, 0, _state);
    *status = 2;
}


/*************************************************************************
Low-level Hessian update function, to be used by HessianUpdate().

Performs no integrity checks.

INPUT PARAMETERS:
    Hess            -   Hessian state
    H               -   specific Hessian matrix to update, usually one
                        of the Hess fields
    X0, G0          -   point #0 and gradient at #0, array[N]
    X1, G1          -   point #1 and gradient at #1, array[N]

OUTPUT PARAMETERS:
    Status          -   sets update status (informative)

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
static void optserv_hessianupdatelowlevel2(xbfgshessian* hess,
     /* Real    */ ae_matrix* h,
     /* Real    */ const ae_vector* sk,
     /* Real    */ const ae_vector* yk,
     ae_int_t* status,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    double shs;
    double sy;
    double snrm2;
    double hsnrm2;
    double ynrm2;
    double ski;
    double yki;
    double mxs;
    double mxy;
    double mxhs;
    double mxd;

    *status = 0;

    n = hess->n;
    *status = 0;
    
    /*
     * Perform preliminary analysis
     */
    rvectorsetlengthatleast(&hess->hsk, n, _state);
    rmatrixgemv(n, n, 1.0, h, 0, 0, 0, sk, 0, 0.0, &hess->hsk, 0, _state);
    shs = (double)(0);
    sy = (double)(0);
    snrm2 = (double)(0);
    ynrm2 = (double)(0);
    mxs = (double)(0);
    mxy = (double)(0);
    mxhs = (double)(0);
    hsnrm2 = (double)(0);
    mxd = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        ski = sk->ptr.p_double[i];
        yki = yk->ptr.p_double[i];
        shs = shs+ski*hess->hsk.ptr.p_double[i];
        sy = sy+ski*yki;
        snrm2 = snrm2+ski*ski;
        ynrm2 = ynrm2+yki*yki;
        mxs = ae_maxreal(mxs, ae_fabs(ski, _state), _state);
        mxy = ae_maxreal(mxy, ae_fabs(yki, _state), _state);
        mxhs = ae_maxreal(mxhs, ae_fabs(hess->hsk.ptr.p_double[i], _state), _state);
        hsnrm2 = hsnrm2+ae_sqr(hess->hsk.ptr.p_double[i], _state);
        mxd = ae_maxreal(mxd, ae_fabs(h->ptr.pp_double[i][i], _state), _state);
    }
    
    /*
     * First, we discard Hessian components which give non-zero product with Sk.
     */
    ae_assert(ae_fp_greater(hsnrm2,(double)(0)), "UpdateHessian: integrity check failed", _state);
    rmatrixger(n, n, h, 0, 0, -(double)1/shs, &hess->hsk, 0, &hess->hsk, 0, _state);
    *status = 1;
    ae_assert(ae_fp_greater(sy,(double)(0)), "UpdateHessian: integrity check failed", _state);
    rmatrixger(n, n, h, 0, 0, (double)1/sy, yk, 0, yk, 0, _state);
    *status = 2;
}


/*************************************************************************
Invalidate low-rank model

INPUT PARAMETERS:
    Hess            -   Hessian state

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
static void optserv_resetlowrankmodel(xbfgshessian* hess,
     ae_state *_state)
{


    ae_assert(hess->htype==3||hess->htype==4, "OPTSERV: integrity check 9940 failed", _state);
    if( hess->htype==3 )
    {
        hess->lowrankmodelvalid = ae_false;
        hess->lowrankeffdvalid = ae_false;
    }
    if( hess->htype==4 )
    {
        hess->sr1modelvalid = ae_false;
        hess->sr1effdvalid = ae_false;
    }
}


/*************************************************************************
Recomputes low-rank LBFGS or SR1 model

INPUT PARAMETERS:
    Hess            -   Hessian state

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
static void optserv_recomputelowrankmodel(xbfgshessian* hess,
     ae_state *_state)
{


    ae_assert(hess->htype==3||hess->htype==4, "RecomputeLowRankModel: Hessian mode is not supported", _state);
    if( hess->htype==3 )
    {
        optserv_recomputelowrankmodellbfgs(hess, _state);
    }
    if( hess->htype==4 )
    {
        optserv_recomputelowrankmodelsr1(hess, _state);
    }
}


/*************************************************************************
Recomputes low-rank model (DIAG and CORR fields) according to the  current
state of the LBFGS memory

INPUT PARAMETERS:
    Hess            -   Hessian state

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
static void optserv_recomputelowrankmodellbfgs(xbfgshessian* hess,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t memlen;
    ae_int_t i;
    ae_int_t j;
    double reg;
    double mx;


    ae_assert(hess->htype==3, "RecomputeLowRankModelLBFGS: Hessian mode is not supported", _state);
    n = hess->n;
    memlen = hess->memlen;
    
    /*
     * If the model is valid, exit.
     * Otherwise, recompute it from scratch
     */
    if( hess->lowrankmodelvalid )
    {
        return;
    }
    optserv_resetlowrankmodel(hess, _state);
    
    /*
     * Quick exit for MemLen=0.
     * After this block we assume that MemLen>0
     */
    if( memlen==0 )
    {
        hess->lowrankmodelvalid = ae_true;
        hess->lowrankk = 0;
        return;
    }
    
    /*
     * Prepare RAW_CORR2, a right part of correction matrix
     *
     *     Bk = sigma*I - RAW_CORR2'*RAW_BLOCK*RAW_CORR2
     *
     * with RAW_CORR2 being 2MEMLEN*N matrix and RAW_BLOCK being 2MEMLEN*2MEMLEN matrix,
     * as defined by equations 2.17 and 3.22 in 'REPRESENTATION OF QUASI-NEWTON MATRICES
     * AND THEIR USE IN LIMITED MEMORY METHODS' by Byrd, Nocedal and Schnabel.
     *
     * The initial form for Bk is
     *
     *                    [          ]   [ -Dk     Lk' ]-1 [  Y   ]
     *     Bk = sigma*I - [ Y' B0*S' ] * [             ] * [      ]
     *                    [          ]   [ Lk  S*B0*S' ]   [ S*B0 ]
     *
     * with
     *
     *     Lk[i,j]=(Si,Yj) for i>j, 0 otherwise
     *     Dk = diag[(Si,Yj)]
     *
     */
    rallocm(2*memlen, n, &hess->corr2, _state);
    for(i=0; i<=memlen-1; i++)
    {
        rcopyrr(n, &hess->s, i, &hess->corr2, memlen+i, _state);
        rmulr(n, hess->sigma, &hess->corr2, memlen+i, _state);
        rcopyrr(n, &hess->y, i, &hess->corr2, i, _state);
    }
    
    /*
     * Start factorizing central block. Whilst it is indefinite, it has
     * pretty simple factorization
     *
     *     [ -Dk     Lk' ]   [ Dk^(0.5)          ]   [ -I      ]   [ Dk^(0.5)  -Dk^(-0.5)*Lk' ]
     *     [             ] = [                   ] * [         ] * [                          ]
     *     [ Lk  S*B0*S' ]   [ -Lk*Dk^(-0.5)  Jk ]   [      +I ]   [                      Jk' ]
     *
     * First, we compute lower triangular Jk such that Jk*Jk' = S*B0*S' + Lk*inv(Dk)*Lk'
     */
    rallocv(memlen, &hess->buf, _state);
    for(i=0; i<=memlen-1; i++)
    {
        hess->buf.ptr.p_double[i] = (double)1/ae_sqrt(hess->lowranksyt.ptr.pp_double[i][i], _state);
    }
    rsetallocm(memlen, memlen, 0.0, &hess->invsqrtdlk, _state);
    for(i=1; i<=memlen-1; i++)
    {
        rcopyrr(i, &hess->lowranksyt, i, &hess->invsqrtdlk, i, _state);
        rmergemulvr(i, &hess->buf, &hess->invsqrtdlk, i, _state);
    }
    reg = (double)(0);
    mx = (double)(0);
    for(;;)
    {
        
        /*
         * Try Cholesky decomposition
         */
        rcopyallocm(memlen, memlen, &hess->lowranksst, &hess->jk, _state);
        rmatrixgemm(memlen, memlen, memlen, 1.0, &hess->invsqrtdlk, 0, 0, 0, &hess->invsqrtdlk, 0, 0, 1, hess->sigma, &hess->jk, 0, 0, _state);
        for(i=0; i<=memlen-1; i++)
        {
            mx = ae_maxreal(mx, ae_fabs(hess->jk.ptr.pp_double[i][i], _state), _state);
            hess->jk.ptr.pp_double[i][i] = hess->jk.ptr.pp_double[i][i]+reg;
        }
        if( spdmatrixcholeskyrec(&hess->jk, 0, memlen, ae_false, &hess->buf, _state) )
        {
            break;
        }
        
        /*
         * Cholesky decomposition failed, increase regularization
         */
        mx = coalesce(mx, ae_machineepsilon, _state);
        reg = coalesce((double)10*reg, ae_sqrt(ae_machineepsilon, _state)*mx, _state);
    }
    
    /*
     * After computing Jk we proceed to form triangular factorization of the entire block matrix
     */
    rsetallocm(2*memlen, 2*memlen, 0.0, &hess->blk, _state);
    for(i=0; i<=memlen-1; i++)
    {
        hess->blk.ptr.pp_double[i][i] = ae_sqrt(hess->lowranksyt.ptr.pp_double[i][i], _state);
    }
    for(i=0; i<=memlen-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            hess->blk.ptr.pp_double[memlen+i][j] = -hess->invsqrtdlk.ptr.pp_double[i][j];
        }
    }
    for(i=0; i<=memlen-1; i++)
    {
        for(j=0; j<=i; j++)
        {
            hess->blk.ptr.pp_double[memlen+i][memlen+j] = hess->jk.ptr.pp_double[i][j];
        }
    }
    
    /*
     * And finally we merge triangular factor into CORR2 in order to get desired two-factor
     * low rank representation without additional middle matrix. This representation is
     * computed in CORR2 and unpacked into LowRankCp and LowRankCm ('minus' and 'plus' terms).
     */
    rmatrixlefttrsm(2*memlen, n, &hess->blk, 0, 0, ae_false, ae_false, 0, &hess->corr2, 0, 0, _state);
    rallocm(memlen, n, &hess->lowrankcp, _state);
    for(i=0; i<=memlen-1; i++)
    {
        rcopyrr(n, &hess->corr2, i, &hess->lowrankcp, i, _state);
    }
    rallocm(memlen, n, &hess->lowrankcm, _state);
    for(i=0; i<=memlen-1; i++)
    {
        rcopyrr(n, &hess->corr2, memlen+i, &hess->lowrankcm, i, _state);
    }
    
    /*
     * The model was created
     */
    hess->lowrankmodelvalid = ae_true;
    hess->lowrankk = memlen;
}


/*************************************************************************
Recomputes low-rank SR1 model

INPUT PARAMETERS:
    Hess            -   Hessian state

  -- ALGLIB --
     Copyright 09.02.2024 by Bochkanov Sergey
*************************************************************************/
static void optserv_recomputelowrankmodelsr1(xbfgshessian* hess,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t memlen;
    ae_int_t i;
    ae_int_t j;
    double v;
    double ev;
    double eps;
    double scaledsigma;
    double stabsigma;
    double minimalcurvature;
    double absoluteminimum;


    ae_assert(hess->htype==4, "RecomputeLowRankModelSR1: Hessian mode is not supported", _state);
    n = hess->n;
    eps = ae_sqrt(ae_machineepsilon, _state);
    
    /*
     * If the model is valid, exit.
     * Otherwise, recompute it from scratch
     */
    if( hess->sr1modelvalid )
    {
        return;
    }
    optserv_resetlowrankmodel(hess, _state);
    
    /*
     * Rescale S and Y to internal scale, as set in Hess.varScale.
     *
     * Prepare a low-rank representation of SR1 update with additional orthogonality
     * properties. The original form of low-rank SR1 update is
     *
     *                    [              ]   [                          ]-1 [             ]
     *     Bk = sigma*I + [ Y'- Sigma*S' ] * [ Dk+Lk+Lk' - Sigma*Sk*Sk' ] * [ Y - Sigma*S ]
     *                    [              ]   [                          ]   [             ]
     *
     * with
     *
     *     Lk[i,j]=(Si,Yj) for i>j, 0 otherwise
     *     Dk = diag[(Si,Yj)]
     *
     * as defined by equations 2.17 and 5.2 in 'REPRESENTATION OF QUASI-NEWTON MATRICES
     * AND THEIR USE IN LIMITED MEMORY METHODS' by Byrd, Nocedal and Schnabel.
     *
     * We plan to transform it into
     *
     *     Bk = sigma*I + C'*Z*C
     *
     * with Z being diagonal with +1 or -1 elements, and C being orthogonal (but not necessarily
     * orthonormal) matrices, with some rows maybe being zero.
     *
     *
     * First, we perform initial finiteness assurance (dropping degenerate SR1 updates). This
     * function, as defined in 'A study of the limited memory SR1 method in practice' by
     * Xuehua Lu, drops degenerate updates and, additionally, returns a decomposition
     *
     *     inv(M) = Lm'*Dm*Lm, with M=Dk+Lk+Lk'-Sigma*Sk*Sk'
     *
     * An important point is that M=Dk+Lk+Lk'-Sigma*Sk*Sk' is guaranteed to be non-degenerate
     * after the finiteness assurance; however, corresponding Y-sigma*S still can have zero rows,
     * i.e. be rank-deficient. Thus, operations involving potentially rank-deficient Y-sigma*S
     * have to be performed very carefully.
     */
    optserv_scaleandperformfinitenessassurance(hess, &hess->tmps, &hess->tmpy, &scaledsigma, &hess->tmpl, &hess->tmpe, &memlen, _state);
    absoluteminimum = (double)100*ae_machineepsilon;
    minimalcurvature = ae_maxreal(ae_minreal(scaledsigma, hess->mincrv, _state), absoluteminimum, _state);
    stabsigma = ae_maxreal(scaledsigma, minimalcurvature, _state);
    if( memlen==0 )
    {
        hess->sr1modelvalid = ae_true;
        hess->sr1k = 0;
        hess->sr1nrm2 = ae_fabs(scaledsigma, _state);
        hess->sr1nrm2stab = stabsigma;
        rsetallocv(n, scaledsigma, &hess->sr1d, _state);
        rsetallocv(n, stabsigma, &hess->sr1stabd, _state);
        rmergemulv(n, &hess->invscale, &hess->sr1d, _state);
        rmergemulv(n, &hess->invscale, &hess->sr1d, _state);
        rmergemulv(n, &hess->invscale, &hess->sr1stabd, _state);
        rmergemulv(n, &hess->invscale, &hess->sr1stabd, _state);
        return;
    }
    ae_assert(memlen>0&&memlen<=n, "OPTSERV: integrity check 9823 failed", _state);
    
    /*
     * Decompose Y-sigma*S into L*Q with orthogonal Q and lower triangular L and store
     * Q to Hess.tmpU (here L is not to be confused with Lk and/or Lm). The factor L
     * can have exactly zero rows and/or be rank-deficient.
     */
    for(i=0; i<=memlen-1; i++)
    {
        rcopyrr(n, &hess->tmpy, i, &hess->corr2, i, _state);
        raddrr(n, -scaledsigma, &hess->tmps, i, &hess->corr2, i, _state);
    }
    rmatrixlq(&hess->corr2, memlen, n, &hess->tmptau, _state);
    rmatrixlqunpackq(&hess->corr2, memlen, n, &hess->tmptau, memlen, &hess->tmpu, _state);
    
    /*
     * Now we have
     *
     *     Bk = sigma*I + Q'*L'*Lm'*Dm*Lm*L*Q
     *
     * which can be transformed into
     *
     *     Bk = sigma*I + Q'*(Lm'*L'*Dm*Lm*L)*Q
     *        = sigma*I + Q'*BLK*Q
     */
    for(i=0; i<=memlen-1; i++)
    {
        for(j=i+1; j<=memlen-1; j++)
        {
            hess->corr2.ptr.pp_double[i][j] = 0.0;
            hess->tmpl.ptr.pp_double[i][j] = 0.0;
        }
    }
    rallocm(memlen, memlen, &hess->blk2, _state);
    rmatrixgemm(memlen, memlen, memlen, 1.0, &hess->tmpl, 0, 0, 0, &hess->corr2, 0, 0, 0, 0.0, &hess->blk2, 0, 0, _state);
    rcopyallocm(memlen, memlen, &hess->blk2, &hess->blk3, _state);
    for(i=0; i<=memlen-1; i++)
    {
        rmulr(memlen, hess->tmpe.ptr.p_double[i], &hess->blk2, i, _state);
    }
    rallocm(memlen, memlen, &hess->blk, _state);
    rmatrixgemm(memlen, memlen, memlen, 1.0, &hess->blk3, 0, 0, 1, &hess->blk2, 0, 0, 0, 0.0, &hess->blk, 0, 0, _state);
    
    /*
     * Perform eigendecomposition BLK=W*D*W', get
     *
     *     Bk = sigma*I + Q'*BLK*Q
     *        = sigma*I + Q'*W*D*W'*Q
     *        = sigma*I + (W'Q)'*D*(W'Q)
     *
     * then transform diagonal D (not to be confused with Dk) into one
     * with +1 or -1 on its diagonal.
     *
     * Produce both raw and positive definite stabilized updates.
     * Compute SR1Nrm2.
     */
    hess->sr1nrm2 = ae_fabs(scaledsigma, _state);
    hess->sr1nrm2stab = stabsigma;
    if( !smatrixevd(&hess->blk, memlen, 1, ae_false, &hess->tmpe, &hess->tmpq, _state) )
    {
        ae_assert(ae_false, "HessianGetLowRankStabilizedSR1: eigensolver failure", _state);
    }
    rallocm(memlen, n, &hess->sr1c, _state);
    rmatrixgemm(memlen, n, memlen, 1.0, &hess->tmpq, 0, 0, 1, &hess->tmpu, 0, 0, 0, 0.0, &hess->sr1c, 0, 0, _state);
    rallocv(memlen, &hess->sr1z, _state);
    rcopyallocm(memlen, n, &hess->sr1c, &hess->sr1stabc, _state);
    rallocv(memlen, &hess->sr1stabz, _state);
    rsetallocv(n, scaledsigma, &hess->sr1d, _state);
    rsetallocv(n, stabsigma, &hess->sr1stabd, _state);
    for(i=0; i<=memlen-1; i++)
    {
        
        /*
         * Update SR1Nrm2
         */
        hess->sr1nrm2 = ae_maxreal(hess->sr1nrm2, ae_fabs(scaledsigma+hess->tmpe.ptr.p_double[i], _state), _state);
        hess->sr1nrm2stab = ae_maxreal(hess->sr1nrm2stab, stabsigma+hess->tmpe.ptr.p_double[i], _state);
        
        /*
         * Produce raw update
         */
        ev = hess->tmpe.ptr.p_double[i];
        v = ae_fabs(ev, _state);
        v = ae_minreal(v, (ae_fabs(scaledsigma, _state)+(double)1)/eps, _state);
        rmulr(n, ae_sqrt(v, _state), &hess->sr1c, i, _state);
        hess->sr1z.ptr.p_double[i] = possign(ev, _state);
        
        /*
         * Produce stabilized update
         */
        ev = hess->tmpe.ptr.p_double[i]-(stabsigma-scaledsigma);
        v = ae_fabs(ev, _state);
        v = ae_minreal(v, (ae_fabs(stabsigma, _state)+(double)1)/eps, _state);
        if( ae_fp_less(ev,(double)(0)) )
        {
            v = ae_minreal(v, stabsigma-minimalcurvature, _state);
        }
        rmulr(n, ae_sqrt(v, _state), &hess->sr1stabc, i, _state);
        hess->sr1stabz.ptr.p_double[i] = possign(ev, _state);
    }
    hess->sr1k = memlen;
    
    /*
     * Perform unscaling
     */
    rmergemulv(n, &hess->invscale, &hess->sr1d, _state);
    rmergemulv(n, &hess->invscale, &hess->sr1d, _state);
    rmergemulv(n, &hess->invscale, &hess->sr1stabd, _state);
    rmergemulv(n, &hess->invscale, &hess->sr1stabd, _state);
    for(i=0; i<=hess->sr1k-1; i++)
    {
        rmergemulvr(n, &hess->invscale, &hess->sr1c, i, _state);
        rmergemulvr(n, &hess->invscale, &hess->sr1stabc, i, _state);
    }
    
    /*
     * Done
     */
    hess->sr1modelvalid = ae_true;
}


/*************************************************************************
Filters update pairs Hess.S, Hess.Y for the SR1 model, removing ill-defined
updates. May reorder pairs during the pocess, or drop some or all of them.

  -- ALGLIB --
     Copyright 09.02.2024 by Bochkanov Sergey
*************************************************************************/
static void optserv_scaleandperformfinitenessassurance(xbfgshessian* hess,
     /* Real    */ ae_matrix* s,
     /* Real    */ ae_matrix* y,
     double* sigma,
     /* Real    */ ae_matrix* l,
     /* Real    */ ae_vector* d,
     ae_int_t* filteredlen,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t i0;
    ae_int_t i1;
    ae_int_t memlen;
    ae_int_t n;
    ae_int_t wrksize;
    double v;
    double v0;
    double v1;
    double costheta;
    double lintol;


    ae_assert(hess->htype==4, "FinitenessAssurance: Hessian mode is not supported", _state);
    memlen = hess->memlen;
    n = hess->n;
    lintol = (double)100*ae_sqrt(ae_machineepsilon, _state);
    if( memlen==0 )
    {
        *sigma = (double)(1);
        *filteredlen = 0;
        return;
    }
    
    /*
     * Copy and scale S and Y, compute initial scaling factor Sigma (after the variable scaling)
     */
    rcopyallocm(hess->m, hess->n, &hess->s, s, _state);
    rcopyallocm(hess->m, hess->n, &hess->y, y, _state);
    for(i=0; i<=memlen-1; i++)
    {
        rmergemulvr(n, &hess->invscale, s, i, _state);
        rmergemulvr(n, &hess->varscale, y, i, _state);
    }
    *sigma = rdotrr(n, s, memlen-1, y, memlen-1, _state)/(rdotrr(n, s, memlen-1, s, memlen-1, _state)+ae_machineepsilon*ae_machineepsilon);
    if( memlen==1 )
    {
        
        /*
         * There is no need to build an SR1 model for MemLen=1 update because it produces nearly-zero (and
         * sometimes degenerate) correction to the original diagonal matrix.
         */
        *filteredlen = 0;
        return;
    }
    
    /*
     * Enforce sufficient linear independence by zeroing out redundant rows. Rows which are exactly zero
     * will be dropped during the finiteness assurance iteration. The last row (the most recent update)
     * is always retained, that's why we start orthogonalization from the end.
     */
    rcopym(memlen, n, &hess->s, &hess->corr2, _state);
    for(i=0; i<=memlen-1; i++)
    {
        rmulr(n, (double)1/coalesce(ae_sqrt(rdotrr(n, &hess->corr2, i, &hess->corr2, i, _state), _state), (double)(1), _state), &hess->corr2, i, _state);
    }
    for(i=memlen-1; i>=0; i--)
    {
        v0 = rdotrr(n, &hess->corr2, i, &hess->corr2, i, _state);
        if( ae_fp_less(v0,lintol*lintol) )
        {
            rsetr(n, 0.0, s, i, _state);
            rsetr(n, 0.0, y, i, _state);
            rsetr(n, 0.0, &hess->corr2, i, _state);
            continue;
        }
        for(j=i-1; j>=0; j--)
        {
            raddrr(n, -rdotrr(n, &hess->corr2, i, &hess->corr2, j, _state)/v0, &hess->corr2, i, &hess->corr2, j, _state);
        }
    }
    
    /*
     * Prepare finiteness assurance iteration
     */
    rsetallocm(memlen, memlen, 0.0, l, _state);
    rallocv(memlen, d, _state);
    for(i=0; i<=memlen-1; i++)
    {
        rcopyrr(n, y, i, &hess->corr2, i, _state);
        raddrr(n, -*sigma, s, i, &hess->corr2, i, _state);
    }
    rallocm(memlen, memlen, &hess->blk, _state);
    rmatrixgemm(memlen, memlen, n, 1.0, s, 0, 0, 0, y, 0, 0, 1, 0.0, &hess->blk, 0, 0, _state);
    for(i=0; i<=memlen-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            hess->blk.ptr.pp_double[j][i] = hess->blk.ptr.pp_double[i][j];
        }
    }
    rmatrixgemm(memlen, memlen, n, -*sigma, s, 0, 0, 0, s, 0, 0, 1, 1.0, &hess->blk, 0, 0, _state);
    
    /*
     * Iterate until all updates are processed: approved or dropped
     */
    rallocv(memlen, &hess->buf, _state);
    rallocv(memlen, &hess->buf2, _state);
    *filteredlen = 0;
    j = 0;
    wrksize = hess->memlen;
    while(j<wrksize)
    {
        
        /*
         * Compute provisional J-th row of Mk (as defined in the formula 3.7 of 'A study of the
         * limited memory SR1 method in practice' by Xuehua Lu.
         */
        if( j>0 )
        {
            rcopyrv(j, &hess->blk, j, &hess->buf, _state);
            rgemv(j, j, 1.0, l, 0, &hess->buf, 0.0, &hess->buf2, _state);
            rmergedivv(j, d, &hess->buf2, _state);
            rgemv(j, j, -1.0, l, 1, &hess->buf2, 0.0, &hess->buf, _state);
            rcopyvr(j, &hess->buf, l, j, _state);
        }
        l->ptr.pp_double[j][j] = 1.0;
        d->ptr.p_double[j] = rdotrr(j+1, l, j, &hess->blk, j, _state);
        
        /*
         * Compute cos(theta) as defined by the formula 3.13 of 'A study of the
         * limited memory SR1 method in practice' by Xuehua Lu:
         * * first, compute v0=norm2(L[0:MemLen-1,J]*CORR2[J,0:N-1]) and v1=norm2(S[J,0:N-1])
         * * then, compute cos(theta)=D[J]/(v0*v1)
         */
        v0 = 0.0;
        for(i=0; i<=n-1; i++)
        {
            v = 0.0;
            for(k=0; k<=j; k++)
            {
                v = v+l->ptr.pp_double[j][k]*hess->corr2.ptr.pp_double[k][i];
            }
            v0 = v0+v*v;
        }
        v0 = ae_sqrt(v0, _state);
        v1 = ae_sqrt(rdotrr(n, s, j, s, j, _state), _state);
        if( ae_fp_neq(d->ptr.p_double[j],(double)(0))&&ae_fp_neq(v0*v1,(double)(0)) )
        {
            costheta = d->ptr.p_double[j]/(v0*v1);
        }
        else
        {
            costheta = 0.0;
        }
        
        /*
         * If CosTheta is too low, swap the last row with the J-th one and update S, Y, BLK, CORR2.
         * Otherwise, accept the row.
         */
        if( ae_fp_less_eq(ae_fabs(costheta, _state),hess->mincostheta) )
        {
            if( j!=wrksize-1 )
            {
                for(i0=j; i0<=wrksize-2; i0++)
                {
                    rcopyrr(n, s, i0+1, s, i0, _state);
                    rcopyrr(n, y, i0+1, y, i0, _state);
                    rcopyrr(n, &hess->corr2, i0+1, &hess->corr2, i0, _state);
                }
                for(i0=0; i0<=wrksize-2; i0++)
                {
                    for(i1=0; i1<=wrksize-2; i1++)
                    {
                        hess->blk.ptr.pp_double[i0][i1] = hess->blk.ptr.pp_double[icase2(i0<j, i0, i0+1, _state)][icase2(i1<j, i1, i1+1, _state)];
                    }
                }
            }
            wrksize = wrksize-1;
        }
        else
        {
            j = j+1;
            *filteredlen = *filteredlen+1;
        }
    }
    for(j=0; j<=*filteredlen-1; j++)
    {
        d->ptr.p_double[j] = (double)1/d->ptr.p_double[j];
    }
}


/*************************************************************************
Recomputes diagonal of the low-rank model, either LBFGS or SR1 one

INPUT PARAMETERS:
    Hess            -   Hessian state

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
static void optserv_recomputelowrankdiagonal(xbfgshessian* hess,
     ae_state *_state)
{


    ae_assert(hess->htype==3||hess->htype==4, "RecomputeLowRankDiagonal: Hessian mode is not supported", _state);
    if( hess->htype==3 )
    {
        optserv_recomputelowrankdiagonalbfgs(hess, _state);
    }
    if( hess->htype==4 )
    {
        optserv_recomputelowrankdiagonalsr1(hess, _state);
    }
}


/*************************************************************************
Recomputes diagonal of the low-rank model (EFFD) according to the  current
state of the LBFGS memory

INPUT PARAMETERS:
    Hess            -   Hessian state

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
static void optserv_recomputelowrankdiagonalbfgs(xbfgshessian* hess,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t memlen;
    ae_int_t i;


    ae_assert(hess->htype==3, "RecomputeLowRankDiagonalBFGS: Hessian mode is not supported", _state);
    n = hess->n;
    memlen = hess->memlen;
    
    /*
     * If the diagonal is valid, exit.
     * Otherwise, recompute it from scratch
     */
    if( hess->lowrankeffdvalid )
    {
        return;
    }
    optserv_recomputelowrankmodel(hess, _state);
    
    /*
     * Quick exit for MemLen=0
     */
    if( memlen==0 )
    {
        hess->lowrankeffdvalid = ae_true;
        rsetallocv(n, hess->sigma, &hess->lowrankeffd, _state);
        return;
    }
    
    /*
     * Assuming MemLen>0
     */
    rsetallocv(n, hess->sigma, &hess->lowrankeffd, _state);
    rallocv(n, &hess->buf, _state);
    for(i=0; i<=hess->lowrankk-1; i++)
    {
        rcopyrv(n, &hess->lowrankcp, i, &hess->buf, _state);
        rmuladdv(n, &hess->buf, &hess->buf, &hess->lowrankeffd, _state);
        rcopyrv(n, &hess->lowrankcm, i, &hess->buf, _state);
        rnegmuladdv(n, &hess->buf, &hess->buf, &hess->lowrankeffd, _state);
    }
    hess->lowrankeffdvalid = ae_true;
}


/*************************************************************************
Recomputes diagonal of the low-rank SR1 model

INPUT PARAMETERS:
    Hess            -   Hessian state

  -- ALGLIB --
     Copyright 28.11.2022 by Bochkanov Sergey
*************************************************************************/
static void optserv_recomputelowrankdiagonalsr1(xbfgshessian* hess,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;


    ae_assert(hess->htype==4, "RecomputeLowRankDiagonalSR1: Hessian mode is not supported", _state);
    n = hess->n;
    if( hess->sr1effdvalid )
    {
        return;
    }
    optserv_recomputelowrankmodelsr1(hess, _state);
    if( hess->sr1k==0 )
    {
        hess->sr1effdvalid = ae_true;
        rcopyallocv(n, &hess->sr1d, &hess->sr1effd, _state);
        return;
    }
    
    /*
     * Assuming SR1K>0
     */
    rcopyallocv(n, &hess->sr1d, &hess->sr1effd, _state);
    rallocv(n, &hess->buf, _state);
    for(i=0; i<=hess->sr1k-1; i++)
    {
        ae_assert(ae_fp_eq(hess->sr1z.ptr.p_double[i]*hess->sr1z.ptr.p_double[i],(double)(1)), "OPTSERV: ingegrity check 3416 failed", _state);
        rcopyrv(n, &hess->sr1c, i, &hess->buf, _state);
        if( ae_fp_greater(hess->sr1z.ptr.p_double[i],(double)(0)) )
        {
            rmuladdv(n, &hess->buf, &hess->buf, &hess->sr1effd, _state);
        }
        else
        {
            rnegmuladdv(n, &hess->buf, &hess->buf, &hess->sr1effd, _state);
        }
    }
    hess->sr1effdvalid = ae_true;
}


/*************************************************************************
Fast kernel for unscalexbatchfinitebnd()
*************************************************************************/
static ae_bool optserv_unscalexbatchfinitebndkernel(/* Real    */ const ae_vector* xs,
     ae_int_t batchsize,
     /* Real    */ const ae_vector* s,
     ae_int_t n,
     /* Real    */ const ae_vector* sclfinitebndl,
     /* Real    */ const ae_vector* sclfinitebndu,
     /* Real    */ const ae_vector* rawfinitebndl,
     /* Real    */ const ae_vector* rawfinitebndu,
     /* Real    */ ae_vector* xu,
     ae_state *_state)
{
    ae_bool result;


    result = ae_false;
    return result;
}


void _precbuflbfgs_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    precbuflbfgs *p = (precbuflbfgs*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->norms, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->alpha, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rho, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->yk, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->idx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->bufa, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bufb, 0, DT_INT, _state, make_automatic);
}


void _precbuflbfgs_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    precbuflbfgs       *dst = (precbuflbfgs*)_dst;
    const precbuflbfgs *src = (const precbuflbfgs*)_src;
    ae_vector_init_copy(&dst->norms, &src->norms, _state, make_automatic);
    ae_vector_init_copy(&dst->alpha, &src->alpha, _state, make_automatic);
    ae_vector_init_copy(&dst->rho, &src->rho, _state, make_automatic);
    ae_matrix_init_copy(&dst->yk, &src->yk, _state, make_automatic);
    ae_vector_init_copy(&dst->idx, &src->idx, _state, make_automatic);
    ae_vector_init_copy(&dst->bufa, &src->bufa, _state, make_automatic);
    ae_vector_init_copy(&dst->bufb, &src->bufb, _state, make_automatic);
}


void _precbuflbfgs_clear(void* _p)
{
    precbuflbfgs *p = (precbuflbfgs*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->norms);
    ae_vector_clear(&p->alpha);
    ae_vector_clear(&p->rho);
    ae_matrix_clear(&p->yk);
    ae_vector_clear(&p->idx);
    ae_vector_clear(&p->bufa);
    ae_vector_clear(&p->bufb);
}


void _precbuflbfgs_destroy(void* _p)
{
    precbuflbfgs *p = (precbuflbfgs*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->norms);
    ae_vector_destroy(&p->alpha);
    ae_vector_destroy(&p->rho);
    ae_matrix_destroy(&p->yk);
    ae_vector_destroy(&p->idx);
    ae_vector_destroy(&p->bufa);
    ae_vector_destroy(&p->bufb);
}


void _precbuflowrank_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    precbuflowrank *p = (precbuflowrank*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->v, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bufc, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->bufz, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->bufw, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp, 0, DT_REAL, _state, make_automatic);
}


void _precbuflowrank_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    precbuflowrank       *dst = (precbuflowrank*)_dst;
    const precbuflowrank *src = (const precbuflowrank*)_src;
    dst->n = src->n;
    dst->k = src->k;
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    ae_matrix_init_copy(&dst->v, &src->v, _state, make_automatic);
    ae_vector_init_copy(&dst->bufc, &src->bufc, _state, make_automatic);
    ae_matrix_init_copy(&dst->bufz, &src->bufz, _state, make_automatic);
    ae_matrix_init_copy(&dst->bufw, &src->bufw, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp, &src->tmp, _state, make_automatic);
}


void _precbuflowrank_clear(void* _p)
{
    precbuflowrank *p = (precbuflowrank*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->d);
    ae_matrix_clear(&p->v);
    ae_vector_clear(&p->bufc);
    ae_matrix_clear(&p->bufz);
    ae_matrix_clear(&p->bufw);
    ae_vector_clear(&p->tmp);
}


void _precbuflowrank_destroy(void* _p)
{
    precbuflowrank *p = (precbuflowrank*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->d);
    ae_matrix_destroy(&p->v);
    ae_vector_destroy(&p->bufc);
    ae_matrix_destroy(&p->bufz);
    ae_matrix_destroy(&p->bufw);
    ae_vector_destroy(&p->tmp);
}


void _xbfgshessian_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    xbfgshessian *p = (xbfgshessian*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->varscale, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->invscale, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->hcurrent, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->s, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->y, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->lowranksst, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->lowranksyt, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->lowrankcp, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->lowrankcm, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lowrankeffd, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->sr1c, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sr1d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sr1z, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->sr1stabc, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sr1stabz, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sr1stabd, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sr1effd, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->hincoming, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sk, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->yk, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rk, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hsk, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->buf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->buf2, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->corr2, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmps, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpy, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->jk, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->blk, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->blk2, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->blk3, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->invsqrtdlk, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bufvmv, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bufupdhx, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpunstablec, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpunstables, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpu, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpq, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpw, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpsl, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpl, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpe, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmptau, 0, DT_REAL, _state, make_automatic);
}


void _xbfgshessian_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    xbfgshessian       *dst = (xbfgshessian*)_dst;
    const xbfgshessian *src = (const xbfgshessian*)_src;
    dst->htype = src->htype;
    ae_vector_init_copy(&dst->varscale, &src->varscale, _state, make_automatic);
    ae_vector_init_copy(&dst->invscale, &src->invscale, _state, make_automatic);
    dst->n = src->n;
    dst->resetfreq = src->resetfreq;
    dst->stpshort = src->stpshort;
    dst->gammasml = src->gammasml;
    dst->reg = src->reg;
    dst->smallreg = src->smallreg;
    dst->microreg = src->microreg;
    dst->wolfeeps = src->wolfeeps;
    dst->maxhess = src->maxhess;
    dst->m = src->m;
    dst->mincostheta = src->mincostheta;
    dst->mincrv = src->mincrv;
    ae_matrix_init_copy(&dst->hcurrent, &src->hcurrent, _state, make_automatic);
    dst->hage = src->hage;
    dst->sumy2 = src->sumy2;
    dst->sums2 = src->sums2;
    dst->sumsy = src->sumsy;
    dst->memlen = src->memlen;
    dst->sigma = src->sigma;
    ae_matrix_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_matrix_init_copy(&dst->y, &src->y, _state, make_automatic);
    ae_matrix_init_copy(&dst->lowranksst, &src->lowranksst, _state, make_automatic);
    ae_matrix_init_copy(&dst->lowranksyt, &src->lowranksyt, _state, make_automatic);
    dst->lowrankmodelvalid = src->lowrankmodelvalid;
    dst->lowrankk = src->lowrankk;
    ae_matrix_init_copy(&dst->lowrankcp, &src->lowrankcp, _state, make_automatic);
    ae_matrix_init_copy(&dst->lowrankcm, &src->lowrankcm, _state, make_automatic);
    dst->lowrankeffdvalid = src->lowrankeffdvalid;
    ae_vector_init_copy(&dst->lowrankeffd, &src->lowrankeffd, _state, make_automatic);
    dst->sr1modelvalid = src->sr1modelvalid;
    dst->sr1k = src->sr1k;
    ae_matrix_init_copy(&dst->sr1c, &src->sr1c, _state, make_automatic);
    ae_vector_init_copy(&dst->sr1d, &src->sr1d, _state, make_automatic);
    ae_vector_init_copy(&dst->sr1z, &src->sr1z, _state, make_automatic);
    ae_matrix_init_copy(&dst->sr1stabc, &src->sr1stabc, _state, make_automatic);
    ae_vector_init_copy(&dst->sr1stabz, &src->sr1stabz, _state, make_automatic);
    ae_vector_init_copy(&dst->sr1stabd, &src->sr1stabd, _state, make_automatic);
    dst->sr1effdvalid = src->sr1effdvalid;
    ae_vector_init_copy(&dst->sr1effd, &src->sr1effd, _state, make_automatic);
    dst->sr1nrm2 = src->sr1nrm2;
    dst->sr1nrm2stab = src->sr1nrm2stab;
    dst->updatestatus = src->updatestatus;
    ae_matrix_init_copy(&dst->hincoming, &src->hincoming, _state, make_automatic);
    ae_vector_init_copy(&dst->sk, &src->sk, _state, make_automatic);
    ae_vector_init_copy(&dst->yk, &src->yk, _state, make_automatic);
    ae_vector_init_copy(&dst->rk, &src->rk, _state, make_automatic);
    ae_vector_init_copy(&dst->hsk, &src->hsk, _state, make_automatic);
    ae_vector_init_copy(&dst->buf, &src->buf, _state, make_automatic);
    ae_vector_init_copy(&dst->buf2, &src->buf2, _state, make_automatic);
    ae_matrix_init_copy(&dst->corr2, &src->corr2, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmps, &src->tmps, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpy, &src->tmpy, _state, make_automatic);
    ae_matrix_init_copy(&dst->jk, &src->jk, _state, make_automatic);
    ae_matrix_init_copy(&dst->blk, &src->blk, _state, make_automatic);
    ae_matrix_init_copy(&dst->blk2, &src->blk2, _state, make_automatic);
    ae_matrix_init_copy(&dst->blk3, &src->blk3, _state, make_automatic);
    ae_matrix_init_copy(&dst->invsqrtdlk, &src->invsqrtdlk, _state, make_automatic);
    ae_vector_init_copy(&dst->bufvmv, &src->bufvmv, _state, make_automatic);
    ae_vector_init_copy(&dst->bufupdhx, &src->bufupdhx, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpunstablec, &src->tmpunstablec, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpunstables, &src->tmpunstables, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpu, &src->tmpu, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpq, &src->tmpq, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpw, &src->tmpw, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpsl, &src->tmpsl, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpl, &src->tmpl, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpe, &src->tmpe, _state, make_automatic);
    ae_vector_init_copy(&dst->tmptau, &src->tmptau, _state, make_automatic);
}


void _xbfgshessian_clear(void* _p)
{
    xbfgshessian *p = (xbfgshessian*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->varscale);
    ae_vector_clear(&p->invscale);
    ae_matrix_clear(&p->hcurrent);
    ae_matrix_clear(&p->s);
    ae_matrix_clear(&p->y);
    ae_matrix_clear(&p->lowranksst);
    ae_matrix_clear(&p->lowranksyt);
    ae_matrix_clear(&p->lowrankcp);
    ae_matrix_clear(&p->lowrankcm);
    ae_vector_clear(&p->lowrankeffd);
    ae_matrix_clear(&p->sr1c);
    ae_vector_clear(&p->sr1d);
    ae_vector_clear(&p->sr1z);
    ae_matrix_clear(&p->sr1stabc);
    ae_vector_clear(&p->sr1stabz);
    ae_vector_clear(&p->sr1stabd);
    ae_vector_clear(&p->sr1effd);
    ae_matrix_clear(&p->hincoming);
    ae_vector_clear(&p->sk);
    ae_vector_clear(&p->yk);
    ae_vector_clear(&p->rk);
    ae_vector_clear(&p->hsk);
    ae_vector_clear(&p->buf);
    ae_vector_clear(&p->buf2);
    ae_matrix_clear(&p->corr2);
    ae_matrix_clear(&p->tmps);
    ae_matrix_clear(&p->tmpy);
    ae_matrix_clear(&p->jk);
    ae_matrix_clear(&p->blk);
    ae_matrix_clear(&p->blk2);
    ae_matrix_clear(&p->blk3);
    ae_matrix_clear(&p->invsqrtdlk);
    ae_vector_clear(&p->bufvmv);
    ae_vector_clear(&p->bufupdhx);
    ae_matrix_clear(&p->tmpunstablec);
    ae_vector_clear(&p->tmpunstables);
    ae_matrix_clear(&p->tmpu);
    ae_matrix_clear(&p->tmpq);
    ae_matrix_clear(&p->tmpw);
    ae_matrix_clear(&p->tmpsl);
    ae_matrix_clear(&p->tmpl);
    ae_vector_clear(&p->tmpe);
    ae_vector_clear(&p->tmptau);
}


void _xbfgshessian_destroy(void* _p)
{
    xbfgshessian *p = (xbfgshessian*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->varscale);
    ae_vector_destroy(&p->invscale);
    ae_matrix_destroy(&p->hcurrent);
    ae_matrix_destroy(&p->s);
    ae_matrix_destroy(&p->y);
    ae_matrix_destroy(&p->lowranksst);
    ae_matrix_destroy(&p->lowranksyt);
    ae_matrix_destroy(&p->lowrankcp);
    ae_matrix_destroy(&p->lowrankcm);
    ae_vector_destroy(&p->lowrankeffd);
    ae_matrix_destroy(&p->sr1c);
    ae_vector_destroy(&p->sr1d);
    ae_vector_destroy(&p->sr1z);
    ae_matrix_destroy(&p->sr1stabc);
    ae_vector_destroy(&p->sr1stabz);
    ae_vector_destroy(&p->sr1stabd);
    ae_vector_destroy(&p->sr1effd);
    ae_matrix_destroy(&p->hincoming);
    ae_vector_destroy(&p->sk);
    ae_vector_destroy(&p->yk);
    ae_vector_destroy(&p->rk);
    ae_vector_destroy(&p->hsk);
    ae_vector_destroy(&p->buf);
    ae_vector_destroy(&p->buf2);
    ae_matrix_destroy(&p->corr2);
    ae_matrix_destroy(&p->tmps);
    ae_matrix_destroy(&p->tmpy);
    ae_matrix_destroy(&p->jk);
    ae_matrix_destroy(&p->blk);
    ae_matrix_destroy(&p->blk2);
    ae_matrix_destroy(&p->blk3);
    ae_matrix_destroy(&p->invsqrtdlk);
    ae_vector_destroy(&p->bufvmv);
    ae_vector_destroy(&p->bufupdhx);
    ae_matrix_destroy(&p->tmpunstablec);
    ae_vector_destroy(&p->tmpunstables);
    ae_matrix_destroy(&p->tmpu);
    ae_matrix_destroy(&p->tmpq);
    ae_matrix_destroy(&p->tmpw);
    ae_matrix_destroy(&p->tmpsl);
    ae_matrix_destroy(&p->tmpl);
    ae_vector_destroy(&p->tmpe);
    ae_vector_destroy(&p->tmptau);
}


void _varsfuncjac_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    varsfuncjac *p = (varsfuncjac*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->jac, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sj, _state, make_automatic);
}


void _varsfuncjac_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    varsfuncjac       *dst = (varsfuncjac*)_dst;
    const varsfuncjac *src = (const varsfuncjac*)_src;
    dst->n = src->n;
    dst->m = src->m;
    dst->isdense = src->isdense;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    ae_matrix_init_copy(&dst->jac, &src->jac, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sj, &src->sj, _state, make_automatic);
}


void _varsfuncjac_clear(void* _p)
{
    varsfuncjac *p = (varsfuncjac*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->fi);
    ae_matrix_clear(&p->jac);
    _sparsematrix_clear(&p->sj);
}


void _varsfuncjac_destroy(void* _p)
{
    varsfuncjac *p = (varsfuncjac*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->fi);
    ae_matrix_destroy(&p->jac);
    _sparsematrix_destroy(&p->sj);
}


void _nlpstoppingcriteria_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    nlpstoppingcriteria *p = (nlpstoppingcriteria*)_p;
    ae_touch_ptr((void*)p);
}


void _nlpstoppingcriteria_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    nlpstoppingcriteria       *dst = (nlpstoppingcriteria*)_dst;
    const nlpstoppingcriteria *src = (const nlpstoppingcriteria*)_src;
    dst->epsf = src->epsf;
    dst->eps = src->eps;
    dst->maxits = src->maxits;
}


void _nlpstoppingcriteria_clear(void* _p)
{
    nlpstoppingcriteria *p = (nlpstoppingcriteria*)_p;
    ae_touch_ptr((void*)p);
}


void _nlpstoppingcriteria_destroy(void* _p)
{
    nlpstoppingcriteria *p = (nlpstoppingcriteria*)_p;
    ae_touch_ptr((void*)p);
}


void _smoothnessmonitor_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    smoothnessmonitor *p = (smoothnessmonitor*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dcur, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->enqueuedstp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->enqueuedx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->enqueuedfunc, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->enqueuedjac, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sortedstp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sortedidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->lagprobxs, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagprobd, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagprobx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagprobfi, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->lagprobj, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->lagprobvalues, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->lagprobjacobians, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagprobsteps, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagproblagrangians, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->lagrangianprobingrcomm, _state, make_automatic);
    _optguardreport_init(&p->rep, _state, make_automatic);
    _optguardnonc0report_init(&p->nonc0strrep, _state, make_automatic);
    _optguardnonc0report_init(&p->nonc0lngrep, _state, make_automatic);
    _optguardnonc1test0report_init(&p->nonc1test0strrep, _state, make_automatic);
    _optguardnonc1test0report_init(&p->nonc1test0lngrep, _state, make_automatic);
    _optguardnonc1test1report_init(&p->nonc1test1strrep, _state, make_automatic);
    _optguardnonc1test1report_init(&p->nonc1test1lngrep, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->j, 0, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstateg0, _state, make_automatic);
    ae_vector_init(&p->xbase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fbase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fm, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->jm, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->jc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->jp, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->jbaseusr, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->jbasenum, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->stp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bufr, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->f, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->deltax, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->bufi, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->xu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->du, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->f0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->j0, 0, 0, DT_REAL, _state, make_automatic);
}


void _smoothnessmonitor_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    smoothnessmonitor       *dst = (smoothnessmonitor*)_dst;
    const smoothnessmonitor *src = (const smoothnessmonitor*)_src;
    dst->n = src->n;
    dst->k = src->k;
    dst->checksmoothness = src->checksmoothness;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->dcur, &src->dcur, _state, make_automatic);
    dst->enqueuedcnt = src->enqueuedcnt;
    ae_vector_init_copy(&dst->enqueuedstp, &src->enqueuedstp, _state, make_automatic);
    ae_vector_init_copy(&dst->enqueuedx, &src->enqueuedx, _state, make_automatic);
    ae_vector_init_copy(&dst->enqueuedfunc, &src->enqueuedfunc, _state, make_automatic);
    ae_matrix_init_copy(&dst->enqueuedjac, &src->enqueuedjac, _state, make_automatic);
    ae_vector_init_copy(&dst->sortedstp, &src->sortedstp, _state, make_automatic);
    ae_vector_init_copy(&dst->sortedidx, &src->sortedidx, _state, make_automatic);
    dst->sortedcnt = src->sortedcnt;
    dst->lagprobinneriter = src->lagprobinneriter;
    dst->lagprobouteriter = src->lagprobouteriter;
    dst->lagprobstepmax = src->lagprobstepmax;
    dst->lagprobnstepsstored = src->lagprobnstepsstored;
    ae_vector_init_copy(&dst->lagprobxs, &src->lagprobxs, _state, make_automatic);
    ae_vector_init_copy(&dst->lagprobd, &src->lagprobd, _state, make_automatic);
    dst->lagprobstp = src->lagprobstp;
    ae_vector_init_copy(&dst->lagprobx, &src->lagprobx, _state, make_automatic);
    ae_vector_init_copy(&dst->lagprobfi, &src->lagprobfi, _state, make_automatic);
    dst->lagprobrawlag = src->lagprobrawlag;
    ae_matrix_init_copy(&dst->lagprobj, &src->lagprobj, _state, make_automatic);
    ae_matrix_init_copy(&dst->lagprobvalues, &src->lagprobvalues, _state, make_automatic);
    ae_matrix_init_copy(&dst->lagprobjacobians, &src->lagprobjacobians, _state, make_automatic);
    ae_vector_init_copy(&dst->lagprobsteps, &src->lagprobsteps, _state, make_automatic);
    ae_vector_init_copy(&dst->lagproblagrangians, &src->lagproblagrangians, _state, make_automatic);
    _rcommstate_init_copy(&dst->lagrangianprobingrcomm, &src->lagrangianprobingrcomm, _state, make_automatic);
    dst->linesearchspoiled = src->linesearchspoiled;
    dst->linesearchstarted = src->linesearchstarted;
    dst->linesearchinneridx = src->linesearchinneridx;
    dst->linesearchouteridx = src->linesearchouteridx;
    dst->nonc0currentrating = src->nonc0currentrating;
    dst->nonc1currentrating = src->nonc1currentrating;
    dst->badgradhasxj = src->badgradhasxj;
    _optguardreport_init_copy(&dst->rep, &src->rep, _state, make_automatic);
    dst->nonc0strrating = src->nonc0strrating;
    dst->nonc0lngrating = src->nonc0lngrating;
    _optguardnonc0report_init_copy(&dst->nonc0strrep, &src->nonc0strrep, _state, make_automatic);
    _optguardnonc0report_init_copy(&dst->nonc0lngrep, &src->nonc0lngrep, _state, make_automatic);
    dst->nonc1test0strrating = src->nonc1test0strrating;
    dst->nonc1test0lngrating = src->nonc1test0lngrating;
    _optguardnonc1test0report_init_copy(&dst->nonc1test0strrep, &src->nonc1test0strrep, _state, make_automatic);
    _optguardnonc1test0report_init_copy(&dst->nonc1test0lngrep, &src->nonc1test0lngrep, _state, make_automatic);
    dst->nonc1test1strrating = src->nonc1test1strrating;
    dst->nonc1test1lngrating = src->nonc1test1lngrating;
    _optguardnonc1test1report_init_copy(&dst->nonc1test1strrep, &src->nonc1test1strrep, _state, make_automatic);
    _optguardnonc1test1report_init_copy(&dst->nonc1test1lngrep, &src->nonc1test1lngrep, _state, make_automatic);
    dst->needfij = src->needfij;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    ae_matrix_init_copy(&dst->j, &src->j, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstateg0, &src->rstateg0, _state, make_automatic);
    ae_vector_init_copy(&dst->xbase, &src->xbase, _state, make_automatic);
    ae_vector_init_copy(&dst->fbase, &src->fbase, _state, make_automatic);
    ae_vector_init_copy(&dst->fm, &src->fm, _state, make_automatic);
    ae_vector_init_copy(&dst->fc, &src->fc, _state, make_automatic);
    ae_vector_init_copy(&dst->fp, &src->fp, _state, make_automatic);
    ae_vector_init_copy(&dst->jm, &src->jm, _state, make_automatic);
    ae_vector_init_copy(&dst->jc, &src->jc, _state, make_automatic);
    ae_vector_init_copy(&dst->jp, &src->jp, _state, make_automatic);
    ae_matrix_init_copy(&dst->jbaseusr, &src->jbaseusr, _state, make_automatic);
    ae_matrix_init_copy(&dst->jbasenum, &src->jbasenum, _state, make_automatic);
    ae_vector_init_copy(&dst->stp, &src->stp, _state, make_automatic);
    ae_vector_init_copy(&dst->bufr, &src->bufr, _state, make_automatic);
    ae_vector_init_copy(&dst->f, &src->f, _state, make_automatic);
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    ae_vector_init_copy(&dst->deltax, &src->deltax, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpidx, &src->tmpidx, _state, make_automatic);
    ae_vector_init_copy(&dst->bufi, &src->bufi, _state, make_automatic);
    ae_vector_init_copy(&dst->xu, &src->xu, _state, make_automatic);
    ae_vector_init_copy(&dst->du, &src->du, _state, make_automatic);
    ae_vector_init_copy(&dst->f0, &src->f0, _state, make_automatic);
    ae_matrix_init_copy(&dst->j0, &src->j0, _state, make_automatic);
}


void _smoothnessmonitor_clear(void* _p)
{
    smoothnessmonitor *p = (smoothnessmonitor*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->dcur);
    ae_vector_clear(&p->enqueuedstp);
    ae_vector_clear(&p->enqueuedx);
    ae_vector_clear(&p->enqueuedfunc);
    ae_matrix_clear(&p->enqueuedjac);
    ae_vector_clear(&p->sortedstp);
    ae_vector_clear(&p->sortedidx);
    ae_vector_clear(&p->lagprobxs);
    ae_vector_clear(&p->lagprobd);
    ae_vector_clear(&p->lagprobx);
    ae_vector_clear(&p->lagprobfi);
    ae_matrix_clear(&p->lagprobj);
    ae_matrix_clear(&p->lagprobvalues);
    ae_matrix_clear(&p->lagprobjacobians);
    ae_vector_clear(&p->lagprobsteps);
    ae_vector_clear(&p->lagproblagrangians);
    _rcommstate_clear(&p->lagrangianprobingrcomm);
    _optguardreport_clear(&p->rep);
    _optguardnonc0report_clear(&p->nonc0strrep);
    _optguardnonc0report_clear(&p->nonc0lngrep);
    _optguardnonc1test0report_clear(&p->nonc1test0strrep);
    _optguardnonc1test0report_clear(&p->nonc1test0lngrep);
    _optguardnonc1test1report_clear(&p->nonc1test1strrep);
    _optguardnonc1test1report_clear(&p->nonc1test1lngrep);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->fi);
    ae_matrix_clear(&p->j);
    _rcommstate_clear(&p->rstateg0);
    ae_vector_clear(&p->xbase);
    ae_vector_clear(&p->fbase);
    ae_vector_clear(&p->fm);
    ae_vector_clear(&p->fc);
    ae_vector_clear(&p->fp);
    ae_vector_clear(&p->jm);
    ae_vector_clear(&p->jc);
    ae_vector_clear(&p->jp);
    ae_matrix_clear(&p->jbaseusr);
    ae_matrix_clear(&p->jbasenum);
    ae_vector_clear(&p->stp);
    ae_vector_clear(&p->bufr);
    ae_vector_clear(&p->f);
    ae_vector_clear(&p->g);
    ae_vector_clear(&p->deltax);
    ae_vector_clear(&p->tmpidx);
    ae_vector_clear(&p->bufi);
    ae_vector_clear(&p->xu);
    ae_vector_clear(&p->du);
    ae_vector_clear(&p->f0);
    ae_matrix_clear(&p->j0);
}


void _smoothnessmonitor_destroy(void* _p)
{
    smoothnessmonitor *p = (smoothnessmonitor*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->dcur);
    ae_vector_destroy(&p->enqueuedstp);
    ae_vector_destroy(&p->enqueuedx);
    ae_vector_destroy(&p->enqueuedfunc);
    ae_matrix_destroy(&p->enqueuedjac);
    ae_vector_destroy(&p->sortedstp);
    ae_vector_destroy(&p->sortedidx);
    ae_vector_destroy(&p->lagprobxs);
    ae_vector_destroy(&p->lagprobd);
    ae_vector_destroy(&p->lagprobx);
    ae_vector_destroy(&p->lagprobfi);
    ae_matrix_destroy(&p->lagprobj);
    ae_matrix_destroy(&p->lagprobvalues);
    ae_matrix_destroy(&p->lagprobjacobians);
    ae_vector_destroy(&p->lagprobsteps);
    ae_vector_destroy(&p->lagproblagrangians);
    _rcommstate_destroy(&p->lagrangianprobingrcomm);
    _optguardreport_destroy(&p->rep);
    _optguardnonc0report_destroy(&p->nonc0strrep);
    _optguardnonc0report_destroy(&p->nonc0lngrep);
    _optguardnonc1test0report_destroy(&p->nonc1test0strrep);
    _optguardnonc1test0report_destroy(&p->nonc1test0lngrep);
    _optguardnonc1test1report_destroy(&p->nonc1test1strrep);
    _optguardnonc1test1report_destroy(&p->nonc1test1lngrep);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->fi);
    ae_matrix_destroy(&p->j);
    _rcommstate_destroy(&p->rstateg0);
    ae_vector_destroy(&p->xbase);
    ae_vector_destroy(&p->fbase);
    ae_vector_destroy(&p->fm);
    ae_vector_destroy(&p->fc);
    ae_vector_destroy(&p->fp);
    ae_vector_destroy(&p->jm);
    ae_vector_destroy(&p->jc);
    ae_vector_destroy(&p->jp);
    ae_matrix_destroy(&p->jbaseusr);
    ae_matrix_destroy(&p->jbasenum);
    ae_vector_destroy(&p->stp);
    ae_vector_destroy(&p->bufr);
    ae_vector_destroy(&p->f);
    ae_vector_destroy(&p->g);
    ae_vector_destroy(&p->deltax);
    ae_vector_destroy(&p->tmpidx);
    ae_vector_destroy(&p->bufi);
    ae_vector_destroy(&p->xu);
    ae_vector_destroy(&p->du);
    ae_vector_destroy(&p->f0);
    ae_matrix_destroy(&p->j0);
}


void _multiobjectivetestfunction_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    multiobjectivetestfunction *p = (multiobjectivetestfunction*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_init(&p->x0, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->xsol, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->fsol, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagmultbc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagmultlc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagmultnlc, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->rotq, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tgtc, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tgtb, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tgta, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tgtd, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->densea, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->al, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->au, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nlc, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->nlb, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->nla, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->nld, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hu, 0, DT_REAL, _state, make_automatic);
}


void _multiobjectivetestfunction_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    multiobjectivetestfunction       *dst = (multiobjectivetestfunction*)_dst;
    const multiobjectivetestfunction *src = (const multiobjectivetestfunction*)_src;
    dst->problemtype = src->problemtype;
    dst->problemsubtype = src->problemsubtype;
    dst->n = src->n;
    dst->m = src->m;
    ae_matrix_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    dst->k0 = src->k0;
    ae_matrix_init_copy(&dst->xsol, &src->xsol, _state, make_automatic);
    ae_matrix_init_copy(&dst->fsol, &src->fsol, _state, make_automatic);
    dst->ksol = src->ksol;
    ae_vector_init_copy(&dst->lagmultbc, &src->lagmultbc, _state, make_automatic);
    ae_vector_init_copy(&dst->lagmultlc, &src->lagmultlc, _state, make_automatic);
    ae_vector_init_copy(&dst->lagmultnlc, &src->lagmultnlc, _state, make_automatic);
    dst->isrotated = src->isrotated;
    ae_matrix_init_copy(&dst->rotq, &src->rotq, _state, make_automatic);
    ae_vector_init_copy(&dst->tgtc, &src->tgtc, _state, make_automatic);
    ae_matrix_init_copy(&dst->tgtb, &src->tgtb, _state, make_automatic);
    ae_matrix_init_copy(&dst->tgta, &src->tgta, _state, make_automatic);
    ae_matrix_init_copy(&dst->tgtd, &src->tgtd, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    ae_matrix_init_copy(&dst->densea, &src->densea, _state, make_automatic);
    ae_vector_init_copy(&dst->al, &src->al, _state, make_automatic);
    ae_vector_init_copy(&dst->au, &src->au, _state, make_automatic);
    dst->nlinear = src->nlinear;
    ae_vector_init_copy(&dst->nlc, &src->nlc, _state, make_automatic);
    ae_matrix_init_copy(&dst->nlb, &src->nlb, _state, make_automatic);
    ae_matrix_init_copy(&dst->nla, &src->nla, _state, make_automatic);
    ae_matrix_init_copy(&dst->nld, &src->nld, _state, make_automatic);
    ae_vector_init_copy(&dst->hl, &src->hl, _state, make_automatic);
    ae_vector_init_copy(&dst->hu, &src->hu, _state, make_automatic);
    dst->nnonlinear = src->nnonlinear;
}


void _multiobjectivetestfunction_clear(void* _p)
{
    multiobjectivetestfunction *p = (multiobjectivetestfunction*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_clear(&p->x0);
    ae_matrix_clear(&p->xsol);
    ae_matrix_clear(&p->fsol);
    ae_vector_clear(&p->lagmultbc);
    ae_vector_clear(&p->lagmultlc);
    ae_vector_clear(&p->lagmultnlc);
    ae_matrix_clear(&p->rotq);
    ae_vector_clear(&p->tgtc);
    ae_matrix_clear(&p->tgtb);
    ae_matrix_clear(&p->tgta);
    ae_matrix_clear(&p->tgtd);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_matrix_clear(&p->densea);
    ae_vector_clear(&p->al);
    ae_vector_clear(&p->au);
    ae_vector_clear(&p->nlc);
    ae_matrix_clear(&p->nlb);
    ae_matrix_clear(&p->nla);
    ae_matrix_clear(&p->nld);
    ae_vector_clear(&p->hl);
    ae_vector_clear(&p->hu);
}


void _multiobjectivetestfunction_destroy(void* _p)
{
    multiobjectivetestfunction *p = (multiobjectivetestfunction*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_destroy(&p->x0);
    ae_matrix_destroy(&p->xsol);
    ae_matrix_destroy(&p->fsol);
    ae_vector_destroy(&p->lagmultbc);
    ae_vector_destroy(&p->lagmultlc);
    ae_vector_destroy(&p->lagmultnlc);
    ae_matrix_destroy(&p->rotq);
    ae_vector_destroy(&p->tgtc);
    ae_matrix_destroy(&p->tgtb);
    ae_matrix_destroy(&p->tgta);
    ae_matrix_destroy(&p->tgtd);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_matrix_destroy(&p->densea);
    ae_vector_destroy(&p->al);
    ae_vector_destroy(&p->au);
    ae_vector_destroy(&p->nlc);
    ae_matrix_destroy(&p->nlb);
    ae_matrix_destroy(&p->nla);
    ae_matrix_destroy(&p->nld);
    ae_vector_destroy(&p->hl);
    ae_vector_destroy(&p->hu);
}


void _linesearchstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    linesearchstate *p = (linesearchstate*)_p;
    ae_touch_ptr((void*)p);
    _rcommstate_init(&p->rstate, _state, make_automatic);
}


void _linesearchstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    linesearchstate       *dst = (linesearchstate*)_dst;
    const linesearchstate *src = (const linesearchstate*)_src;
    dst->f0 = src->f0;
    dst->g0 = src->g0;
    dst->alpha1 = src->alpha1;
    dst->alphamax = src->alphamax;
    dst->c1 = src->c1;
    dst->c2 = src->c2;
    dst->strongwolfecond = src->strongwolfecond;
    dst->maxits = src->maxits;
    dst->dotrace = src->dotrace;
    dst->tracelevel = src->tracelevel;
    dst->bestalphasofar = src->bestalphasofar;
    dst->bestfsofar = src->bestfsofar;
    dst->alphaprev = src->alphaprev;
    dst->fprev = src->fprev;
    dst->gprev = src->gprev;
    dst->alphacur = src->alphacur;
    dst->fcur = src->fcur;
    dst->gcur = src->gcur;
    dst->alphalo = src->alphalo;
    dst->alphahi = src->alphahi;
    dst->flo = src->flo;
    dst->fhi = src->fhi;
    dst->glo = src->glo;
    dst->ghi = src->ghi;
    dst->nfev = src->nfev;
    dst->alphasol = src->alphasol;
    dst->terminationtype = src->terminationtype;
    dst->alphatrial = src->alphatrial;
    dst->ftrial = src->ftrial;
    dst->gtrial = src->gtrial;
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
}


void _linesearchstate_clear(void* _p)
{
    linesearchstate *p = (linesearchstate*)_p;
    ae_touch_ptr((void*)p);
    _rcommstate_clear(&p->rstate);
}


void _linesearchstate_destroy(void* _p)
{
    linesearchstate *p = (linesearchstate*)_p;
    ae_touch_ptr((void*)p);
    _rcommstate_destroy(&p->rstate);
}


void _nlpfilter_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    nlpfilter *p = (nlpfilter*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->filterf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->filterh, 0, DT_REAL, _state, make_automatic);
}


void _nlpfilter_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    nlpfilter       *dst = (nlpfilter*)_dst;
    const nlpfilter *src = (const nlpfilter*)_src;
    dst->maxh = src->maxh;
    dst->filtersize = src->filtersize;
    dst->maxdominating = src->maxdominating;
    dst->gammaf = src->gammaf;
    dst->gammah = src->gammah;
    dst->violationistoohigh = src->violationistoohigh;
    ae_vector_init_copy(&dst->filterf, &src->filterf, _state, make_automatic);
    ae_vector_init_copy(&dst->filterh, &src->filterh, _state, make_automatic);
}


void _nlpfilter_clear(void* _p)
{
    nlpfilter *p = (nlpfilter*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->filterf);
    ae_vector_clear(&p->filterh);
}


void _nlpfilter_destroy(void* _p)
{
    nlpfilter *p = (nlpfilter*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->filterf);
    ae_vector_destroy(&p->filterh);
}


#endif
#if defined(AE_COMPILE_MINLBFGS) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
        LIMITED MEMORY BFGS METHOD FOR LARGE SCALE OPTIMIZATION

DESCRIPTION:
The subroutine minimizes function F(x) of N arguments by  using  a  quasi-
Newton method (LBFGS scheme) which is optimized to use  a  minimum  amount
of memory.
The subroutine generates the approximation of an inverse Hessian matrix by
using information about the last M steps of the algorithm  (instead of N).
It lessens a required amount of memory from a value  of  order  N^2  to  a
value of order 2*N*M.


REQUIREMENTS:
Algorithm will request following information during its operation:
* function value F and its gradient G (simultaneously) at given point X


USAGE:
1. User initializes algorithm state with MinLBFGSCreate() call
2. User tunes solver parameters with MinLBFGSSetCond() MinLBFGSSetStpMax()
   and other functions
3. User calls MinLBFGSOptimize() function which takes algorithm  state and
   pointer (delegate, etc.) to callback function which calculates F/G.
4. User calls MinLBFGSResults() to get solution
5. Optionally user may call MinLBFGSRestartFrom() to solve another problem
   with same N/M but another starting point and/or another function.
   MinLBFGSRestartFrom() allows to reuse already initialized structure.

INPUT PARAMETERS:
    N       -   problem dimension. N>0
    M       -   number of corrections in the BFGS scheme of Hessian
                approximation update. Recommended value:  3<=M<=7. The smaller
                value causes worse convergence, the bigger will  not  cause  a
                considerably better convergence, but will cause a fall in  the
                performance. M<=N.
    X       -   initial solution approximation, array[0..N-1].


OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state
    
IMPORTANT: the   LBFGS  optimizer  supports  parallel  parallel  numerical
           differentiation  ('callback    parallelism').   This   feature,
           which  is  present  in  commercial   ALGLIB   editions  greatly
           accelerates  optimization  with  numerical  differentiation  of
           an expensive target functions.

           Callback parallelism is usually  beneficial  when  computing  a
           numerical gradient requires  more  than  several  milliseconds.

           See ALGLIB Reference Manual, 'Working with commercial version'
           section,  and  comments  on  minlbfgsoptimize() function for
           more information.

NOTES:
1. you may tune stopping conditions with MinLBFGSSetCond() function
2. if target function contains exp() or other fast growing functions,  and
   optimization algorithm makes too large steps which leads  to  overflow,
   use MinLBFGSSetStpMax() function to bound algorithm's  steps.  However,
   L-BFGS rarely needs such a tuning.


  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgscreate(ae_int_t n,
     ae_int_t m,
     /* Real    */ const ae_vector* x,
     minlbfgsstate* state,
     ae_state *_state)
{

    _minlbfgsstate_clear(state);

    ae_assert(n>=1, "MinLBFGSCreate: N<1!", _state);
    ae_assert(m>=1, "MinLBFGSCreate: M<1", _state);
    ae_assert(m<=n, "MinLBFGSCreate: M>N", _state);
    ae_assert(x->cnt>=n, "MinLBFGSCreate: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, n, _state), "MinLBFGSCreate: X contains infinite or NaN values!", _state);
    minlbfgscreatex(n, m, x, 0, 0.0, state, _state);
}


/*************************************************************************
The subroutine is finite difference variant of MinLBFGSCreate().  It  uses
finite differences in order to differentiate target function.

Description below contains information which is specific to  this function
only. We recommend to read comments on MinLBFGSCreate() in  order  to  get
more information about creation of LBFGS optimizer.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    M       -   number of corrections in the BFGS scheme of Hessian
                approximation update. Recommended value:  3<=M<=7. The smaller
                value causes worse convergence, the bigger will  not  cause  a
                considerably better convergence, but will cause a fall in  the
                performance. M<=N.
    X       -   starting point, array[0..N-1].
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state


IMPORTANT: the   LBFGS  optimizer  supports  parallel  parallel  numerical
           differentiation  ('callback    parallelism').   This   feature,
           which  is  present  in  commercial   ALGLIB   editions  greatly
           accelerates  optimization  with  numerical  differentiation  of
           an expensive target functions.

           Callback parallelism is usually  beneficial  when  computing  a
           numerical gradient requires  more  than  several  milliseconds.

           See ALGLIB Reference Manual, 'Working with commercial version'
           section,  and  comments  on  minlbfgsoptimize() function for
           more information.

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinLBFGSSetScale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large truncation  errors, while too small
   step will result in too large numerical  errors.  1.0E-6  can  be  good
   value to start with.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is   less  robust  and  precise.  LBFGS  needs  exact  gradient values.
   Imprecise gradient may slow  down  convergence,  especially  on  highly
   nonlinear problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
void minlbfgscreatef(ae_int_t n,
     ae_int_t m,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minlbfgsstate* state,
     ae_state *_state)
{

    _minlbfgsstate_clear(state);

    ae_assert(n>=1, "MinLBFGSCreateF: N too small!", _state);
    ae_assert(m>=1, "MinLBFGSCreateF: M<1", _state);
    ae_assert(m<=n, "MinLBFGSCreateF: M>N", _state);
    ae_assert(x->cnt>=n, "MinLBFGSCreateF: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, n, _state), "MinLBFGSCreateF: X contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(diffstep, _state), "MinLBFGSCreateF: DiffStep is infinite or NaN!", _state);
    ae_assert(ae_fp_greater(diffstep,(double)(0)), "MinLBFGSCreateF: DiffStep is non-positive!", _state);
    minlbfgscreatex(n, m, x, 0, diffstep, state, _state);
}


/*************************************************************************
This function sets stopping conditions for L-BFGS optimization algorithm.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsG    -   >=0
                The  subroutine  finishes  its  work   if   the  condition
                |v|<EpsG is satisfied, where:
                * |.| means Euclidian norm
                * v - scaled gradient vector, v[i]=g[i]*s[i]
                * g - gradient
                * s - scaling coefficients set by MinLBFGSSetScale()
    EpsF    -   >=0
                The  subroutine  finishes  its work if on k+1-th iteration
                the  condition  |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1}
                is satisfied.
    EpsX    -   >=0
                The subroutine finishes its work if  on  k+1-th  iteration
                the condition |v|<=EpsX is fulfilled, where:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - ste pvector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinLBFGSSetScale()
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsG=0, EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to
automatic stopping criterion selection (small EpsX).

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetcond(minlbfgsstate* state,
     double epsg,
     double epsf,
     double epsx,
     ae_int_t maxits,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsg, _state), "MinLBFGSSetCond: EpsG is not finite number!", _state);
    ae_assert(ae_fp_greater_eq(epsg,(double)(0)), "MinLBFGSSetCond: negative EpsG!", _state);
    ae_assert(ae_isfinite(epsf, _state), "MinLBFGSSetCond: EpsF is not finite number!", _state);
    ae_assert(ae_fp_greater_eq(epsf,(double)(0)), "MinLBFGSSetCond: negative EpsF!", _state);
    ae_assert(ae_isfinite(epsx, _state), "MinLBFGSSetCond: EpsX is not finite number!", _state);
    ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "MinLBFGSSetCond: negative EpsX!", _state);
    ae_assert(maxits>=0, "MinLBFGSSetCond: negative MaxIts!", _state);
    if( ((ae_fp_eq(epsg,(double)(0))&&ae_fp_eq(epsf,(double)(0)))&&ae_fp_eq(epsx,(double)(0)))&&maxits==0 )
    {
        epsx = 1.0E-6;
    }
    state->epsg = epsg;
    state->epsf = epsf;
    state->epsx = epsx;
    state->maxits = maxits;
}


/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinLBFGSOptimize().


  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetxrep(minlbfgsstate* state,
     ae_bool needxrep,
     ae_state *_state)
{


    state->xrep = needxrep;
}


/*************************************************************************
This function sets maximum step length

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    StpMax  -   maximum step length, >=0. Set StpMax to 0.0 (default),  if
                you don't want to limit step length.

Use this subroutine when you optimize target function which contains exp()
or  other  fast  growing  functions,  and optimization algorithm makes too
large  steps  which  leads  to overflow. This function allows us to reject
steps  that  are  too  large  (and  therefore  expose  us  to the possible
overflow) without actually calculating function value at the x+stp*d.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetstpmax(minlbfgsstate* state,
     double stpmax,
     ae_state *_state)
{


    ae_assert(ae_isfinite(stpmax, _state), "MinLBFGSSetStpMax: StpMax is not finite!", _state);
    ae_assert(ae_fp_greater_eq(stpmax,(double)(0)), "MinLBFGSSetStpMax: StpMax<0!", _state);
    state->stpmax = stpmax;
}


/*************************************************************************
This function sets scaling coefficients for LBFGS optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Scaling is also used by finite difference variant of the optimizer  - step
along I-th axis is equal to DiffStep*S[I].

In  most  optimizers  (and  in  the  LBFGS  too)  scaling is NOT a form of
preconditioning. It just  affects  stopping  conditions.  You  should  set
preconditioner  by  separate  call  to  one  of  the  MinLBFGSSetPrec...()
functions.

There  is  special  preconditioning  mode, however,  which  uses   scaling
coefficients to form diagonal preconditioning matrix. You  can  turn  this
mode on, if you want.   But  you should understand that scaling is not the
same thing as preconditioning - these are two different, although  related
forms of tuning solver.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetscale(minlbfgsstate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(s->cnt>=state->n, "MinLBFGSSetScale: Length(S)<N", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "MinLBFGSSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "MinLBFGSSetScale: S contains zero elements", _state);
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
Extended subroutine for internal use only.

Accepts additional parameters:

    Flags - additional settings:
            * Flags = 0     means no additional settings
            * Flags = 1     "do not allocate memory". used when solving
                            a many subsequent tasks with  same N/M  values.
                            First  call MUST  be without this flag bit set,
                            subsequent  calls   of   MinLBFGS   with   same
                            MinLBFGSState structure can set Flags to 1.
    DiffStep - numerical differentiation step

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgscreatex(ae_int_t n,
     ae_int_t m,
     /* Real    */ const ae_vector* x,
     ae_int_t flags,
     double diffstep,
     minlbfgsstate* state,
     ae_state *_state)
{
    ae_bool allocatemem;
    ae_int_t i;


    ae_assert(n>=1, "MinLBFGS: N too small!", _state);
    ae_assert(m>=1, "MinLBFGS: M too small!", _state);
    ae_assert(m<=n, "MinLBFGS: M too large!", _state);
    
    /*
     * Initialize
     */
    state->protocolversion = 1;
    state->teststep = (double)(0);
    state->smoothnessguardlevel = 0;
    smoothnessmonitorinit(&state->smonitor, &state->s, 0, 0, ae_false, _state);
    state->diffstep = diffstep;
    state->n = n;
    state->m = m;
    allocatemem = flags%2==0;
    flags = flags/2;
    if( allocatemem )
    {
        rvectorsetlengthatleast(&state->rho, m, _state);
        rvectorsetlengthatleast(&state->theta, m, _state);
        rmatrixsetlengthatleast(&state->yk, m, n, _state);
        rmatrixsetlengthatleast(&state->sk, m, n, _state);
        rvectorsetlengthatleast(&state->d, n, _state);
        rvectorsetlengthatleast(&state->xp, n, _state);
        rvectorsetlengthatleast(&state->x, n, _state);
        rvectorsetlengthatleast(&state->xbase, n, _state);
        rvectorsetlengthatleast(&state->s, n, _state);
        rvectorsetlengthatleast(&state->invs, n, _state);
        rvectorsetlengthatleast(&state->lastscaleused, n, _state);
        rvectorsetlengthatleast(&state->g, n, _state);
        rvectorsetlengthatleast(&state->work, n, _state);
    }
    for(i=0; i<=n-1; i++)
    {
        state->s.ptr.p_double[i] = 1.0;
        state->invs.ptr.p_double[i] = 1.0;
        state->lastscaleused.ptr.p_double[i] = 1.0;
    }
    state->prectype = 0;
    minlbfgssetcond(state, (double)(0), (double)(0), (double)(0), 0, _state);
    minlbfgssetxrep(state, ae_false, _state);
    minlbfgssetstpmax(state, (double)(0), _state);
    minlbfgsrestartfrom(state, x, _state);
}


/*************************************************************************
Modification  of  the  preconditioner:  default  preconditioner    (simple
scaling, same for all elements of X) is used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTE:  you  can  change  preconditioner  "on  the  fly",  during algorithm
iterations.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetprecdefault(minlbfgsstate* state, ae_state *_state)
{


    state->prectype = 0;
}


/*************************************************************************
Modification of the preconditioner: Cholesky factorization of  approximate
Hessian is used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    P       -   triangular preconditioner, Cholesky factorization of
                the approximate Hessian. array[0..N-1,0..N-1],
                (if larger, only leading N elements are used).
    IsUpper -   whether upper or lower triangle of P is given
                (other triangle is not referenced)

After call to this function preconditioner is changed to P  (P  is  copied
into the internal buffer).

NOTE:  you  can  change  preconditioner  "on  the  fly",  during algorithm
iterations.

NOTE 2:  P  should  be nonsingular. Exception will be thrown otherwise.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetpreccholesky(minlbfgsstate* state,
     /* Real    */ const ae_matrix* p,
     ae_bool isupper,
     ae_state *_state)
{
    ae_int_t i;
    double mx;


    ae_assert(isfinitertrmatrix(p, state->n, isupper, _state), "MinLBFGSSetPrecCholesky: P contains infinite or NAN values!", _state);
    mx = (double)(0);
    for(i=0; i<=state->n-1; i++)
    {
        mx = ae_maxreal(mx, ae_fabs(p->ptr.pp_double[i][i], _state), _state);
    }
    ae_assert(ae_fp_greater(mx,(double)(0)), "MinLBFGSSetPrecCholesky: P is strictly singular!", _state);
    if( state->denseh.rows<state->n||state->denseh.cols<state->n )
    {
        ae_matrix_set_length(&state->denseh, state->n, state->n, _state);
    }
    state->prectype = 1;
    if( isupper )
    {
        rmatrixcopy(state->n, state->n, p, 0, 0, &state->denseh, 0, 0, _state);
    }
    else
    {
        rmatrixtranspose(state->n, state->n, p, 0, 0, &state->denseh, 0, 0, _state);
    }
}


/*************************************************************************
Modification  of  the  preconditioner:  diagonal of approximate Hessian is
used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    D       -   diagonal of the approximate Hessian, array[0..N-1],
                (if larger, only leading N elements are used).

NOTE:  you  can  change  preconditioner  "on  the  fly",  during algorithm
iterations.

NOTE 2: D[i] should be positive. Exception will be thrown otherwise.

NOTE 3: you should pass diagonal of approximate Hessian - NOT ITS INVERSE.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetprecdiag(minlbfgsstate* state,
     /* Real    */ const ae_vector* d,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(d->cnt>=state->n, "MinLBFGSSetPrecDiag: D is too short", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(d->ptr.p_double[i], _state), "MinLBFGSSetPrecDiag: D contains infinite or NAN elements", _state);
        ae_assert(ae_fp_greater(d->ptr.p_double[i],(double)(0)), "MinLBFGSSetPrecDiag: D contains non-positive elements", _state);
    }
    rvectorsetlengthatleast(&state->diagh, state->n, _state);
    state->prectype = 2;
    for(i=0; i<=state->n-1; i++)
    {
        state->diagh.ptr.p_double[i] = d->ptr.p_double[i];
    }
}


/*************************************************************************
Modification of the preconditioner: scale-based diagonal preconditioning.

This preconditioning mode can be useful when you  don't  have  approximate
diagonal of Hessian, but you know that your  variables  are  badly  scaled
(for  example,  one  variable is in [1,10], and another in [1000,100000]),
and most part of the ill-conditioning comes from different scales of vars.

In this case simple  scale-based  preconditioner,  with H[i] = 1/(s[i]^2),
can greatly improve convergence.

IMPRTANT: you should set scale of your variables  with  MinLBFGSSetScale()
call  (before  or after MinLBFGSSetPrecScale() call). Without knowledge of
the scale of your variables scale-based preconditioner will be  just  unit
matrix.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetprecscale(minlbfgsstate* state, ae_state *_state)
{


    state->prectype = 3;
}


/*************************************************************************
This function sets low-rank preconditioner for Hessian matrix  H=D+W'*C*W,
where:
* H is a Hessian matrix, which is approximated by D/W/C
* D is a NxN diagonal positive definite matrix
* W is a KxN low-rank correction
* C is a KxK positive definite diagonal factor of low-rank correction

This preconditioner is inexact but fast - it requires O(N*K)  time  to  be
applied. Preconditioner P is calculated by artificially constructing a set
of BFGS updates which tries to reproduce behavior of H:
* Sk = Wk (k-th row of W)
* Yk = (D+Wk'*Ck*Wk)*Sk
* Yk/Sk are reordered by ascending of C[k]*norm(Wk)^2

Here we assume that rows of Wk are orthogonal or nearly orthogonal,  which
allows us to have O(N*K+K^2) update instead of O(N*K^2) one. Reordering of
updates is essential for having good performance on non-orthogonal problems
(updates which do not add much of curvature are added first,  and  updates
which add very large eigenvalues are added last and override effect of the
first updates).

In practice, this preconditioner is perfect when ortogonal  correction  is
applied; on non-orthogonal problems sometimes  it  allows  to  achieve  5x
speedup (when compared to non-preconditioned solver).

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetprecrankklbfgsfast(minlbfgsstate* state,
     /* Real    */ const ae_vector* d,
     /* Real    */ const ae_vector* c,
     /* Real    */ const ae_matrix* w,
     ae_int_t cnt,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;


    n = state->n;
    state->prectype = 4;
    state->preck = cnt;
    rvectorsetlengthatleast(&state->precc, cnt, _state);
    rvectorsetlengthatleast(&state->precd, n, _state);
    rmatrixsetlengthatleast(&state->precw, cnt, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->precd.ptr.p_double[i] = d->ptr.p_double[i];
    }
    for(i=0; i<=cnt-1; i++)
    {
        state->precc.ptr.p_double[i] = c->ptr.p_double[i];
        for(j=0; j<=n-1; j++)
        {
            state->precw.ptr.pp_double[i][j] = w->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
This function  sets  exact  low-rank  preconditioner  for  Hessian  matrix
H=D+W'*C*W, where:
* H is a Hessian matrix, which is approximated by D/W/C
* D is a NxN diagonal positive definite matrix
* W is a KxN low-rank correction
* C is a KxK semidefinite diagonal factor of low-rank correction

This preconditioner is exact but slow - it requires O(N*K^2)  time  to  be
built and O(N*K) time to be applied. Woodbury matrix identity is  used  to
build inverse matrix.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetpreclowrankexact(minlbfgsstate* state,
     /* Real    */ const ae_vector* d,
     /* Real    */ const ae_vector* c,
     /* Real    */ const ae_matrix* w,
     ae_int_t cnt,
     ae_state *_state)
{


    state->prectype = 5;
    preparelowrankpreconditioner(d, c, w, state->n, cnt, &state->lowrankbuf, _state);
}


/*************************************************************************

CALLBACK PARALLELISM:

The LBFGS optimizer supports parallel numerical differentiation ('callback
parallelism').  This  feature,  which  is  present  in  commercial  ALGLIB
editions,  greatly  accelerates  numerical  differentiation  of  expensive
targets.

Callback parallelism is usually beneficial computing a numerical  gradient
requires more than several milliseconds. In this case the job of computing
individual gradient components can be split between multiple threads. Even
inexpensive  targets  can  benefit  from  parallelism, if  you  have  many
variables.

ALGLIB Reference Manual, 'Working with commercial  version' section, tells
how to activate callback parallelism for your programming language.

CALLBACKS ACCEPTED

1. This function has two different implementations: one which  uses  exact
   (analytical) user-supplied gradient,  and one which uses function value
   only  and  numerically  differentiates  function  in  order  to  obtain
   gradient.

   Depending  on  the  specific  function  used to create optimizer object
   (either MinLBFGSCreate() for analytical gradient  or  MinLBFGSCreateF()
   for numerical differentiation) you should choose appropriate variant of
   MinLBFGSOptimize() - one  which  accepts  function  AND gradient or one
   which accepts function ONLY.

   Be careful to choose variant of MinLBFGSOptimize() which corresponds to
   your optimization scheme! Table below lists different  combinations  of
   callback (function/gradient) passed to MinLBFGSOptimize()  and specific
   function used to create optimizer.


                     |         USER PASSED TO MinLBFGSOptimize()
   CREATED WITH      |  function only   |  function and gradient
   ------------------------------------------------------------
   MinLBFGSCreateF() |     work                FAIL
   MinLBFGSCreate()  |     FAIL                work

   Here "FAIL" denotes inappropriate combinations  of  optimizer  creation
   function  and  MinLBFGSOptimize()  version.   Attemps   to   use   such
   combination (for example, to create optimizer with MinLBFGSCreateF() and
   to pass gradient information to MinCGOptimize()) will lead to exception
   being thrown. Either  you  did  not pass gradient when it WAS needed or
   you passed gradient when it was NOT needed.

  -- ALGLIB --
     Copyright 20.03.2009 by Bochkanov Sergey
*************************************************************************/
ae_bool minlbfgsiteration(minlbfgsstate* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ic;
    ae_int_t mcinfo;
    double v;
    double vv;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        n = state->rstate.ia.ptr.p_int[0];
        m = state->rstate.ia.ptr.p_int[1];
        i = state->rstate.ia.ptr.p_int[2];
        j = state->rstate.ia.ptr.p_int[3];
        ic = state->rstate.ia.ptr.p_int[4];
        mcinfo = state->rstate.ia.ptr.p_int[5];
        v = state->rstate.ra.ptr.p_double[0];
        vv = state->rstate.ra.ptr.p_double[1];
    }
    else
    {
        n = 359;
        m = -58;
        i = -919;
        j = -909;
        ic = 81;
        mcinfo = 255;
        v = 74.0;
        vv = -788.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    if( state->rstate.stage==6 )
    {
        goto lbl_6;
    }
    if( state->rstate.stage==7 )
    {
        goto lbl_7;
    }
    if( state->rstate.stage==8 )
    {
        goto lbl_8;
    }
    if( state->rstate.stage==9 )
    {
        goto lbl_9;
    }
    if( state->rstate.stage==10 )
    {
        goto lbl_10;
    }
    if( state->rstate.stage==11 )
    {
        goto lbl_11;
    }
    if( state->rstate.stage==12 )
    {
        goto lbl_12;
    }
    if( state->rstate.stage==13 )
    {
        goto lbl_13;
    }
    if( state->rstate.stage==14 )
    {
        goto lbl_14;
    }
    if( state->rstate.stage==15 )
    {
        goto lbl_15;
    }
    if( state->rstate.stage==16 )
    {
        goto lbl_16;
    }
    if( state->rstate.stage==17 )
    {
        goto lbl_17;
    }
    if( state->rstate.stage==18 )
    {
        goto lbl_18;
    }
    if( state->rstate.stage==19 )
    {
        goto lbl_19;
    }
    if( state->rstate.stage==20 )
    {
        goto lbl_20;
    }
    if( state->rstate.stage==21 )
    {
        goto lbl_21;
    }
    
    /*
     * Routine body
     */
    
    /*
     * Unload frequently used variables from State structure
     * (just for typing convinience)
     */
    n = state->n;
    m = state->m;
    
    /*
     * Init
     */
    state->userterminationneeded = ae_false;
    state->repterminationtype = 0;
    state->repiterationscount = 0;
    state->repnfev = 0;
    smoothnessmonitorinit(&state->smonitor, &state->s, n, 1, state->smoothnessguardlevel>0, _state);
    rvectorsetlengthatleast(&state->invs, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->lastscaleused.ptr.p_double[i] = state->s.ptr.p_double[i];
        state->invs.ptr.p_double[i] = (double)1/state->s.ptr.p_double[i];
    }
    
    /*
     * Allocate temporaries, as mandated by the V2 protocol
     */
    if( state->protocolversion==2 )
    {
        rallocm(1, n, &state->tmpj1, _state);
        rallocv(1, &state->tmpf1, _state);
        rallocv(n, &state->tmpg1, _state);
        rallocv(n, &state->tmpx1, _state);
    }
    
    /*
     *  Check, that transferred derivative value is right
     */
    state->stp = (double)(0);
    if( !(ae_fp_eq(state->diffstep,(double)(0))&&ae_fp_greater(state->teststep,(double)(0))) )
    {
        goto lbl_22;
    }
lbl_24:
    if( !smoothnessmonitorcheckgradientatx0(&state->smonitor, &state->xbase, &state->s, &state->s, &state->s, ae_false, state->teststep, _state) )
    {
        goto lbl_25;
    }
    if( state->protocolversion!=2 )
    {
        goto lbl_26;
    }
    
    /*
     * Use V2 reverse communication protocol
     */
    state->requesttype = 2;
    state->querysize = 1;
    state->queryfuncs = 1;
    state->queryvars = n;
    state->querydim = 0;
    rcopyallocv(n, &state->smonitor.x, &state->querydata, _state);
    rallocv(1, &state->replyfi, _state);
    rallocv(n, &state->replydj, _state);
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    state->smonitor.fi.ptr.p_double[0] = state->replyfi.ptr.p_double[0];
    rcopyvr(n, &state->replydj, &state->smonitor.j, 0, _state);
    goto lbl_27;
lbl_26:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLBFGS: unexpected protocol, integrity check 0125 failed", _state);
    minlbfgs_clearrequestfields(state, _state);
    rcopyv(n, &state->smonitor.x, &state->x, _state);
    state->needfg = ae_true;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->needfg = ae_false;
    state->smonitor.fi.ptr.p_double[0] = state->f;
    rcopyvr(n, &state->g, &state->smonitor.j, 0, _state);
lbl_27:
    goto lbl_24;
lbl_25:
lbl_22:
    
    /*
     * Calculate F/G at the initial point
     */
    if( state->protocolversion!=2 )
    {
        goto lbl_28;
    }
    
    /*
     * Issue request using V2 protocol
     */
    rcopyv(n, &state->xbase, &state->x, _state);
    state->stp = (double)(0);
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_30;
    }
    
    /*
     * Request dense analytic Jacobian
     */
    state->requesttype = 2;
    state->querysize = 1;
    state->queryfuncs = 1;
    state->queryvars = n;
    state->querydim = 0;
    rcopyallocv(n, &state->x, &state->querydata, _state);
    rallocv(1, &state->replyfi, _state);
    rallocv(n, &state->replydj, _state);
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->f = state->replyfi.ptr.p_double[0];
    rcopyallocv(n, &state->replydj, &state->g, _state);
    goto lbl_31;
lbl_30:
    
    /*
     * Request dense numerical Jacobian, propose 5-point formula
     */
    state->requesttype = 3;
    state->querysize = 1;
    state->queryfuncs = 1;
    state->queryvars = n;
    state->querydim = 0;
    state->queryformulasize = 5;
    rallocv(n+n*2*state->queryformulasize, &state->querydata, _state);
    rcopyallocv(n, &state->x, &state->querydata, _state);
    for(i=0; i<=n-1; i++)
    {
        v = state->diffstep*state->s.ptr.p_double[i];
        state->querydata.ptr.p_double[n+10*i+0*2+0] = (double)(0);
        state->querydata.ptr.p_double[n+10*i+0*2+1] = (double)(0);
        state->querydata.ptr.p_double[n+10*i+1*2+0] = state->querydata.ptr.p_double[i]-1.0*v;
        state->querydata.ptr.p_double[n+10*i+1*2+1] = (double)1/((double)6*v);
        state->querydata.ptr.p_double[n+10*i+2*2+0] = state->querydata.ptr.p_double[i]-0.5*v;
        state->querydata.ptr.p_double[n+10*i+2*2+1] = -(double)8/((double)6*v);
        state->querydata.ptr.p_double[n+10*i+3*2+0] = state->querydata.ptr.p_double[i]+0.5*v;
        state->querydata.ptr.p_double[n+10*i+3*2+1] = (double)8/((double)6*v);
        state->querydata.ptr.p_double[n+10*i+4*2+0] = state->querydata.ptr.p_double[i]+1.0*v;
        state->querydata.ptr.p_double[n+10*i+4*2+1] = -(double)1/((double)6*v);
    }
    rallocv(1, &state->replyfi, _state);
    rallocv(n, &state->replydj, _state);
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    state->f = state->replyfi.ptr.p_double[0];
    rcopyallocv(n, &state->replydj, &state->g, _state);
lbl_31:
    if( !state->xrep )
    {
        goto lbl_32;
    }
    state->requesttype = -1;
    state->queryvars = n;
    state->reportf = state->f;
    rcopyallocv(n, &state->x, &state->reportx, _state);
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
lbl_32:
    goto lbl_29;
lbl_28:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLBFGS: unexpected protocol, integrity check 9600 failed", _state);
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = state->xbase.ptr.p_double[i];
    }
    state->stp = (double)(0);
    minlbfgs_clearrequestfields(state, _state);
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_34;
    }
    state->needfg = ae_true;
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    state->needfg = ae_false;
    goto lbl_35;
lbl_34:
    state->needf = ae_true;
    state->rstate.stage = 6;
    goto lbl_rcomm;
lbl_6:
    state->fbase = state->f;
    i = 0;
lbl_36:
    if( i>n-1 )
    {
        goto lbl_38;
    }
    v = state->x.ptr.p_double[i];
    state->x.ptr.p_double[i] = v-state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 7;
    goto lbl_rcomm;
lbl_7:
    state->fm2 = state->f;
    state->x.ptr.p_double[i] = v-0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 8;
    goto lbl_rcomm;
lbl_8:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = v+0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 9;
    goto lbl_rcomm;
lbl_9:
    state->fp1 = state->f;
    state->x.ptr.p_double[i] = v+state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 10;
    goto lbl_rcomm;
lbl_10:
    state->fp2 = state->f;
    state->x.ptr.p_double[i] = v;
    state->g.ptr.p_double[i] = ((double)8*(state->fp1-state->fm1)-(state->fp2-state->fm2))/((double)6*state->diffstep*state->s.ptr.p_double[i]);
    i = i+1;
    goto lbl_36;
lbl_38:
    state->f = state->fbase;
    state->needf = ae_false;
lbl_35:
    if( !state->xrep )
    {
        goto lbl_39;
    }
    minlbfgs_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 11;
    goto lbl_rcomm;
lbl_11:
    state->xupdated = ae_false;
lbl_39:
lbl_29:
    trimprepare(state->f, &state->trimthreshold, _state);
    if( state->userterminationneeded )
    {
        
        /*
         * User requested termination
         */
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    state->repnfev = 1;
    state->fold = state->f;
    v = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr(state->g.ptr.p_double[i]*state->s.ptr.p_double[i], _state);
    }
    if( ae_fp_less_eq(ae_sqrt(v, _state),state->epsg) )
    {
        state->repterminationtype = 4;
        result = ae_false;
        return result;
    }
    
    /*
     * Choose initial step and direction.
     * Apply preconditioner, if we have something other than default.
     */
    rcopymulv(n, -1.0, &state->g, &state->d, _state);
    if( state->prectype==0 )
    {
        
        /*
         * Default preconditioner is used, but we can't use it before iterations will start
         */
        v = ae_sqrt(rdotv2(n, &state->g, _state), _state);
        if( ae_fp_eq(state->stpmax,(double)(0)) )
        {
            state->stp = ae_minreal(1.0/v, (double)(1), _state);
        }
        else
        {
            state->stp = ae_minreal(1.0/v, state->stpmax, _state);
        }
    }
    if( state->prectype==1 )
    {
        
        /*
         * Cholesky preconditioner is used
         */
        fblscholeskysolve(&state->denseh, 1.0, n, ae_true, &state->d, &state->autobuf, _state);
        state->stp = (double)(1);
    }
    if( state->prectype==2 )
    {
        
        /*
         * diagonal approximation is used
         */
        rmergedivv(n, &state->diagh, &state->d, _state);
        state->stp = (double)(1);
    }
    if( state->prectype==3 )
    {
        
        /*
         * scale-based preconditioner is used
         */
        for(i=0; i<=n-1; i++)
        {
            state->d.ptr.p_double[i] = state->d.ptr.p_double[i]*state->s.ptr.p_double[i]*state->s.ptr.p_double[i];
        }
        state->stp = (double)(1);
    }
    if( state->prectype==4 )
    {
        
        /*
         * rank-k BFGS-based preconditioner is used
         */
        inexactlbfgspreconditioner(&state->d, n, &state->precd, &state->precc, &state->precw, state->preck, &state->precbuf, _state);
        state->stp = (double)(1);
    }
    if( state->prectype==5 )
    {
        
        /*
         * exact low-rank preconditioner is used
         */
        applylowrankpreconditioner(&state->d, &state->lowrankbuf, _state);
        state->stp = (double)(1);
    }
    linminnormalized(&state->d, &state->stp, n, _state);
    state->longeststp = state->stp;
    
    /*
     * Main cycle
     */
    state->k = 0;
lbl_41:
    if( ae_false )
    {
        goto lbl_42;
    }
    
    /*
     * Main cycle: prepare to 1-D line search
     */
    state->p = state->k%m;
    state->q = ae_minint(state->k, m-1, _state);
    
    /*
     * Store X[k], G[k]
     */
    ae_v_move(&state->xp.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_moveneg(&state->sk.ptr.pp_double[state->p][0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_moveneg(&state->yk.ptr.pp_double[state->p][0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    
    /*
     * Minimize F(x+alpha*d)
     * Calculate S[k], Y[k]
     */
    state->mcstage = 0;
    if( state->k!=0 )
    {
        state->stp = 1.0;
    }
    linminnormalized(&state->d, &state->stp, n, _state);
    state->stplimit = (double)10*state->longeststp;
    if( ae_fp_neq(state->stpmax,(double)(0)) )
    {
        state->stplimit = ae_minreal(state->stplimit, state->stpmax, _state);
    }
    smoothnessmonitorstartlinesearch1u(&state->smonitor, &state->s, &state->invs, &state->x, state->f, &state->g, state->k, -1, _state);
    mcsrch(n, &state->x, &state->f, &state->g, &state->d, &state->stp, state->stplimit, minlbfgs_gtol, &mcinfo, &state->nfev, &state->work, &state->lstate, &state->mcstage, _state);
lbl_43:
    if( state->mcstage==0 )
    {
        goto lbl_44;
    }
    if( state->protocolversion!=2 )
    {
        goto lbl_45;
    }
    
    /*
     * Issue request using V2 protocol
     */
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_47;
    }
    
    /*
     * Request dense analytic Jacobian
     */
    state->requesttype = 2;
    state->querysize = 1;
    state->queryfuncs = 1;
    state->queryvars = n;
    state->querydim = 0;
    rcopyallocv(n, &state->x, &state->querydata, _state);
    rallocv(1, &state->replyfi, _state);
    rallocv(n, &state->replydj, _state);
    state->rstate.stage = 12;
    goto lbl_rcomm;
lbl_12:
    state->f = state->replyfi.ptr.p_double[0];
    rcopyallocv(n, &state->replydj, &state->g, _state);
    goto lbl_48;
lbl_47:
    
    /*
     * Request dense numerical Jacobian, propose 5-point formula
     */
    state->requesttype = 3;
    state->querysize = 1;
    state->queryfuncs = 1;
    state->queryvars = n;
    state->querydim = 0;
    state->queryformulasize = 5;
    rallocv(n+n*2*5, &state->querydata, _state);
    rcopyallocv(n, &state->x, &state->querydata, _state);
    for(i=0; i<=n-1; i++)
    {
        v = state->diffstep*state->s.ptr.p_double[i];
        state->querydata.ptr.p_double[n+10*i+0*2+0] = (double)(0);
        state->querydata.ptr.p_double[n+10*i+0*2+1] = (double)(0);
        state->querydata.ptr.p_double[n+10*i+1*2+0] = state->querydata.ptr.p_double[i]-1.0*v;
        state->querydata.ptr.p_double[n+10*i+1*2+1] = (double)1/((double)6*v);
        state->querydata.ptr.p_double[n+10*i+2*2+0] = state->querydata.ptr.p_double[i]-0.5*v;
        state->querydata.ptr.p_double[n+10*i+2*2+1] = -(double)8/((double)6*v);
        state->querydata.ptr.p_double[n+10*i+3*2+0] = state->querydata.ptr.p_double[i]+0.5*v;
        state->querydata.ptr.p_double[n+10*i+3*2+1] = (double)8/((double)6*v);
        state->querydata.ptr.p_double[n+10*i+4*2+0] = state->querydata.ptr.p_double[i]+1.0*v;
        state->querydata.ptr.p_double[n+10*i+4*2+1] = -(double)1/((double)6*v);
    }
    rallocv(1, &state->replyfi, _state);
    rallocv(n, &state->replydj, _state);
    state->rstate.stage = 13;
    goto lbl_rcomm;
lbl_13:
    state->f = state->replyfi.ptr.p_double[0];
    rcopyallocv(n, &state->replydj, &state->g, _state);
lbl_48:
    goto lbl_46;
lbl_45:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLBFGS: unexpected protocol, integrity check 1506 failed", _state);
    minlbfgs_clearrequestfields(state, _state);
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_49;
    }
    state->needfg = ae_true;
    state->rstate.stage = 14;
    goto lbl_rcomm;
lbl_14:
    state->needfg = ae_false;
    goto lbl_50;
lbl_49:
    state->needf = ae_true;
    state->rstate.stage = 15;
    goto lbl_rcomm;
lbl_15:
    state->fbase = state->f;
    i = 0;
lbl_51:
    if( i>n-1 )
    {
        goto lbl_53;
    }
    v = state->x.ptr.p_double[i];
    state->x.ptr.p_double[i] = v-state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 16;
    goto lbl_rcomm;
lbl_16:
    state->fm2 = state->f;
    state->x.ptr.p_double[i] = v-0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 17;
    goto lbl_rcomm;
lbl_17:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = v+0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 18;
    goto lbl_rcomm;
lbl_18:
    state->fp1 = state->f;
    state->x.ptr.p_double[i] = v+state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 19;
    goto lbl_rcomm;
lbl_19:
    state->fp2 = state->f;
    state->x.ptr.p_double[i] = v;
    state->g.ptr.p_double[i] = ((double)8*(state->fp1-state->fm1)-(state->fp2-state->fm2))/((double)6*state->diffstep*state->s.ptr.p_double[i]);
    i = i+1;
    goto lbl_51;
lbl_53:
    state->f = state->fbase;
    state->needf = ae_false;
lbl_50:
lbl_46:
    smoothnessmonitorenqueuepoint1u(&state->smonitor, &state->s, &state->invs, &state->d, state->stp, &state->x, state->f, &state->g, _state);
    trimfunction(&state->f, &state->g, n, state->trimthreshold, _state);
    mcsrch(n, &state->x, &state->f, &state->g, &state->d, &state->stp, state->stplimit, minlbfgs_gtol, &mcinfo, &state->nfev, &state->work, &state->lstate, &state->mcstage, _state);
    goto lbl_43;
lbl_44:
    smoothnessmonitorfinalizelinesearch(&state->smonitor, _state);
    if( state->userterminationneeded )
    {
        
        /*
         * User requested termination.
         * Restore previous point and return.
         */
        ae_v_move(&state->x.ptr.p_double[0], 1, &state->xp.ptr.p_double[0], 1, ae_v_len(0,n-1));
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    if( !state->xrep )
    {
        goto lbl_54;
    }
    
    /*
     * report
     */
    if( state->protocolversion!=2 )
    {
        goto lbl_56;
    }
    state->requesttype = -1;
    state->queryvars = n;
    state->reportf = state->f;
    rcopyallocv(n, &state->x, &state->reportx, _state);
    state->rstate.stage = 20;
    goto lbl_rcomm;
lbl_20:
    goto lbl_57;
lbl_56:
    ae_assert(state->protocolversion==1, "MINLBFGS: unexpected protocol, integrity check 8007 failed", _state);
    minlbfgs_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 21;
    goto lbl_rcomm;
lbl_21:
    state->xupdated = ae_false;
lbl_57:
lbl_54:
    state->repnfev = state->repnfev+state->nfev;
    state->repiterationscount = state->repiterationscount+1;
    ae_v_add(&state->sk.ptr.pp_double[state->p][0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_add(&state->yk.ptr.pp_double[state->p][0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->longeststp = ae_maxreal(state->longeststp, state->stp, _state);
    
    /*
     * Stopping conditions
     */
    v = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr(state->g.ptr.p_double[i]*state->s.ptr.p_double[i], _state);
    }
    if( !ae_isfinite(v, _state)||!ae_isfinite(state->f, _state) )
    {
        
        /*
         * Abnormal termination - infinities in function/gradient
         */
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    if( state->repiterationscount>=state->maxits&&state->maxits>0 )
    {
        
        /*
         * Too many iterations
         */
        state->repterminationtype = 5;
        result = ae_false;
        return result;
    }
    if( ae_fp_less_eq(ae_sqrt(v, _state),state->epsg) )
    {
        
        /*
         * Gradient is small enough
         */
        state->repterminationtype = 4;
        result = ae_false;
        return result;
    }
    if( ae_fp_less_eq(state->fold-state->f,state->epsf*ae_maxreal(ae_fabs(state->fold, _state), ae_maxreal(ae_fabs(state->f, _state), 1.0, _state), _state)) )
    {
        
        /*
         * F(k+1)-F(k) is small enough
         */
        state->repterminationtype = 1;
        result = ae_false;
        return result;
    }
    v = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr(state->sk.ptr.pp_double[state->p][i]/state->s.ptr.p_double[i], _state);
    }
    if( ae_fp_less_eq(ae_sqrt(v, _state),state->epsx) )
    {
        
        /*
         * X(k+1)-X(k) is small enough
         */
        state->repterminationtype = 2;
        result = ae_false;
        return result;
    }
    
    /*
     * If Wolfe conditions are satisfied, we can update
     * limited memory model.
     *
     * However, if conditions are not satisfied (NFEV limit is met,
     * function is too wild, ...), we'll skip L-BFGS update
     */
    if( mcinfo!=1 )
    {
        
        /*
         * Skip update.
         *
         * In such cases we'll initialize search direction by
         * antigradient vector, because it  leads to more
         * transparent code with less number of special cases
         */
        state->fold = state->f;
        ae_v_moveneg(&state->d.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    }
    else
    {
        
        /*
         * Calculate Rho[k], GammaK
         */
        v = ae_v_dotproduct(&state->yk.ptr.pp_double[state->p][0], 1, &state->sk.ptr.pp_double[state->p][0], 1, ae_v_len(0,n-1));
        vv = ae_v_dotproduct(&state->yk.ptr.pp_double[state->p][0], 1, &state->yk.ptr.pp_double[state->p][0], 1, ae_v_len(0,n-1));
        if( ae_fp_eq(v,(double)(0))||ae_fp_eq(vv,(double)(0)) )
        {
            
            /*
             * Rounding errors make further iterations impossible.
             */
            state->repterminationtype = -2;
            result = ae_false;
            return result;
        }
        state->rho.ptr.p_double[state->p] = (double)1/v;
        state->gammak = v/vv;
        
        /*
         *  Calculate d(k+1) = -H(k+1)*g(k+1)
         *
         *  for I:=K downto K-Q do
         *      V = s(i)^T * work(iteration:I)
         *      theta(i) = V
         *      work(iteration:I+1) = work(iteration:I) - V*Rho(i)*y(i)
         *  work(last iteration) = H0*work(last iteration) - preconditioner
         *  for I:=K-Q to K do
         *      V = y(i)^T*work(iteration:I)
         *      work(iteration:I+1) = work(iteration:I) +(-V+theta(i))*Rho(i)*s(i)
         *
         *  NOW WORK CONTAINS d(k+1)
         */
        ae_v_move(&state->work.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
        for(i=state->k; i>=state->k-state->q; i--)
        {
            ic = i%m;
            v = ae_v_dotproduct(&state->sk.ptr.pp_double[ic][0], 1, &state->work.ptr.p_double[0], 1, ae_v_len(0,n-1));
            state->theta.ptr.p_double[ic] = v;
            vv = v*state->rho.ptr.p_double[ic];
            ae_v_subd(&state->work.ptr.p_double[0], 1, &state->yk.ptr.pp_double[ic][0], 1, ae_v_len(0,n-1), vv);
        }
        if( state->prectype==0 )
        {
            
            /*
             * Simple preconditioner is used
             */
            v = state->gammak;
            ae_v_muld(&state->work.ptr.p_double[0], 1, ae_v_len(0,n-1), v);
        }
        if( state->prectype==1 )
        {
            
            /*
             * Cholesky preconditioner is used
             */
            fblscholeskysolve(&state->denseh, (double)(1), n, ae_true, &state->work, &state->autobuf, _state);
        }
        if( state->prectype==2 )
        {
            
            /*
             * diagonal approximation is used
             */
            for(i=0; i<=n-1; i++)
            {
                state->work.ptr.p_double[i] = state->work.ptr.p_double[i]/state->diagh.ptr.p_double[i];
            }
        }
        if( state->prectype==3 )
        {
            
            /*
             * scale-based preconditioner is used
             */
            for(i=0; i<=n-1; i++)
            {
                state->work.ptr.p_double[i] = state->work.ptr.p_double[i]*state->s.ptr.p_double[i]*state->s.ptr.p_double[i];
            }
        }
        if( state->prectype==4 )
        {
            
            /*
             * Rank-K BFGS-based preconditioner is used
             */
            inexactlbfgspreconditioner(&state->work, n, &state->precd, &state->precc, &state->precw, state->preck, &state->precbuf, _state);
        }
        if( state->prectype==5 )
        {
            
            /*
             * Exact low-rank preconditioner is used
             */
            applylowrankpreconditioner(&state->work, &state->lowrankbuf, _state);
        }
        for(i=state->k-state->q; i<=state->k; i++)
        {
            ic = i%m;
            v = ae_v_dotproduct(&state->yk.ptr.pp_double[ic][0], 1, &state->work.ptr.p_double[0], 1, ae_v_len(0,n-1));
            vv = state->rho.ptr.p_double[ic]*(-v+state->theta.ptr.p_double[ic]);
            ae_v_addd(&state->work.ptr.p_double[0], 1, &state->sk.ptr.pp_double[ic][0], 1, ae_v_len(0,n-1), vv);
        }
        ae_v_moveneg(&state->d.ptr.p_double[0], 1, &state->work.ptr.p_double[0], 1, ae_v_len(0,n-1));
        
        /*
         * Next step
         */
        state->fold = state->f;
        state->k = state->k+1;
    }
    goto lbl_41;
lbl_42:
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = n;
    state->rstate.ia.ptr.p_int[1] = m;
    state->rstate.ia.ptr.p_int[2] = i;
    state->rstate.ia.ptr.p_int[3] = j;
    state->rstate.ia.ptr.p_int[4] = ic;
    state->rstate.ia.ptr.p_int[5] = mcinfo;
    state->rstate.ra.ptr.p_double[0] = v;
    state->rstate.ra.ptr.p_double[1] = vv;
    return result;
}


/*************************************************************************
This  function  activates/deactivates verification  of  the  user-supplied
analytic gradient.

Upon  activation  of  this  option  OptGuard  integrity  checker  performs
numerical differentiation of your target function  at  the  initial  point
(note: future versions may also perform check  at  the  final  point)  and
compares numerical gradient with analytic one provided by you.

If difference is too large, an error flag is set and optimization  session
continues. After optimization session is over, you can retrieve the report
which  stores  both  gradients  and  specific  components  highlighted  as
suspicious by the OptGuard.

The primary OptGuard report can be retrieved with minlbfgsoptguardresults().

IMPORTANT: gradient check is a high-overhead option which  will  cost  you
           about 3*N additional function evaluations. In many cases it may
           cost as much as the rest of the optimization session.
           
           YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO
           CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY.

NOTE: unlike previous incarnation of the gradient checking code,  OptGuard
      does NOT interrupt optimization even if it discovers bad gradient.
      
INPUT PARAMETERS:
    State       -   structure used to store algorithm state
    TestStep    -   verification step used for numerical differentiation:
                    * TestStep=0 turns verification off
                    * TestStep>0 activates verification
                    You should carefully choose TestStep. Value  which  is
                    too large (so large that  function  behavior  is  non-
                    cubic at this scale) will lead  to  false  alarms. Too
                    short step will result in rounding  errors  dominating
                    numerical derivative.
                    
                    You may use different step for different parameters by
                    means of setting scale with minlbfgssetscale().

=== EXPLANATION ==========================================================                    

In order to verify gradient algorithm performs following steps:
  * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i],
    where X[i] is i-th component of the initial point and S[i] is a  scale
    of i-th parameter
  * F(X) is evaluated at these trial points
  * we perform one more evaluation in the middle point of the interval
  * we  build  cubic  model using function values and derivatives at trial
    points and we compare its prediction with actual value in  the  middle
    point

  -- ALGLIB --
     Copyright 15.06.2014 by Bochkanov Sergey
*************************************************************************/
void minlbfgsoptguardgradient(minlbfgsstate* state,
     double teststep,
     ae_state *_state)
{


    ae_assert(ae_isfinite(teststep, _state), "MinLBFGSOptGuardGradient: TestStep contains NaN or INF", _state);
    ae_assert(ae_fp_greater_eq(teststep,(double)(0)), "MinLBFGSOptGuardGradient: invalid argument TestStep(TestStep<0)", _state);
    state->teststep = teststep;
}


/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0)
b) nonsmooth     target function (non-C1)

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.
      
      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.
      
      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.
      
      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.
  
=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minlbfgsoptguardsmoothness(minlbfgsstate* state,
     ae_int_t level,
     ae_state *_state)
{


    ae_assert(level==0||level==1, "MinLBFGSOptGuardSmoothness: unexpected value of level parameter", _state);
    state->smoothnessguardlevel = level;
}


/*************************************************************************
Results of OptGuard integrity check, should be called  after  optimization
session is over.

=== PRIMARY REPORT =======================================================

OptGuard performs several checks which are intended to catch common errors
in the implementation of nonlinear function/gradient:
* incorrect analytic gradient
* discontinuous (non-C0) target functions (constraints)
* nonsmooth     (non-C1) target functions (constraints)

Each of these checks is activated with appropriate function:
* minlbfgsoptguardgradient() for gradient verification
* minlbfgsoptguardsmoothness() for C0/C1 checks

Following flags are set when these errors are suspected:
* rep.badgradsuspected, and additionally:
  * rep.badgradvidx for specific variable (gradient element) suspected
  * rep.badgradxbase, a point where gradient is tested
  * rep.badgraduser, user-provided gradient  (stored  as  2D  matrix  with
    single row in order to make  report  structure  compatible  with  more
    complex optimizers like MinNLC or MinLM)
  * rep.badgradnum,   reference    gradient    obtained    via   numerical
    differentiation (stored as  2D matrix with single row in order to make
    report structure compatible with more complex optimizers  like  MinNLC
    or MinLM)
* rep.nonc0suspected
* rep.nonc1suspected

=== ADDITIONAL REPORTS/LOGS ==============================================
    
Several different tests are performed to catch C0/C1 errors, you can  find
out specific test signaled error by looking to:
* rep.nonc0test0positive, for non-C0 test #0
* rep.nonc1test0positive, for non-C1 test #0
* rep.nonc1test1positive, for non-C1 test #1

Additional information (including line search logs)  can  be  obtained  by
means of:
* minlbfgsoptguardnonc1test0results()
* minlbfgsoptguardnonc1test1results()
which return detailed error reports, specific points where discontinuities
were found, and so on.

==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    rep     -   generic OptGuard report;  more  detailed  reports  can  be
                retrieved with other functions.

NOTE: false negatives (nonsmooth problems are not identified as  nonsmooth
      ones) are possible although unlikely.
      
      The reason  is  that  you  need  to  make several evaluations around
      nonsmoothness  in  order  to  accumulate  enough  information  about
      function curvature. Say, if you start right from the nonsmooth point,
      optimizer simply won't get enough data to understand what  is  going
      wrong before it terminates due to abrupt changes in the  derivative.
      It is also  possible  that  "unlucky"  step  will  move  us  to  the
      termination too quickly.
      
      Our current approach is to have less than 0.1%  false  negatives  in
      our test examples  (measured  with  multiple  restarts  from  random
      points), and to have exactly 0% false positives.
   
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minlbfgsoptguardresults(minlbfgsstate* state,
     optguardreport* rep,
     ae_state *_state)
{

    _optguardreport_clear(rep);

    smoothnessmonitorexportreport(&state->smonitor, rep, _state);
}


/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #0

Nonsmoothness (non-C1) test #0 studies  function  values  (not  gradient!)
obtained during line searches and monitors  behavior  of  the  directional
derivative estimate.

This test is less powerful than test #1, but it does  not  depend  on  the
gradient values and thus it is more robust against artifacts introduced by
numerical differentiation.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], f[] - arrays of length CNT which store step lengths and  function
  values at these points; f[i] is evaluated in x0+stp[i]*d.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
  
==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #0 "strong" report
    lngrep  -   C1 test #0 "long" report
    
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minlbfgsoptguardnonc1test0results(const minlbfgsstate* state,
     optguardnonc1test0report* strrep,
     optguardnonc1test0report* lngrep,
     ae_state *_state)
{

    _optguardnonc1test0report_clear(strrep);
    _optguardnonc1test0report_clear(lngrep);

    smoothnessmonitorexportc1test0report(&state->smonitor.nonc1test0strrep, &state->lastscaleused, strrep, _state);
    smoothnessmonitorexportc1test0report(&state->smonitor.nonc1test0lngrep, &state->lastscaleused, lngrep, _state);
}


/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #1

Nonsmoothness (non-C1)  test  #1  studies  individual  components  of  the
gradient computed during line search.

When precise analytic gradient is provided this test is more powerful than
test #0  which  works  with  function  values  and  ignores  user-provided
gradient.  However,  test  #0  becomes  more   powerful   when   numerical
differentiation is employed (in such cases test #1 detects  higher  levels
of numerical noise and becomes too conservative).

This test also tells specific components of the gradient which violate  C1
continuity, which makes it more informative than #0, which just tells that
continuity is violated.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* vidx - is an index of the variable in [0,N) with nonsmooth derivative
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], g[] - arrays of length CNT which store step lengths and  gradient
  values at these points; g[i] is evaluated in  x0+stp[i]*d  and  contains
  vidx-th component of the gradient.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
  
==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #1 "strong" report
    lngrep  -   C1 test #1 "long" report
    
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minlbfgsoptguardnonc1test1results(minlbfgsstate* state,
     optguardnonc1test1report* strrep,
     optguardnonc1test1report* lngrep,
     ae_state *_state)
{

    _optguardnonc1test1report_clear(strrep);
    _optguardnonc1test1report_clear(lngrep);

    smoothnessmonitorexportc1test1report(&state->smonitor.nonc1test1strrep, &state->lastscaleused, strrep, _state);
    smoothnessmonitorexportc1test1report(&state->smonitor.nonc1test1lngrep, &state->lastscaleused, lngrep, _state);
}


/*************************************************************************
L-BFGS algorithm results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report:
                * Rep.TerminationType completetion code:
                    * -8    internal integrity control  detected  infinite
                            or NAN values in  function/gradient.  Abnormal
                            termination signalled.
                    * -2    rounding errors prevent further improvement.
                            X contains best point found.
                    * -1    incorrect parameters were specified
                    *  1    relative function improvement is no more than
                            EpsF.
                    *  2    relative step is no more than EpsX.
                    *  4    gradient norm is no more than EpsG
                    *  5    MaxIts steps was taken
                    *  7    stopping conditions are too stringent,
                            further improvement is impossible
                    *  8    terminated by user who called minlbfgsrequesttermination().
                            X contains point which was "current accepted" when
                            termination request was submitted.
                * Rep.IterationsCount contains iterations count
                * NFEV countains number of function calculations

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgsresults(const minlbfgsstate* state,
     /* Real    */ ae_vector* x,
     minlbfgsreport* rep,
     ae_state *_state)
{

    ae_vector_clear(x);
    _minlbfgsreport_clear(rep);

    minlbfgsresultsbuf(state, x, rep, _state);
}


/*************************************************************************
L-BFGS algorithm results

Buffered implementation of MinLBFGSResults which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 20.08.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgsresultsbuf(const minlbfgsstate* state,
     /* Real    */ ae_vector* x,
     minlbfgsreport* rep,
     ae_state *_state)
{


    if( x->cnt<state->n )
    {
        ae_vector_set_length(x, state->n, _state);
    }
    ae_v_move(&x->ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,state->n-1));
    rep->iterationscount = state->repiterationscount;
    rep->nfev = state->repnfev;
    rep->terminationtype = state->repterminationtype;
}


/*************************************************************************
This  subroutine restarts LBFGS algorithm from new point. All optimization
parameters are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure used to store algorithm state
    X       -   new starting point.

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgsrestartfrom(minlbfgsstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{


    ae_assert(x->cnt>=state->n, "MinLBFGSRestartFrom: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, state->n, _state), "MinLBFGSRestartFrom: X contains infinite or NaN values!", _state);
    ae_v_move(&state->xbase.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,state->n-1));
    ae_vector_set_length(&state->rstate.ia, 5+1, _state);
    ae_vector_set_length(&state->rstate.ra, 1+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.
      
NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void minlbfgsrequesttermination(minlbfgsstate* state, ae_state *_state)
{


    state->userterminationneeded = ae_true;
}


/*************************************************************************
Set V1 reverse communication protocol
*************************************************************************/
void minlbfgssetprotocolv1(minlbfgsstate* state, ae_state *_state)
{


    state->protocolversion = 1;
    ae_vector_set_length(&state->rstate.ia, 5+1, _state);
    ae_vector_set_length(&state->rstate.ra, 1+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Set V2 reverse communication protocol
*************************************************************************/
void minlbfgssetprotocolv2(minlbfgsstate* state, ae_state *_state)
{


    state->protocolversion = 2;
    ae_vector_set_length(&state->rstate.ia, 5+1, _state);
    ae_vector_set_length(&state->rstate.ra, 1+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Clears request fileds (to be sure that we don't forgot to clear something)
*************************************************************************/
static void minlbfgs_clearrequestfields(minlbfgsstate* state,
     ae_state *_state)
{


    ae_assert(state->protocolversion==1, "MINLBFGS: unexpected protocol", _state);
    state->needf = ae_false;
    state->needfg = ae_false;
    state->xupdated = ae_false;
}


void _minlbfgsstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minlbfgsstate *p = (minlbfgsstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rho, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->yk, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->sk, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->theta, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->work, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xbase, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->denseh, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagh, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->precc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->precd, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->precw, 0, 0, DT_REAL, _state, make_automatic);
    _precbuflbfgs_init(&p->precbuf, _state, make_automatic);
    _precbuflowrank_init(&p->lowrankbuf, _state, make_automatic);
    ae_vector_init(&p->autobuf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->invs, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->reportx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->querydata, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyfi, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replydj, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->replysj, _state, make_automatic);
    ae_vector_init(&p->tmpx1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpc1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpf1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpg1, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpj1, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->tmps1, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    _linminstate_init(&p->lstate, _state, make_automatic);
    _smoothnessmonitor_init(&p->smonitor, _state, make_automatic);
    ae_vector_init(&p->lastscaleused, 0, DT_REAL, _state, make_automatic);
}


void _minlbfgsstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minlbfgsstate       *dst = (minlbfgsstate*)_dst;
    const minlbfgsstate *src = (const minlbfgsstate*)_src;
    dst->n = src->n;
    dst->m = src->m;
    dst->epsg = src->epsg;
    dst->epsf = src->epsf;
    dst->epsx = src->epsx;
    dst->maxits = src->maxits;
    dst->xrep = src->xrep;
    dst->stpmax = src->stpmax;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    dst->diffstep = src->diffstep;
    dst->nfev = src->nfev;
    dst->mcstage = src->mcstage;
    dst->k = src->k;
    dst->q = src->q;
    dst->p = src->p;
    ae_vector_init_copy(&dst->rho, &src->rho, _state, make_automatic);
    ae_matrix_init_copy(&dst->yk, &src->yk, _state, make_automatic);
    ae_matrix_init_copy(&dst->sk, &src->sk, _state, make_automatic);
    ae_vector_init_copy(&dst->xp, &src->xp, _state, make_automatic);
    ae_vector_init_copy(&dst->theta, &src->theta, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    dst->stp = src->stp;
    dst->longeststp = src->longeststp;
    ae_vector_init_copy(&dst->work, &src->work, _state, make_automatic);
    dst->fold = src->fold;
    dst->trimthreshold = src->trimthreshold;
    ae_vector_init_copy(&dst->xbase, &src->xbase, _state, make_automatic);
    dst->prectype = src->prectype;
    dst->gammak = src->gammak;
    ae_matrix_init_copy(&dst->denseh, &src->denseh, _state, make_automatic);
    ae_vector_init_copy(&dst->diagh, &src->diagh, _state, make_automatic);
    ae_vector_init_copy(&dst->precc, &src->precc, _state, make_automatic);
    ae_vector_init_copy(&dst->precd, &src->precd, _state, make_automatic);
    ae_matrix_init_copy(&dst->precw, &src->precw, _state, make_automatic);
    dst->preck = src->preck;
    _precbuflbfgs_init_copy(&dst->precbuf, &src->precbuf, _state, make_automatic);
    _precbuflowrank_init_copy(&dst->lowrankbuf, &src->lowrankbuf, _state, make_automatic);
    dst->fbase = src->fbase;
    dst->fm2 = src->fm2;
    dst->fm1 = src->fm1;
    dst->fp1 = src->fp1;
    dst->fp2 = src->fp2;
    dst->stplimit = src->stplimit;
    ae_vector_init_copy(&dst->autobuf, &src->autobuf, _state, make_automatic);
    ae_vector_init_copy(&dst->invs, &src->invs, _state, make_automatic);
    dst->protocolversion = src->protocolversion;
    dst->userterminationneeded = src->userterminationneeded;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    dst->f = src->f;
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    dst->needf = src->needf;
    dst->needfg = src->needfg;
    dst->xupdated = src->xupdated;
    dst->requesttype = src->requesttype;
    ae_vector_init_copy(&dst->reportx, &src->reportx, _state, make_automatic);
    dst->reportf = src->reportf;
    dst->querysize = src->querysize;
    dst->queryfuncs = src->queryfuncs;
    dst->queryvars = src->queryvars;
    dst->querydim = src->querydim;
    dst->queryformulasize = src->queryformulasize;
    ae_vector_init_copy(&dst->querydata, &src->querydata, _state, make_automatic);
    ae_vector_init_copy(&dst->replyfi, &src->replyfi, _state, make_automatic);
    ae_vector_init_copy(&dst->replydj, &src->replydj, _state, make_automatic);
    _sparsematrix_init_copy(&dst->replysj, &src->replysj, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpx1, &src->tmpx1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpc1, &src->tmpc1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpf1, &src->tmpf1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpg1, &src->tmpg1, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpj1, &src->tmpj1, _state, make_automatic);
    _sparsematrix_init_copy(&dst->tmps1, &src->tmps1, _state, make_automatic);
    dst->teststep = src->teststep;
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    dst->repiterationscount = src->repiterationscount;
    dst->repnfev = src->repnfev;
    dst->repterminationtype = src->repterminationtype;
    _linminstate_init_copy(&dst->lstate, &src->lstate, _state, make_automatic);
    dst->smoothnessguardlevel = src->smoothnessguardlevel;
    _smoothnessmonitor_init_copy(&dst->smonitor, &src->smonitor, _state, make_automatic);
    ae_vector_init_copy(&dst->lastscaleused, &src->lastscaleused, _state, make_automatic);
}


void _minlbfgsstate_clear(void* _p)
{
    minlbfgsstate *p = (minlbfgsstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->rho);
    ae_matrix_clear(&p->yk);
    ae_matrix_clear(&p->sk);
    ae_vector_clear(&p->xp);
    ae_vector_clear(&p->theta);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->work);
    ae_vector_clear(&p->xbase);
    ae_matrix_clear(&p->denseh);
    ae_vector_clear(&p->diagh);
    ae_vector_clear(&p->precc);
    ae_vector_clear(&p->precd);
    ae_matrix_clear(&p->precw);
    _precbuflbfgs_clear(&p->precbuf);
    _precbuflowrank_clear(&p->lowrankbuf);
    ae_vector_clear(&p->autobuf);
    ae_vector_clear(&p->invs);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->g);
    ae_vector_clear(&p->reportx);
    ae_vector_clear(&p->querydata);
    ae_vector_clear(&p->replyfi);
    ae_vector_clear(&p->replydj);
    _sparsematrix_clear(&p->replysj);
    ae_vector_clear(&p->tmpx1);
    ae_vector_clear(&p->tmpc1);
    ae_vector_clear(&p->tmpf1);
    ae_vector_clear(&p->tmpg1);
    ae_matrix_clear(&p->tmpj1);
    _sparsematrix_clear(&p->tmps1);
    _rcommstate_clear(&p->rstate);
    _linminstate_clear(&p->lstate);
    _smoothnessmonitor_clear(&p->smonitor);
    ae_vector_clear(&p->lastscaleused);
}


void _minlbfgsstate_destroy(void* _p)
{
    minlbfgsstate *p = (minlbfgsstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->rho);
    ae_matrix_destroy(&p->yk);
    ae_matrix_destroy(&p->sk);
    ae_vector_destroy(&p->xp);
    ae_vector_destroy(&p->theta);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->work);
    ae_vector_destroy(&p->xbase);
    ae_matrix_destroy(&p->denseh);
    ae_vector_destroy(&p->diagh);
    ae_vector_destroy(&p->precc);
    ae_vector_destroy(&p->precd);
    ae_matrix_destroy(&p->precw);
    _precbuflbfgs_destroy(&p->precbuf);
    _precbuflowrank_destroy(&p->lowrankbuf);
    ae_vector_destroy(&p->autobuf);
    ae_vector_destroy(&p->invs);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->g);
    ae_vector_destroy(&p->reportx);
    ae_vector_destroy(&p->querydata);
    ae_vector_destroy(&p->replyfi);
    ae_vector_destroy(&p->replydj);
    _sparsematrix_destroy(&p->replysj);
    ae_vector_destroy(&p->tmpx1);
    ae_vector_destroy(&p->tmpc1);
    ae_vector_destroy(&p->tmpf1);
    ae_vector_destroy(&p->tmpg1);
    ae_matrix_destroy(&p->tmpj1);
    _sparsematrix_destroy(&p->tmps1);
    _rcommstate_destroy(&p->rstate);
    _linminstate_destroy(&p->lstate);
    _smoothnessmonitor_destroy(&p->smonitor);
    ae_vector_destroy(&p->lastscaleused);
}


void _minlbfgsreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minlbfgsreport *p = (minlbfgsreport*)_p;
    ae_touch_ptr((void*)p);
}


void _minlbfgsreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minlbfgsreport       *dst = (minlbfgsreport*)_dst;
    const minlbfgsreport *src = (const minlbfgsreport*)_src;
    dst->iterationscount = src->iterationscount;
    dst->nfev = src->nfev;
    dst->terminationtype = src->terminationtype;
}


void _minlbfgsreport_clear(void* _p)
{
    minlbfgsreport *p = (minlbfgsreport*)_p;
    ae_touch_ptr((void*)p);
}


void _minlbfgsreport_destroy(void* _p)
{
    minlbfgsreport *p = (minlbfgsreport*)_p;
    ae_touch_ptr((void*)p);
}


#endif
#if defined(AE_COMPILE_CQMODELS) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
This subroutine is used to initialize CQM. By default, empty NxN model  is
generated, with Alpha=Lambda=Theta=0.0 and zero b.

Previously allocated buffer variables are reused as much as possible.

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqminit(ae_int_t n, convexquadraticmodel* s, ae_state *_state)
{
    ae_int_t i;


    s->n = n;
    s->k = 0;
    s->nfree = n;
    s->ecakind = -1;
    s->alpha = 0.0;
    s->tau = 0.0;
    s->theta = 0.0;
    s->ismaintermchanged = ae_true;
    s->issecondarytermchanged = ae_true;
    s->islineartermchanged = ae_true;
    s->isactivesetchanged = ae_true;
    bvectorsetlengthatleast(&s->activeset, n, _state);
    rvectorsetlengthatleast(&s->xc, n, _state);
    rvectorsetlengthatleast(&s->eb, n, _state);
    rvectorsetlengthatleast(&s->tq1, n, _state);
    rvectorsetlengthatleast(&s->txc, n, _state);
    rvectorsetlengthatleast(&s->tb, n, _state);
    rvectorsetlengthatleast(&s->b, s->n, _state);
    rvectorsetlengthatleast(&s->tk1, s->n, _state);
    for(i=0; i<=n-1; i++)
    {
        s->activeset.ptr.p_bool[i] = ae_false;
        s->xc.ptr.p_double[i] = 0.0;
        s->b.ptr.p_double[i] = 0.0;
    }
}


/*************************************************************************
This subroutine changes main quadratic term of the model.

INPUT PARAMETERS:
    S       -   model
    A       -   NxN matrix, only upper or lower triangle is referenced
    IsUpper -   True, when matrix is stored in upper triangle
    Alpha   -   multiplier; when Alpha=0, A is not referenced at all

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqmseta(convexquadraticmodel* s,
     /* Real    */ const ae_matrix* a,
     ae_bool isupper,
     double alpha,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;


    ae_assert(ae_isfinite(alpha, _state)&&ae_fp_greater_eq(alpha,(double)(0)), "CQMSetA: Alpha<0 or is not finite number", _state);
    ae_assert(ae_fp_eq(alpha,(double)(0))||isfinitertrmatrix(a, s->n, isupper, _state), "CQMSetA: A is not finite NxN matrix", _state);
    s->alpha = alpha;
    if( ae_fp_greater(alpha,(double)(0)) )
    {
        rmatrixsetlengthatleast(&s->a, s->n, s->n, _state);
        rmatrixsetlengthatleast(&s->ecadense, s->n, s->n, _state);
        rmatrixsetlengthatleast(&s->tq2dense, s->n, s->n, _state);
        for(i=0; i<=s->n-1; i++)
        {
            for(j=i; j<=s->n-1; j++)
            {
                if( isupper )
                {
                    v = a->ptr.pp_double[i][j];
                }
                else
                {
                    v = a->ptr.pp_double[j][i];
                }
                s->a.ptr.pp_double[i][j] = v;
                s->a.ptr.pp_double[j][i] = v;
            }
        }
    }
    s->ismaintermchanged = ae_true;
}


/*************************************************************************
This subroutine changes main quadratic term of the model.

INPUT PARAMETERS:
    S       -   model
    A       -   possibly preallocated buffer
    
OUTPUT PARAMETERS:
    A       -   NxN matrix, full matrix is returned.
                Zero matrix is returned if model is empty.

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqmgeta(const convexquadraticmodel* s,
     /* Real    */ ae_matrix* a,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_int_t n;


    n = s->n;
    rmatrixsetlengthatleast(a, n, n, _state);
    if( ae_fp_greater(s->alpha,(double)(0)) )
    {
        v = s->alpha;
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a->ptr.pp_double[i][j] = v*s->a.ptr.pp_double[i][j];
            }
        }
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a->ptr.pp_double[i][j] = 0.0;
            }
        }
    }
}


/*************************************************************************
This subroutine rewrites diagonal of the main quadratic term of the  model
(dense  A)  by  vector  Z/Alpha (current value of the Alpha coefficient is
used).

IMPORTANT: in  case  model  has  no  dense  quadratic  term, this function
           allocates N*N dense matrix of zeros, and fills its diagonal  by
           non-zero values.

INPUT PARAMETERS:
    S       -   model
    Z       -   new diagonal, array[N]

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqmrewritedensediagonal(convexquadraticmodel* s,
     /* Real    */ const ae_vector* z,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;


    n = s->n;
    if( ae_fp_eq(s->alpha,(double)(0)) )
    {
        rmatrixsetlengthatleast(&s->a, s->n, s->n, _state);
        rmatrixsetlengthatleast(&s->ecadense, s->n, s->n, _state);
        rmatrixsetlengthatleast(&s->tq2dense, s->n, s->n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                s->a.ptr.pp_double[i][j] = 0.0;
            }
        }
        s->alpha = 1.0;
    }
    for(i=0; i<=s->n-1; i++)
    {
        s->a.ptr.pp_double[i][i] = z->ptr.p_double[i]/s->alpha;
    }
    s->ismaintermchanged = ae_true;
}


/*************************************************************************
This subroutine changes diagonal quadratic term of the model.

INPUT PARAMETERS:
    S       -   model
    D       -   array[N], semidefinite diagonal matrix
    Tau     -   multiplier; when Tau=0, D is not referenced at all

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqmsetd(convexquadraticmodel* s,
     /* Real    */ const ae_vector* d,
     double tau,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(ae_isfinite(tau, _state)&&ae_fp_greater_eq(tau,(double)(0)), "CQMSetD: Tau<0 or is not finite number", _state);
    ae_assert(ae_fp_eq(tau,(double)(0))||isfinitevector(d, s->n, _state), "CQMSetD: D is not finite Nx1 vector", _state);
    s->tau = tau;
    if( ae_fp_greater(tau,(double)(0)) )
    {
        rvectorsetlengthatleast(&s->d, s->n, _state);
        rvectorsetlengthatleast(&s->ecadiag, s->n, _state);
        rvectorsetlengthatleast(&s->tq2diag, s->n, _state);
        for(i=0; i<=s->n-1; i++)
        {
            ae_assert(ae_fp_greater_eq(d->ptr.p_double[i],(double)(0)), "CQMSetD: D[i]<0", _state);
            s->d.ptr.p_double[i] = d->ptr.p_double[i];
        }
    }
    s->ismaintermchanged = ae_true;
}


/*************************************************************************
This subroutine drops main quadratic term A from the model. It is same  as
call  to  CQMSetA()  with  zero  A,   but gives better performance because
algorithm  knows  that  matrix  is  zero  and  can  optimize    subsequent
calculations.

INPUT PARAMETERS:
    S       -   model

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqmdropa(convexquadraticmodel* s, ae_state *_state)
{


    s->alpha = 0.0;
    s->ismaintermchanged = ae_true;
}


/*************************************************************************
This subroutine changes linear term of the model

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqmsetb(convexquadraticmodel* s,
     /* Real    */ const ae_vector* b,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(isfinitevector(b, s->n, _state), "CQMSetB: B is not finite vector", _state);
    rvectorsetlengthatleast(&s->b, s->n, _state);
    for(i=0; i<=s->n-1; i++)
    {
        s->b.ptr.p_double[i] = b->ptr.p_double[i];
    }
    s->islineartermchanged = ae_true;
}


/*************************************************************************
This subroutine changes linear term of the model

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqmsetq(convexquadraticmodel* s,
     /* Real    */ const ae_matrix* q,
     /* Real    */ const ae_vector* r,
     ae_int_t k,
     double theta,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    ae_assert(k>=0, "CQMSetQ: K<0", _state);
    ae_assert((k==0||ae_fp_eq(theta,(double)(0)))||apservisfinitematrix(q, k, s->n, _state), "CQMSetQ: Q is not finite matrix", _state);
    ae_assert((k==0||ae_fp_eq(theta,(double)(0)))||isfinitevector(r, k, _state), "CQMSetQ: R is not finite vector", _state);
    ae_assert(ae_isfinite(theta, _state)&&ae_fp_greater_eq(theta,(double)(0)), "CQMSetQ: Theta<0 or is not finite number", _state);
    
    /*
     * degenerate case: K=0 or Theta=0
     */
    if( k==0||ae_fp_eq(theta,(double)(0)) )
    {
        s->k = 0;
        s->theta = (double)(0);
        s->issecondarytermchanged = ae_true;
        return;
    }
    
    /*
     * General case: both Theta>0 and K>0
     */
    s->k = k;
    s->theta = theta;
    rmatrixsetlengthatleast(&s->q, s->k, s->n, _state);
    rvectorsetlengthatleast(&s->r, s->k, _state);
    rmatrixsetlengthatleast(&s->eq, s->k, s->n, _state);
    rmatrixsetlengthatleast(&s->eccm, s->k, s->k, _state);
    rmatrixsetlengthatleast(&s->tk2, s->k, s->n, _state);
    for(i=0; i<=s->k-1; i++)
    {
        for(j=0; j<=s->n-1; j++)
        {
            s->q.ptr.pp_double[i][j] = q->ptr.pp_double[i][j];
        }
        s->r.ptr.p_double[i] = r->ptr.p_double[i];
    }
    s->issecondarytermchanged = ae_true;
}


/*************************************************************************
This subroutine changes active set

INPUT PARAMETERS
    S       -   model
    X       -   array[N], constraint values
    ActiveSet-  array[N], active set. If ActiveSet[I]=True, then I-th
                variables is constrained to X[I].

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqmsetactiveset(convexquadraticmodel* s,
     /* Real    */ const ae_vector* x,
     /* Boolean */ const ae_vector* activeset,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(x->cnt>=s->n, "CQMSetActiveSet: Length(X)<N", _state);
    ae_assert(activeset->cnt>=s->n, "CQMSetActiveSet: Length(ActiveSet)<N", _state);
    for(i=0; i<=s->n-1; i++)
    {
        s->isactivesetchanged = s->isactivesetchanged||(s->activeset.ptr.p_bool[i]&&!activeset->ptr.p_bool[i]);
        s->isactivesetchanged = s->isactivesetchanged||(activeset->ptr.p_bool[i]&&!s->activeset.ptr.p_bool[i]);
        s->activeset.ptr.p_bool[i] = activeset->ptr.p_bool[i];
        if( activeset->ptr.p_bool[i] )
        {
            ae_assert(ae_isfinite(x->ptr.p_double[i], _state), "CQMSetActiveSet: X[] contains infinite constraints", _state);
            s->isactivesetchanged = s->isactivesetchanged||ae_fp_neq(s->xc.ptr.p_double[i],x->ptr.p_double[i]);
            s->xc.ptr.p_double[i] = x->ptr.p_double[i];
        }
    }
}


/*************************************************************************
This subroutine evaluates model at X. Active constraints are ignored.

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
double cqmeval(const convexquadraticmodel* s,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    double v;
    double result;


    n = s->n;
    ae_assert(isfinitevector(x, n, _state), "CQMEval: X is not finite vector", _state);
    result = 0.0;
    
    /*
     * main quadratic term
     */
    if( ae_fp_greater(s->alpha,(double)(0)) )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                result = result+s->alpha*0.5*x->ptr.p_double[i]*s->a.ptr.pp_double[i][j]*x->ptr.p_double[j];
            }
        }
    }
    if( ae_fp_greater(s->tau,(double)(0)) )
    {
        for(i=0; i<=n-1; i++)
        {
            result = result+0.5*ae_sqr(x->ptr.p_double[i], _state)*s->tau*s->d.ptr.p_double[i];
        }
    }
    
    /*
     * secondary quadratic term
     */
    if( ae_fp_greater(s->theta,(double)(0)) )
    {
        for(i=0; i<=s->k-1; i++)
        {
            v = ae_v_dotproduct(&s->q.ptr.pp_double[i][0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
            result = result+0.5*s->theta*ae_sqr(v-s->r.ptr.p_double[i], _state);
        }
    }
    
    /*
     * linear term
     */
    for(i=0; i<=s->n-1; i++)
    {
        result = result+x->ptr.p_double[i]*s->b.ptr.p_double[i];
    }
    return result;
}


/*************************************************************************
This subroutine evaluates model at X. Active constraints are ignored.
It returns:
    R   -   model value
    Noise-  estimate of the numerical noise in data

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqmevalx(const convexquadraticmodel* s,
     /* Real    */ const ae_vector* x,
     double* r,
     double* noise,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    double v;
    double v2;
    double mxq;
    double eps;

    *r = 0.0;
    *noise = 0.0;

    n = s->n;
    ae_assert(isfinitevector(x, n, _state), "CQMEval: X is not finite vector", _state);
    *r = 0.0;
    *noise = 0.0;
    eps = (double)2*ae_machineepsilon;
    mxq = 0.0;
    
    /*
     * Main quadratic term.
     *
     * Noise from the main quadratic term is equal to the
     * maximum summand in the term.
     */
    if( ae_fp_greater(s->alpha,(double)(0)) )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = s->alpha*0.5*x->ptr.p_double[i]*s->a.ptr.pp_double[i][j]*x->ptr.p_double[j];
                *r = *r+v;
                *noise = ae_maxreal(*noise, eps*ae_fabs(v, _state), _state);
            }
        }
    }
    if( ae_fp_greater(s->tau,(double)(0)) )
    {
        for(i=0; i<=n-1; i++)
        {
            v = 0.5*ae_sqr(x->ptr.p_double[i], _state)*s->tau*s->d.ptr.p_double[i];
            *r = *r+v;
            *noise = ae_maxreal(*noise, eps*ae_fabs(v, _state), _state);
        }
    }
    
    /*
     * secondary quadratic term
     *
     * Noise from the secondary quadratic term is estimated as follows:
     * * noise in qi*x-r[i] is estimated as
     *   Eps*MXQ = Eps*max(|r[i]|, |q[i,j]*x[j]|)
     * * noise in (qi*x-r[i])^2 is estimated as
     *   NOISE = (|qi*x-r[i]|+Eps*MXQ)^2-(|qi*x-r[i]|)^2
     *         = Eps*MXQ*(2*|qi*x-r[i]|+Eps*MXQ)
     */
    if( ae_fp_greater(s->theta,(double)(0)) )
    {
        for(i=0; i<=s->k-1; i++)
        {
            v = 0.0;
            mxq = ae_fabs(s->r.ptr.p_double[i], _state);
            for(j=0; j<=n-1; j++)
            {
                v2 = s->q.ptr.pp_double[i][j]*x->ptr.p_double[j];
                v = v+v2;
                mxq = ae_maxreal(mxq, ae_fabs(v2, _state), _state);
            }
            *r = *r+0.5*s->theta*ae_sqr(v-s->r.ptr.p_double[i], _state);
            *noise = ae_maxreal(*noise, eps*mxq*((double)2*ae_fabs(v-s->r.ptr.p_double[i], _state)+eps*mxq), _state);
        }
    }
    
    /*
     * linear term
     */
    for(i=0; i<=s->n-1; i++)
    {
        *r = *r+x->ptr.p_double[i]*s->b.ptr.p_double[i];
        *noise = ae_maxreal(*noise, eps*ae_fabs(x->ptr.p_double[i]*s->b.ptr.p_double[i], _state), _state);
    }
    
    /*
     * Final update of the noise
     */
    *noise = (double)n*(*noise);
}


/*************************************************************************
This  subroutine  evaluates  gradient of the model; active constraints are
ignored.

INPUT PARAMETERS:
    S       -   convex model
    X       -   point, array[N]
    G       -   possibly preallocated buffer; resized, if too small

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqmgradunconstrained(const convexquadraticmodel* s,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    double v;


    n = s->n;
    ae_assert(isfinitevector(x, n, _state), "CQMEvalGradUnconstrained: X is not finite vector", _state);
    rvectorsetlengthatleast(g, n, _state);
    for(i=0; i<=n-1; i++)
    {
        g->ptr.p_double[i] = (double)(0);
    }
    
    /*
     * main quadratic term
     */
    if( ae_fp_greater(s->alpha,(double)(0)) )
    {
        for(i=0; i<=n-1; i++)
        {
            v = 0.0;
            for(j=0; j<=n-1; j++)
            {
                v = v+s->alpha*s->a.ptr.pp_double[i][j]*x->ptr.p_double[j];
            }
            g->ptr.p_double[i] = g->ptr.p_double[i]+v;
        }
    }
    if( ae_fp_greater(s->tau,(double)(0)) )
    {
        for(i=0; i<=n-1; i++)
        {
            g->ptr.p_double[i] = g->ptr.p_double[i]+x->ptr.p_double[i]*s->tau*s->d.ptr.p_double[i];
        }
    }
    
    /*
     * secondary quadratic term
     */
    if( ae_fp_greater(s->theta,(double)(0)) )
    {
        for(i=0; i<=s->k-1; i++)
        {
            v = ae_v_dotproduct(&s->q.ptr.pp_double[i][0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
            v = s->theta*(v-s->r.ptr.p_double[i]);
            ae_v_addd(&g->ptr.p_double[0], 1, &s->q.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
        }
    }
    
    /*
     * linear term
     */
    for(i=0; i<=n-1; i++)
    {
        g->ptr.p_double[i] = g->ptr.p_double[i]+s->b.ptr.p_double[i];
    }
}


/*************************************************************************
This subroutine evaluates x'*(0.5*alpha*A+tau*D)*x

NOTE: Tmp[] must be preallocated array whose length is at least N

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
double cqmxtadx2(const convexquadraticmodel* s,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* tmp,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    double result;


    n = s->n;
    ae_assert(isfinitevector(x, n, _state), "CQMXTADX2: X is not finite vector", _state);
    ae_assert(tmp->cnt>=n, "CQMXTADX2: Length(Tmp)<N", _state);
    result = 0.0;
    
    /*
     * main quadratic term
     */
    if( ae_fp_greater(s->alpha,(double)(0)) )
    {
        result = result+s->alpha*0.5*rmatrixsyvmv(n, &s->a, 0, 0, ae_true, x, 0, tmp, _state);
    }
    if( ae_fp_greater(s->tau,(double)(0)) )
    {
        for(i=0; i<=n-1; i++)
        {
            result = result+0.5*ae_sqr(x->ptr.p_double[i], _state)*s->tau*s->d.ptr.p_double[i];
        }
    }
    return result;
}


/*************************************************************************
This subroutine evaluates (0.5*alpha*A+tau*D)*x

Y is automatically resized if needed

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqmadx(const convexquadraticmodel* s,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* y,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;


    n = s->n;
    ae_assert(isfinitevector(x, n, _state), "CQMEval: X is not finite vector", _state);
    rvectorsetlengthatleast(y, n, _state);
    
    /*
     * main quadratic term
     */
    for(i=0; i<=n-1; i++)
    {
        y->ptr.p_double[i] = (double)(0);
    }
    if( ae_fp_greater(s->alpha,(double)(0)) )
    {
        rmatrixsymv(n, s->alpha, &s->a, 0, 0, ae_true, x, 0, 1.0, y, 0, _state);
    }
    if( ae_fp_greater(s->tau,(double)(0)) )
    {
        for(i=0; i<=n-1; i++)
        {
            y->ptr.p_double[i] = y->ptr.p_double[i]+x->ptr.p_double[i]*s->tau*s->d.ptr.p_double[i];
        }
    }
}


/*************************************************************************
This subroutine finds optimum of the model. It returns  False  on  failure
(indefinite/semidefinite matrix).  Optimum  is  found  subject  to  active
constraints.

INPUT PARAMETERS
    S       -   model
    X       -   possibly preallocated buffer; automatically resized, if
                too small enough.

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
ae_bool cqmconstrainedoptimum(convexquadraticmodel* s,
     /* Real    */ ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nfree;
    ae_int_t k;
    ae_int_t i;
    double v;
    ae_int_t cidx0;
    ae_int_t itidx;
    ae_bool result;


    
    /*
     * Rebuild internal structures
     */
    if( !cqmodels_cqmrebuild(s, _state) )
    {
        result = ae_false;
        return result;
    }
    n = s->n;
    k = s->k;
    nfree = s->nfree;
    result = ae_true;
    
    /*
     * Calculate initial point for the iterative refinement:
     * * free components are set to zero
     * * constrained components are set to their constrained values
     */
    rvectorsetlengthatleast(x, n, _state);
    for(i=0; i<=n-1; i++)
    {
        if( s->activeset.ptr.p_bool[i] )
        {
            x->ptr.p_double[i] = s->xc.ptr.p_double[i];
        }
        else
        {
            x->ptr.p_double[i] = (double)(0);
        }
    }
    
    /*
     * Iterative refinement.
     *
     * In an ideal world without numerical errors it would be enough
     * to make just one Newton step from initial point:
     *   x_new = -H^(-1)*grad(x=0)
     * However, roundoff errors can significantly deteriorate quality
     * of the solution. So we have to recalculate gradient and to
     * perform Newton steps several times.
     *
     * Below we perform fixed number of Newton iterations.
     */
    for(itidx=0; itidx<=cqmodels_newtonrefinementits-1; itidx++)
    {
        
        /*
         * Calculate gradient at the current point.
         * Move free components of the gradient in the beginning.
         */
        cqmgradunconstrained(s, x, &s->tmpg, _state);
        cidx0 = 0;
        for(i=0; i<=n-1; i++)
        {
            if( !s->activeset.ptr.p_bool[i] )
            {
                s->tmpg.ptr.p_double[cidx0] = s->tmpg.ptr.p_double[i];
                cidx0 = cidx0+1;
            }
        }
        
        /*
         * Free components of the extrema are calculated in the first NFree elements of TXC.
         *
         * First, we have to calculate original Newton step, without rank-K perturbations
         */
        ae_v_moveneg(&s->txc.ptr.p_double[0], 1, &s->tmpg.ptr.p_double[0], 1, ae_v_len(0,nfree-1));
        cqmodels_cqmsolveea(s, &s->txc, &s->tmp0, _state);
        
        /*
         * Then, we account for rank-K correction.
         * Woodbury matrix identity is used.
         */
        if( s->k>0&&ae_fp_greater(s->theta,(double)(0)) )
        {
            rvectorsetlengthatleast(&s->tmp0, ae_maxint(nfree, k, _state), _state);
            rvectorsetlengthatleast(&s->tmp1, ae_maxint(nfree, k, _state), _state);
            ae_v_moveneg(&s->tmp1.ptr.p_double[0], 1, &s->tmpg.ptr.p_double[0], 1, ae_v_len(0,nfree-1));
            cqmodels_cqmsolveea(s, &s->tmp1, &s->tmp0, _state);
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&s->eq.ptr.pp_double[i][0], 1, &s->tmp1.ptr.p_double[0], 1, ae_v_len(0,nfree-1));
                s->tmp0.ptr.p_double[i] = v;
            }
            fblscholeskysolve(&s->eccm, 1.0, k, ae_true, &s->tmp0, &s->tmp1, _state);
            for(i=0; i<=nfree-1; i++)
            {
                s->tmp1.ptr.p_double[i] = 0.0;
            }
            for(i=0; i<=k-1; i++)
            {
                v = s->tmp0.ptr.p_double[i];
                ae_v_addd(&s->tmp1.ptr.p_double[0], 1, &s->eq.ptr.pp_double[i][0], 1, ae_v_len(0,nfree-1), v);
            }
            cqmodels_cqmsolveea(s, &s->tmp1, &s->tmp0, _state);
            ae_v_sub(&s->txc.ptr.p_double[0], 1, &s->tmp1.ptr.p_double[0], 1, ae_v_len(0,nfree-1));
        }
        
        /*
         * Unpack components from TXC into X. We pass through all
         * free components of X and add our step.
         */
        cidx0 = 0;
        for(i=0; i<=n-1; i++)
        {
            if( !s->activeset.ptr.p_bool[i] )
            {
                x->ptr.p_double[i] = x->ptr.p_double[i]+s->txc.ptr.p_double[cidx0];
                cidx0 = cidx0+1;
            }
        }
    }
    return result;
}


/*************************************************************************
This function scales vector  by  multiplying it by inverse of the diagonal
of the Hessian matrix. It should be used to  accelerate  steepest  descent
phase of the QP solver.

Although  it  is  called  "scale-grad",  it  can be called for any vector,
whether it is gradient, anti-gradient, or just some vector.

This function does NOT takes into account current set of  constraints,  it
just performs matrix-vector multiplication  without  taking  into  account
constraints.

INPUT PARAMETERS:
    S       -   model
    X       -   vector to scale

OUTPUT PARAMETERS:
    X       -   scaled vector
    
NOTE:
    when called for non-SPD matrices, it silently skips components of X
    which correspond to zero or negative diagonal elements.
    
NOTE:
    this function uses diagonals of A and D; it ignores Q - rank-K term of
    the quadratic model.

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
void cqmscalevector(convexquadraticmodel* s,
     /* Real    */ ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    double v;


    n = s->n;
    for(i=0; i<=n-1; i++)
    {
        v = 0.0;
        if( ae_fp_greater(s->alpha,(double)(0)) )
        {
            v = v+s->a.ptr.pp_double[i][i];
        }
        if( ae_fp_greater(s->tau,(double)(0)) )
        {
            v = v+s->d.ptr.p_double[i];
        }
        if( ae_fp_greater(v,(double)(0)) )
        {
            x->ptr.p_double[i] = x->ptr.p_double[i]/v;
        }
    }
}


/*************************************************************************
This function returns diagonal of the A-term.

INPUT PARAMETERS:
    S       -   model

OUTPUT PARAMETERS:
    D       -   diagonal of the A (or zero)

  -- ALGLIB --
     Copyright 26.12.2017 by Bochkanov Sergey
*************************************************************************/
void cqmgetdiaga(convexquadraticmodel* s,
     /* Real    */ ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;


    n = s->n;
    rvectorsetlengthatleast(x, n, _state);
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater(s->alpha,(double)(0)) )
        {
            x->ptr.p_double[i] = s->a.ptr.pp_double[i][i];
        }
        else
        {
            x->ptr.p_double[i] = (double)(0);
        }
    }
}


/*************************************************************************
This subroutine calls CQMRebuild() and evaluates model at X subject to
active constraints.

It  is  intended  for  debug  purposes only, because it evaluates model by
means of temporaries, which were calculated  by  CQMRebuild().  The   only
purpose of this function  is  to  check  correctness  of  CQMRebuild()  by
comparing results of this function with ones obtained by CQMEval(),  which
is  used  as  reference  point. The  idea is that significant deviation in
results  of  these  two  functions  is  evidence  of  some  error  in  the
CQMRebuild().

NOTE: suffix T denotes that temporaries marked by T-prefix are used. There
      is one more variant of this function, which uses  "effective"  model
      built by CQMRebuild().

NOTE2: in case CQMRebuild() fails (due to model non-convexity), this
      function returns NAN.

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
double cqmdebugconstrainedevalt(convexquadraticmodel* s,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nfree;
    ae_int_t i;
    ae_int_t j;
    double v;
    double result;


    n = s->n;
    ae_assert(isfinitevector(x, n, _state), "CQMDebugConstrainedEvalT: X is not finite vector", _state);
    if( !cqmodels_cqmrebuild(s, _state) )
    {
        result = _state->v_nan;
        return result;
    }
    result = 0.0;
    nfree = s->nfree;
    
    /*
     * Reorder variables
     */
    j = 0;
    for(i=0; i<=n-1; i++)
    {
        if( !s->activeset.ptr.p_bool[i] )
        {
            ae_assert(j<nfree, "CQMDebugConstrainedEvalT: internal error", _state);
            s->txc.ptr.p_double[j] = x->ptr.p_double[i];
            j = j+1;
        }
    }
    
    /*
     * TQ2, TQ1, TQ0
     *
     */
    if( ae_fp_greater(s->alpha,(double)(0)) )
    {
        
        /*
         * Dense TQ2
         */
        for(i=0; i<=nfree-1; i++)
        {
            for(j=0; j<=nfree-1; j++)
            {
                result = result+0.5*s->txc.ptr.p_double[i]*s->tq2dense.ptr.pp_double[i][j]*s->txc.ptr.p_double[j];
            }
        }
    }
    else
    {
        
        /*
         * Diagonal TQ2
         */
        for(i=0; i<=nfree-1; i++)
        {
            result = result+0.5*s->tq2diag.ptr.p_double[i]*ae_sqr(s->txc.ptr.p_double[i], _state);
        }
    }
    for(i=0; i<=nfree-1; i++)
    {
        result = result+s->tq1.ptr.p_double[i]*s->txc.ptr.p_double[i];
    }
    result = result+s->tq0;
    
    /*
     * TK2, TK1, TK0
     */
    if( s->k>0&&ae_fp_greater(s->theta,(double)(0)) )
    {
        for(i=0; i<=s->k-1; i++)
        {
            v = (double)(0);
            for(j=0; j<=nfree-1; j++)
            {
                v = v+s->tk2.ptr.pp_double[i][j]*s->txc.ptr.p_double[j];
            }
            result = result+0.5*ae_sqr(v, _state);
        }
        for(i=0; i<=nfree-1; i++)
        {
            result = result+s->tk1.ptr.p_double[i]*s->txc.ptr.p_double[i];
        }
        result = result+s->tk0;
    }
    
    /*
     * TB (Bf and Bc parts)
     */
    for(i=0; i<=n-1; i++)
    {
        result = result+s->tb.ptr.p_double[i]*s->txc.ptr.p_double[i];
    }
    return result;
}


/*************************************************************************
This subroutine calls CQMRebuild() and evaluates model at X subject to
active constraints.

It  is  intended  for  debug  purposes only, because it evaluates model by
means of "effective" matrices built by CQMRebuild(). The only  purpose  of
this function is to check correctness of CQMRebuild() by comparing results
of this function with  ones  obtained  by  CQMEval(),  which  is  used  as
reference  point.  The  idea  is  that significant deviation in results of
these two functions is evidence of some error in the CQMRebuild().

NOTE: suffix E denotes that effective matrices. There is one more  variant
      of this function, which uses temporary matrices built by
      CQMRebuild().

NOTE2: in case CQMRebuild() fails (due to model non-convexity), this
      function returns NAN.

  -- ALGLIB --
     Copyright 12.06.2012 by Bochkanov Sergey
*************************************************************************/
double cqmdebugconstrainedevale(convexquadraticmodel* s,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nfree;
    ae_int_t i;
    ae_int_t j;
    double v;
    double result;


    n = s->n;
    ae_assert(isfinitevector(x, n, _state), "CQMDebugConstrainedEvalE: X is not finite vector", _state);
    if( !cqmodels_cqmrebuild(s, _state) )
    {
        result = _state->v_nan;
        return result;
    }
    result = 0.0;
    nfree = s->nfree;
    
    /*
     * Reorder variables
     */
    j = 0;
    for(i=0; i<=n-1; i++)
    {
        if( !s->activeset.ptr.p_bool[i] )
        {
            ae_assert(j<nfree, "CQMDebugConstrainedEvalE: internal error", _state);
            s->txc.ptr.p_double[j] = x->ptr.p_double[i];
            j = j+1;
        }
    }
    
    /*
     * ECA
     */
    ae_assert((s->ecakind==0||s->ecakind==1)||(s->ecakind==-1&&nfree==0), "CQMDebugConstrainedEvalE: unexpected ECAKind", _state);
    if( s->ecakind==0 )
    {
        
        /*
         * Dense ECA
         */
        for(i=0; i<=nfree-1; i++)
        {
            v = 0.0;
            for(j=i; j<=nfree-1; j++)
            {
                v = v+s->ecadense.ptr.pp_double[i][j]*s->txc.ptr.p_double[j];
            }
            result = result+0.5*ae_sqr(v, _state);
        }
    }
    if( s->ecakind==1 )
    {
        
        /*
         * Diagonal ECA
         */
        for(i=0; i<=nfree-1; i++)
        {
            result = result+0.5*ae_sqr(s->ecadiag.ptr.p_double[i]*s->txc.ptr.p_double[i], _state);
        }
    }
    
    /*
     * EQ
     */
    for(i=0; i<=s->k-1; i++)
    {
        v = 0.0;
        for(j=0; j<=nfree-1; j++)
        {
            v = v+s->eq.ptr.pp_double[i][j]*s->txc.ptr.p_double[j];
        }
        result = result+0.5*ae_sqr(v, _state);
    }
    
    /*
     * EB
     */
    for(i=0; i<=nfree-1; i++)
    {
        result = result+s->eb.ptr.p_double[i]*s->txc.ptr.p_double[i];
    }
    
    /*
     * EC
     */
    result = result+s->ec;
    return result;
}


/*************************************************************************
Internal function, rebuilds "effective" model subject to constraints.
Returns False on failure (non-SPD main quadratic term)

  -- ALGLIB --
     Copyright 10.05.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool cqmodels_cqmrebuild(convexquadraticmodel* s,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nfree;
    ae_int_t k;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ridx0;
    ae_int_t ridx1;
    ae_int_t cidx0;
    ae_int_t cidx1;
    double v;
    ae_bool result;


    if( ae_fp_eq(s->alpha,(double)(0))&&ae_fp_eq(s->tau,(double)(0)) )
    {
        
        /*
         * Non-SPD model, quick exit
         */
        result = ae_false;
        return result;
    }
    result = ae_true;
    n = s->n;
    k = s->k;
    
    /*
     * Determine number of free variables.
     * Fill TXC - array whose last N-NFree elements store constraints.
     */
    if( s->isactivesetchanged )
    {
        s->nfree = 0;
        for(i=0; i<=n-1; i++)
        {
            if( !s->activeset.ptr.p_bool[i] )
            {
                s->nfree = s->nfree+1;
            }
        }
        j = s->nfree;
        for(i=0; i<=n-1; i++)
        {
            if( s->activeset.ptr.p_bool[i] )
            {
                s->txc.ptr.p_double[j] = s->xc.ptr.p_double[i];
                j = j+1;
            }
        }
    }
    nfree = s->nfree;
    
    /*
     * Re-evaluate TQ2/TQ1/TQ0, if needed
     */
    if( s->isactivesetchanged||s->ismaintermchanged )
    {
        
        /*
         * Handle cases Alpha>0 and Alpha=0 separately:
         * * in the first case we have dense matrix
         * * in the second one we have diagonal matrix, which can be
         *   handled more efficiently
         */
        if( ae_fp_greater(s->alpha,(double)(0)) )
        {
            
            /*
             * Alpha>0, dense QP
             *
             * Split variables into two groups - free (F) and constrained (C). Reorder
             * variables in such way that free vars come first, constrained are last:
             * x = [xf, xc].
             * 
             * Main quadratic term x'*(alpha*A+tau*D)*x now splits into quadratic part,
             * linear part and constant part:
             *                   ( alpha*Aff+tau*Df  alpha*Afc        ) ( xf )              
             *   0.5*( xf' xc' )*(                                    )*(    ) =
             *                   ( alpha*Acf         alpha*Acc+tau*Dc ) ( xc )
             *
             *   = 0.5*xf'*(alpha*Aff+tau*Df)*xf + (alpha*Afc*xc)'*xf + 0.5*xc'(alpha*Acc+tau*Dc)*xc
             *                    
             * We store these parts into temporary variables:
             * * alpha*Aff+tau*Df, alpha*Afc, alpha*Acc+tau*Dc are stored into upper
             *   triangle of TQ2
             * * alpha*Afc*xc is stored into TQ1
             * * 0.5*xc'(alpha*Acc+tau*Dc)*xc is stored into TQ0
             *
             * Below comes first part of the work - generation of TQ2:
             * * we pass through rows of A and copy I-th row into upper block (Aff/Afc) or
             *   lower one (Acf/Acc) of TQ2, depending on presence of X[i] in the active set.
             *   RIdx0 variable contains current position for insertion into upper block,
             *   RIdx1 contains current position for insertion into lower one.
             * * within each row, we copy J-th element into left half (Aff/Acf) or right
             *   one (Afc/Acc), depending on presence of X[j] in the active set. CIdx0
             *   contains current position for insertion into left block, CIdx1 contains
             *   position for insertion into right one.
             * * during copying, we multiply elements by alpha and add diagonal matrix D.
             */
            ridx0 = 0;
            ridx1 = s->nfree;
            for(i=0; i<=n-1; i++)
            {
                cidx0 = 0;
                cidx1 = s->nfree;
                for(j=0; j<=n-1; j++)
                {
                    if( !s->activeset.ptr.p_bool[i]&&!s->activeset.ptr.p_bool[j] )
                    {
                        
                        /*
                         * Element belongs to Aff
                         */
                        v = s->alpha*s->a.ptr.pp_double[i][j];
                        if( i==j&&ae_fp_greater(s->tau,(double)(0)) )
                        {
                            v = v+s->tau*s->d.ptr.p_double[i];
                        }
                        s->tq2dense.ptr.pp_double[ridx0][cidx0] = v;
                    }
                    if( !s->activeset.ptr.p_bool[i]&&s->activeset.ptr.p_bool[j] )
                    {
                        
                        /*
                         * Element belongs to Afc
                         */
                        s->tq2dense.ptr.pp_double[ridx0][cidx1] = s->alpha*s->a.ptr.pp_double[i][j];
                    }
                    if( s->activeset.ptr.p_bool[i]&&!s->activeset.ptr.p_bool[j] )
                    {
                        
                        /*
                         * Element belongs to Acf
                         */
                        s->tq2dense.ptr.pp_double[ridx1][cidx0] = s->alpha*s->a.ptr.pp_double[i][j];
                    }
                    if( s->activeset.ptr.p_bool[i]&&s->activeset.ptr.p_bool[j] )
                    {
                        
                        /*
                         * Element belongs to Acc
                         */
                        v = s->alpha*s->a.ptr.pp_double[i][j];
                        if( i==j&&ae_fp_greater(s->tau,(double)(0)) )
                        {
                            v = v+s->tau*s->d.ptr.p_double[i];
                        }
                        s->tq2dense.ptr.pp_double[ridx1][cidx1] = v;
                    }
                    if( s->activeset.ptr.p_bool[j] )
                    {
                        cidx1 = cidx1+1;
                    }
                    else
                    {
                        cidx0 = cidx0+1;
                    }
                }
                if( s->activeset.ptr.p_bool[i] )
                {
                    ridx1 = ridx1+1;
                }
                else
                {
                    ridx0 = ridx0+1;
                }
            }
            
            /*
             * Now we have TQ2, and we can evaluate TQ1.
             * In the special case when we have Alpha=0, NFree=0 or NFree=N,
             * TQ1 is filled by zeros.
             */
            for(i=0; i<=n-1; i++)
            {
                s->tq1.ptr.p_double[i] = 0.0;
            }
            if( s->nfree>0&&s->nfree<n )
            {
                rmatrixmv(s->nfree, n-s->nfree, &s->tq2dense, 0, s->nfree, 0, &s->txc, s->nfree, &s->tq1, 0, _state);
            }
            
            /*
             * And finally, we evaluate TQ0.
             */
            v = 0.0;
            for(i=s->nfree; i<=n-1; i++)
            {
                for(j=s->nfree; j<=n-1; j++)
                {
                    v = v+0.5*s->txc.ptr.p_double[i]*s->tq2dense.ptr.pp_double[i][j]*s->txc.ptr.p_double[j];
                }
            }
            s->tq0 = v;
        }
        else
        {
            
            /*
             * Alpha=0, diagonal QP
             *
             * Split variables into two groups - free (F) and constrained (C). Reorder
             * variables in such way that free vars come first, constrained are last:
             * x = [xf, xc].
             * 
             * Main quadratic term x'*(tau*D)*x now splits into quadratic and constant
             * parts:
             *                   ( tau*Df        ) ( xf )              
             *   0.5*( xf' xc' )*(               )*(    ) =
             *                   (        tau*Dc ) ( xc )
             *
             *   = 0.5*xf'*(tau*Df)*xf + 0.5*xc'(tau*Dc)*xc
             *                    
             * We store these parts into temporary variables:
             * * tau*Df is stored in TQ2Diag
             * * 0.5*xc'(tau*Dc)*xc is stored into TQ0
             */
            s->tq0 = 0.0;
            ridx0 = 0;
            for(i=0; i<=n-1; i++)
            {
                if( !s->activeset.ptr.p_bool[i] )
                {
                    s->tq2diag.ptr.p_double[ridx0] = s->tau*s->d.ptr.p_double[i];
                    ridx0 = ridx0+1;
                }
                else
                {
                    s->tq0 = s->tq0+0.5*s->tau*s->d.ptr.p_double[i]*ae_sqr(s->xc.ptr.p_double[i], _state);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                s->tq1.ptr.p_double[i] = 0.0;
            }
        }
    }
    
    /*
     * Re-evaluate TK2/TK1/TK0, if needed
     */
    if( s->isactivesetchanged||s->issecondarytermchanged )
    {
        
        /*
         * Split variables into two groups - free (F) and constrained (C). Reorder
         * variables in such way that free vars come first, constrained are last:
         * x = [xf, xc].
         * 
         * Secondary term theta*(Q*x-r)'*(Q*x-r) now splits into quadratic part,
         * linear part and constant part:
         *             (          ( xf )     )'  (          ( xf )     )
         *   0.5*theta*( (Qf Qc)'*(    ) - r ) * ( (Qf Qc)'*(    ) - r ) =
         *             (          ( xc )     )   (          ( xc )     )
         *
         *   = 0.5*theta*xf'*(Qf'*Qf)*xf + theta*((Qc*xc-r)'*Qf)*xf + 
         *     + theta*(-r'*(Qc*xc-r)-0.5*r'*r+0.5*xc'*Qc'*Qc*xc)
         *                    
         * We store these parts into temporary variables:
         * * sqrt(theta)*Qf is stored into TK2
         * * theta*((Qc*xc-r)'*Qf) is stored into TK1
         * * theta*(-r'*(Qc*xc-r)-0.5*r'*r+0.5*xc'*Qc'*Qc*xc) is stored into TK0
         *
         * We use several other temporaries to store intermediate results:
         * * Tmp0 - to store Qc*xc-r
         * * Tmp1 - to store Qc*xc
         *
         * Generation of TK2/TK1/TK0 is performed as follows:
         * * we fill TK2/TK1/TK0 (to handle K=0 or Theta=0)
         * * other steps are performed only for K>0 and Theta>0
         * * we pass through columns of Q and copy I-th column into left block (Qf) or
         *   right one (Qc) of TK2, depending on presence of X[i] in the active set.
         *   CIdx0 variable contains current position for insertion into upper block,
         *   CIdx1 contains current position for insertion into lower one.
         * * we calculate Qc*xc-r and store it into Tmp0
         * * we calculate TK0 and TK1
         * * we multiply leading part of TK2 which stores Qf by sqrt(theta)
         *   it is important to perform this step AFTER calculation of TK0 and TK1,
         *   because we need original (non-modified) Qf to calculate TK0 and TK1.
         */
        for(j=0; j<=n-1; j++)
        {
            for(i=0; i<=k-1; i++)
            {
                s->tk2.ptr.pp_double[i][j] = 0.0;
            }
            s->tk1.ptr.p_double[j] = 0.0;
        }
        s->tk0 = 0.0;
        if( s->k>0&&ae_fp_greater(s->theta,(double)(0)) )
        {
            
            /*
             * Split Q into Qf and Qc
             * Calculate Qc*xc-r, store in Tmp0
             */
            rvectorsetlengthatleast(&s->tmp0, k, _state);
            rvectorsetlengthatleast(&s->tmp1, k, _state);
            cidx0 = 0;
            cidx1 = nfree;
            for(i=0; i<=k-1; i++)
            {
                s->tmp1.ptr.p_double[i] = 0.0;
            }
            for(j=0; j<=n-1; j++)
            {
                if( s->activeset.ptr.p_bool[j] )
                {
                    for(i=0; i<=k-1; i++)
                    {
                        s->tk2.ptr.pp_double[i][cidx1] = s->q.ptr.pp_double[i][j];
                        s->tmp1.ptr.p_double[i] = s->tmp1.ptr.p_double[i]+s->q.ptr.pp_double[i][j]*s->txc.ptr.p_double[cidx1];
                    }
                    cidx1 = cidx1+1;
                }
                else
                {
                    for(i=0; i<=k-1; i++)
                    {
                        s->tk2.ptr.pp_double[i][cidx0] = s->q.ptr.pp_double[i][j];
                    }
                    cidx0 = cidx0+1;
                }
            }
            for(i=0; i<=k-1; i++)
            {
                s->tmp0.ptr.p_double[i] = s->tmp1.ptr.p_double[i]-s->r.ptr.p_double[i];
            }
            
            /*
             * Calculate TK0
             */
            v = 0.0;
            for(i=0; i<=k-1; i++)
            {
                v = v+s->theta*(0.5*ae_sqr(s->tmp1.ptr.p_double[i], _state)-s->r.ptr.p_double[i]*s->tmp0.ptr.p_double[i]-0.5*ae_sqr(s->r.ptr.p_double[i], _state));
            }
            s->tk0 = v;
            
            /*
             * Calculate TK1
             */
            if( nfree>0 )
            {
                for(i=0; i<=k-1; i++)
                {
                    v = s->theta*s->tmp0.ptr.p_double[i];
                    ae_v_addd(&s->tk1.ptr.p_double[0], 1, &s->tk2.ptr.pp_double[i][0], 1, ae_v_len(0,nfree-1), v);
                }
            }
            
            /*
             * Calculate TK2
             */
            if( nfree>0 )
            {
                v = ae_sqrt(s->theta, _state);
                for(i=0; i<=k-1; i++)
                {
                    ae_v_muld(&s->tk2.ptr.pp_double[i][0], 1, ae_v_len(0,nfree-1), v);
                }
            }
        }
    }
    
    /*
     * Re-evaluate TB
     */
    if( s->isactivesetchanged||s->islineartermchanged )
    {
        ridx0 = 0;
        ridx1 = nfree;
        for(i=0; i<=n-1; i++)
        {
            if( s->activeset.ptr.p_bool[i] )
            {
                s->tb.ptr.p_double[ridx1] = s->b.ptr.p_double[i];
                ridx1 = ridx1+1;
            }
            else
            {
                s->tb.ptr.p_double[ridx0] = s->b.ptr.p_double[i];
                ridx0 = ridx0+1;
            }
        }
    }
    
    /*
     * Compose ECA: either dense ECA or diagonal ECA
     */
    if( (s->isactivesetchanged||s->ismaintermchanged)&&nfree>0 )
    {
        if( ae_fp_greater(s->alpha,(double)(0)) )
        {
            
            /*
             * Dense ECA
             */
            s->ecakind = 0;
            for(i=0; i<=nfree-1; i++)
            {
                for(j=i; j<=nfree-1; j++)
                {
                    s->ecadense.ptr.pp_double[i][j] = s->tq2dense.ptr.pp_double[i][j];
                }
            }
            if( !spdmatrixcholeskyrec(&s->ecadense, 0, nfree, ae_true, &s->tmp0, _state) )
            {
                result = ae_false;
                return result;
            }
        }
        else
        {
            
            /*
             * Diagonal ECA
             */
            s->ecakind = 1;
            for(i=0; i<=nfree-1; i++)
            {
                if( ae_fp_less(s->tq2diag.ptr.p_double[i],(double)(0)) )
                {
                    result = ae_false;
                    return result;
                }
                s->ecadiag.ptr.p_double[i] = ae_sqrt(s->tq2diag.ptr.p_double[i], _state);
            }
        }
    }
    
    /*
     * Compose EQ
     */
    if( s->isactivesetchanged||s->issecondarytermchanged )
    {
        for(i=0; i<=k-1; i++)
        {
            for(j=0; j<=nfree-1; j++)
            {
                s->eq.ptr.pp_double[i][j] = s->tk2.ptr.pp_double[i][j];
            }
        }
    }
    
    /*
     * Calculate ECCM
     */
    if( ((((s->isactivesetchanged||s->ismaintermchanged)||s->issecondarytermchanged)&&s->k>0)&&ae_fp_greater(s->theta,(double)(0)))&&nfree>0 )
    {
        
        /*
         * Calculate ECCM - Cholesky factor of the "effective" capacitance
         * matrix CM = I + EQ*inv(EffectiveA)*EQ'.
         *
         * We calculate CM as follows:
         *   CM = I + EQ*inv(EffectiveA)*EQ'
         *      = I + EQ*ECA^(-1)*ECA^(-T)*EQ'
         *      = I + (EQ*ECA^(-1))*(EQ*ECA^(-1))'
         *
         * Then we perform Cholesky decomposition of CM.
         */
        rmatrixsetlengthatleast(&s->tmp2, k, n, _state);
        rmatrixcopy(k, nfree, &s->eq, 0, 0, &s->tmp2, 0, 0, _state);
        ae_assert(s->ecakind==0||s->ecakind==1, "CQMRebuild: unexpected ECAKind", _state);
        if( s->ecakind==0 )
        {
            rmatrixrighttrsm(k, nfree, &s->ecadense, 0, 0, ae_true, ae_false, 0, &s->tmp2, 0, 0, _state);
        }
        if( s->ecakind==1 )
        {
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=nfree-1; j++)
                {
                    s->tmp2.ptr.pp_double[i][j] = s->tmp2.ptr.pp_double[i][j]/s->ecadiag.ptr.p_double[j];
                }
            }
        }
        for(i=0; i<=k-1; i++)
        {
            for(j=0; j<=k-1; j++)
            {
                s->eccm.ptr.pp_double[i][j] = 0.0;
            }
            s->eccm.ptr.pp_double[i][i] = 1.0;
        }
        rmatrixsyrk(k, nfree, 1.0, &s->tmp2, 0, 0, 0, 1.0, &s->eccm, 0, 0, ae_true, _state);
        if( !spdmatrixcholeskyrec(&s->eccm, 0, k, ae_true, &s->tmp0, _state) )
        {
            result = ae_false;
            return result;
        }
    }
    
    /*
     * Compose EB and EC
     *
     * NOTE: because these quantities are cheap to compute, we do not
     * use caching here.
     */
    for(i=0; i<=nfree-1; i++)
    {
        s->eb.ptr.p_double[i] = s->tq1.ptr.p_double[i]+s->tk1.ptr.p_double[i]+s->tb.ptr.p_double[i];
    }
    s->ec = s->tq0+s->tk0;
    for(i=nfree; i<=n-1; i++)
    {
        s->ec = s->ec+s->tb.ptr.p_double[i]*s->txc.ptr.p_double[i];
    }
    
    /*
     * Change cache status - everything is cached 
     */
    s->ismaintermchanged = ae_false;
    s->issecondarytermchanged = ae_false;
    s->islineartermchanged = ae_false;
    s->isactivesetchanged = ae_false;
    return result;
}


/*************************************************************************
Internal function, solves system Effective_A*x = b.
It should be called after successful completion of CQMRebuild().

INPUT PARAMETERS:
    S       -   quadratic model, after call to CQMRebuild()
    X       -   right part B, array[S.NFree]
    Tmp     -   temporary array, automatically reallocated if needed

OUTPUT PARAMETERS:
    X       -   solution, array[S.NFree]
    
NOTE: when called with zero S.NFree, returns silently
NOTE: this function assumes that EA is non-degenerate

  -- ALGLIB --
     Copyright 10.05.2011 by Bochkanov Sergey
*************************************************************************/
static void cqmodels_cqmsolveea(convexquadraticmodel* s,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* tmp,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert((s->ecakind==0||s->ecakind==1)||(s->ecakind==-1&&s->nfree==0), "CQMSolveEA: unexpected ECAKind", _state);
    if( s->ecakind==0 )
    {
        
        /*
         * Dense ECA, use FBLSCholeskySolve() dense solver.
         */
        fblscholeskysolve(&s->ecadense, 1.0, s->nfree, ae_true, x, tmp, _state);
    }
    if( s->ecakind==1 )
    {
        
        /*
         * Diagonal ECA
         */
        for(i=0; i<=s->nfree-1; i++)
        {
            x->ptr.p_double[i] = x->ptr.p_double[i]/ae_sqr(s->ecadiag.ptr.p_double[i], _state);
        }
    }
}


void _convexquadraticmodel_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    convexquadraticmodel *p = (convexquadraticmodel*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_init(&p->a, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->q, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->b, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->r, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->activeset, 0, DT_BOOL, _state, make_automatic);
    ae_matrix_init(&p->tq2dense, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tk2, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tq2diag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tq1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tk1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->txc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tb, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->ecadense, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->eq, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->eccm, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ecadiag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->eb, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpg, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmp2, 0, 0, DT_REAL, _state, make_automatic);
}


void _convexquadraticmodel_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    convexquadraticmodel       *dst = (convexquadraticmodel*)_dst;
    const convexquadraticmodel *src = (const convexquadraticmodel*)_src;
    dst->n = src->n;
    dst->k = src->k;
    dst->alpha = src->alpha;
    dst->tau = src->tau;
    dst->theta = src->theta;
    ae_matrix_init_copy(&dst->a, &src->a, _state, make_automatic);
    ae_matrix_init_copy(&dst->q, &src->q, _state, make_automatic);
    ae_vector_init_copy(&dst->b, &src->b, _state, make_automatic);
    ae_vector_init_copy(&dst->r, &src->r, _state, make_automatic);
    ae_vector_init_copy(&dst->xc, &src->xc, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    ae_vector_init_copy(&dst->activeset, &src->activeset, _state, make_automatic);
    ae_matrix_init_copy(&dst->tq2dense, &src->tq2dense, _state, make_automatic);
    ae_matrix_init_copy(&dst->tk2, &src->tk2, _state, make_automatic);
    ae_vector_init_copy(&dst->tq2diag, &src->tq2diag, _state, make_automatic);
    ae_vector_init_copy(&dst->tq1, &src->tq1, _state, make_automatic);
    ae_vector_init_copy(&dst->tk1, &src->tk1, _state, make_automatic);
    dst->tq0 = src->tq0;
    dst->tk0 = src->tk0;
    ae_vector_init_copy(&dst->txc, &src->txc, _state, make_automatic);
    ae_vector_init_copy(&dst->tb, &src->tb, _state, make_automatic);
    dst->nfree = src->nfree;
    dst->ecakind = src->ecakind;
    ae_matrix_init_copy(&dst->ecadense, &src->ecadense, _state, make_automatic);
    ae_matrix_init_copy(&dst->eq, &src->eq, _state, make_automatic);
    ae_matrix_init_copy(&dst->eccm, &src->eccm, _state, make_automatic);
    ae_vector_init_copy(&dst->ecadiag, &src->ecadiag, _state, make_automatic);
    ae_vector_init_copy(&dst->eb, &src->eb, _state, make_automatic);
    dst->ec = src->ec;
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpg, &src->tmpg, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmp2, &src->tmp2, _state, make_automatic);
    dst->ismaintermchanged = src->ismaintermchanged;
    dst->issecondarytermchanged = src->issecondarytermchanged;
    dst->islineartermchanged = src->islineartermchanged;
    dst->isactivesetchanged = src->isactivesetchanged;
}


void _convexquadraticmodel_clear(void* _p)
{
    convexquadraticmodel *p = (convexquadraticmodel*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_clear(&p->a);
    ae_matrix_clear(&p->q);
    ae_vector_clear(&p->b);
    ae_vector_clear(&p->r);
    ae_vector_clear(&p->xc);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->activeset);
    ae_matrix_clear(&p->tq2dense);
    ae_matrix_clear(&p->tk2);
    ae_vector_clear(&p->tq2diag);
    ae_vector_clear(&p->tq1);
    ae_vector_clear(&p->tk1);
    ae_vector_clear(&p->txc);
    ae_vector_clear(&p->tb);
    ae_matrix_clear(&p->ecadense);
    ae_matrix_clear(&p->eq);
    ae_matrix_clear(&p->eccm);
    ae_vector_clear(&p->ecadiag);
    ae_vector_clear(&p->eb);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmp1);
    ae_vector_clear(&p->tmpg);
    ae_matrix_clear(&p->tmp2);
}


void _convexquadraticmodel_destroy(void* _p)
{
    convexquadraticmodel *p = (convexquadraticmodel*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_destroy(&p->a);
    ae_matrix_destroy(&p->q);
    ae_vector_destroy(&p->b);
    ae_vector_destroy(&p->r);
    ae_vector_destroy(&p->xc);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->activeset);
    ae_matrix_destroy(&p->tq2dense);
    ae_matrix_destroy(&p->tk2);
    ae_vector_destroy(&p->tq2diag);
    ae_vector_destroy(&p->tq1);
    ae_vector_destroy(&p->tk1);
    ae_vector_destroy(&p->txc);
    ae_vector_destroy(&p->tb);
    ae_matrix_destroy(&p->ecadense);
    ae_matrix_destroy(&p->eq);
    ae_matrix_destroy(&p->eccm);
    ae_vector_destroy(&p->ecadiag);
    ae_vector_destroy(&p->eb);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmp1);
    ae_vector_destroy(&p->tmpg);
    ae_matrix_destroy(&p->tmp2);
}


#endif
#if defined(AE_COMPILE_LPQPSERV) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
This function generates scaled (by S) and shifted (by XC) reformulation of
two-sided "lower-bound/upper-bound" constraints stored in mixed dense/sparse format.

INPUT PARAMETERS:
    S               -   scale vector, array[N]:
                        * I-th element contains scale of I-th variable,
                        * SC[I]>0
    XOrigin         -   origin term, array[N]. Can be zero.
    N               -   number of variables.
    SparseA         -   sparse M*N constraint matrix in CRS format;
                        ignored if M=0.
    M               -   sparse constraint count, M>=0
    AL              -   lower bounds for constraints, array[M]
    AU              -   upper bounds for constraints, array[M]

OUTPUT PARAMETERS:
    SparseA         -   replaced by scaled/shifted constraints
    AL              -   replaced by scaled/shifted lower bounds
    AU              -   replaced by scaled/shifted upper bounds

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void scaleshiftmixedlcinplace(/* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t n,
     sparsematrix* sparsea,
     ae_int_t msparse,
     /* Real    */ ae_matrix* densea,
     ae_int_t mdense,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t k0;
    ae_int_t k1;
    double v;
    double vv;


    ae_assert(msparse==0||((sparsea->matrixtype==1&&sparsea->m==msparse)&&sparsea->n==n), "ScaleShiftMixedLCInplace: non-CRS sparse constraint matrix!", _state);
    for(i=0; i<=msparse+mdense-1; i++)
    {
        
        /*
         * Scale/shift constraint; shift its lower and upper bounds
         */
        if( i<msparse )
        {
            v = 0.0;
            k0 = sparsea->ridx.ptr.p_int[i];
            k1 = sparsea->ridx.ptr.p_int[i+1]-1;
            for(k=k0; k<=k1; k++)
            {
                j = sparsea->idx.ptr.p_int[k];
                vv = sparsea->vals.ptr.p_double[k];
                v = v+vv*xorigin->ptr.p_double[j];
                sparsea->vals.ptr.p_double[k] = vv*s->ptr.p_double[j];
            }
            al->ptr.p_double[i] = al->ptr.p_double[i]-v;
            au->ptr.p_double[i] = au->ptr.p_double[i]-v;
        }
        else
        {
            v = 0.0;
            for(j=0; j<=n-1; j++)
            {
                vv = densea->ptr.pp_double[i-msparse][j];
                v = v+vv*xorigin->ptr.p_double[j];
                densea->ptr.pp_double[i-msparse][j] = vv*s->ptr.p_double[j];
            }
            al->ptr.p_double[i] = al->ptr.p_double[i]-v;
            au->ptr.p_double[i] = au->ptr.p_double[i]-v;
        }
    }
}


/*************************************************************************
This function generates scaled (by S) and shifted (by XC) reformulation of
the box constraints.

INPUT PARAMETERS:
    S               -   scale vector, array[N]:
                        * I-th element contains scale of I-th variable,
                        * SC[I]>0
    XOrigin         -   origin term, array[N]. Can be zero.
    BndL            -     raw lower bounds, array[N]
    BndU            -   raw upper bounds, array[N]
    N               -   number of variables.

OUTPUT PARAMETERS:
    BndL            -   replaced by scaled/shifted lower bounds, array[N]
    BndU            -   replaced by scaled/shifted upper bounds, array[N]

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void scaleshiftbcinplace(/* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool hasbndl;
    ae_bool hasbndu;


    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state)&&s->ptr.p_double[i]>0.0, "ScaleShiftBC: S[i] is nonpositive", _state);
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "ScaleShiftBC: BndL[i] is +INF or NAN", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "ScaleShiftBC: BndU[i] is -INF or NAN", _state);
        hasbndl = ae_isfinite(bndl->ptr.p_double[i], _state);
        hasbndu = ae_isfinite(bndu->ptr.p_double[i], _state);
        if( (hasbndl&&hasbndu)&&ae_fp_eq(bndl->ptr.p_double[i],bndu->ptr.p_double[i]) )
        {
            
            /*
             * Make sure that BndL[I]=BndU[I] bit-to-bit
             * even with CRAZY optimizing compiler.
             */
            bndu->ptr.p_double[i] = (bndu->ptr.p_double[i]-xorigin->ptr.p_double[i])/s->ptr.p_double[i];
            bndl->ptr.p_double[i] = bndu->ptr.p_double[i];
            continue;
        }
        if( hasbndl )
        {
            bndl->ptr.p_double[i] = (bndl->ptr.p_double[i]-xorigin->ptr.p_double[i])/s->ptr.p_double[i];
        }
        if( hasbndu )
        {
            bndu->ptr.p_double[i] = (bndu->ptr.p_double[i]-xorigin->ptr.p_double[i])/s->ptr.p_double[i];
        }
    }
}


/*************************************************************************
This function generates scaled (by S) and shifted (by XC) reformulation of
two-sided "lower-bound/range" constraints stored in dense format.

INPUT PARAMETERS:
    S               -   scale vector, array[N]:
                        * I-th element contains scale of I-th variable,
                        * SC[I]>0
    XOrigin         -   origin term, array[N]. Can be zero.
    N               -   number of variables.
    DenseA          -   array[M,N], constraint matrix
    AB              -   lower bounds for constraints, always present and
                        finite, array[M]
    AR              -   ranges for constraints, can be zero (equality
                        constraint), positive (range constraint) or +INF
                        (lower bound constraint), array[M]
    M               -   constraint count, M>=0

OUTPUT PARAMETERS:
    DenseA          -   replaced by scaled/shifted constraints, array[M,N]
    AB              -   replaced by scaled/shifted lower bounds, array[M]
    AR              -   replaced by scaled/shifted ranges, array[M]

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void scaleshiftdensebrlcinplace(/* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t n,
     /* Real    */ ae_matrix* densea,
     /* Real    */ ae_vector* ab,
     /* Real    */ ae_vector* ar,
     ae_int_t m,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;
    double vv;


    for(i=0; i<=m-1; i++)
    {
        
        /*
         * Scale/shift constraint; shift its lower bound
         *
         * NOTE: range is NOT scaled or shifted
         */
        v = 0.0;
        for(j=0; j<=n-1; j++)
        {
            vv = densea->ptr.pp_double[i][j];
            v = v+vv*xorigin->ptr.p_double[j];
            densea->ptr.pp_double[i][j] = vv*s->ptr.p_double[j];
        }
        ab->ptr.p_double[i] = ab->ptr.p_double[i]-v;
    }
}


/*************************************************************************
This function generates scaled (by S) and shifted (by XC) reformulation of
two-sided "lower-bound/range" constraints stored in dense format.

INPUT PARAMETERS:
    S               -   scale vector, array[N]:
                        * I-th element contains scale of I-th variable,
                        * SC[I]>0
    XOrigin         -   origin term, array[N]. Can be zero.
    N               -   number of variables.
    SparseA         -   sparse MSparse*N constraint matrix in CRS format;
                        ignored if MSparse=0.
    MSparse         -   dense constraint count, MSparse>=0
    DenseA          -   array[MDense,N], constraint matrix;
                        ignored if MDense=0.
    MDense          -   dense constraint count, MDense>=0
    AB              -   lower bounds for constraints, always present and
                        finite, array[MSparse+MDense]
    AR              -   ranges for constraints, can be zero (equality
                        constraint), positive (range constraint) or +INF
                        (lower bound constraint), array[MSparse+MDense]

OUTPUT PARAMETERS:
    DenseA          -   replaced by scaled/shifted constraints, array[MDense,N]
    SparseA         -   replaced by scaled/shifted constraints, array[MSparse,N]
    AB              -   replaced by scaled/shifted lower bounds, array[MDense+MSparse]
    AR              -   replaced by scaled/shifted ranges, array[MDense+MSparse]

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void scaleshiftmixedbrlcinplace(/* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t n,
     sparsematrix* sparsea,
     ae_int_t msparse,
     /* Real    */ ae_matrix* densea,
     ae_int_t mdense,
     /* Real    */ ae_vector* ab,
     /* Real    */ ae_vector* ar,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t k0;
    ae_int_t k1;
    double v;
    double vv;


    ae_assert(msparse==0||((sparsea->matrixtype==1&&sparsea->m==msparse)&&sparsea->n==n), "ScaleShiftMixedBRLCInplace: non-CRS sparse constraint matrix!", _state);
    for(i=0; i<=msparse-1; i++)
    {
        
        /*
         * Scale/shift constraint; shift its lower bound
         *
         * NOTE: range is NOT scaled or shifted
         */
        v = 0.0;
        k0 = sparsea->ridx.ptr.p_int[i];
        k1 = sparsea->ridx.ptr.p_int[i+1]-1;
        for(k=k0; k<=k1; k++)
        {
            j = sparsea->idx.ptr.p_int[k];
            vv = sparsea->vals.ptr.p_double[k];
            v = v+vv*xorigin->ptr.p_double[j];
            sparsea->vals.ptr.p_double[k] = vv*s->ptr.p_double[j];
        }
        ab->ptr.p_double[i] = ab->ptr.p_double[i]-v;
    }
    for(i=0; i<=mdense-1; i++)
    {
        
        /*
         * Scale/shift constraint; shift its lower bound
         *
         * NOTE: range is NOT scaled or shifted
         */
        v = 0.0;
        for(j=0; j<=n-1; j++)
        {
            vv = densea->ptr.pp_double[i][j];
            v = v+vv*xorigin->ptr.p_double[j];
            densea->ptr.pp_double[i][j] = vv*s->ptr.p_double[j];
        }
        ab->ptr.p_double[msparse+i] = ab->ptr.p_double[msparse+i]-v;
    }
}


/*************************************************************************
This function generates scaled (by S) and shifted (by XC) reformulation of
two-sided "lower-bound/upper-bound" constraints stored in sparse format.

INPUT PARAMETERS:
    S               -   scale vector, array[N]:
                        * I-th element contains scale of I-th variable,
                        * SC[I]>0
    XOrigin         -   origin term, array[N]. Can be zero.
    N               -   number of variables.
    SparseA         -   sparse M*N constraint matrix in CRS format;
                        ignored if M=0.
    M               -   sparse constraint count, M>=0
    AL              -   lower bounds for constraints, array[M]
    AU              -   upper bounds for constraints, array[M]

OUTPUT PARAMETERS:
    SparseA         -   replaced by scaled/shifted constraints
    AL              -   replaced by scaled/shifted lower bounds
    AU              -   replaced by scaled/shifted upper bounds

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void scaleshiftsparselcinplace(/* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t n,
     sparsematrix* sparsea,
     ae_int_t m,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t k0;
    ae_int_t k1;
    double v;
    double vv;


    ae_assert(m==0||((sparsea->matrixtype==1&&sparsea->m==m)&&sparsea->n==n), "ScaleShiftSparseLCInplace: non-CRS sparse constraint matrix!", _state);
    for(i=0; i<=m-1; i++)
    {
        
        /*
         * Scale/shift constraint; shift its lower and upper bounds
         */
        v = 0.0;
        k0 = sparsea->ridx.ptr.p_int[i];
        k1 = sparsea->ridx.ptr.p_int[i+1]-1;
        for(k=k0; k<=k1; k++)
        {
            j = sparsea->idx.ptr.p_int[k];
            vv = sparsea->vals.ptr.p_double[k];
            v = v+vv*xorigin->ptr.p_double[j];
            sparsea->vals.ptr.p_double[k] = vv*s->ptr.p_double[j];
        }
        al->ptr.p_double[i] = al->ptr.p_double[i]-v;
        au->ptr.p_double[i] = au->ptr.p_double[i]-v;
    }
}


/*************************************************************************
This function generates scaled (by S) reformulation of dense quadratic and
linear terms in QP problem.

INPUT PARAMETERS:
    N               -   number of variables.
    DenseA          -   array[NMain,NMain], quadratic term
    IsUpper         -   whether upper or lower triangle is present
    NMain           -   number of nonslack vars, 1<=NMain<=NTotal
    DenseB          -   array[NTotal], linear term
    NTotal          -   total number of variables, NTotal>=1
    S               -   scale vector, array[NTotal]:
                        * I-th element contains scale of I-th variable,
                        * SC[I]>0

OUTPUT PARAMETERS:
    DenseA          -   replaced by scaled term, array[N,N]
    DenseB          -   replaced by scaled term, array[N]

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void scaledenseqpinplace(/* Real    */ ae_matrix* densea,
     ae_bool isupper,
     ae_int_t nmain,
     /* Real    */ ae_vector* denseb,
     ae_int_t ntotal,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    ae_int_t j1;
    double si;


    for(i=0; i<=nmain-1; i++)
    {
        si = s->ptr.p_double[i];
        if( isupper )
        {
            j0 = i;
            j1 = nmain-1;
        }
        else
        {
            j0 = 0;
            j1 = i;
        }
        for(j=j0; j<=j1; j++)
        {
            densea->ptr.pp_double[i][j] = densea->ptr.pp_double[i][j]*si*s->ptr.p_double[j];
        }
    }
    for(i=0; i<=ntotal-1; i++)
    {
        denseb->ptr.p_double[i] = denseb->ptr.p_double[i]*s->ptr.p_double[i];
    }
}


/*************************************************************************
This function generates scaled (by S) reformulation of sparse quadratic and
linear terms in QP problem.

INPUT PARAMETERS:
    S               -   scale vector, array[N]:
                        * I-th element contains scale of I-th variable,
                        * SC[I]>0
    N               -   number of variables.
    SparseA         -   quadratic term, NxN SparseMatrix in CRS format
                        (any triangle can be present, we will scale everything)
    DenseCorrC      -   array[CorrRank,N], low rank correction C'*D*C being
                        added to the quadratic term
    DenseCorrD      -   array[CorrRank], low rank correction, diagonal factors
    CorrRank        -   correction rank, >=0
    DenseB          -   array[N], linear term

OUTPUT PARAMETERS:
    SparseA         -   replaced by scaled term
    DenseB          -   replaced by scaled term

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void scalesparseqpinplace(/* Real    */ const ae_vector* s,
     ae_int_t n,
     sparsematrix* sparsea,
     /* Real    */ ae_matrix* densecorrc,
     /* Real    */ ae_vector* densecorrd,
     ae_int_t corrrank,
     /* Real    */ ae_vector* denseb,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t k;
    double si;


    ae_assert((sparsea->matrixtype==1&&sparsea->m==n)&&sparsea->n==n, "ScaleSparseQPInplace: SparseA in unexpected format", _state);
    for(i=0; i<=n-1; i++)
    {
        si = s->ptr.p_double[i];
        k0 = sparsea->ridx.ptr.p_int[i];
        k1 = sparsea->ridx.ptr.p_int[i+1]-1;
        for(k=k0; k<=k1; k++)
        {
            sparsea->vals.ptr.p_double[k] = sparsea->vals.ptr.p_double[k]*si*s->ptr.p_double[sparsea->idx.ptr.p_int[k]];
        }
        denseb->ptr.p_double[i] = denseb->ptr.p_double[i]*si;
    }
    for(k=0; k<=corrrank-1; k++)
    {
        for(i=0; i<=n-1; i++)
        {
            densecorrc->ptr.pp_double[k][i] = densecorrc->ptr.pp_double[k][i]*s->ptr.p_double[i];
        }
    }
}


/*************************************************************************
This function normalizes two-sided "lower-bound/range" constraints  stored
in dense format in such a way that L2 norms of rows (right hand  side  NOT
included) become equal to 1.0. Exactly zero rows are handled correctly.

INPUT PARAMETERS:
    DenseA          -   array[M,N], constraint matrix
    AB              -   lower bounds for constraints, always present and
                        finite, array[M]
    AR              -   ranges for constraints, can be zero (equality
                        constraint), positive (range constraint) or +INF
                        (lower bound constraint), array[M]
    N               -   number of variables, N>=1.
    M               -   constraint count, M>=0
    NeedNorms       -   whether we need row norms or not

OUTPUT PARAMETERS:
    DenseA          -   replaced by normalized constraints, array[M,N]
    AB              -   replaced by normalized lower bounds, array[M]
    AR              -   replaced by normalized ranges, array[M]
    RowNorms        -   if NeedNorms is true, leading M elements (resized
                        if length is less than M) are filled by row norms
                        before normalization was performed.
    

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void normalizedensebrlcinplace(/* Real    */ ae_matrix* densea,
     /* Real    */ ae_vector* ab,
     /* Real    */ ae_vector* ar,
     ae_int_t n,
     ae_int_t m,
     /* Real    */ ae_vector* rownorms,
     ae_bool neednorms,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;
    double vv;


    if( neednorms )
    {
        rvectorsetlengthatleast(rownorms, m, _state);
    }
    for(i=0; i<=m-1; i++)
    {
        vv = 0.0;
        for(j=0; j<=n-1; j++)
        {
            v = densea->ptr.pp_double[i][j];
            vv = vv+v*v;
        }
        vv = ae_sqrt(vv, _state);
        if( neednorms )
        {
            rownorms->ptr.p_double[i] = vv;
        }
        if( ae_fp_greater(vv,(double)(0)) )
        {
            vv = (double)1/vv;
            for(j=0; j<=n-1; j++)
            {
                densea->ptr.pp_double[i][j] = densea->ptr.pp_double[i][j]*vv;
            }
            ab->ptr.p_double[i] = ab->ptr.p_double[i]*vv;
            if( ae_isfinite(ar->ptr.p_double[i], _state) )
            {
                ar->ptr.p_double[i] = ar->ptr.p_double[i]*vv;
            }
        }
    }
}


/*************************************************************************
This function normalizes two-sided "lower-bound/range" constraints  stored
in dense format in such a way that L2 norms of rows (right hand  side  NOT
included) become equal to 1.0. Exactly zero rows are handled correctly.

INPUT PARAMETERS:
    SparseA         -   sparse MSparse*N constraint matrix in CRS format;
                        ignored if MSparse=0.
    MSparse         -   dense constraint count, MSparse>=0
    DenseA          -   array[MDense,N], constraint matrix;
                        ignored if MDense=0.
    MDense          -   dense constraint count, MDense>=0
    AB              -   lower bounds for constraints, always present and
                        finite, array[MSparse+MDense]
    AR              -   ranges for constraints, can be zero (equality
                        constraint), positive (range constraint) or +INF
                        (lower bound constraint), array[MSparse+MDense]
    N               -   number of variables, N>=1.
    LimitedAmplification-   whether row amplification is limited or not:
                        * if False, rows with small norms (less than 1.0)
                          are always normalized
                        * if True, we do not increase individual row norms
                          during normalization - only decrease. However,
                          we may apply one amplification round to the entire
                          constraint matrix, i.e. amplify all rows by the same
                          coefficient. As result, we do not overamplify
                          any single row, but still make sure than the entire
                          problem is well scaled.
                        * large rows are always normalized
    NeedNorms       -   whether we need row norms or not

OUTPUT PARAMETERS:
    DenseA          -   replaced by normalized constraints, array[M,N]
    AB              -   replaced by normalized lower bounds, array[M]
    AR              -   replaced by normalized ranges, array[M]
    RowNorms        -   if NeedNorms is true, leading M elements (resized
                        if length is less than M) are filled by row norms
                        before normalization was performed.
    

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void normalizemixedbrlcinplace(sparsematrix* sparsea,
     ae_int_t msparse,
     /* Real    */ ae_matrix* densea,
     ae_int_t mdense,
     /* Real    */ ae_vector* ab,
     /* Real    */ ae_vector* ar,
     ae_int_t n,
     ae_bool limitedamplification,
     /* Real    */ ae_vector* rownorms,
     ae_bool neednorms,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t k0;
    ae_int_t k1;
    double v;
    double vv;
    double maxnrm2;


    ae_assert(msparse==0||((sparsea->matrixtype==1&&sparsea->m==msparse)&&sparsea->n==n), "ScaleShiftMixedBRLCInplace: non-CRS sparse constraint matrix!", _state);
    if( neednorms )
    {
        rvectorsetlengthatleast(rownorms, mdense+msparse, _state);
    }
    
    /*
     * First round of normalization - normalize row 2-norms subject to limited amplification status
     */
    maxnrm2 = (double)(0);
    for(i=0; i<=msparse-1; i++)
    {
        vv = 0.0;
        k0 = sparsea->ridx.ptr.p_int[i];
        k1 = sparsea->ridx.ptr.p_int[i+1]-1;
        for(k=k0; k<=k1; k++)
        {
            v = sparsea->vals.ptr.p_double[k];
            vv = vv+v*v;
        }
        vv = ae_sqrt(vv, _state);
        maxnrm2 = ae_maxreal(maxnrm2, vv, _state);
        if( limitedamplification )
        {
            vv = ae_maxreal(vv, 1.0, _state);
        }
        if( neednorms )
        {
            rownorms->ptr.p_double[i] = vv;
        }
        if( ae_fp_greater(vv,(double)(0)) )
        {
            vv = (double)1/vv;
            for(k=k0; k<=k1; k++)
            {
                sparsea->vals.ptr.p_double[k] = sparsea->vals.ptr.p_double[k]*vv;
            }
            ab->ptr.p_double[i] = ab->ptr.p_double[i]*vv;
            if( ae_isfinite(ar->ptr.p_double[i], _state) )
            {
                ar->ptr.p_double[i] = ar->ptr.p_double[i]*vv;
            }
        }
    }
    for(i=0; i<=mdense-1; i++)
    {
        vv = 0.0;
        for(j=0; j<=n-1; j++)
        {
            v = densea->ptr.pp_double[i][j];
            vv = vv+v*v;
        }
        vv = ae_sqrt(vv, _state);
        maxnrm2 = ae_maxreal(maxnrm2, vv, _state);
        if( limitedamplification )
        {
            vv = ae_maxreal(vv, 1.0, _state);
        }
        if( neednorms )
        {
            rownorms->ptr.p_double[msparse+i] = vv;
        }
        if( ae_fp_greater(vv,(double)(0)) )
        {
            vv = (double)1/vv;
            for(j=0; j<=n-1; j++)
            {
                densea->ptr.pp_double[i][j] = densea->ptr.pp_double[i][j]*vv;
            }
            ab->ptr.p_double[msparse+i] = ab->ptr.p_double[msparse+i]*vv;
            if( ae_isfinite(ar->ptr.p_double[msparse+i], _state) )
            {
                ar->ptr.p_double[msparse+i] = ar->ptr.p_double[msparse+i]*vv;
            }
        }
    }
    
    /*
     * If amplification was limited, perform second round of normalization
     */
    if( limitedamplification )
    {
        
        /*
         * Recompute maximum norm
         */
        maxnrm2 = (double)(0);
        for(i=0; i<=msparse-1; i++)
        {
            vv = 0.0;
            k0 = sparsea->ridx.ptr.p_int[i];
            k1 = sparsea->ridx.ptr.p_int[i+1]-1;
            for(k=k0; k<=k1; k++)
            {
                v = sparsea->vals.ptr.p_double[k];
                vv = vv+v*v;
            }
            vv = ae_sqrt(vv, _state);
            maxnrm2 = ae_maxreal(maxnrm2, vv, _state);
        }
        for(i=0; i<=mdense-1; i++)
        {
            maxnrm2 = ae_maxreal(maxnrm2, ae_sqrt(rdotrr(n, densea, i, densea, i, _state), _state), _state);
        }
        
        /*
         * Amplify, if needed 
         */
        if( ae_fp_less(maxnrm2,1.0)&&ae_fp_greater(maxnrm2,(double)(0)) )
        {
            if( neednorms )
            {
                rmulv(mdense+msparse, maxnrm2, rownorms, _state);
            }
            vv = (double)1/maxnrm2;
            for(i=0; i<=msparse-1; i++)
            {
                k0 = sparsea->ridx.ptr.p_int[i];
                k1 = sparsea->ridx.ptr.p_int[i+1]-1;
                for(k=k0; k<=k1; k++)
                {
                    sparsea->vals.ptr.p_double[k] = sparsea->vals.ptr.p_double[k]*vv;
                }
                ab->ptr.p_double[i] = ab->ptr.p_double[i]*vv;
                if( ae_isfinite(ar->ptr.p_double[i], _state) )
                {
                    ar->ptr.p_double[i] = ar->ptr.p_double[i]*vv;
                }
            }
            for(i=0; i<=mdense-1; i++)
            {
                rmulr(n, vv, densea, i, _state);
                ab->ptr.p_double[msparse+i] = ab->ptr.p_double[msparse+i]*vv;
                if( ae_isfinite(ar->ptr.p_double[msparse+i], _state) )
                {
                    ar->ptr.p_double[msparse+i] = ar->ptr.p_double[msparse+i]*vv;
                }
            }
        }
    }
}


/*************************************************************************
This function normalizes two-sided  "lower-bound/upper-bound"  constraints
stored in dense  format in such a way that L2 norms of  rows  (right  hand
side NOT included) become equal to 1.0. 

Exactly zero rows are handled correctly.

INPUT PARAMETERS:
    DenseA          -   dense M*N constraint matrix;
                        ignored if M=0.
    M               -   constraint count, M>=0
    AL              -   lower bounds for constraints, array[M]
    AU              -   upper bounds for constraints, array[M]
    N               -   number of variables, N>=1.
    LimitedAmplification-   whether row amplification is limited or not:
                        * if False, rows with small norms (less than 1.0)
                          are always normalized
                        * if True, we do not increase individual row norms
                          during normalization - only decrease. However,
                          we may apply one amplification rount to entire
                          constraint matrix, i.e. amplify all rows by same
                          coefficient. As result, we do not overamplify
                          any single row, but still make sure than entire
                          problem is well scaled.
                        * large rows are always normalized
    NeedNorms       -   whether we need row norms or not

OUTPUT PARAMETERS:
    SparseA         -   replaced by normalized constraints
    AL              -   replaced by normalized lower bounds
    AU              -   replaced by normalized upper bounds
    RowNorms        -   if NeedNorms is true, leading M elements (resized
                        if length is less than M) are filled by row norms
                        before normalization was performed.
    

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void normalizedenselcinplace(/* Real    */ ae_matrix* densea,
     ae_int_t m,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t n,
     ae_bool limitedamplification,
     /* Real    */ ae_vector* rownorms,
     ae_bool neednorms,
     ae_state *_state)
{
    ae_int_t i;
    double vv;
    double maxnrm2;


    ae_assert(m==0||(densea->rows>=m&&densea->cols>=n), "NormalizeDenseLCInplace: matrix size is too small", _state);
    if( neednorms )
    {
        rallocv(m, rownorms, _state);
    }
    
    /*
     * First round of normalization - normalize row 2-norms subject to limited amplification status
     */
    for(i=0; i<=m-1; i++)
    {
        vv = ae_sqrt(rdotrr(n, densea, i, densea, i, _state), _state);
        if( limitedamplification )
        {
            vv = ae_maxreal(vv, 1.0, _state);
        }
        if( neednorms )
        {
            rownorms->ptr.p_double[i] = vv;
        }
        if( ae_fp_greater(vv,(double)(0)) )
        {
            vv = (double)1/vv;
            rmulr(n, vv, densea, i, _state);
            al->ptr.p_double[i] = al->ptr.p_double[i]*vv;
            au->ptr.p_double[i] = au->ptr.p_double[i]*vv;
        }
    }
    
    /*
     * If amplification was limited, perform second round of normalization
     */
    if( limitedamplification )
    {
        maxnrm2 = (double)(0);
        for(i=0; i<=m-1; i++)
        {
            maxnrm2 = ae_maxreal(maxnrm2, ae_sqrt(rdotrr(n, densea, i, densea, i, _state), _state), _state);
        }
        if( ae_fp_less(maxnrm2,1.0)&&ae_fp_greater(maxnrm2,(double)(0)) )
        {
            if( neednorms )
            {
                rmulv(m, maxnrm2, rownorms, _state);
            }
            vv = (double)1/maxnrm2;
            for(i=0; i<=m-1; i++)
            {
                rmulr(n, vv, densea, i, _state);
                al->ptr.p_double[i] = al->ptr.p_double[i]*vv;
                au->ptr.p_double[i] = au->ptr.p_double[i]*vv;
            }
        }
    }
}


/*************************************************************************
This function normalizes two-sided  "lower-bound/upper-bound"  constraints
stored in sparse format in such a way that L2 norms of  rows  (right  hand
side NOT included) become equal to 1.0. 

Exactly zero rows are handled correctly.

INPUT PARAMETERS:
    SparseA         -   sparse M*N constraint matrix in CRS format;
                        ignored if M=0.
    M               -   constraint count, M>=0
    AL              -   lower bounds for constraints, array[M]
    AU              -   upper bounds for constraints, array[M]
    N               -   number of variables, N>=1.
    LimitedAmplification-   whether row amplification is limited or not:
                        * if False, rows with small norms (less than 1.0)
                          are always normalized
                        * if True, we do not increase individual row norms
                          during normalization - only decrease. However,
                          we may apply one amplification rount to entire
                          constraint matrix, i.e. amplify all rows by same
                          coefficient. As result, we do not overamplify
                          any single row, but still make sure than entire
                          problem is well scaled.
                        * large rows are always normalized
    NeedNorms       -   whether we need row norms or not

OUTPUT PARAMETERS:
    SparseA         -   replaced by normalized constraints
    AL              -   replaced by normalized lower bounds
    AU              -   replaced by normalized upper bounds
    RowNorms        -   if NeedNorms is true, leading M elements (resized
                        if length is less than M) are filled by row norms
                        before normalization was performed.
    

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void normalizesparselcinplace(sparsematrix* sparsea,
     ae_int_t m,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t n,
     ae_bool limitedamplification,
     /* Real    */ ae_vector* rownorms,
     ae_bool neednorms,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t k;
    ae_int_t k0;
    ae_int_t k1;
    double v;
    double vv;
    double maxnrm2;


    ae_assert(m==0||((sparsea->matrixtype==1&&sparsea->m==m)&&sparsea->n==n), "ScaleShiftMixedBRLCInplace: non-CRS sparse constraint matrix!", _state);
    if( neednorms )
    {
        rallocv(m, rownorms, _state);
    }
    
    /*
     * First round of normalization - normalize row 2-norms subject to limited amplification status
     */
    maxnrm2 = (double)(0);
    for(i=0; i<=m-1; i++)
    {
        vv = 0.0;
        k0 = sparsea->ridx.ptr.p_int[i];
        k1 = sparsea->ridx.ptr.p_int[i+1]-1;
        for(k=k0; k<=k1; k++)
        {
            v = sparsea->vals.ptr.p_double[k];
            vv = vv+v*v;
        }
        vv = ae_sqrt(vv, _state);
        maxnrm2 = ae_maxreal(maxnrm2, vv, _state);
        if( limitedamplification )
        {
            vv = ae_maxreal(vv, 1.0, _state);
        }
        if( neednorms )
        {
            rownorms->ptr.p_double[i] = vv;
        }
        if( ae_fp_greater(vv,(double)(0)) )
        {
            vv = (double)1/vv;
            for(k=k0; k<=k1; k++)
            {
                sparsea->vals.ptr.p_double[k] = sparsea->vals.ptr.p_double[k]*vv;
            }
            al->ptr.p_double[i] = al->ptr.p_double[i]*vv;
            au->ptr.p_double[i] = au->ptr.p_double[i]*vv;
        }
    }
    
    /*
     * If amplification was limited, perform second round of normalization
     */
    if( limitedamplification )
    {
        
        /*
         * Recompute maximum row norm
         */
        maxnrm2 = (double)(0);
        for(i=0; i<=m-1; i++)
        {
            vv = 0.0;
            k0 = sparsea->ridx.ptr.p_int[i];
            k1 = sparsea->ridx.ptr.p_int[i+1]-1;
            for(k=k0; k<=k1; k++)
            {
                v = sparsea->vals.ptr.p_double[k];
                vv = vv+v*v;
            }
            vv = ae_sqrt(vv, _state);
            maxnrm2 = ae_maxreal(maxnrm2, vv, _state);
        }
        
        /*
         * Perform limited amplification, if needed
         */
        if( ae_fp_less(maxnrm2,1.0)&&ae_fp_greater(maxnrm2,(double)(0)) )
        {
            if( neednorms )
            {
                rmulv(m, maxnrm2, rownorms, _state);
            }
            vv = (double)1/maxnrm2;
            for(i=0; i<=m-1; i++)
            {
                k0 = sparsea->ridx.ptr.p_int[i];
                k1 = sparsea->ridx.ptr.p_int[i+1]-1;
                for(k=k0; k<=k1; k++)
                {
                    sparsea->vals.ptr.p_double[k] = sparsea->vals.ptr.p_double[k]*vv;
                }
                al->ptr.p_double[i] = al->ptr.p_double[i]*vv;
                au->ptr.p_double[i] = au->ptr.p_double[i]*vv;
            }
        }
    }
}


/*************************************************************************
This function normalizes two-sided  "lower-bound/upper-bound"  constraints
stored in sparse format in such a way that maximum of row L2 norms  (right
hand side NOT included) become equal to 1.0. 

Exactly zero rows are handled correctly.

INPUT PARAMETERS:
    SparseA         -   sparse M*N constraint matrix in CRS format;
                        ignored if M=0.
    M               -   constraint count, M>=0
    AL              -   lower bounds for constraints, array[M]
    AU              -   upper bounds for constraints, array[M]
    N               -   number of variables, N>=1.
    NeedScales      -   whether we need rescaling factors or not

OUTPUT PARAMETERS:
    SparseA         -   replaced by normalized constraints
    AL              -   replaced by normalized lower bounds
    AU              -   replaced by normalized upper bounds
    AScales         -   if NeedScales is true, leading M elements (resized
                        if length is less than M) are filled by the same
                        number which was used to scale a matrix. If all
                        rows are zero, AScales[] is set to 1.0
    

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void normalizesparselcinplaceuniform(sparsematrix* sparsea,
     ae_int_t m,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t n,
     /* Real    */ ae_vector* ascales,
     ae_bool needscales,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t k;
    ae_int_t k0;
    ae_int_t k1;
    double v;
    double vv;
    double maxnrm2;


    ae_assert(m==0||((sparsea->matrixtype==1&&sparsea->m==m)&&sparsea->n==n), "ScaleShiftMixedBRLCInplace: non-CRS sparse constraint matrix!", _state);
    if( needscales )
    {
        rallocv(m, ascales, _state);
    }
    
    /*
     * Compute maximum row norm
     */
    maxnrm2 = (double)(0);
    for(i=0; i<=m-1; i++)
    {
        vv = 0.0;
        k0 = sparsea->ridx.ptr.p_int[i];
        k1 = sparsea->ridx.ptr.p_int[i+1]-1;
        for(k=k0; k<=k1; k++)
        {
            v = sparsea->vals.ptr.p_double[k];
            vv = vv+v*v;
        }
        vv = ae_sqrt(vv, _state);
        maxnrm2 = ae_maxreal(maxnrm2, vv, _state);
    }
    if( ae_fp_eq(maxnrm2,(double)(0)) )
    {
        if( needscales )
        {
            rsetv(m, 1.0, ascales, _state);
        }
        return;
    }
    
    /*
     * Renormalize
     */
    vv = (double)1/maxnrm2;
    if( needscales )
    {
        rsetv(m, maxnrm2, ascales, _state);
    }
    for(i=0; i<=m-1; i++)
    {
        k0 = sparsea->ridx.ptr.p_int[i];
        k1 = sparsea->ridx.ptr.p_int[i+1]-1;
        for(k=k0; k<=k1; k++)
        {
            sparsea->vals.ptr.p_double[k] = sparsea->vals.ptr.p_double[k]*vv;
        }
        al->ptr.p_double[i] = al->ptr.p_double[i]*vv;
        au->ptr.p_double[i] = au->ptr.p_double[i]*vv;
    }
}


/*************************************************************************
This function normalizes dense QP problem in such a way that maximum  over
its linear/quadratic coefficients max(max(A),max(B)) becomes equal to 1.0.

NOTE: completely zero A and B are handled correctly.

INPUT PARAMETERS:
    DenseA          -   array[NMain,NMain], quadratic term
    IsUpper         -   whether upper or lower triangle is present
    NMain           -   number of nonslack vars, 1<=NMain<=NTotal
    DenseB          -   array[NTotal], linear term
    NTotal          -   total number of variables.

OUTPUT PARAMETERS:
    DenseA          -   replaced by normalized term
    DenseB          -   replaced by normalized term
    
RESULT:
    max(max(A),max(B)) is returned

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
double normalizedenseqpinplace(/* Real    */ ae_matrix* densea,
     ae_bool isupper,
     ae_int_t nmain,
     /* Real    */ ae_vector* denseb,
     ae_int_t ntotal,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    ae_int_t j1;
    double mx;
    double v;
    double result;


    mx = (double)(0);
    for(i=0; i<=nmain-1; i++)
    {
        if( isupper )
        {
            j0 = i;
            j1 = nmain-1;
        }
        else
        {
            j0 = 0;
            j1 = i;
        }
        for(j=j0; j<=j1; j++)
        {
            mx = ae_maxreal(mx, ae_fabs(densea->ptr.pp_double[i][j], _state), _state);
        }
    }
    for(i=0; i<=ntotal-1; i++)
    {
        mx = ae_maxreal(mx, ae_fabs(denseb->ptr.p_double[i], _state), _state);
    }
    result = mx;
    if( ae_fp_eq(mx,(double)(0)) )
    {
        return result;
    }
    v = (double)1/mx;
    for(i=0; i<=nmain-1; i++)
    {
        if( isupper )
        {
            j0 = i;
            j1 = nmain-1;
        }
        else
        {
            j0 = 0;
            j1 = i;
        }
        for(j=j0; j<=j1; j++)
        {
            densea->ptr.pp_double[i][j] = densea->ptr.pp_double[i][j]*v;
        }
    }
    for(i=0; i<=ntotal-1; i++)
    {
        denseb->ptr.p_double[i] = denseb->ptr.p_double[i]*v;
    }
    return result;
}


/*************************************************************************
This function normalizes sparse QP problem in such a way that maximum over
its linear/quadratic coefficients max(max(A),max(B)) becomes equal to 1.0.

NOTE: completely zero A and B are handled correctly.

INPUT PARAMETERS:
    SparseA         -   Sparse NxN matrix, either upper or lower triangle,
                        diagonal MUST be present
    IsUpper         -   which triangle is present (other one is ignored)
    DenseCorrC      -   array[CorrRank,N], low rank correction C'*D*C being
                        added to the quadratic term
    DenseCorrD      -   array[CorrRank], low rank correction, diagonal factors
    CorrRank        -   correction rank, >=0
    DenseB          -   array[N], linear term
    N               -   number of variables.

OUTPUT PARAMETERS:
    DenseA          -   replaced by normalized term, array[N,N]
    DenseB          -   replaced by normalized term, array[N]
    
RESULT:
    max(max(A),max(B)) is returned

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
double normalizesparseqpinplace(sparsematrix* sparsea,
     ae_bool isupper,
     /* Real    */ ae_matrix* densecorrc,
     /* Real    */ ae_vector* densecorrd,
     ae_int_t corrrank,
     /* Real    */ ae_vector* denseb,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t k;
    ae_int_t k0;
    ae_int_t k1;
    double mx;
    double v;
    double result;


    ae_assert((sparsea->matrixtype==1&&sparsea->m==n)&&sparsea->n==n, "NormalizeSparseQPInplace: SparseA in unexpected format", _state);
    mx = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(sparsea->didx.ptr.p_int[i]+1==sparsea->uidx.ptr.p_int[i], "NormalizeSparseQPInplace: critical integrity check failed, sparse diagonal not found", _state);
        v = sparsea->vals.ptr.p_double[sparsea->didx.ptr.p_int[i]];
        for(k=0; k<=corrrank-1; k++)
        {
            v = v+densecorrd->ptr.p_double[k]*ae_sqr(densecorrc->ptr.pp_double[k][i], _state);
        }
        mx = ae_maxreal(mx, ae_fabs(v, _state), _state);
        mx = ae_maxreal(mx, ae_fabs(denseb->ptr.p_double[i], _state), _state);
    }
    result = mx;
    if( ae_fp_eq(mx,(double)(0)) )
    {
        return result;
    }
    v = (double)1/mx;
    for(i=0; i<=n-1; i++)
    {
        k0 = sparsea->ridx.ptr.p_int[i];
        k1 = sparsea->ridx.ptr.p_int[i+1]-1;
        for(k=k0; k<=k1; k++)
        {
            sparsea->vals.ptr.p_double[k] = sparsea->vals.ptr.p_double[k]*v;
        }
        denseb->ptr.p_double[i] = denseb->ptr.p_double[i]*v;
    }
    for(k=0; k<=corrrank-1; k++)
    {
        rmulr(n, ae_sqrt(v, _state), densecorrc, k, _state);
    }
    return result;
}


/*************************************************************************
This function performs transformation of X from scaled/shifted coordinates
to unscaled/unshifted ones, paying special attention to box constraints:
* points which were exactly at the boundary before scaling will be  mapped
  to corresponding boundary after scaling
* in any case, unscaled box constraints will be satisfied

  -- ALGLIB --
     Copyright 02.06.2015 by Bochkanov Sergey
*************************************************************************/
void unscaleunshiftpointbc(/* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     /* Real    */ const ae_vector* rawbndl,
     /* Real    */ const ae_vector* rawbndu,
     /* Real    */ const ae_vector* sclsftbndl,
     /* Real    */ const ae_vector* sclsftbndu,
     /* Boolean */ const ae_vector* hasbndl,
     /* Boolean */ const ae_vector* hasbndu,
     /* Real    */ ae_vector* x,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;


    for(i=0; i<=n-1; i++)
    {
        if( hasbndl->ptr.p_bool[i]&&ae_fp_less_eq(x->ptr.p_double[i],sclsftbndl->ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = rawbndl->ptr.p_double[i];
            continue;
        }
        if( hasbndu->ptr.p_bool[i]&&ae_fp_greater_eq(x->ptr.p_double[i],sclsftbndu->ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = rawbndu->ptr.p_double[i];
            continue;
        }
        x->ptr.p_double[i] = x->ptr.p_double[i]*s->ptr.p_double[i]+xorigin->ptr.p_double[i];
        if( hasbndl->ptr.p_bool[i]&&ae_fp_less_eq(x->ptr.p_double[i],rawbndl->ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = rawbndl->ptr.p_double[i];
        }
        if( hasbndu->ptr.p_bool[i]&&ae_fp_greater_eq(x->ptr.p_double[i],rawbndu->ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = rawbndu->ptr.p_double[i];
        }
    }
}


/*************************************************************************
Converts dense/sparse linear and quadratic terms (the latter can be given
by the upper/lower triangle) to the dense lower triangular form.

INPUT PARAMETERS:
    RawC                -   array[N], linear term
    N                   -   variables count
    DenseH              -   if HKind=0: array[NMain,NMain], dense quadratic term
                            (either upper or lower triangle)
    SparseH             -   if HKind=1: array[NMain,NMain], sparse quadratic term
                            (either upper or lower triangle)
    HKind               -   0 or 1, quadratic term format
    IsUpper             -   whether dense/sparse H contains lower or upper
                            triangle of the quadratic term
    H                   -   possibly preallocated matrix
    
OUTPUT PARAMETERS:
    C                   -   array[N], scaled linear term
    H                   -   array[N,N], lower triangle is converted H

  -- ALGLIB --
     Copyright 23.05.2024 by Bochkanov Sergey
*************************************************************************/
void quadraticlinearconverttodenseltr(/* Real    */ const ae_vector* rawc,
     ae_int_t n,
     /* Real    */ const ae_matrix* denseh,
     const sparsematrix* sparseh,
     ae_int_t hkind,
     ae_bool isupper,
     /* Real    */ ae_vector* c,
     /* Real    */ ae_matrix* h,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    ae_int_t j1;
    double v;


    ae_assert(hkind==0||hkind==1, "QuadraticLinearScaleConvertToDenseLTR: incorrect HKind", _state);
    ae_assert(n>=1, "QuadraticLinearScaleConvertToDenseLTR: N<1", _state);
    
    /*
     * Raw linear term
     */
    rcopyallocv(n, rawc, c, _state);
    
    /*
     * Quadratic term, without normalization
     *
     * NOTE: we perform integrity check for inifinities/NANs by
     *       computing sum of all matrix elements and checking its
     *       value for being finite. It is a bit faster than checking
     *       each element individually.
     */
    rallocm(n, n, h, _state);
    if( hkind==0 )
    {
        
        /*
         * Copy dense quadratic term
         */
        if( isupper )
        {
            rmatrixtranspose(n, n, denseh, 0, 0, h, 0, 0, _state);
        }
        else
        {
            rmatrixcopy(n, n, denseh, 0, 0, h, 0, 0, _state);
        }
    }
    if( hkind==1 )
    {
        
        /*
         * Extract sparse quadratic term
         */
        ae_assert(sparseh->matrixtype==1, "QuadraticLinearScaleConvertToDenseLTR: unexpected sparse matrix format", _state);
        ae_assert(sparseh->m==n, "QuadraticLinearScaleConvertToDenseLTR: unexpected sparse matrix size", _state);
        ae_assert(sparseh->n==n, "QuadraticLinearScaleConvertToDenseLTR: unexpected sparse matrix size", _state);
        rsetm(n, n, 0.0, h, _state);
        for(i=0; i<=n-1; i++)
        {
            
            /*
             * diagonal element
             */
            if( sparseh->didx.ptr.p_int[i]!=sparseh->uidx.ptr.p_int[i] )
            {
                h->ptr.pp_double[i][i] = sparseh->vals.ptr.p_double[sparseh->didx.ptr.p_int[i]];
            }
            
            /*
             * Off-diagonal elements
             */
            if( isupper )
            {
                
                /*
                 * superdiagonal elements are moved to the subdiagonal part
                 */
                j0 = sparseh->uidx.ptr.p_int[i];
                j1 = sparseh->ridx.ptr.p_int[i+1]-1;
                for(j=j0; j<=j1; j++)
                {
                    h->ptr.pp_double[sparseh->idx.ptr.p_int[j]][i] = sparseh->vals.ptr.p_double[j];
                }
            }
            else
            {
                
                /*
                 * subdiagonal elements are moved to the subdiagonal part
                 */
                j0 = sparseh->ridx.ptr.p_int[i];
                j1 = sparseh->didx.ptr.p_int[i]-1;
                for(j=j0; j<=j1; j++)
                {
                    h->ptr.pp_double[i][sparseh->idx.ptr.p_int[j]] = sparseh->vals.ptr.p_double[j];
                }
            }
        }
    }
    v = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        v = v+rdotrr(i+1, h, i, h, i, _state);
    }
    ae_assert(ae_isfinite(v, _state), "QuadraticLinearScaleConvertToDenseLTR: H contains infinite or NaN values!", _state);
}


#endif
#if defined(AE_COMPILE_SNNLS) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
This subroutine is used to initialize SNNLS solver.

By default, empty NNLS problem is produced, but we allocated enough  space
to store problems with NSMax+NDMax columns and  NRMax  rows.  It  is  good
place to provide algorithm with initial estimate of the space requirements,
although you may underestimate problem size or even pass zero estimates  -
in this case buffer variables will be resized automatically  when  you set
NNLS problem.

Previously allocated buffer variables are reused as much as possible. This
function does not clear structure completely, it tries to preserve as much
dynamically allocated memory as possible.

  -- ALGLIB --
     Copyright 10.10.2012 by Bochkanov Sergey
*************************************************************************/
void snnlsinit(ae_int_t nsmax,
     ae_int_t ndmax,
     ae_int_t nrmax,
     snnlssolver* s,
     ae_state *_state)
{


    s->ns = 0;
    s->nd = 0;
    s->nr = 0;
    rmatrixsetlengthatleast(&s->densea, nrmax, ndmax, _state);
    rmatrixsetlengthatleast(&s->tmpca, nrmax, ndmax, _state);
    rvectorsetlengthatleast(&s->b, nrmax, _state);
    bvectorsetlengthatleast(&s->nnc, nsmax+ndmax, _state);
    s->debugflops = 0.0;
    s->debugmaxinnerits = 0;
}


/*************************************************************************
This subroutine is used to set NNLS problem:

        ( [ 1     |      ]   [   ]   [   ] )^2
        ( [   1   |      ]   [   ]   [   ] )
    min ( [     1 |  Ad  ] * [ x ] - [ b ] )    s.t. x>=0
        ( [       |      ]   [   ]   [   ] )
        ( [       |      ]   [   ]   [   ] )

where:
* identity matrix has NS*NS size (NS<=NR, NS can be zero)
* dense matrix Ad has NR*ND size
* b is NR*1 vector
* x is (NS+ND)*1 vector
* all elements of x are non-negative (this constraint can be removed later
  by calling SNNLSDropNNC() function)

Previously allocated buffer variables are reused as much as possible.
After you set problem, you can solve it with SNNLSSolve().

INPUT PARAMETERS:
    S   -   SNNLS solver, must be initialized with SNNLSInit() call
    A   -   array[NR,ND], dense part of the system
    B   -   array[NR], right part
    NS  -   size of the sparse part of the system, 0<=NS<=NR
    ND  -   size of the dense part of the system, ND>=0
    NR  -   rows count, NR>0

NOTE:
    1. You can have NS+ND=0, solver will correctly accept such combination
       and return empty array as problem solution.
    
  -- ALGLIB --
     Copyright 10.10.2012 by Bochkanov Sergey
*************************************************************************/
void snnlssetproblem(snnlssolver* s,
     /* Real    */ const ae_matrix* a,
     /* Real    */ const ae_vector* b,
     ae_int_t ns,
     ae_int_t nd,
     ae_int_t nr,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(nd>=0, "SNNLSSetProblem: ND<0", _state);
    ae_assert(ns>=0, "SNNLSSetProblem: NS<0", _state);
    ae_assert(nr>0, "SNNLSSetProblem: NR<=0", _state);
    ae_assert(ns<=nr, "SNNLSSetProblem: NS>NR", _state);
    ae_assert(a->rows>=nr||nd==0, "SNNLSSetProblem: rows(A)<NR", _state);
    ae_assert(a->cols>=nd, "SNNLSSetProblem: cols(A)<ND", _state);
    ae_assert(b->cnt>=nr, "SNNLSSetProblem: length(B)<NR", _state);
    ae_assert(apservisfinitematrix(a, nr, nd, _state), "SNNLSSetProblem: A contains INF/NAN", _state);
    ae_assert(isfinitevector(b, nr, _state), "SNNLSSetProblem: B contains INF/NAN", _state);
    
    /*
     * Copy problem
     */
    s->ns = ns;
    s->nd = nd;
    s->nr = nr;
    if( nd>0 )
    {
        rmatrixsetlengthatleast(&s->densea, nr, nd, _state);
        for(i=0; i<=nr-1; i++)
        {
            ae_v_move(&s->densea.ptr.pp_double[i][0], 1, &a->ptr.pp_double[i][0], 1, ae_v_len(0,nd-1));
        }
    }
    rvectorsetlengthatleast(&s->b, nr, _state);
    ae_v_move(&s->b.ptr.p_double[0], 1, &b->ptr.p_double[0], 1, ae_v_len(0,nr-1));
    bvectorsetlengthatleast(&s->nnc, ns+nd, _state);
    for(i=0; i<=ns+nd-1; i++)
    {
        s->nnc.ptr.p_bool[i] = ae_true;
    }
}


/*************************************************************************
This subroutine drops non-negativity constraint from the  problem  set  by
SNNLSSetProblem() call. This function must be called AFTER problem is set,
because each SetProblem() call resets constraints to their  default  state
(all constraints are present).

INPUT PARAMETERS:
    S   -   SNNLS solver, must be initialized with SNNLSInit() call,
            problem must be set with SNNLSSetProblem() call.
    Idx -   constraint index, 0<=IDX<NS+ND
    
  -- ALGLIB --
     Copyright 10.10.2012 by Bochkanov Sergey
*************************************************************************/
void snnlsdropnnc(snnlssolver* s, ae_int_t idx, ae_state *_state)
{


    ae_assert(idx>=0, "SNNLSDropNNC: Idx<0", _state);
    ae_assert(idx<s->ns+s->nd, "SNNLSDropNNC: Idx>=NS+ND", _state);
    s->nnc.ptr.p_bool[idx] = ae_false;
}


/*************************************************************************
This subroutine is used to solve NNLS problem.

INPUT PARAMETERS:
    S   -   SNNLS solver, must be initialized with SNNLSInit() call and
            problem must be set up with SNNLSSetProblem() call.
    X   -   possibly preallocated buffer, automatically resized if needed

OUTPUT PARAMETERS:
    X   -   array[NS+ND], solution
    
NOTE:
    1. You can have NS+ND=0, solver will correctly accept such combination
       and return empty array as problem solution.
    
    2. Internal field S.DebugFLOPS contains rough estimate of  FLOPs  used
       to solve problem. It can be used for debugging purposes. This field
       is real-valued.
    
  -- ALGLIB --
     Copyright 10.10.2012 by Bochkanov Sergey
*************************************************************************/
void snnlssolve(snnlssolver* s,
     /* Real    */ ae_vector* x,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t ns;
    ae_int_t nd;
    ae_int_t nr;
    ae_bool wasactivation;
    double lambdav;
    double v0;
    double v1;
    double v;
    ae_int_t outerits;
    ae_int_t innerits;
    ae_int_t maxouterits;
    double xtol;
    double kicklength;
    ae_bool kickneeded;
    double f0;
    double f1;
    double dnrm;
    ae_int_t actidx;
    double stp;
    double stpmax;


    
    /*
     * Prepare
     */
    ns = s->ns;
    nd = s->nd;
    nr = s->nr;
    s->debugflops = 0.0;
    
    /*
     * Handle special cases:
     * * NS+ND=0
     * * ND=0
     */
    if( ns+nd==0 )
    {
        return;
    }
    if( nd==0 )
    {
        rvectorsetlengthatleast(x, ns, _state);
        for(i=0; i<=ns-1; i++)
        {
            x->ptr.p_double[i] = s->b.ptr.p_double[i];
            if( s->nnc.ptr.p_bool[i] )
            {
                x->ptr.p_double[i] = ae_maxreal(x->ptr.p_double[i], 0.0, _state);
            }
        }
        return;
    }
    
    /*
     * Main cycle of BLEIC-SNNLS algorithm.
     * Below we assume that ND>0.
     */
    rvectorsetlengthatleast(x, ns+nd, _state);
    rvectorsetlengthatleast(&s->xn, ns+nd, _state);
    rvectorsetlengthatleast(&s->xp, ns+nd, _state);
    rvectorsetlengthatleast(&s->g, ns+nd, _state);
    rvectorsetlengthatleast(&s->d, ns+nd, _state);
    rvectorsetlengthatleast(&s->r, nr, _state);
    rvectorsetlengthatleast(&s->diagaa, nd, _state);
    rvectorsetlengthatleast(&s->regdiag, ns+nd, _state);
    rvectorsetlengthatleast(&s->dx, ns+nd, _state);
    for(i=0; i<=ns+nd-1; i++)
    {
        x->ptr.p_double[i] = 0.0;
        s->regdiag.ptr.p_double[i] = 1.0;
    }
    lambdav = 1.0E6*ae_machineepsilon;
    maxouterits = 10;
    outerits = 0;
    innerits = 0;
    xtol = 1.0E3*ae_machineepsilon;
    kicklength = ae_sqrt(ae_minrealnumber, _state);
    for(;;)
    {
        
        /*
         * Initial check for correctness of X
         */
        for(i=0; i<=ns+nd-1; i++)
        {
            ae_assert(!s->nnc.ptr.p_bool[i]||ae_fp_greater_eq(x->ptr.p_double[i],(double)(0)), "SNNLS: integrity check failed", _state);
        }
        
        /*
         * Calculate gradient G and constrained descent direction D
         */
        snnls_funcgradu(s, x, &s->r, &s->g, &f0, _state);
        for(i=0; i<=ns+nd-1; i++)
        {
            if( (s->nnc.ptr.p_bool[i]&&ae_fp_eq(x->ptr.p_double[i],(double)(0)))&&ae_fp_greater(s->g.ptr.p_double[i],(double)(0)) )
            {
                s->d.ptr.p_double[i] = 0.0;
            }
            else
            {
                s->d.ptr.p_double[i] = -s->g.ptr.p_double[i];
            }
        }
        
        /*
         * Decide whether we need "kick" stage: special stage
         * that moves us away from boundary constraints which are
         * not strictly active (i.e. such constraints that x[i]=0.0 and d[i]>0).
         *
         * If we need kick stage, we make a kick - and restart iteration.
         * If not, after this block we can rely on the fact that
         * for all x[i]=0.0 we have d[i]=0.0
         *
         * NOTE: we do not increase outer iterations counter here
         */
        kickneeded = ae_false;
        for(i=0; i<=ns+nd-1; i++)
        {
            if( (s->nnc.ptr.p_bool[i]&&ae_fp_eq(x->ptr.p_double[i],0.0))&&ae_fp_greater(s->d.ptr.p_double[i],0.0) )
            {
                kickneeded = ae_true;
            }
        }
        if( kickneeded )
        {
            
            /*
             * Perform kick.
             * Restart.
             * Do not increase iterations counter.
             */
            for(i=0; i<=ns+nd-1; i++)
            {
                if( ae_fp_eq(x->ptr.p_double[i],0.0)&&ae_fp_greater(s->d.ptr.p_double[i],0.0) )
                {
                    x->ptr.p_double[i] = x->ptr.p_double[i]+kicklength;
                }
            }
            continue;
        }
        
        /*
         * Newton phase
         * Reduce problem to constrained triangular form and perform Newton
         * steps with quick activation of constrants  (triangular  form  is
         * updated in order to handle changed constraints).
         */
        for(i=0; i<=ns+nd-1; i++)
        {
            s->xp.ptr.p_double[i] = x->ptr.p_double[i];
        }
        snnls_trdprepare(s, x, &s->regdiag, lambdav, &s->trdd, &s->trda, &s->tmp0, &s->tmp1, &s->tmp2, &s->tmplq, _state);
        for(;;)
        {
            
            /*
             * Skip if debug limit on inner iterations count is turned on.
             */
            if( s->debugmaxinnerits>0&&innerits>=s->debugmaxinnerits )
            {
                break;
            }
            
            /*
             * Prepare step vector.
             */
            snnls_funcgradu(s, x, &s->r, &s->g, &f0, _state);
            for(i=0; i<=ns+nd-1; i++)
            {
                s->d.ptr.p_double[i] = -s->g.ptr.p_double[i];
                if( s->nnc.ptr.p_bool[i]&&ae_fp_eq(x->ptr.p_double[i],0.0) )
                {
                    s->d.ptr.p_double[i] = 0.0;
                }
            }
            snnls_trdsolve(&s->trdd, &s->trda, ns, nd, &s->d, _state);
            
            /*
             * Perform unconstrained trial step and compare function values.
             */
            for(i=0; i<=ns+nd-1; i++)
            {
                s->xn.ptr.p_double[i] = x->ptr.p_double[i]+s->d.ptr.p_double[i];
            }
            snnls_func(s, &s->xn, &f1, _state);
            if( ae_fp_greater_eq(f1,f0) )
            {
                break;
            }
            
            /*
             * Calculate length of D, maximum step and component which is
             * activated by this step. Break if D is exactly zero.
             */
            dnrm = 0.0;
            for(i=0; i<=ns+nd-1; i++)
            {
                dnrm = dnrm+ae_sqr(s->d.ptr.p_double[i], _state);
            }
            dnrm = ae_sqrt(dnrm, _state);
            actidx = -1;
            stpmax = 1.0E50;
            for(i=0; i<=ns+nd-1; i++)
            {
                if( s->nnc.ptr.p_bool[i]&&ae_fp_less(s->d.ptr.p_double[i],0.0) )
                {
                    v = stpmax;
                    stpmax = safeminposrv(x->ptr.p_double[i], -s->d.ptr.p_double[i], stpmax, _state);
                    if( ae_fp_less(stpmax,v) )
                    {
                        actidx = i;
                    }
                }
            }
            if( ae_fp_eq(dnrm,0.0) )
            {
                break;
            }
            
            /*
             * Perform constrained step and update X
             * and triangular model.
             */
            stp = ae_minreal(1.0, stpmax, _state);
            for(i=0; i<=ns+nd-1; i++)
            {
                v = x->ptr.p_double[i]+stp*s->d.ptr.p_double[i];
                if( s->nnc.ptr.p_bool[i] )
                {
                    v = ae_maxreal(v, 0.0, _state);
                }
                s->xn.ptr.p_double[i] = v;
            }
            if( ae_fp_eq(stp,stpmax)&&actidx>=0 )
            {
                s->xn.ptr.p_double[actidx] = 0.0;
            }
            wasactivation = ae_false;
            for(i=0; i<=ns+nd-1; i++)
            {
                if( ae_fp_eq(s->xn.ptr.p_double[i],0.0)&&ae_fp_neq(x->ptr.p_double[i],0.0) )
                {
                    wasactivation = ae_true;
                    snnls_trdfixvariable(&s->trdd, &s->trda, ns, nd, i, &s->tmpcholesky, _state);
                }
            }
            for(i=0; i<=ns+nd-1; i++)
            {
                x->ptr.p_double[i] = s->xn.ptr.p_double[i];
            }
            
            /*
             * Increment iterations counter.
             * Terminate if no constraint was activated.
             */
            inc(&innerits, _state);
            if( !wasactivation )
            {
                break;
            }
        }
        
        /*
         * Update outer iterations counter.
         *
         * Break if necessary:
         * * maximum number of outer iterations performed
         * * relative change in X is small enough
         */
        inc(&outerits, _state);
        if( outerits>=maxouterits )
        {
            break;
        }
        v = (double)(0);
        for(i=0; i<=ns+nd-1; i++)
        {
            v0 = ae_fabs(s->xp.ptr.p_double[i], _state);
            v1 = ae_fabs(x->ptr.p_double[i], _state);
            if( ae_fp_neq(v0,(double)(0))||ae_fp_neq(v1,(double)(0)) )
            {
                v = ae_maxreal(v, ae_fabs(x->ptr.p_double[i]-s->xp.ptr.p_double[i], _state)/ae_maxreal(v0, v1, _state), _state);
            }
        }
        if( ae_fp_less_eq(v,xtol) )
        {
            break;
        }
    }
}


/*************************************************************************
This function calculates:
* residual vector R = A*x-b
* unconstrained gradient vector G
* function value F = 0.5*|R|^2

R and G must have at least N elements.

  -- ALGLIB --
     Copyright 15.07.2015 by Bochkanov Sergey
*************************************************************************/
static void snnls_funcgradu(const snnlssolver* s,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* r,
     /* Real    */ ae_vector* g,
     double* f,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t nr;
    ae_int_t nd;
    ae_int_t ns;
    double v;

    *f = 0.0;

    nr = s->nr;
    nd = s->nd;
    ns = s->ns;
    *f = 0.0;
    for(i=0; i<=nr-1; i++)
    {
        v = ae_v_dotproduct(&s->densea.ptr.pp_double[i][0], 1, &x->ptr.p_double[ns], 1, ae_v_len(0,nd-1));
        if( i<ns )
        {
            v = v+x->ptr.p_double[i];
        }
        v = v-s->b.ptr.p_double[i];
        r->ptr.p_double[i] = v;
        *f = *f+0.5*v*v;
    }
    for(i=0; i<=ns-1; i++)
    {
        g->ptr.p_double[i] = r->ptr.p_double[i];
    }
    for(i=ns; i<=ns+nd-1; i++)
    {
        g->ptr.p_double[i] = 0.0;
    }
    for(i=0; i<=nr-1; i++)
    {
        v = r->ptr.p_double[i];
        ae_v_addd(&g->ptr.p_double[ns], 1, &s->densea.ptr.pp_double[i][0], 1, ae_v_len(ns,ns+nd-1), v);
    }
}


/*************************************************************************
This function calculates function value F = 0.5*|R|^2 at X.

  -- ALGLIB --
     Copyright 15.07.2015 by Bochkanov Sergey
*************************************************************************/
static void snnls_func(const snnlssolver* s,
     /* Real    */ const ae_vector* x,
     double* f,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t nr;
    ae_int_t nd;
    ae_int_t ns;
    double v;

    *f = 0.0;

    nr = s->nr;
    nd = s->nd;
    ns = s->ns;
    *f = 0.0;
    for(i=0; i<=nr-1; i++)
    {
        v = ae_v_dotproduct(&s->densea.ptr.pp_double[i][0], 1, &x->ptr.p_double[ns], 1, ae_v_len(0,nd-1));
        if( i<ns )
        {
            v = v+x->ptr.p_double[i];
        }
        v = v-s->b.ptr.p_double[i];
        *f = *f+0.5*v*v;
    }
}


static void snnls_trdprepare(snnlssolver* s,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* diag,
     double lambdav,
     /* Real    */ ae_vector* trdd,
     /* Real    */ ae_matrix* trda,
     /* Real    */ ae_vector* tmp0,
     /* Real    */ ae_vector* tmp1,
     /* Real    */ ae_vector* tmp2,
     /* Real    */ ae_matrix* tmplq,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t ns;
    ae_int_t nd;
    ae_int_t nr;
    double v;
    double cs;
    double sn;
    double r;


    
    /*
     * Prepare
     */
    ns = s->ns;
    nd = s->nd;
    nr = s->nr;
    
    /*
     * Triangular reduction
     */
    rvectorsetlengthatleast(trdd, ns, _state);
    rmatrixsetlengthatleast(trda, ns+nd, nd, _state);
    rmatrixsetlengthatleast(tmplq, nd, nr+nd, _state);
    for(i=0; i<=ns-1; i++)
    {
        
        /*
         * Apply rotation to I-th row and corresponding row of
         * regularizer. Here V is diagonal element of I-th row,
         * which is set to 1.0 or 0.0 depending on variable
         * status (constrained or not).
         */
        v = 1.0;
        if( s->nnc.ptr.p_bool[i]&&ae_fp_eq(x->ptr.p_double[i],0.0) )
        {
            v = 0.0;
        }
        generaterotation(v, lambdav, &cs, &sn, &r, _state);
        trdd->ptr.p_double[i] = cs*v+sn*lambdav;
        for(j=0; j<=nd-1; j++)
        {
            v = s->densea.ptr.pp_double[i][j];
            trda->ptr.pp_double[i][j] = cs*v;
            tmplq->ptr.pp_double[j][i] = -sn*v;
        }
    }
    for(j=0; j<=nd-1; j++)
    {
        for(i=ns; i<=nr-1; i++)
        {
            tmplq->ptr.pp_double[j][i] = s->densea.ptr.pp_double[i][j];
        }
    }
    for(j=0; j<=nd-1; j++)
    {
        if( s->nnc.ptr.p_bool[ns+j]&&ae_fp_eq(x->ptr.p_double[ns+j],0.0) )
        {
            
            /*
             * Variable is constrained, entire row is set to zero.
             */
            for(i=0; i<=nr-1; i++)
            {
                tmplq->ptr.pp_double[j][i] = 0.0;
            }
            for(i=0; i<=ns-1; i++)
            {
                trda->ptr.pp_double[i][j] = 0.0;
            }
        }
    }
    for(i=0; i<=nd-1; i++)
    {
        for(j=0; j<=nd-1; j++)
        {
            tmplq->ptr.pp_double[j][nr+i] = 0.0;
        }
        tmplq->ptr.pp_double[i][nr+i] = lambdav*diag->ptr.p_double[i];
    }
    rvectorsetlengthatleast(tmp0, nr+nd+1, _state);
    rvectorsetlengthatleast(tmp1, nr+nd+1, _state);
    rvectorsetlengthatleast(tmp2, nr+nd+1, _state);
    rmatrixlqbasecase(tmplq, nd, nr+nd, tmp0, tmp1, tmp2, _state);
    for(i=0; i<=nd-1; i++)
    {
        if( ae_fp_less(tmplq->ptr.pp_double[i][i],0.0) )
        {
            for(j=i; j<=nd-1; j++)
            {
                tmplq->ptr.pp_double[j][i] = -tmplq->ptr.pp_double[j][i];
            }
        }
    }
    for(i=0; i<=nd-1; i++)
    {
        for(j=0; j<=i; j++)
        {
            trda->ptr.pp_double[ns+j][i] = tmplq->ptr.pp_double[i][j];
        }
    }
}


static void snnls_trdsolve(/* Real    */ const ae_vector* trdd,
     /* Real    */ const ae_matrix* trda,
     ae_int_t ns,
     ae_int_t nd,
     /* Real    */ ae_vector* d,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;


    
    /*
     * Solve U'*y=d first.
     *
     * This section includes two parts:
     * * solve diagonal part of U'
     * * solve dense part of U'
     */
    for(i=0; i<=ns-1; i++)
    {
        d->ptr.p_double[i] = d->ptr.p_double[i]/trdd->ptr.p_double[i];
        v = d->ptr.p_double[i];
        for(j=0; j<=nd-1; j++)
        {
            d->ptr.p_double[ns+j] = d->ptr.p_double[ns+j]-v*trda->ptr.pp_double[i][j];
        }
    }
    for(i=0; i<=nd-1; i++)
    {
        d->ptr.p_double[ns+i] = d->ptr.p_double[ns+i]/trda->ptr.pp_double[ns+i][i];
        v = d->ptr.p_double[ns+i];
        for(j=i+1; j<=nd-1; j++)
        {
            d->ptr.p_double[ns+j] = d->ptr.p_double[ns+j]-v*trda->ptr.pp_double[ns+i][j];
        }
    }
    
    /*
     * Solve U*x=y then.
     *
     * This section includes two parts:
     * * solve trailing triangular part of U
     * * solve combination of diagonal and dense parts of U
     */
    for(i=nd-1; i>=0; i--)
    {
        v = 0.0;
        for(j=i+1; j<=nd-1; j++)
        {
            v = v+trda->ptr.pp_double[ns+i][j]*d->ptr.p_double[ns+j];
        }
        d->ptr.p_double[ns+i] = (d->ptr.p_double[ns+i]-v)/trda->ptr.pp_double[ns+i][i];
    }
    for(i=ns-1; i>=0; i--)
    {
        v = 0.0;
        for(j=0; j<=nd-1; j++)
        {
            v = v+trda->ptr.pp_double[i][j]*d->ptr.p_double[ns+j];
        }
        d->ptr.p_double[i] = (d->ptr.p_double[i]-v)/trdd->ptr.p_double[i];
    }
}


static void snnls_trdfixvariable(/* Real    */ ae_vector* trdd,
     /* Real    */ ae_matrix* trda,
     ae_int_t ns,
     ae_int_t nd,
     ae_int_t idx,
     /* Real    */ ae_vector* tmp,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double cs;
    double sn;
    double r;
    double v;
    double vv;


    ae_assert(ns>=0, "TRDFixVariable: integrity error", _state);
    ae_assert(nd>=0, "TRDFixVariable: integrity error", _state);
    ae_assert(ns+nd>0, "TRDFixVariable: integrity error", _state);
    ae_assert(idx>=0, "TRDFixVariable: integrity error", _state);
    ae_assert(idx<ns+nd, "TRDFixVariable: integrity error", _state);
    rvectorsetlengthatleast(tmp, nd, _state);
    
    /*
     * Depending on variable index, two situations are possible
     */
    if( idx<ns )
    {
        
        /*
         * We fix variable in the diagonal part of the model. It means
         * that prior to fixing we have:
         *
         *     (     |     )
         *     (  D  |     )
         *     (     |     )
         *     (-----|  A  )
         *     (     |0    )
         *     (     |00   )
         *     (     |000  )
         *     (     |0000 )
         *     (     |00000)
         *
         * then we replace idx-th column by zeros:
         *
         *     (D 0  |     )
         *     (  0  |     )
         *     (  0 D|     )
         *     (-----|  A  )
         *     (     |     )
         *     (     |     )
         *     (     |     )
         *
         * and append row with unit element to bottom, in order to
         * regularize problem
         *
         *     (D 0  |     )
         *     (  0  |     )
         *     (  0 D|     )
         *     (-----|  A  )
         *     (     |     )
         *     (     |     )
         *     (     |     )
         *     (00100|00000) <- appended
         *
         * and then we nullify this row by applying rotations:
         *
         *     (D 0  |     )
         *     (  0  |     ) <- first rotation is applied here
         *     (  0 D|     )
         *     (-----|  A  ) <- subsequent rotations are applied to this row and rows below
         *     (     |     )
         *     (     |     )
         *     (     |     )
         *     (  0  |  0  ) <- as result, row becomes zero
         *
         * and triangular structure is preserved
         */
        if( nd==0 )
        {
            
            /*
             * Quick exit for empty dense part
             */
            trdd->ptr.p_double[idx] = 1.0;
            return;
        }
        for(j=0; j<=nd-1; j++)
        {
            
            /*
             * Apply first rotation
             */
            tmp->ptr.p_double[j] = trda->ptr.pp_double[idx][j];
            trda->ptr.pp_double[idx][j] = 0.0;
        }
        trdd->ptr.p_double[idx] = 1.0;
        for(i=0; i<=nd-1; i++)
        {
            if( ae_fp_neq(tmp->ptr.p_double[i],(double)(0)) )
            {
                
                /*
                 * Apply subsequent rotations with bottom triangular part of A
                 */
                generaterotation(trda->ptr.pp_double[ns+i][i], tmp->ptr.p_double[i], &cs, &sn, &r, _state);
                for(j=i; j<=nd-1; j++)
                {
                    v = trda->ptr.pp_double[ns+i][j];
                    vv = tmp->ptr.p_double[j];
                    trda->ptr.pp_double[ns+i][j] = v*cs+vv*sn;
                    tmp->ptr.p_double[j] = vv*cs-v*sn;
                }
            }
        }
    }
    else
    {
        
        /*
         * We fix variable in the dense part of the model. It means
         * that prior to fixing we have:
         *
         *     (     |     )
         *     (  D  |     )
         *     (     |     )
         *     (-----|  A  )
         *     (     |0    )
         *     (     |00   )
         *     (     |000  )
         *     (     |0000 )
         *     (     |00000)
         *
         * then we replace idx-th column by zeros:
         *
         *     (     |  0  )
         *     (  D  |  0  )
         *     (     |  0  )
         *     (-----|A 0 A)
         *     (     |  0  )
         *     (     |  0  )
         *     (     |  0  )
         *
         * and append row with unit element to bottom, in order to
         * regularize problem
         *
         *     (     |  0  )
         *     (  D  |  0  )
         *     (     |  0  )
         *     (-----|A 0 A)
         *     (     |  0  )
         *     (     |  0  )
         *     (     |  0  )
         *     (00000|00100) <- appended
         *
         * and then we nullify this row by applying rotations:
         *
         *     (D 0  |     )
         *     (  0  |     )
         *     (  0 D|     )
         *     (-----|  A  )
         *     (     |     )
         *     (     |     ) <- first rotation is applied here
         *     (     |     ) <- subsequent rotations are applied to rows below
         *     (  0  |  0  ) <- as result, row becomes zero
         *
         * and triangular structure is preserved.
         */
        k = idx-ns;
        for(i=0; i<=ns+nd-1; i++)
        {
            trda->ptr.pp_double[i][k] = 0.0;
        }
        for(j=k+1; j<=nd-1; j++)
        {
            
            /*
             * Apply first rotation
             */
            tmp->ptr.p_double[j] = trda->ptr.pp_double[idx][j];
            trda->ptr.pp_double[idx][j] = 0.0;
        }
        trda->ptr.pp_double[idx][k] = 1.0;
        for(i=k+1; i<=nd-1; i++)
        {
            if( ae_fp_neq(tmp->ptr.p_double[i],(double)(0)) )
            {
                
                /*
                 * Apply subsequent rotations with bottom triangular part of A
                 */
                generaterotation(trda->ptr.pp_double[ns+i][i], tmp->ptr.p_double[i], &cs, &sn, &r, _state);
                for(j=i; j<=nd-1; j++)
                {
                    v = trda->ptr.pp_double[ns+i][j];
                    vv = tmp->ptr.p_double[j];
                    trda->ptr.pp_double[ns+i][j] = v*cs+vv*sn;
                    tmp->ptr.p_double[j] = vv*cs-v*sn;
                }
            }
        }
    }
}


void _snnlssolver_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    snnlssolver *p = (snnlssolver*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_init(&p->densea, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->b, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nnc, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->xn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xp, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpca, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmplq, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->trda, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->trdd, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->crb, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagaa, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cb, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cborg, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpcholesky, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->r, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->regdiag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rdtmprowmap, 0, DT_INT, _state, make_automatic);
}


void _snnlssolver_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    snnlssolver       *dst = (snnlssolver*)_dst;
    const snnlssolver *src = (const snnlssolver*)_src;
    dst->ns = src->ns;
    dst->nd = src->nd;
    dst->nr = src->nr;
    ae_matrix_init_copy(&dst->densea, &src->densea, _state, make_automatic);
    ae_vector_init_copy(&dst->b, &src->b, _state, make_automatic);
    ae_vector_init_copy(&dst->nnc, &src->nnc, _state, make_automatic);
    dst->debugflops = src->debugflops;
    dst->debugmaxinnerits = src->debugmaxinnerits;
    ae_vector_init_copy(&dst->xn, &src->xn, _state, make_automatic);
    ae_vector_init_copy(&dst->xp, &src->xp, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpca, &src->tmpca, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmplq, &src->tmplq, _state, make_automatic);
    ae_matrix_init_copy(&dst->trda, &src->trda, _state, make_automatic);
    ae_vector_init_copy(&dst->trdd, &src->trdd, _state, make_automatic);
    ae_vector_init_copy(&dst->crb, &src->crb, _state, make_automatic);
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    ae_vector_init_copy(&dst->dx, &src->dx, _state, make_automatic);
    ae_vector_init_copy(&dst->diagaa, &src->diagaa, _state, make_automatic);
    ae_vector_init_copy(&dst->cb, &src->cb, _state, make_automatic);
    ae_vector_init_copy(&dst->cx, &src->cx, _state, make_automatic);
    ae_vector_init_copy(&dst->cborg, &src->cborg, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpcholesky, &src->tmpcholesky, _state, make_automatic);
    ae_vector_init_copy(&dst->r, &src->r, _state, make_automatic);
    ae_vector_init_copy(&dst->regdiag, &src->regdiag, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp2, &src->tmp2, _state, make_automatic);
    ae_vector_init_copy(&dst->rdtmprowmap, &src->rdtmprowmap, _state, make_automatic);
}


void _snnlssolver_clear(void* _p)
{
    snnlssolver *p = (snnlssolver*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_clear(&p->densea);
    ae_vector_clear(&p->b);
    ae_vector_clear(&p->nnc);
    ae_vector_clear(&p->xn);
    ae_vector_clear(&p->xp);
    ae_matrix_clear(&p->tmpca);
    ae_matrix_clear(&p->tmplq);
    ae_matrix_clear(&p->trda);
    ae_vector_clear(&p->trdd);
    ae_vector_clear(&p->crb);
    ae_vector_clear(&p->g);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->dx);
    ae_vector_clear(&p->diagaa);
    ae_vector_clear(&p->cb);
    ae_vector_clear(&p->cx);
    ae_vector_clear(&p->cborg);
    ae_vector_clear(&p->tmpcholesky);
    ae_vector_clear(&p->r);
    ae_vector_clear(&p->regdiag);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmp1);
    ae_vector_clear(&p->tmp2);
    ae_vector_clear(&p->rdtmprowmap);
}


void _snnlssolver_destroy(void* _p)
{
    snnlssolver *p = (snnlssolver*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_destroy(&p->densea);
    ae_vector_destroy(&p->b);
    ae_vector_destroy(&p->nnc);
    ae_vector_destroy(&p->xn);
    ae_vector_destroy(&p->xp);
    ae_matrix_destroy(&p->tmpca);
    ae_matrix_destroy(&p->tmplq);
    ae_matrix_destroy(&p->trda);
    ae_vector_destroy(&p->trdd);
    ae_vector_destroy(&p->crb);
    ae_vector_destroy(&p->g);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->dx);
    ae_vector_destroy(&p->diagaa);
    ae_vector_destroy(&p->cb);
    ae_vector_destroy(&p->cx);
    ae_vector_destroy(&p->cborg);
    ae_vector_destroy(&p->tmpcholesky);
    ae_vector_destroy(&p->r);
    ae_vector_destroy(&p->regdiag);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmp1);
    ae_vector_destroy(&p->tmp2);
    ae_vector_destroy(&p->rdtmprowmap);
}


#endif
#if defined(AE_COMPILE_SACTIVESETS) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
This   subroutine   is   used  to initialize active set. By default, empty
N-variable model with no constraints is  generated.  Previously  allocated
buffer variables are reused as much as possible.

Two use cases for this object are described below.

CASE 1 - STEEPEST DESCENT:

    SASInit()
    repeat:
        SASReactivateConstraints()
        SASDescentDirection()
        SASExploreDirection()
        SASMoveTo()
    until convergence

CASE 1 - PRECONDITIONED STEEPEST DESCENT:

    SASInit()
    repeat:
        SASReactivateConstraintsPrec()
        SASDescentDirectionPrec()
        SASExploreDirection()
        SASMoveTo()
    until convergence

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
void sasinit(ae_int_t n, sactiveset* s, ae_state *_state)
{
    ae_int_t i;


    s->n = n;
    s->algostate = 0;
    
    /*
     * Constraints
     */
    s->constraintschanged = ae_true;
    s->nec = 0;
    s->nic = 0;
    rvectorsetlengthatleast(&s->bndl, n, _state);
    bvectorsetlengthatleast(&s->hasbndl, n, _state);
    rvectorsetlengthatleast(&s->bndu, n, _state);
    bvectorsetlengthatleast(&s->hasbndu, n, _state);
    for(i=0; i<=n-1; i++)
    {
        s->bndl.ptr.p_double[i] = _state->v_neginf;
        s->bndu.ptr.p_double[i] = _state->v_posinf;
        s->hasbndl.ptr.p_bool[i] = ae_false;
        s->hasbndu.ptr.p_bool[i] = ae_false;
    }
    
    /*
     * current point, scale
     */
    s->hasxc = ae_false;
    rvectorsetlengthatleast(&s->xc, n, _state);
    rvectorsetlengthatleast(&s->s, n, _state);
    rvectorsetlengthatleast(&s->h, n, _state);
    for(i=0; i<=n-1; i++)
    {
        s->xc.ptr.p_double[i] = 0.0;
        s->s.ptr.p_double[i] = 1.0;
        s->h.ptr.p_double[i] = 1.0;
    }
    
    /*
     * Other
     */
    rvectorsetlengthatleast(&s->unitdiagonal, n, _state);
    for(i=0; i<=n-1; i++)
    {
        s->unitdiagonal.ptr.p_double[i] = 1.0;
    }
}


/*************************************************************************
This function sets scaling coefficients for SAS object.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

During orthogonalization phase, scale is used to calculate drop tolerances
(whether vector is significantly non-zero or not).

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
void sassetscale(sactiveset* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(state->algostate==0, "SASSetScale: you may change scale only in modification mode", _state);
    ae_assert(s->cnt>=state->n, "SASSetScale: Length(S)<N", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "SASSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "SASSetScale: S contains zero elements", _state);
    }
    for(i=0; i<=state->n-1; i++)
    {
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
Modification  of  the  preconditioner:  diagonal of approximate Hessian is
used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    D       -   diagonal of the approximate Hessian, array[0..N-1],
                (if larger, only leading N elements are used).

NOTE 1: D[i] should be positive. Exception will be thrown otherwise.

NOTE 2: you should pass diagonal of approximate Hessian - NOT ITS INVERSE.

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
void sassetprecdiag(sactiveset* state,
     /* Real    */ const ae_vector* d,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(state->algostate==0, "SASSetPrecDiag: you may change preconditioner only in modification mode", _state);
    ae_assert(d->cnt>=state->n, "SASSetPrecDiag: D is too short", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(d->ptr.p_double[i], _state), "SASSetPrecDiag: D contains infinite or NAN elements", _state);
        ae_assert(ae_fp_greater(d->ptr.p_double[i],(double)(0)), "SASSetPrecDiag: D contains non-positive elements", _state);
    }
    for(i=0; i<=state->n-1; i++)
    {
        state->h.ptr.p_double[i] = d->ptr.p_double[i];
    }
}


/*************************************************************************
This function sets/changes boundary constraints.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF.

NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
void sassetbc(sactiveset* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    ae_assert(state->algostate==0, "SASSetBC: you may change constraints only in modification mode", _state);
    n = state->n;
    ae_assert(bndl->cnt>=n, "SASSetBC: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=n, "SASSetBC: Length(BndU)<N", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "SASSetBC: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "SASSetBC: BndL contains NAN or -INF", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
    }
    state->constraintschanged = ae_true;
}


/*************************************************************************
This function sets linear constraints for SAS object.

Linear constraints are inactive by default (after initial creation).

INPUT PARAMETERS:
    State   -   SAS structure
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0

NOTE 1: linear (non-bound) constraints are satisfied only approximately:
* there always exists some minor violation (about Epsilon in magnitude)
  due to rounding errors
* numerical differentiation, if used, may  lead  to  function  evaluations
  outside  of the feasible  area,   because   algorithm  does  NOT  change
  numerical differentiation formula according to linear constraints.
If you want constraints to be  satisfied  exactly, try to reformulate your
problem  in  such  manner  that  all constraints will become boundary ones
(this kind of constraints is always satisfied exactly, both in  the  final
solution and in all intermediate points).

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void sassetlc(sactiveset* state,
     /* Real    */ const ae_matrix* c,
     /* Integer */ const ae_vector* ct,
     ae_int_t k,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;


    ae_assert(state->algostate==0, "SASSetLC: you may change constraints only in modification mode", _state);
    n = state->n;
    
    /*
     * First, check for errors in the inputs
     */
    ae_assert(k>=0, "SASSetLC: K<0", _state);
    ae_assert(c->cols>=n+1||k==0, "SASSetLC: Cols(C)<N+1", _state);
    ae_assert(c->rows>=k, "SASSetLC: Rows(C)<K", _state);
    ae_assert(ct->cnt>=k, "SASSetLC: Length(CT)<K", _state);
    ae_assert(apservisfinitematrix(c, k, n+1, _state), "SASSetLC: C contains infinite or NaN values!", _state);
    
    /*
     * Handle zero K
     */
    if( k==0 )
    {
        state->nec = 0;
        state->nic = 0;
        state->constraintschanged = ae_true;
        return;
    }
    
    /*
     * Equality constraints are stored first, in the upper
     * NEC rows of State.CLEIC matrix. Inequality constraints
     * are stored in the next NIC rows.
     *
     * NOTE: we convert inequality constraints to the form
     * A*x<=b before copying them.
     */
    rmatrixsetlengthatleast(&state->cleic, k, n+1, _state);
    state->nec = 0;
    state->nic = 0;
    for(i=0; i<=k-1; i++)
    {
        if( ct->ptr.p_int[i]==0 )
        {
            ae_v_move(&state->cleic.ptr.pp_double[state->nec][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
            state->nec = state->nec+1;
        }
    }
    for(i=0; i<=k-1; i++)
    {
        if( ct->ptr.p_int[i]!=0 )
        {
            if( ct->ptr.p_int[i]>0 )
            {
                ae_v_moveneg(&state->cleic.ptr.pp_double[state->nec+state->nic][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
            }
            else
            {
                ae_v_move(&state->cleic.ptr.pp_double[state->nec+state->nic][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
            }
            state->nic = state->nic+1;
        }
    }
    
    /*
     * Mark state as changed
     */
    state->constraintschanged = ae_true;
}


/*************************************************************************
Another variation of SASSetLC(), which accepts  linear  constraints  using
another representation.

Linear constraints are inactive by default (after initial creation).

INPUT PARAMETERS:
    State   -   SAS structure
    CLEIC   -   linear constraints, array[NEC+NIC,N+1].
                Each row of C represents one constraint:
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                First NEC rows store equality constraints, next NIC -  are
                inequality ones.
                All elements of C (including right part) must be finite.
    NEC     -   number of equality constraints, NEC>=0
    NIC     -   number of inequality constraints, NIC>=0

NOTE 1: linear (non-bound) constraints are satisfied only approximately:
* there always exists some minor violation (about Epsilon in magnitude)
  due to rounding errors
* numerical differentiation, if used, may  lead  to  function  evaluations
  outside  of the feasible  area,   because   algorithm  does  NOT  change
  numerical differentiation formula according to linear constraints.
If you want constraints to be  satisfied  exactly, try to reformulate your
problem  in  such  manner  that  all constraints will become boundary ones
(this kind of constraints is always satisfied exactly, both in  the  final
solution and in all intermediate points).

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void sassetlcx(sactiveset* state,
     /* Real    */ const ae_matrix* cleic,
     ae_int_t nec,
     ae_int_t nic,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;


    ae_assert(state->algostate==0, "SASSetLCX: you may change constraints only in modification mode", _state);
    n = state->n;
    
    /*
     * First, check for errors in the inputs
     */
    ae_assert(nec>=0, "SASSetLCX: NEC<0", _state);
    ae_assert(nic>=0, "SASSetLCX: NIC<0", _state);
    ae_assert(cleic->cols>=n+1||nec+nic==0, "SASSetLCX: Cols(CLEIC)<N+1", _state);
    ae_assert(cleic->rows>=nec+nic, "SASSetLCX: Rows(CLEIC)<NEC+NIC", _state);
    ae_assert(apservisfinitematrix(cleic, nec+nic, n+1, _state), "SASSetLCX: CLEIC contains infinite or NaN values!", _state);
    
    /*
     * Store constraints
     */
    rmatrixsetlengthatleast(&state->cleic, nec+nic, n+1, _state);
    state->nec = nec;
    state->nic = nic;
    for(i=0; i<=nec+nic-1; i++)
    {
        for(j=0; j<=n; j++)
        {
            state->cleic.ptr.pp_double[i][j] = cleic->ptr.pp_double[i][j];
        }
    }
    
    /*
     * Mark state as changed
     */
    state->constraintschanged = ae_true;
}


/*************************************************************************
This subroutine turns on optimization mode:
1. feasibility in X is enforced  (in case X=S.XC and constraints  have not
   changed, algorithm just uses X without any modifications at all)
2. constraints are marked as "candidate" or "inactive"

INPUT PARAMETERS:
    S   -   active set object
    X   -   initial point (candidate), array[N]. It is expected that X
            contains only finite values (we do not check it).
    
OUTPUT PARAMETERS:
    S   -   state is changed
    X   -   initial point can be changed to enforce feasibility
    
RESULT:
    True in case feasible point was found (mode was changed to "optimization")
    False in case no feasible point was found (mode was not changed)

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
ae_bool sasstartoptimization(sactiveset* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t i;
    ae_int_t j;
    double v;
    double v0;
    double v1;
    double vv;
    double vc;
    double vx;
    ae_bool result;


    ae_assert(state->algostate==0, "SASStartOptimization: already in optimization mode", _state);
    result = ae_false;
    n = state->n;
    nec = state->nec;
    nic = state->nic;
    
    /*
     * Enforce feasibility and calculate set of "candidate"/"active" constraints.
     * Always active equality constraints are marked as "active", all other constraints
     * are marked as "candidate".
     */
    ivectorsetlengthatleast(&state->cstatus, n+nec+nic, _state);
    for(i=0; i<=n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i] )
        {
            if( ae_fp_greater(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                return result;
            }
        }
    }
    ae_v_move(&state->xc.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
    if( state->nec+state->nic>0 )
    {
        
        /*
         * General linear constraints are present.
         * Try to use fast code for feasible initial point with modest
         * memory requirements.
         */
        rvectorsetlengthatleast(&state->tmp0, n, _state);
        state->feasinitpt = ae_true;
        for(i=0; i<=n-1; i++)
        {
            state->tmp0.ptr.p_double[i] = x->ptr.p_double[i];
            state->cstatus.ptr.p_int[i] = -1;
            if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                state->tmp0.ptr.p_double[i] = state->bndl.ptr.p_double[i];
                state->cstatus.ptr.p_int[i] = 1;
                continue;
            }
            if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less_eq(state->tmp0.ptr.p_double[i],state->bndl.ptr.p_double[i]) )
            {
                state->cstatus.ptr.p_int[i] = 0;
                state->tmp0.ptr.p_double[i] = state->bndl.ptr.p_double[i];
            }
            if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater_eq(state->tmp0.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                state->cstatus.ptr.p_int[i] = 0;
                state->tmp0.ptr.p_double[i] = state->bndu.ptr.p_double[i];
            }
        }
        for(i=0; i<=state->nec+state->nic-1; i++)
        {
            v = -state->cleic.ptr.pp_double[i][n];
            v0 = (double)(0);
            v1 = (double)(0);
            for(j=0; j<=n-1; j++)
            {
                vx = state->tmp0.ptr.p_double[j]/state->s.ptr.p_double[j];
                vc = state->cleic.ptr.pp_double[i][j]*state->s.ptr.p_double[j];
                v = v+vx*vc;
                v0 = v0+ae_sqr(vx, _state);
                v1 = v1+ae_sqr(vc, _state);
            }
            vv = ae_sqrt(v0, _state)*ae_sqrt(v1, _state)*(double)1000*ae_machineepsilon;
            if( i<state->nec )
            {
                state->cstatus.ptr.p_int[n+i] = 1;
                state->feasinitpt = state->feasinitpt&&ae_fp_less(ae_fabs(v, _state),vv);
            }
            else
            {
                state->feasinitpt = state->feasinitpt&&ae_fp_less(v,vv);
                if( ae_fp_less(v,-vv) )
                {
                    state->cstatus.ptr.p_int[n+i] = -1;
                }
                else
                {
                    state->cstatus.ptr.p_int[n+i] = 0;
                }
            }
        }
        if( state->feasinitpt )
        {
            ae_v_move(&state->xc.ptr.p_double[0], 1, &state->tmp0.ptr.p_double[0], 1, ae_v_len(0,n-1));
        }
        
        /*
         * Fast code failed? Use general code with ~(N+NIC)^2 memory requirements
         */
        if( !state->feasinitpt )
        {
            rvectorsetlengthatleast(&state->tmp0, n, _state);
            rvectorsetlengthatleast(&state->tmpfeas, n+state->nic, _state);
            rmatrixsetlengthatleast(&state->tmpm0, state->nec+state->nic, n+state->nic+1, _state);
            for(i=0; i<=state->nec+state->nic-1; i++)
            {
                ae_v_move(&state->tmpm0.ptr.pp_double[i][0], 1, &state->cleic.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                for(j=n; j<=n+state->nic-1; j++)
                {
                    state->tmpm0.ptr.pp_double[i][j] = (double)(0);
                }
                if( i>=state->nec )
                {
                    state->tmpm0.ptr.pp_double[i][n+i-state->nec] = 1.0;
                }
                state->tmpm0.ptr.pp_double[i][n+state->nic] = state->cleic.ptr.pp_double[i][n];
            }
            ae_v_move(&state->tmpfeas.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
            for(i=0; i<=state->nic-1; i++)
            {
                v = ae_v_dotproduct(&state->cleic.ptr.pp_double[i+state->nec][0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
                state->tmpfeas.ptr.p_double[i+n] = ae_maxreal(state->cleic.ptr.pp_double[i+state->nec][n]-v, 0.0, _state);
            }
            if( !findfeasiblepoint(&state->tmpfeas, &state->bndl, &state->hasbndl, &state->bndu, &state->hasbndu, n, state->nic, &state->tmpm0, state->nec+state->nic, 1.0E-6, &i, &j, _state) )
            {
                return result;
            }
            ae_v_move(&state->xc.ptr.p_double[0], 1, &state->tmpfeas.ptr.p_double[0], 1, ae_v_len(0,n-1));
            for(i=0; i<=n-1; i++)
            {
                if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
                {
                    state->cstatus.ptr.p_int[i] = 1;
                    continue;
                }
                if( (state->hasbndl.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->bndl.ptr.p_double[i]))||(state->hasbndu.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->bndu.ptr.p_double[i])) )
                {
                    state->cstatus.ptr.p_int[i] = 0;
                    continue;
                }
                state->cstatus.ptr.p_int[i] = -1;
            }
            for(i=0; i<=state->nec-1; i++)
            {
                state->cstatus.ptr.p_int[n+i] = 1;
            }
            for(i=0; i<=state->nic-1; i++)
            {
                if( ae_fp_eq(state->tmpfeas.ptr.p_double[n+i],(double)(0)) )
                {
                    state->cstatus.ptr.p_int[n+state->nec+i] = 0;
                }
                else
                {
                    state->cstatus.ptr.p_int[n+state->nec+i] = -1;
                }
            }
        }
    }
    else
    {
        
        /*
         * Only box constraints are present, quick code can be used
         */
        for(i=0; i<=n-1; i++)
        {
            state->cstatus.ptr.p_int[i] = -1;
            if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                state->cstatus.ptr.p_int[i] = 1;
                state->xc.ptr.p_double[i] = state->bndl.ptr.p_double[i];
                continue;
            }
            if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less_eq(state->xc.ptr.p_double[i],state->bndl.ptr.p_double[i]) )
            {
                state->xc.ptr.p_double[i] = state->bndl.ptr.p_double[i];
                state->cstatus.ptr.p_int[i] = 0;
                continue;
            }
            if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater_eq(state->xc.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                state->xc.ptr.p_double[i] = state->bndu.ptr.p_double[i];
                state->cstatus.ptr.p_int[i] = 0;
                continue;
            }
        }
        state->feasinitpt = ae_true;
    }
    
    /*
     * Change state, allocate temporaries
     */
    result = ae_true;
    state->algostate = 1;
    state->basisisready = ae_false;
    state->hasxc = ae_true;
    return result;
}


/*************************************************************************
This function explores search direction and calculates bound for  step  as
well as information for activation of constraints.

INPUT PARAMETERS:
    State       -   SAS structure which stores current point and all other
                    active set related information
    D           -   descent direction to explore

OUTPUT PARAMETERS:
    StpMax      -   upper  limit  on  step  length imposed by yet inactive
                    constraints. Can be  zero  in  case  some  constraints
                    can be activated by zero step.  Equal  to  some  large
                    value in case step is unlimited.
    CIdx        -   -1 for unlimited step, in [0,N+NEC+NIC) in case of
                    limited step.
    VVal        -   value which is assigned to X[CIdx] during activation.
                    For CIdx<0 or CIdx>=N some dummy value is assigned to
                    this parameter.
*************************************************************************/
void sasexploredirection(const sactiveset* state,
     /* Real    */ const ae_vector* d,
     double* stpmax,
     ae_int_t* cidx,
     double* vval,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t i;
    double prevmax;
    double vc;
    double vd;

    *stpmax = 0.0;
    *cidx = 0;
    *vval = 0.0;

    ae_assert(state->algostate==1, "SASExploreDirection: is not in optimization mode", _state);
    n = state->n;
    nec = state->nec;
    nic = state->nic;
    *cidx = -1;
    *vval = (double)(0);
    *stpmax = 1.0E50;
    for(i=0; i<=n-1; i++)
    {
        if( state->cstatus.ptr.p_int[i]<=0 )
        {
            ae_assert(!state->hasbndl.ptr.p_bool[i]||ae_fp_greater_eq(state->xc.ptr.p_double[i],state->bndl.ptr.p_double[i]), "SASExploreDirection: internal error - infeasible X", _state);
            ae_assert(!state->hasbndu.ptr.p_bool[i]||ae_fp_less_eq(state->xc.ptr.p_double[i],state->bndu.ptr.p_double[i]), "SASExploreDirection: internal error - infeasible X", _state);
            if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less(d->ptr.p_double[i],(double)(0)) )
            {
                prevmax = *stpmax;
                *stpmax = safeminposrv(state->xc.ptr.p_double[i]-state->bndl.ptr.p_double[i], -d->ptr.p_double[i], *stpmax, _state);
                if( ae_fp_less(*stpmax,prevmax) )
                {
                    *cidx = i;
                    *vval = state->bndl.ptr.p_double[i];
                }
            }
            if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater(d->ptr.p_double[i],(double)(0)) )
            {
                prevmax = *stpmax;
                *stpmax = safeminposrv(state->bndu.ptr.p_double[i]-state->xc.ptr.p_double[i], d->ptr.p_double[i], *stpmax, _state);
                if( ae_fp_less(*stpmax,prevmax) )
                {
                    *cidx = i;
                    *vval = state->bndu.ptr.p_double[i];
                }
            }
        }
    }
    for(i=nec; i<=nec+nic-1; i++)
    {
        if( state->cstatus.ptr.p_int[n+i]<=0 )
        {
            vc = ae_v_dotproduct(&state->cleic.ptr.pp_double[i][0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
            vc = vc-state->cleic.ptr.pp_double[i][n];
            vd = ae_v_dotproduct(&state->cleic.ptr.pp_double[i][0], 1, &d->ptr.p_double[0], 1, ae_v_len(0,n-1));
            if( ae_fp_less_eq(vd,(double)(0)) )
            {
                continue;
            }
            if( ae_fp_less(vc,(double)(0)) )
            {
                
                /*
                 * XC is strictly feasible with respect to I-th constraint,
                 * we can perform non-zero step because there is non-zero distance
                 * between XC and bound.
                 */
                prevmax = *stpmax;
                *stpmax = safeminposrv(-vc, vd, *stpmax, _state);
                if( ae_fp_less(*stpmax,prevmax) )
                {
                    *cidx = n+i;
                }
            }
            else
            {
                
                /*
                 * XC is at the boundary (or slightly beyond it), and step vector
                 * points beyond the boundary.
                 *
                 * The only thing we can do is to perform zero step and activate
                 * I-th constraint.
                 */
                *stpmax = (double)(0);
                *cidx = n+i;
            }
        }
    }
}


/*************************************************************************
This subroutine moves current point to XN, which can be:
a) point in the direction previously explored  with  SASExploreDirection()
   function (in this case NeedAct/CIdx/CVal are used)
b) point in arbitrary direction, not necessarily previously  checked  with
   SASExploreDirection() function.

Step may activate one constraint. It is assumed than XN  is  approximately
feasible (small error as  large  as several  ulps  is  possible).   Strict
feasibility  with  respect  to  bound  constraints  is  enforced    during
activation, feasibility with respect to general linear constraints is  not
enforced.

This function activates boundary constraints, such that both is True:
1) XC[I] is not at the boundary
2) XN[I] is at the boundary or beyond it

INPUT PARAMETERS:
    S       -   active set object
    XN      -   new point.
    NeedAct -   True in case one constraint needs activation
    CIdx    -   index of constraint, in [0,N+NEC+NIC).
                Ignored if NeedAct is false.
                This value is calculated by SASExploreDirection().
    CVal    -   for CIdx in [0,N) this field stores value which is
                assigned to XC[CIdx] during activation. CVal is ignored in
                other cases.
                This value is calculated by SASExploreDirection().
    
OUTPUT PARAMETERS:
    S       -   current point and list of active constraints are changed.

RESULT:
    >0, in case at least one inactive non-candidate constraint was activated
    =0, in case only "candidate" constraints were activated
    <0, in case no constraints were activated by the step

NOTE: in general case State.XC<>XN because activation of  constraints  may
      slightly change current point (to enforce feasibility).

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
ae_int_t sasmoveto(sactiveset* state,
     /* Real    */ const ae_vector* xn,
     ae_bool needact,
     ae_int_t cidx,
     double cval,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t i;
    ae_bool wasactivation;
    ae_int_t result;


    ae_assert(state->algostate==1, "SASMoveTo: is not in optimization mode", _state);
    n = state->n;
    nec = state->nec;
    nic = state->nic;
    
    /*
     * Save previous state, update current point
     */
    rvectorsetlengthatleast(&state->mtx, n, _state);
    ivectorsetlengthatleast(&state->mtas, n+nec+nic, _state);
    for(i=0; i<=n-1; i++)
    {
        state->mtx.ptr.p_double[i] = state->xc.ptr.p_double[i];
        state->xc.ptr.p_double[i] = xn->ptr.p_double[i];
    }
    for(i=0; i<=n+nec+nic-1; i++)
    {
        state->mtas.ptr.p_int[i] = state->cstatus.ptr.p_int[i];
    }
    
    /*
     * Activate constraints
     */
    bvectorsetlengthatleast(&state->mtnew, n+nec+nic, _state);
    wasactivation = ae_false;
    for(i=0; i<=n+nec+nic-1; i++)
    {
        state->mtnew.ptr.p_bool[i] = ae_false;
    }
    if( needact )
    {
        
        /*
         * Activation
         */
        ae_assert(cidx>=0&&cidx<n+nec+nic, "SASMoveTo: incorrect CIdx", _state);
        if( cidx<n )
        {
            
            /*
             * CIdx in [0,N-1] means that bound constraint was activated.
             * We activate it explicitly to avoid situation when roundoff-error
             * prevents us from moving EXACTLY to x=CVal.
             */
            state->xc.ptr.p_double[cidx] = cval;
        }
        state->cstatus.ptr.p_int[cidx] = 1;
        state->mtnew.ptr.p_bool[cidx] = ae_true;
        wasactivation = ae_true;
    }
    for(i=0; i<=n-1; i++)
    {
        
        /*
         * Post-check (some constraints may be activated because of numerical errors)
         */
        if( (state->hasbndl.ptr.p_bool[i]&&ae_fp_less_eq(state->xc.ptr.p_double[i],state->bndl.ptr.p_double[i]))&&ae_fp_neq(state->xc.ptr.p_double[i],state->mtx.ptr.p_double[i]) )
        {
            state->xc.ptr.p_double[i] = state->bndl.ptr.p_double[i];
            state->cstatus.ptr.p_int[i] = 1;
            state->mtnew.ptr.p_bool[i] = ae_true;
            wasactivation = ae_true;
        }
        if( (state->hasbndu.ptr.p_bool[i]&&ae_fp_greater_eq(state->xc.ptr.p_double[i],state->bndu.ptr.p_double[i]))&&ae_fp_neq(state->xc.ptr.p_double[i],state->mtx.ptr.p_double[i]) )
        {
            state->xc.ptr.p_double[i] = state->bndu.ptr.p_double[i];
            state->cstatus.ptr.p_int[i] = 1;
            state->mtnew.ptr.p_bool[i] = ae_true;
            wasactivation = ae_true;
        }
    }
    
    /*
     * Determine return status:
     * * -1 in case no constraints were activated
     * *  0 in case only "candidate" constraints were activated
     * * +1 in case at least one "non-candidate" constraint was activated
     */
    if( wasactivation )
    {
        
        /*
         * Step activated one/several constraints, but sometimes it is spurious
         * activation - RecalculateConstraints() tells us that constraint is
         * inactive (negative Largrange multiplier), but step activates it
         * because of numerical noise.
         *
         * This block of code checks whether step activated truly new constraints
         * (ones which were not in the active set at the solution):
         *
         * * for non-boundary constraint it is enough to check that previous value
         *   of CStatus[i] is negative (=far from boundary), and new one is
         *   positive (=we are at the boundary, constraint is activated).
         *
         * * for boundary constraints previous criterion won't work. Each variable
         *   has two constraints, and simply checking their status is not enough -
         *   we have to correctly identify cases when we leave one boundary
         *   (PrevActiveSet[i]=0) and move to another boundary (CStatus[i]>0).
         *   Such cases can be identified if we compare previous X with new X.
         *
         * In case only "candidate" constraints were activated, result variable
         * is set to 0. In case at least one new constraint was activated, result
         * is set to 1.
         */
        result = 0;
        for(i=0; i<=n-1; i++)
        {
            if( state->cstatus.ptr.p_int[i]>0&&ae_fp_neq(state->xc.ptr.p_double[i],state->mtx.ptr.p_double[i]) )
            {
                result = 1;
            }
        }
        for(i=n; i<=n+state->nec+state->nic-1; i++)
        {
            if( state->mtas.ptr.p_int[i]<0&&state->cstatus.ptr.p_int[i]>0 )
            {
                result = 1;
            }
        }
    }
    else
    {
        
        /*
         * No activation, return -1
         */
        result = -1;
    }
    
    /*
     * Update basis
     */
    sasappendtobasis(state, &state->mtnew, _state);
    return result;
}


/*************************************************************************
This subroutine performs immediate activation of one constraint:
* "immediate" means that we do not have to move to activate it
* in case boundary constraint is activated, we enforce current point to be
  exactly at the boundary

INPUT PARAMETERS:
    S       -   active set object
    CIdx    -   index of constraint, in [0,N+NEC+NIC).
                This value is calculated by SASExploreDirection().
    CVal    -   for CIdx in [0,N) this field stores value which is
                assigned to XC[CIdx] during activation. CVal is ignored in
                other cases.
                This value is calculated by SASExploreDirection().

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
void sasimmediateactivation(sactiveset* state,
     ae_int_t cidx,
     double cval,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(state->algostate==1, "SASMoveTo: is not in optimization mode", _state);
    if( cidx<state->n )
    {
        state->xc.ptr.p_double[cidx] = cval;
    }
    state->cstatus.ptr.p_int[cidx] = 1;
    bvectorsetlengthatleast(&state->mtnew, state->n+state->nec+state->nic, _state);
    for(i=0; i<=state->n+state->nec+state->nic-1; i++)
    {
        state->mtnew.ptr.p_bool[i] = ae_false;
    }
    state->mtnew.ptr.p_bool[cidx] = ae_true;
    sasappendtobasis(state, &state->mtnew, _state);
}


/*************************************************************************
This subroutine calculates descent direction subject to current active set.

INPUT PARAMETERS:
    S       -   active set object
    G       -   array[N], gradient
    D       -   possibly prealocated buffer;
                automatically resized if needed.
    
OUTPUT PARAMETERS:
    D       -   descent direction projected onto current active set.
                Components of D which correspond to active boundary
                constraints are forced to be exactly zero.
                In case D is non-zero, it is normalized to have unit norm.
                
NOTE: in  case active set has N  active  constraints  (or  more),  descent
      direction is forced to be exactly zero.

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
void sasconstraineddescent(sactiveset* state,
     /* Real    */ const ae_vector* g,
     /* Real    */ ae_vector* d,
     ae_state *_state)
{


    ae_assert(state->algostate==1, "SASConstrainedDescent: is not in optimization mode", _state);
    sasrebuildbasis(state, _state);
    sactivesets_constraineddescent(state, g, &state->unitdiagonal, &state->idensebatch, ae_true, d, _state);
}


/*************************************************************************
This  subroutine  calculates  preconditioned  descent direction subject to
current active set.

INPUT PARAMETERS:
    S       -   active set object
    G       -   array[N], gradient
    D       -   possibly prealocated buffer;
                automatically resized if needed.
    
OUTPUT PARAMETERS:
    D       -   descent direction projected onto current active set.
                Components of D which correspond to active boundary
                constraints are forced to be exactly zero.
                In case D is non-zero, it is normalized to have unit norm.
                
NOTE: in  case active set has N  active  constraints  (or  more),  descent
      direction is forced to be exactly zero.

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
void sasconstraineddescentprec(sactiveset* state,
     /* Real    */ const ae_vector* g,
     /* Real    */ ae_vector* d,
     ae_state *_state)
{


    ae_assert(state->algostate==1, "SASConstrainedDescentPrec: is not in optimization mode", _state);
    sasrebuildbasis(state, _state);
    sactivesets_constraineddescent(state, g, &state->h, &state->pdensebatch, ae_true, d, _state);
}


/*************************************************************************
This subroutine calculates projection   of  direction  vector  to  current
active set.

INPUT PARAMETERS:
    S       -   active set object
    D       -   array[N], direction
    
OUTPUT PARAMETERS:
    D       -   direction projected onto current active set.
                Components of D which correspond to active boundary
                constraints are forced to be exactly zero.
                
NOTE: in  case active set has N  active  constraints  (or  more),  descent
      direction is forced to be exactly zero.

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
void sasconstraineddirection(sactiveset* state,
     /* Real    */ ae_vector* d,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(state->algostate==1, "SASConstrainedAntigradientPrec: is not in optimization mode", _state);
    sasrebuildbasis(state, _state);
    sactivesets_constraineddescent(state, d, &state->unitdiagonal, &state->idensebatch, ae_false, &state->cdtmp, _state);
    for(i=0; i<=state->n-1; i++)
    {
        d->ptr.p_double[i] = -state->cdtmp.ptr.p_double[i];
    }
}


/*************************************************************************
This subroutine calculates product of direction vector and  preconditioner
multiplied subject to current active set.

INPUT PARAMETERS:
    S       -   active set object
    D       -   array[N], direction
    
OUTPUT PARAMETERS:
    D       -   preconditioned direction projected onto current active set.
                Components of D which correspond to active boundary
                constraints are forced to be exactly zero.
                
NOTE: in  case active set has N  active  constraints  (or  more),  descent
      direction is forced to be exactly zero.

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
void sasconstraineddirectionprec(sactiveset* state,
     /* Real    */ ae_vector* d,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(state->algostate==1, "SASConstrainedAntigradientPrec: is not in optimization mode", _state);
    sasrebuildbasis(state, _state);
    sactivesets_constraineddescent(state, d, &state->h, &state->pdensebatch, ae_false, &state->cdtmp, _state);
    for(i=0; i<=state->n-1; i++)
    {
        d->ptr.p_double[i] = -state->cdtmp.ptr.p_double[i];
    }
}


/*************************************************************************
This  subroutine  performs  correction of some (possibly infeasible) point
with respect to a) current active set, b) all boundary  constraints,  both
active and inactive:

0) we calculate L1 penalty term for violation of active linear constraints
   (one which is returned by SASActiveLCPenalty1() function).
1) first, it performs projection (orthogonal with respect to scale  matrix
   S) of X into current active set: X -> X1.
2) next, we perform projection with respect to  ALL  boundary  constraints
   which are violated at X1: X1 -> X2.
3) X is replaced by X2.

The idea is that this function can preserve and enforce feasibility during
optimization, and additional penalty parameter can be used to prevent algo
from leaving feasible set because of rounding errors.

INPUT PARAMETERS:
    S       -   active set object
    X       -   array[N], candidate point
    
OUTPUT PARAMETERS:
    X       -   "improved" candidate point:
                a) feasible with respect to all boundary constraints
                b) feasibility with respect to active set is retained at
                   good level.
    Penalty -   penalty term, which can be added to function value if user
                wants to penalize violation of constraints (recommended).
                
NOTE: this function is not intended to find exact  projection  (i.e.  best
      approximation) of X into feasible set. It just improves situation  a
      bit.
      Regular  use  of   this function will help you to retain feasibility
      - if you already have something to start  with  and  constrain  your
      steps is such way that the only source of infeasibility are roundoff
      errors.

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
void sascorrection(sactiveset* state,
     /* Real    */ ae_vector* x,
     double* penalty,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    double v;

    *penalty = 0.0;

    ae_assert(state->algostate==1, "SASCorrection: is not in optimization mode", _state);
    sasrebuildbasis(state, _state);
    n = state->n;
    rvectorsetlengthatleast(&state->corrtmp, n, _state);
    
    /*
     * Calculate penalty term.
     */
    *penalty = sasactivelcpenalty1(state, x, _state);
    
    /*
     * Perform projection 1.
     *
     * This projecton is given by:
     *
     *     x_proj = x - S*S*As'*(As*x-b)
     *
     * where x is original x before projection, S is a scale matrix,
     * As is a matrix of equality constraints (active set) which were
     * orthogonalized with respect to inner product given by S (i.e. we
     * have As*S*S'*As'=I), b is a right part of the orthogonalized
     * constraints.
     *
     * NOTE: you can verify that x_proj is strictly feasible w.r.t.
     *       active set by multiplying it by As - you will get
     *       As*x_proj = As*x - As*x + b = b.
     *
     *       This formula for projection can be obtained by solving
     *       following minimization problem.
     *
     *           min ||inv(S)*(x_proj-x)||^2 s.t. As*x_proj=b
     *
     * NOTE: we apply sparse batch by examining CStatus[]; it is guaranteed
     *       to contain sparse batch, but avoids roundoff errors associated
     *       with the fact that some box constraints were moved to sparse
     *       storage
     *       
     */
    ae_v_move(&state->corrtmp.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
    for(i=0; i<=state->densebatchsize-1; i++)
    {
        v = -state->sdensebatch.ptr.pp_double[i][n];
        for(j=0; j<=n-1; j++)
        {
            v = v+state->sdensebatch.ptr.pp_double[i][j]*state->corrtmp.ptr.p_double[j];
        }
        for(j=0; j<=n-1; j++)
        {
            state->corrtmp.ptr.p_double[j] = state->corrtmp.ptr.p_double[j]-v*state->sdensebatch.ptr.pp_double[i][j]*ae_sqr(state->s.ptr.p_double[j], _state);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->cstatus.ptr.p_int[i]>0 )
        {
            state->corrtmp.ptr.p_double[i] = state->xc.ptr.p_double[i];
        }
    }
    
    /*
     * Perform projection 2
     */
    for(i=0; i<=n-1; i++)
    {
        x->ptr.p_double[i] = state->corrtmp.ptr.p_double[i];
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less(x->ptr.p_double[i],state->bndl.ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = state->bndl.ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater(x->ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = state->bndu.ptr.p_double[i];
        }
    }
}


/*************************************************************************
This  subroutine returns L1 penalty for violation of active general linear
constraints (violation of boundary or inactive linear constraints  is  not
added to penalty).

Penalty term is equal to:
    
    Penalty = SUM( Abs((C_i*x-R_i)/Alpha_i) )
    
Here:
* summation is performed for I=0...NEC+NIC-1, CStatus[N+I]>0
  (only for rows of CLEIC which are in active set)
* C_i is I-th row of CLEIC
* R_i is corresponding right part
* S is a scale matrix
* Alpha_i = ||S*C_i|| - is a scaling coefficient which "normalizes"
  I-th summation term according to its scale.

INPUT PARAMETERS:
    S       -   active set object
    X       -   array[N], candidate point

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
double sasactivelcpenalty1(sactiveset* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    double v;
    double alpha;
    double p;
    double result;


    ae_assert(state->algostate==1, "SASActiveLCPenalty1: is not in optimization mode", _state);
    sasrebuildbasis(state, _state);
    n = state->n;
    nec = state->nec;
    nic = state->nic;
    
    /*
     * Calculate penalty term.
     */
    result = (double)(0);
    for(i=0; i<=nec+nic-1; i++)
    {
        if( state->cstatus.ptr.p_int[n+i]>0 )
        {
            alpha = (double)(0);
            p = -state->cleic.ptr.pp_double[i][n];
            for(j=0; j<=n-1; j++)
            {
                v = state->cleic.ptr.pp_double[i][j];
                p = p+v*x->ptr.p_double[j];
                alpha = alpha+ae_sqr(v*state->s.ptr.p_double[j], _state);
            }
            alpha = ae_sqrt(alpha, _state);
            if( ae_fp_neq(alpha,(double)(0)) )
            {
                result = result+ae_fabs(p/alpha, _state);
            }
        }
    }
    return result;
}


/*************************************************************************
This subroutine calculates scaled norm of  vector  after  projection  onto
subspace of active constraints. Most often this function is used  to  test
stopping conditions.

INPUT PARAMETERS:
    S       -   active set object
    D       -   vector whose norm is calculated
    
RESULT:
    Vector norm (after projection and scaling)
    
NOTE: projection is performed first, scaling is performed after projection
                
NOTE: if we have N active constraints, zero value (exact zero) is returned

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
double sasscaledconstrainednorm(sactiveset* state,
     /* Real    */ const ae_vector* d,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    double v;
    double result;


    ae_assert(state->algostate==1, "SASMoveTo: is not in optimization mode", _state);
    n = state->n;
    rvectorsetlengthatleast(&state->scntmp, n, _state);
    
    /*
     * Prepare basis (if needed)
     */
    sasrebuildbasis(state, _state);
    
    /*
     * Calculate descent direction
     */
    if( state->sparsebatchsize+state->densebatchsize>=n )
    {
        
        /*
         * Quick exit if number of active constraints is N or larger
         */
        result = 0.0;
        return result;
    }
    for(i=0; i<=n-1; i++)
    {
        state->scntmp.ptr.p_double[i] = d->ptr.p_double[i];
    }
    for(i=0; i<=state->densebatchsize-1; i++)
    {
        v = ae_v_dotproduct(&state->idensebatch.ptr.pp_double[i][0], 1, &state->scntmp.ptr.p_double[0], 1, ae_v_len(0,n-1));
        ae_v_subd(&state->scntmp.ptr.p_double[0], 1, &state->idensebatch.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->cstatus.ptr.p_int[i]>0 )
        {
            state->scntmp.ptr.p_double[i] = (double)(0);
        }
    }
    v = 0.0;
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr(state->s.ptr.p_double[i]*state->scntmp.ptr.p_double[i], _state);
    }
    result = ae_sqrt(v, _state);
    return result;
}


/*************************************************************************
This subroutine turns off optimization mode.

INPUT PARAMETERS:
    S   -   active set object
    
OUTPUT PARAMETERS:
    S   -   state is changed

NOTE: this function can be called many times for optimizer which was
      already stopped.

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
void sasstopoptimization(sactiveset* state, ae_state *_state)
{


    state->algostate = 0;
}


/*************************************************************************
This function recalculates constraints - activates  and  deactivates  them
according to gradient value at current point. Algorithm  assumes  that  we
want to make steepest descent step from  current  point;  constraints  are
activated and deactivated in such way that we won't violate any constraint
by steepest descent step.

After call to this function active set is ready to  try  steepest  descent
step (SASDescentDirection-SASExploreDirection-SASMoveTo).

Only already "active" and "candidate" elements of ActiveSet are  examined;
constraints which are not active are not examined.

INPUT PARAMETERS:
    State       -   active set object
    GC          -   array[N], gradient at XC
    
OUTPUT PARAMETERS:
    State       -   active set object, with new set of constraint

  -- ALGLIB --
     Copyright 26.09.2012 by Bochkanov Sergey
*************************************************************************/
void sasreactivateconstraints(sactiveset* state,
     /* Real    */ const ae_vector* gc,
     ae_state *_state)
{


    ae_assert(state->algostate==1, "SASReactivateConstraints: must be in optimization mode", _state);
    sactivesets_reactivateconstraints(state, gc, &state->unitdiagonal, _state);
}


/*************************************************************************
This function recalculates constraints - activates  and  deactivates  them
according to gradient value at current point.

Algorithm  assumes  that  we  want  to make Quasi-Newton step from current
point with diagonal Quasi-Newton matrix H. Constraints are  activated  and
deactivated in such way that we won't violate any constraint by step.

After call to  this  function  active set is ready to  try  preconditioned
steepest descent step (SASDescentDirection-SASExploreDirection-SASMoveTo).

Only already "active" and "candidate" elements of ActiveSet are  examined;
constraints which are not active are not examined.

INPUT PARAMETERS:
    State       -   active set object
    GC          -   array[N], gradient at XC
    
OUTPUT PARAMETERS:
    State       -   active set object, with new set of constraint

  -- ALGLIB --
     Copyright 26.09.2012 by Bochkanov Sergey
*************************************************************************/
void sasreactivateconstraintsprec(sactiveset* state,
     /* Real    */ const ae_vector* gc,
     ae_state *_state)
{


    ae_assert(state->algostate==1, "SASReactivateConstraintsPrec: must be in optimization mode", _state);
    sactivesets_reactivateconstraints(state, gc, &state->h, _state);
}


/*************************************************************************
This function builds three orthonormal basises for current active set:
* P-orthogonal one, which is orthogonalized with inner product
  (x,y) = x'*P*y, where P=inv(H) is current preconditioner
* S-orthogonal one, which is orthogonalized with inner product
  (x,y) = x'*S'*S*y, where S is diagonal scaling matrix
* I-orthogonal one, which is orthogonalized with standard dot product

NOTE: all sets of orthogonal vectors are guaranteed  to  have  same  size.
      P-orthogonal basis is built first, I/S-orthogonal basises are forced
      to have same number of vectors as P-orthogonal one (padded  by  zero
      vectors if needed).
      
NOTE: this function tracks changes in active set; first call  will  result
      in reorthogonalization

INPUT PARAMETERS:
    State   -   active set object
    H       -   diagonal preconditioner, H[i]>0

OUTPUT PARAMETERS:
    State   -   active set object with new basis
    
  -- ALGLIB --
     Copyright 20.06.2012 by Bochkanov Sergey
*************************************************************************/
void sasrebuildbasis(sactiveset* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t i;
    ae_int_t j;
    ae_bool hasactivelin;
    ae_int_t candidatescnt;
    double v;
    double vv;
    double vmax;
    ae_int_t kmax;


    if( state->basisisready )
    {
        return;
    }
    n = state->n;
    nec = state->nec;
    nic = state->nic;
    rvectorsetlengthatleast(&state->tmp0, n, _state);
    rvectorsetlengthatleast(&state->tmpprodp, n, _state);
    rvectorsetlengthatleast(&state->tmpprods, n, _state);
    rvectorsetlengthatleast(&state->tmpcp, n+1, _state);
    rvectorsetlengthatleast(&state->tmpcs, n+1, _state);
    rvectorsetlengthatleast(&state->tmpci, n+1, _state);
    rmatrixsetlengthatleast(&state->tmpbasis, nec+nic, n+1, _state);
    rmatrixsetlengthatleast(&state->pdensebatch, nec+nic, n+1, _state);
    rmatrixsetlengthatleast(&state->idensebatch, nec+nic, n+1, _state);
    rmatrixsetlengthatleast(&state->sdensebatch, nec+nic, n+1, _state);
    ivectorsetlengthatleast(&state->sparsebatch, n, _state);
    state->sparsebatchsize = 0;
    state->densebatchsize = 0;
    state->basisage = 0;
    state->basisisready = ae_true;
    
    /*
     * Determine number of active boundary and non-boundary
     * constraints, move them to TmpBasis. Quick exit if no
     * non-boundary constraints were detected.
     */
    hasactivelin = ae_false;
    for(i=0; i<=nec+nic-1; i++)
    {
        if( state->cstatus.ptr.p_int[n+i]>0 )
        {
            hasactivelin = ae_true;
        }
    }
    for(j=0; j<=n-1; j++)
    {
        if( state->cstatus.ptr.p_int[j]>0 )
        {
            state->sparsebatch.ptr.p_int[state->sparsebatchsize] = j;
            state->sparsebatchsize = state->sparsebatchsize+1;
        }
    }
    if( !hasactivelin )
    {
        return;
    }
    
    /*
     * Prepare precomputed values
     */
    rvectorsetlengthatleast(&state->tmpreciph, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->tmpreciph.ptr.p_double[i] = (double)1/state->h.ptr.p_double[i];
    }
    
    /*
     * Prepare initial candidate set:
     * * select active constraints
     * * normalize (inner product is given by preconditioner)
     * * orthogonalize with respect to active box constraints
     * * copy normalized/orthogonalized candidates to PBasis/SBasis/IBasis
     */
    candidatescnt = 0;
    for(i=0; i<=nec+nic-1; i++)
    {
        if( state->cstatus.ptr.p_int[n+i]>0 )
        {
            ae_v_move(&state->tmpbasis.ptr.pp_double[candidatescnt][0], 1, &state->cleic.ptr.pp_double[i][0], 1, ae_v_len(0,n));
            inc(&candidatescnt, _state);
        }
    }
    for(i=0; i<=candidatescnt-1; i++)
    {
        v = 0.0;
        for(j=0; j<=n-1; j++)
        {
            v = v+ae_sqr(state->tmpbasis.ptr.pp_double[i][j], _state)*state->tmpreciph.ptr.p_double[j];
        }
        if( ae_fp_greater(v,(double)(0)) )
        {
            v = (double)1/ae_sqrt(v, _state);
            for(j=0; j<=n; j++)
            {
                state->tmpbasis.ptr.pp_double[i][j] = state->tmpbasis.ptr.pp_double[i][j]*v;
            }
        }
    }
    for(j=0; j<=n-1; j++)
    {
        if( state->cstatus.ptr.p_int[j]>0 )
        {
            for(i=0; i<=candidatescnt-1; i++)
            {
                state->tmpbasis.ptr.pp_double[i][n] = state->tmpbasis.ptr.pp_double[i][n]-state->tmpbasis.ptr.pp_double[i][j]*state->xc.ptr.p_double[j];
                state->tmpbasis.ptr.pp_double[i][j] = 0.0;
            }
        }
    }
    for(i=0; i<=candidatescnt-1; i++)
    {
        for(j=0; j<=n; j++)
        {
            state->pdensebatch.ptr.pp_double[i][j] = state->tmpbasis.ptr.pp_double[i][j];
            state->sdensebatch.ptr.pp_double[i][j] = state->tmpbasis.ptr.pp_double[i][j];
            state->idensebatch.ptr.pp_double[i][j] = state->tmpbasis.ptr.pp_double[i][j];
        }
    }
    
    /*
     * Perform orthogonalization of general linear constraints with respect
     * to each other (constraints in P/S/IBasis are already normalized w.r.t.
     * box constraints). During this process we select strictly active constraints
     * from the candidate set, and drop ones which were detected as redundant
     * during orthogonalization.
     *
     * Orthogonalization is performed with the help of Gram-Schmidt process.
     * Due to accumulation of round-off errors it is beneficial to perform
     * pivoting, i.e. to select candidate vector with largest norm at each
     * step.
     *
     * First (basic) version of the algorithm is:
     *     0. split all constraints into two sets: basis ones (initially empty)
     *        and candidate ones (all constraints)
     *     1. fill PBasis with H-normalized candidate constraints, fill
     *        corresponding entries of S/IBasis with corresponding
     *        (non-normalized) constraints
     *     2. select row of PBasis with largest norm, move it (and its S/IBasis
     *        counterparts) to the beginning of the candidate set, H-normalize
     *        this row (rows of S/IBasis are normalized using corresponding norms).
     *        Stop if largest row is nearly (or exactly) zero.
     *     3. orthogonalize remaining rows of P/S/IBasis with respect to
     *        one chosen at step (2). It can be done efficiently using
     *        combination of DGEMV/DGER BLAS calls.
     *     4. increase basis size by one, decrease candidate set size by one,
     *        goto (2)
     *
     * However, naive implementation of the algorithm above spends significant
     * amount of time in step (2) - selection of row with largest H-norm. Step
     * (3) can be efficiently implemented with optimized BLAS, but we have no
     * optimized BLAS kernels for step(2). And because step (3) changes row norms,
     * step (2) have to be re-calculated every time, which is quite slow.
     *
     * We can save significant amount of calculations by noticing that:
     * * step (3) DECREASES row norms, but never increases it
     * * we can maintain upper bounds for row H-norms is a separate array,
     *   use them for initial evaluation of best candidates, and update them
     *   after we find some promising row (all bounds are invalidated after
     *   step 3, but their old values still carry some information)
     * * it is beneficial re-evaluate bounds only for rows which are
     *   significantly (at least few percents) larger than best one found so far
     * * because rows are initially normalized, initial values for upper bounds
     *   can be set to 1.0
     */
    ae_assert(state->densebatchsize==0, "SAS: integrity check failed", _state);
    ae_assert(ae_fp_greater(sactivesets_minnormseparation,(double)(0)), "SAS: integrity check failed", _state);
    rvectorsetlengthatleast(&state->tmpnormestimates, candidatescnt, _state);
    for(i=0; i<=candidatescnt-1; i++)
    {
        state->tmpnormestimates.ptr.p_double[i] = 1.0;
    }
    while(state->sparsebatchsize+state->densebatchsize<n)
    {
        
        /*
         * No candidates? We are done!
         */
        if( candidatescnt==0 )
        {
            break;
        }
        
        /*
         * Find largest vector
         */
        vmax = (double)(0);
        kmax = -1;
        for(i=state->densebatchsize; i<=state->densebatchsize+candidatescnt-1; i++)
        {
            
            /*
             * Use upper bound for row norm for initial evaluation.
             * Skip rows whose upper bound is less than best candidate
             * found so far.
             *
             * NOTE: in fact, we may skip rows whose upper bound is
             *       marginally higher than that of best candidate.
             *       No need to perform costly re-evaluation in order
             *       to get just few percents of improvement.
             */
            if( ae_fp_less(state->tmpnormestimates.ptr.p_double[i],vmax*((double)1+sactivesets_minnormseparation)) )
            {
                continue;
            }
            
            /*
             * OK, upper bound is large enough... lets perform full
             * re-evaluation and update of the estimate.
             */
            v = 0.0;
            for(j=0; j<=n-1; j++)
            {
                vv = state->pdensebatch.ptr.pp_double[i][j];
                v = v+vv*vv*state->tmpreciph.ptr.p_double[j];
            }
            v = ae_sqrt(v, _state);
            state->tmpnormestimates.ptr.p_double[i] = v;
            
            /*
             * Now compare with best candidate so far
             */
            if( ae_fp_greater(v,vmax) )
            {
                vmax = v;
                kmax = i;
            }
        }
        if( ae_fp_less(vmax,1.0E4*ae_machineepsilon)||kmax<0 )
        {
            
            /*
             * All candidates are either zero or too small (after orthogonalization)
             */
            candidatescnt = 0;
            break;
        }
        
        /*
         * Candidate is selected for inclusion into basis set.
         *
         * Move candidate row to the beginning of candidate array (which is
         * right past the end of the approved basis). Normalize (for P-basis
         * we perform preconditioner-based normalization, for S-basis - scale
         * based, for I-basis - identity based).
         */
        swaprows(&state->pdensebatch, state->densebatchsize, kmax, n+1, _state);
        swaprows(&state->sdensebatch, state->densebatchsize, kmax, n+1, _state);
        swaprows(&state->idensebatch, state->densebatchsize, kmax, n+1, _state);
        swapelements(&state->tmpnormestimates, state->densebatchsize, kmax, _state);
        v = (double)1/vmax;
        ae_v_muld(&state->pdensebatch.ptr.pp_double[state->densebatchsize][0], 1, ae_v_len(0,n), v);
        v = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            vv = state->sdensebatch.ptr.pp_double[state->densebatchsize][j]*state->s.ptr.p_double[j];
            v = v+vv*vv;
        }
        ae_assert(ae_fp_greater(v,(double)(0)), "SActiveSet.RebuildBasis(): integrity check failed, SNorm=0", _state);
        v = (double)1/ae_sqrt(v, _state);
        ae_v_muld(&state->sdensebatch.ptr.pp_double[state->densebatchsize][0], 1, ae_v_len(0,n), v);
        v = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            vv = state->idensebatch.ptr.pp_double[state->densebatchsize][j];
            v = v+vv*vv;
        }
        ae_assert(ae_fp_greater(v,(double)(0)), "SActiveSet.RebuildBasis(): integrity check failed, INorm=0", _state);
        v = (double)1/ae_sqrt(v, _state);
        ae_v_muld(&state->idensebatch.ptr.pp_double[state->densebatchsize][0], 1, ae_v_len(0,n), v);
        
        /*
         * Reorthogonalize other candidates with respect to candidate #0:
         * * calculate projections en masse with GEMV()
         * * subtract projections with GER()
         */
        rvectorsetlengthatleast(&state->tmp0, candidatescnt-1, _state);
        for(j=0; j<=n-1; j++)
        {
            state->tmpprodp.ptr.p_double[j] = state->pdensebatch.ptr.pp_double[state->densebatchsize][j]/state->h.ptr.p_double[j];
            state->tmpprods.ptr.p_double[j] = state->sdensebatch.ptr.pp_double[state->densebatchsize][j]*ae_sqr(state->s.ptr.p_double[j], _state);
        }
        for(j=0; j<=n; j++)
        {
            state->tmpcp.ptr.p_double[j] = state->pdensebatch.ptr.pp_double[state->densebatchsize][j];
            state->tmpcs.ptr.p_double[j] = state->sdensebatch.ptr.pp_double[state->densebatchsize][j];
            state->tmpci.ptr.p_double[j] = state->idensebatch.ptr.pp_double[state->densebatchsize][j];
        }
        rmatrixgemv(candidatescnt-1, n, 1.0, &state->pdensebatch, state->densebatchsize+1, 0, 0, &state->tmpprodp, 0, 0.0, &state->tmp0, 0, _state);
        rmatrixger(candidatescnt-1, n+1, &state->pdensebatch, state->densebatchsize+1, 0, -1.0, &state->tmp0, 0, &state->tmpcp, 0, _state);
        rmatrixgemv(candidatescnt-1, n, 1.0, &state->sdensebatch, state->densebatchsize+1, 0, 0, &state->tmpprods, 0, 0.0, &state->tmp0, 0, _state);
        rmatrixger(candidatescnt-1, n+1, &state->sdensebatch, state->densebatchsize+1, 0, -1.0, &state->tmp0, 0, &state->tmpcs, 0, _state);
        rmatrixgemv(candidatescnt-1, n, 1.0, &state->idensebatch, state->densebatchsize+1, 0, 0, &state->tmpci, 0, 0.0, &state->tmp0, 0, _state);
        rmatrixger(candidatescnt-1, n+1, &state->idensebatch, state->densebatchsize+1, 0, -1.0, &state->tmp0, 0, &state->tmpci, 0, _state);
        
        /*
         * Increase basis, decrease candidates count
         */
        inc(&state->densebatchsize, _state);
        dec(&candidatescnt, _state);
    }
}


/*************************************************************************
This  function  appends new constraints (if possible; sometimes it isn't!)
to three orthonormal basises for current active set:
* P-orthogonal one, which is orthogonalized with inner product
  (x,y) = x'*P*y, where P=inv(H) is current preconditioner
* S-orthogonal one, which is orthogonalized with inner product
  (x,y) = x'*S'*S*y, where S is diagonal scaling matrix
* I-orthogonal one, which is orthogonalized with standard dot product

NOTE: all sets of orthogonal vectors are guaranteed  to  have  same  size.
      P-orthogonal basis is built first, I/S-orthogonal basises are forced
      to have same number of vectors as P-orthogonal one (padded  by  zero
      vectors if needed).
      
NOTE: this function may fail to update basis without  full  recalculation;
      in such case it will set BasisIsReady to False and silently  return;
      if it succeeds, it will increase BasisSize.

INPUT PARAMETERS:
    State       -   active set object
    NewEntries  -   array[N+NEC+NIC], indexes of constraints being  added
                    are marked as True; it is responsibility of the caller
                    to specify only those constraints which were previously
                    inactive; when  some  constraint is  already  in   the
                    active set, algorithm behavior is undefined.

OUTPUT PARAMETERS:
    State   -   active set object with new basis
    
  -- ALGLIB --
     Copyright 03.10.2017 by Bochkanov Sergey
*************************************************************************/
void sasappendtobasis(sactiveset* state,
     /* Boolean */ const ae_vector* newentries,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t i;
    ae_int_t j;
    ae_int_t t;
    ae_int_t nact;
    double v;
    double vp;
    double vs;
    double vi;
    double initnormp;
    double projnormp;
    double projnorms;
    double projnormi;


    if( !state->basisisready )
    {
        return;
    }
    n = state->n;
    nec = state->nec;
    nic = state->nic;
    
    /*
     * Count number of constraints to activate;
     * perform integrity check.
     */
    nact = 0;
    for(i=0; i<=n-1; i++)
    {
        if( newentries->ptr.p_bool[i] )
        {
            nact = nact+1;
        }
    }
    for(i=n; i<=n+nec-1; i++)
    {
        ae_assert(!newentries->ptr.p_bool[i], "SAS: integrity check failed (appendtobasis.0)", _state);
    }
    for(i=n+nec; i<=n+nec+nic-1; i++)
    {
        if( newentries->ptr.p_bool[i] )
        {
            nact = nact+1;
        }
    }
    if( nact+state->basisage>sactivesets_maxbasisage )
    {
        state->basisisready = ae_false;
        return;
    }
    
    /*
     * Resize basis matrices if needed
     */
    rmatrixgrowrowsto(&state->pdensebatch, state->densebatchsize+nact, n+1, _state);
    rmatrixgrowrowsto(&state->sdensebatch, state->densebatchsize+nact, n+1, _state);
    rmatrixgrowrowsto(&state->idensebatch, state->densebatchsize+nact, n+1, _state);
    
    /*
     * Try adding recommended entries to basis.
     * If reorthogonalization removes too much of candidate constraint,
     * we will invalidate basis and try to rebuild it from scratch.
     */
    rvectorsetlengthatleast(&state->tmp0, n+1, _state);
    rvectorsetlengthatleast(&state->tmpcp, n+1, _state);
    rvectorsetlengthatleast(&state->tmpcs, n+1, _state);
    rvectorsetlengthatleast(&state->tmpci, n+1, _state);
    rvectorsetlengthatleast(&state->tmpprodp, n, _state);
    rvectorsetlengthatleast(&state->tmpprods, n, _state);
    for(t=0; t<=n+nec+nic-1; t++)
    {
        if( newentries->ptr.p_bool[t] )
        {
            
            /*
             * Basis is full? Quick skip!
             */
            if( state->sparsebatchsize+state->densebatchsize>=n )
            {
                ae_assert(state->sparsebatchsize+state->densebatchsize==n, "SAS: integrity check failed (sasappendtobasis)", _state);
                break;
            }
            
            /*
             * Copy constraint to temporary storage.
             */
            if( t<n )
            {
                
                /*
                 * Copy box constraint
                 */
                for(j=0; j<=n; j++)
                {
                    state->tmp0.ptr.p_double[j] = (double)(0);
                }
                state->tmp0.ptr.p_double[t] = 1.0;
                state->tmp0.ptr.p_double[n] = state->xc.ptr.p_double[t];
            }
            else
            {
                
                /*
                 * Copy general linear constraint
                 */
                for(j=0; j<=n; j++)
                {
                    state->tmp0.ptr.p_double[j] = state->cleic.ptr.pp_double[t-n][j];
                }
            }
            
            /*
             * Calculate initial norm (preconditioner is used for norm calculation).
             */
            initnormp = 0.0;
            for(j=0; j<=n-1; j++)
            {
                v = state->tmp0.ptr.p_double[j];
                initnormp = initnormp+v*v/state->h.ptr.p_double[j];
            }
            initnormp = ae_sqrt(initnormp, _state);
            if( ae_fp_eq(initnormp,(double)(0)) )
            {
                
                /*
                 * Well, it is not expected. Let's just rebuild basis
                 * from scratch and forget about this strange situation...
                 */
                state->basisisready = ae_false;
                return;
            }
            
            /*
             * Orthogonalize Tmp0 w.r.t. sparse batch (box constraints stored in sparse storage).
             *
             * Copy to TmpCP/TmpCS/TmpCI (P for preconditioner-based inner product
             * used for orthogonalization, S for scale-based orthogonalization,
             * I for "traditional" inner product used for Gram-Schmidt orthogonalization).
             */
            for(i=0; i<=state->sparsebatchsize-1; i++)
            {
                j = state->sparsebatch.ptr.p_int[i];
                state->tmp0.ptr.p_double[n] = state->tmp0.ptr.p_double[n]-state->tmp0.ptr.p_double[j]*state->xc.ptr.p_double[j];
                state->tmp0.ptr.p_double[j] = 0.0;
            }
            for(j=0; j<=n; j++)
            {
                state->tmpcp.ptr.p_double[j] = state->tmp0.ptr.p_double[j];
                state->tmpcs.ptr.p_double[j] = state->tmp0.ptr.p_double[j];
                state->tmpci.ptr.p_double[j] = state->tmp0.ptr.p_double[j];
            }
            
            /*
             * Orthogonalize TmpCP/S/I with respect to active linear constraints from dense batch.
             * Corresponding norm (preconditioner, scale, identity) is used in each case.
             */
            for(j=0; j<=n-1; j++)
            {
                state->tmpprodp.ptr.p_double[j] = (double)1/state->h.ptr.p_double[j];
                state->tmpprods.ptr.p_double[j] = ae_sqr(state->s.ptr.p_double[j], _state);
            }
            for(i=0; i<=state->densebatchsize-1; i++)
            {
                vp = (double)(0);
                vs = (double)(0);
                vi = (double)(0);
                for(j=0; j<=n-1; j++)
                {
                    vp = vp+state->pdensebatch.ptr.pp_double[i][j]*state->tmpcp.ptr.p_double[j]*state->tmpprodp.ptr.p_double[j];
                    vs = vs+state->sdensebatch.ptr.pp_double[i][j]*state->tmpcs.ptr.p_double[j]*state->tmpprods.ptr.p_double[j];
                    vi = vi+state->idensebatch.ptr.pp_double[i][j]*state->tmpci.ptr.p_double[j];
                }
                ae_v_subd(&state->tmpcp.ptr.p_double[0], 1, &state->pdensebatch.ptr.pp_double[i][0], 1, ae_v_len(0,n), vp);
                ae_v_subd(&state->tmpcs.ptr.p_double[0], 1, &state->sdensebatch.ptr.pp_double[i][0], 1, ae_v_len(0,n), vs);
                ae_v_subd(&state->tmpci.ptr.p_double[0], 1, &state->idensebatch.ptr.pp_double[i][0], 1, ae_v_len(0,n), vi);
            }
            projnormp = 0.0;
            projnorms = 0.0;
            projnormi = 0.0;
            for(j=0; j<=n-1; j++)
            {
                projnormp = projnormp+ae_sqr(state->tmpcp.ptr.p_double[j], _state)/state->h.ptr.p_double[j];
                projnorms = projnorms+ae_sqr(state->tmpcs.ptr.p_double[j], _state)*ae_sqr(state->s.ptr.p_double[j], _state);
                projnormi = projnormi+ae_sqr(state->tmpci.ptr.p_double[j], _state);
            }
            projnormp = ae_sqrt(projnormp, _state);
            projnorms = ae_sqrt(projnorms, _state);
            projnormi = ae_sqrt(projnormi, _state);
            if( ae_fp_less_eq(projnormp,sactivesets_maxbasisdecay*initnormp) )
            {
                state->basisisready = ae_false;
                return;
                
                /*
                 * Nearly zero row, skip
                 */
            }
            ae_assert(ae_fp_greater(projnormp,(double)(0)), "SAS: integrity check failed, ProjNormP=0", _state);
            ae_assert(ae_fp_greater(projnorms,(double)(0)), "SAS: integrity check failed, ProjNormS=0", _state);
            ae_assert(ae_fp_greater(projnormi,(double)(0)), "SAS: integrity check failed, ProjNormI=0", _state);
            v = (double)1/projnormp;
            ae_v_moved(&state->pdensebatch.ptr.pp_double[state->densebatchsize][0], 1, &state->tmpcp.ptr.p_double[0], 1, ae_v_len(0,n), v);
            v = (double)1/projnorms;
            ae_v_moved(&state->sdensebatch.ptr.pp_double[state->densebatchsize][0], 1, &state->tmpcs.ptr.p_double[0], 1, ae_v_len(0,n), v);
            v = (double)1/projnormi;
            ae_v_moved(&state->idensebatch.ptr.pp_double[state->densebatchsize][0], 1, &state->tmpci.ptr.p_double[0], 1, ae_v_len(0,n), v);
            
            /*
             * Increase set size
             */
            inc(&state->densebatchsize, _state);
            inc(&state->basisage, _state);
        }
    }
}


/*************************************************************************
This  subroutine  calculates  preconditioned  descent direction subject to
current active set.

INPUT PARAMETERS:
    State   -   active set object
    G       -   array[N], gradient
    H       -   array[N], Hessian matrix
    HA      -   active constraints orthogonalized in such way
                that HA*inv(H)*HA'= I.
    Normalize-  whether we need normalized descent or not
    D       -   possibly preallocated buffer; automatically resized.
    
OUTPUT PARAMETERS:
    D       -   descent direction projected onto current active set.
                Components of D which correspond to active boundary
                constraints are forced to be exactly zero.
                In case D is non-zero and Normalize is True, it is
                normalized to have unit norm.
                
NOTE: if we have N active constraints, D is explicitly set to zero.

  -- ALGLIB --
     Copyright 21.12.2012 by Bochkanov Sergey
*************************************************************************/
static void sactivesets_constraineddescent(sactiveset* state,
     /* Real    */ const ae_vector* g,
     /* Real    */ const ae_vector* h,
     /* Real    */ const ae_matrix* ha,
     ae_bool normalize,
     /* Real    */ ae_vector* d,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    double v;


    ae_assert(state->algostate==1, "SAS: internal error in ConstrainedDescent() - not in optimization mode", _state);
    ae_assert(state->basisisready, "SAS: internal error in ConstrainedDescent() - no basis", _state);
    n = state->n;
    rvectorsetlengthatleast(d, n, _state);
    
    /*
     * Calculate preconditioned constrained descent direction:
     *
     *     d := -inv(H)*( g - HA'*(HA*inv(H)*g) )
     *
     * Formula above always gives direction which is orthogonal to rows of HA.
     * You can verify it by multiplication of both sides by HA[i] (I-th row),
     * taking into account that HA*inv(H)*HA'= I (by definition of HA - it is
     * orthogonal basis with inner product given by inv(H)).
     */
    for(i=0; i<=n-1; i++)
    {
        d->ptr.p_double[i] = g->ptr.p_double[i];
    }
    for(i=0; i<=state->densebatchsize-1; i++)
    {
        v = 0.0;
        for(j=0; j<=n-1; j++)
        {
            v = v+ha->ptr.pp_double[i][j]*d->ptr.p_double[j]/h->ptr.p_double[j];
        }
        ae_v_subd(&d->ptr.p_double[0], 1, &ha->ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->cstatus.ptr.p_int[i]>0 )
        {
            d->ptr.p_double[i] = (double)(0);
        }
    }
    v = 0.0;
    for(i=0; i<=n-1; i++)
    {
        d->ptr.p_double[i] = -d->ptr.p_double[i]/h->ptr.p_double[i];
        v = v+ae_sqr(d->ptr.p_double[i], _state);
    }
    v = ae_sqrt(v, _state);
    if( state->sparsebatchsize+state->densebatchsize>=n )
    {
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            d->ptr.p_double[i] = (double)(0);
        }
    }
    if( normalize&&ae_fp_greater(v,(double)(0)) )
    {
        for(i=0; i<=n-1; i++)
        {
            d->ptr.p_double[i] = d->ptr.p_double[i]/v;
        }
    }
}


/*************************************************************************
This function recalculates constraints - activates  and  deactivates  them
according to gradient value at current point.

Algorithm  assumes  that  we  want  to make Quasi-Newton step from current
point with diagonal Quasi-Newton matrix H. Constraints are  activated  and
deactivated in such way that we won't violate any constraint by step.

Only already "active" and "candidate" elements of ActiveSet are  examined;
constraints which are not active are not examined.

INPUT PARAMETERS:
    State       -   active set object
    GC          -   array[N], gradient at XC
    H           -   array[N], Hessian matrix
    
OUTPUT PARAMETERS:
    State       -   active set object, with new set of constraint

  -- ALGLIB --
     Copyright 26.09.2012 by Bochkanov Sergey
*************************************************************************/
static void sactivesets_reactivateconstraints(sactiveset* state,
     /* Real    */ const ae_vector* gc,
     /* Real    */ const ae_vector* h,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t i;
    ae_int_t j;
    ae_int_t idx0;
    ae_int_t idx1;
    double v;
    ae_int_t nactivebnd;
    ae_int_t nactivelin;
    ae_int_t nactiveconstraints;
    double rowscale;


    ae_assert(state->algostate==1, "SASReactivateConstraintsPrec: must be in optimization mode", _state);
    
    /*
     * Prepare
     */
    n = state->n;
    nec = state->nec;
    nic = state->nic;
    state->basisisready = ae_false;
    
    /*
     * Handle important special case - no linear constraints,
     * only boundary constraints are present
     */
    if( nec+nic==0 )
    {
        for(i=0; i<=n-1; i++)
        {
            if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                state->cstatus.ptr.p_int[i] = 1;
                continue;
            }
            if( (state->hasbndl.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->bndl.ptr.p_double[i]))&&ae_fp_greater_eq(gc->ptr.p_double[i],(double)(0)) )
            {
                state->cstatus.ptr.p_int[i] = 1;
                continue;
            }
            if( (state->hasbndu.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->bndu.ptr.p_double[i]))&&ae_fp_less_eq(gc->ptr.p_double[i],(double)(0)) )
            {
                state->cstatus.ptr.p_int[i] = 1;
                continue;
            }
            state->cstatus.ptr.p_int[i] = -1;
        }
        return;
    }
    
    /*
     * General case.
     * Allocate temporaries.
     */
    rvectorsetlengthatleast(&state->rctmpg, n, _state);
    rvectorsetlengthatleast(&state->rctmprightpart, n, _state);
    rvectorsetlengthatleast(&state->rctmps, n, _state);
    rmatrixsetlengthatleast(&state->rctmpdense0, n, nec+nic, _state);
    rmatrixsetlengthatleast(&state->rctmpdense1, n, nec+nic, _state);
    bvectorsetlengthatleast(&state->rctmpisequality, n+nec+nic, _state);
    ivectorsetlengthatleast(&state->rctmpconstraintidx, n+nec+nic, _state);
    
    /*
     * Calculate descent direction
     */
    ae_v_moveneg(&state->rctmpg.ptr.p_double[0], 1, &gc->ptr.p_double[0], 1, ae_v_len(0,n-1));
    
    /*
     * Determine candidates to the active set.
     *
     * After this block constraints become either "inactive" (CStatus[i]<0)
     * or "candidates" (CStatus[i]=0). Previously active constraints always
     * become "candidates".
     */
    for(i=0; i<=n-1; i++)
    {
        state->cstatus.ptr.p_int[i] = -1;
    }
    for(i=n; i<=n+nec+nic-1; i++)
    {
        if( state->cstatus.ptr.p_int[i]>0 )
        {
            state->cstatus.ptr.p_int[i] = 0;
        }
        else
        {
            state->cstatus.ptr.p_int[i] = -1;
        }
    }
    nactiveconstraints = 0;
    nactivebnd = 0;
    nactivelin = 0;
    for(i=0; i<=n-1; i++)
    {
        
        /*
         * Activate boundary constraints:
         * * copy constraint index to RCTmpConstraintIdx
         * * set corresponding element of CStatus[] to "candidate"
         * * fill RCTmpS by either +1 (lower bound) or -1 (upper bound)
         * * set RCTmpIsEquality to False (BndL<BndU) or True (BndL=BndU)
         * * increase counters
         */
        if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            
            /*
             * Equality constraint is activated
             */
            state->rctmpconstraintidx.ptr.p_int[nactiveconstraints] = i;
            state->cstatus.ptr.p_int[i] = 0;
            state->rctmps.ptr.p_double[i] = 1.0;
            state->rctmpisequality.ptr.p_bool[nactiveconstraints] = ae_true;
            nactiveconstraints = nactiveconstraints+1;
            nactivebnd = nactivebnd+1;
            continue;
        }
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->bndl.ptr.p_double[i]) )
        {
            
            /*
             * Lower bound is activated
             */
            state->rctmpconstraintidx.ptr.p_int[nactiveconstraints] = i;
            state->cstatus.ptr.p_int[i] = 0;
            state->rctmps.ptr.p_double[i] = -1.0;
            state->rctmpisequality.ptr.p_bool[nactiveconstraints] = ae_false;
            nactiveconstraints = nactiveconstraints+1;
            nactivebnd = nactivebnd+1;
            continue;
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            
            /*
             * Upper bound is activated
             */
            state->rctmpconstraintidx.ptr.p_int[nactiveconstraints] = i;
            state->cstatus.ptr.p_int[i] = 0;
            state->rctmps.ptr.p_double[i] = 1.0;
            state->rctmpisequality.ptr.p_bool[nactiveconstraints] = ae_false;
            nactiveconstraints = nactiveconstraints+1;
            nactivebnd = nactivebnd+1;
            continue;
        }
    }
    for(i=0; i<=nec+nic-1; i++)
    {
        if( i>=nec&&state->cstatus.ptr.p_int[n+i]<0 )
        {
            
            /*
             * Inequality constraints are skipped if both (a) constraint was
             * not active, and (b) we are too far away from the boundary.
             */
            rowscale = 0.0;
            v = -state->cleic.ptr.pp_double[i][n];
            for(j=0; j<=n-1; j++)
            {
                v = v+state->cleic.ptr.pp_double[i][j]*state->xc.ptr.p_double[j];
                rowscale = ae_maxreal(rowscale, ae_fabs(state->cleic.ptr.pp_double[i][j]*state->s.ptr.p_double[j], _state), _state);
            }
            if( ae_fp_less_eq(v,-1.0E5*ae_machineepsilon*rowscale) )
            {
                
                /*
                 * NOTE: it is important to check for non-strict inequality
                 *       because we have to correctly handle zero constraint
                 *       0*x<=0
                 */
                continue;
            }
        }
        ae_v_move(&state->rctmpdense0.ptr.pp_double[0][nactivelin], state->rctmpdense0.stride, &state->cleic.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
        state->rctmpconstraintidx.ptr.p_int[nactiveconstraints] = n+i;
        state->cstatus.ptr.p_int[n+i] = 0;
        state->rctmpisequality.ptr.p_bool[nactiveconstraints] = i<nec;
        nactiveconstraints = nactiveconstraints+1;
        nactivelin = nactivelin+1;
    }
    
    /*
     * Skip if no "candidate" constraints was found
     */
    if( nactiveconstraints==0 )
    {
        for(i=0; i<=n-1; i++)
        {
            if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                state->cstatus.ptr.p_int[i] = 1;
                continue;
            }
            if( (state->hasbndl.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->bndl.ptr.p_double[i]))&&ae_fp_greater_eq(gc->ptr.p_double[i],(double)(0)) )
            {
                state->cstatus.ptr.p_int[i] = 1;
                continue;
            }
            if( (state->hasbndu.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->bndu.ptr.p_double[i]))&&ae_fp_less_eq(gc->ptr.p_double[i],(double)(0)) )
            {
                state->cstatus.ptr.p_int[i] = 1;
                continue;
            }
        }
        return;
    }
    
    /*
     * General case.
     *
     * APPROACH TO CONSTRAINTS ACTIVATION/DEACTIVATION
     *
     * We have NActiveConstraints "candidates": NActiveBnd boundary candidates,
     * NActiveLin linear candidates. Indexes of boundary constraints are stored
     * in RCTmpConstraintIdx[0:NActiveBnd-1], indexes of linear ones are stored
     * in RCTmpConstraintIdx[NActiveBnd:NActiveBnd+NActiveLin-1]. Some of the
     * constraints are equality ones, some are inequality - as specified by 
     * RCTmpIsEquality[i].
     *
     * Now we have to determine active subset of "candidates" set. In order to
     * do so we solve following constrained minimization problem:
     *         (                         )^2
     *     min ( SUM(lambda[i]*A[i]) + G )
     *         (                         )
     * Here:
     * * G is a gradient (column vector)
     * * A[i] is a column vector, linear (left) part of I-th constraint.
     *   I=0..NActiveConstraints-1, first NActiveBnd elements of A are just
     *   subset of identity matrix (boundary constraints), next NActiveLin
     *   elements are subset of rows of the matrix of general linear constraints.
     * * lambda[i] is a Lagrange multiplier corresponding to I-th constraint
     *
     * NOTE: for preconditioned setting A is replaced by A*H^(-0.5), G is
     *       replaced by G*H^(-0.5). We apply this scaling at the last stage,
     *       before passing data to NNLS solver.
     *
     * Minimization is performed subject to non-negativity constraints on
     * lambda[i] corresponding to inequality constraints. Inequality constraints
     * which correspond to non-zero lambda are activated, equality constraints
     * are always considered active.
     *
     * Informally speaking, we "decompose" descent direction -G and represent
     * it as sum of constraint vectors and "residual" part (which is equal to
     * the actual descent direction subject to constraints).
     *
     * SOLUTION OF THE NNLS PROBLEM
     *
     * We solve this optimization problem with Non-Negative Least Squares solver,
     * which can efficiently solve least squares problems of the form
     *
     *         ( [ I | AU ]     )^2
     *     min ( [   |    ]*x-b )   s.t. non-negativity constraints on some x[i]
     *         ( [ 0 | AL ]     )
     *
     * In order to use this solver we have to rearrange rows of A[] and G in
     * such way that first NActiveBnd columns of A store identity matrix (before
     * sorting non-zero elements are randomly distributed in the first NActiveBnd
     * columns of A, during sorting we move them to first NActiveBnd rows).
     *
     * Then we create instance of NNLS solver (we reuse instance left from the
     * previous run of the optimization problem) and solve NNLS problem.
     */
    idx0 = 0;
    idx1 = nactivebnd;
    for(i=0; i<=n-1; i++)
    {
        if( state->cstatus.ptr.p_int[i]>=0 )
        {
            v = (double)1/ae_sqrt(h->ptr.p_double[i], _state);
            for(j=0; j<=nactivelin-1; j++)
            {
                state->rctmpdense1.ptr.pp_double[idx0][j] = state->rctmpdense0.ptr.pp_double[i][j]/state->rctmps.ptr.p_double[i]*v;
            }
            state->rctmprightpart.ptr.p_double[idx0] = state->rctmpg.ptr.p_double[i]/state->rctmps.ptr.p_double[i]*v;
            idx0 = idx0+1;
        }
        else
        {
            v = (double)1/ae_sqrt(h->ptr.p_double[i], _state);
            for(j=0; j<=nactivelin-1; j++)
            {
                state->rctmpdense1.ptr.pp_double[idx1][j] = state->rctmpdense0.ptr.pp_double[i][j]*v;
            }
            state->rctmprightpart.ptr.p_double[idx1] = state->rctmpg.ptr.p_double[i]*v;
            idx1 = idx1+1;
        }
    }
    snnlsinit(n, ae_minint(nec+nic, n, _state), n, &state->solver, _state);
    snnlssetproblem(&state->solver, &state->rctmpdense1, &state->rctmprightpart, nactivebnd, nactiveconstraints-nactivebnd, n, _state);
    for(i=0; i<=nactiveconstraints-1; i++)
    {
        if( state->rctmpisequality.ptr.p_bool[i] )
        {
            snnlsdropnnc(&state->solver, i, _state);
        }
    }
    snnlssolve(&state->solver, &state->rctmplambdas, _state);
    
    /*
     * After solution of the problem we activate equality constraints (always active)
     * and inequality constraints with non-zero Lagrange multipliers. Then we reorthogonalize
     * active constraints.
     */
    for(i=0; i<=n+nec+nic-1; i++)
    {
        state->cstatus.ptr.p_int[i] = -1;
    }
    for(i=0; i<=nactiveconstraints-1; i++)
    {
        if( state->rctmpisequality.ptr.p_bool[i]||ae_fp_greater(state->rctmplambdas.ptr.p_double[i],(double)(0)) )
        {
            state->cstatus.ptr.p_int[state->rctmpconstraintidx.ptr.p_int[i]] = 1;
        }
        else
        {
            state->cstatus.ptr.p_int[state->rctmpconstraintidx.ptr.p_int[i]] = 0;
        }
    }
    sasrebuildbasis(state, _state);
}


void _sactiveset_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    sactiveset *p = (sactiveset*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->xc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->h, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cstatus, 0, DT_INT, _state, make_automatic);
    ae_matrix_init(&p->sdensebatch, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->pdensebatch, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->idensebatch, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sparsebatch, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->cleic, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->mtnew, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->mtx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->mtas, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->cdtmp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->corrtmp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->unitdiagonal, 0, DT_REAL, _state, make_automatic);
    _snnlssolver_init(&p->solver, _state, make_automatic);
    ae_vector_init(&p->scntmp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpfeas, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpm0, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rctmps, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rctmpg, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rctmprightpart, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->rctmpdense0, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->rctmpdense1, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rctmpisequality, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->rctmpconstraintidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->rctmplambdas, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpbasis, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpnormestimates, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpreciph, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpprodp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpprods, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpcp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpcs, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpci, 0, DT_REAL, _state, make_automatic);
}


void _sactiveset_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    sactiveset       *dst = (sactiveset*)_dst;
    const sactiveset *src = (const sactiveset*)_src;
    dst->n = src->n;
    dst->algostate = src->algostate;
    ae_vector_init_copy(&dst->xc, &src->xc, _state, make_automatic);
    dst->hasxc = src->hasxc;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->h, &src->h, _state, make_automatic);
    ae_vector_init_copy(&dst->cstatus, &src->cstatus, _state, make_automatic);
    dst->basisisready = src->basisisready;
    ae_matrix_init_copy(&dst->sdensebatch, &src->sdensebatch, _state, make_automatic);
    ae_matrix_init_copy(&dst->pdensebatch, &src->pdensebatch, _state, make_automatic);
    ae_matrix_init_copy(&dst->idensebatch, &src->idensebatch, _state, make_automatic);
    dst->densebatchsize = src->densebatchsize;
    ae_vector_init_copy(&dst->sparsebatch, &src->sparsebatch, _state, make_automatic);
    dst->sparsebatchsize = src->sparsebatchsize;
    dst->basisage = src->basisage;
    dst->feasinitpt = src->feasinitpt;
    dst->constraintschanged = src->constraintschanged;
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    ae_matrix_init_copy(&dst->cleic, &src->cleic, _state, make_automatic);
    dst->nec = src->nec;
    dst->nic = src->nic;
    ae_vector_init_copy(&dst->mtnew, &src->mtnew, _state, make_automatic);
    ae_vector_init_copy(&dst->mtx, &src->mtx, _state, make_automatic);
    ae_vector_init_copy(&dst->mtas, &src->mtas, _state, make_automatic);
    ae_vector_init_copy(&dst->cdtmp, &src->cdtmp, _state, make_automatic);
    ae_vector_init_copy(&dst->corrtmp, &src->corrtmp, _state, make_automatic);
    ae_vector_init_copy(&dst->unitdiagonal, &src->unitdiagonal, _state, make_automatic);
    _snnlssolver_init_copy(&dst->solver, &src->solver, _state, make_automatic);
    ae_vector_init_copy(&dst->scntmp, &src->scntmp, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpfeas, &src->tmpfeas, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpm0, &src->tmpm0, _state, make_automatic);
    ae_vector_init_copy(&dst->rctmps, &src->rctmps, _state, make_automatic);
    ae_vector_init_copy(&dst->rctmpg, &src->rctmpg, _state, make_automatic);
    ae_vector_init_copy(&dst->rctmprightpart, &src->rctmprightpart, _state, make_automatic);
    ae_matrix_init_copy(&dst->rctmpdense0, &src->rctmpdense0, _state, make_automatic);
    ae_matrix_init_copy(&dst->rctmpdense1, &src->rctmpdense1, _state, make_automatic);
    ae_vector_init_copy(&dst->rctmpisequality, &src->rctmpisequality, _state, make_automatic);
    ae_vector_init_copy(&dst->rctmpconstraintidx, &src->rctmpconstraintidx, _state, make_automatic);
    ae_vector_init_copy(&dst->rctmplambdas, &src->rctmplambdas, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpbasis, &src->tmpbasis, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpnormestimates, &src->tmpnormestimates, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpreciph, &src->tmpreciph, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpprodp, &src->tmpprodp, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpprods, &src->tmpprods, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpcp, &src->tmpcp, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpcs, &src->tmpcs, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpci, &src->tmpci, _state, make_automatic);
}


void _sactiveset_clear(void* _p)
{
    sactiveset *p = (sactiveset*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->xc);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->h);
    ae_vector_clear(&p->cstatus);
    ae_matrix_clear(&p->sdensebatch);
    ae_matrix_clear(&p->pdensebatch);
    ae_matrix_clear(&p->idensebatch);
    ae_vector_clear(&p->sparsebatch);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_matrix_clear(&p->cleic);
    ae_vector_clear(&p->mtnew);
    ae_vector_clear(&p->mtx);
    ae_vector_clear(&p->mtas);
    ae_vector_clear(&p->cdtmp);
    ae_vector_clear(&p->corrtmp);
    ae_vector_clear(&p->unitdiagonal);
    _snnlssolver_clear(&p->solver);
    ae_vector_clear(&p->scntmp);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmpfeas);
    ae_matrix_clear(&p->tmpm0);
    ae_vector_clear(&p->rctmps);
    ae_vector_clear(&p->rctmpg);
    ae_vector_clear(&p->rctmprightpart);
    ae_matrix_clear(&p->rctmpdense0);
    ae_matrix_clear(&p->rctmpdense1);
    ae_vector_clear(&p->rctmpisequality);
    ae_vector_clear(&p->rctmpconstraintidx);
    ae_vector_clear(&p->rctmplambdas);
    ae_matrix_clear(&p->tmpbasis);
    ae_vector_clear(&p->tmpnormestimates);
    ae_vector_clear(&p->tmpreciph);
    ae_vector_clear(&p->tmpprodp);
    ae_vector_clear(&p->tmpprods);
    ae_vector_clear(&p->tmpcp);
    ae_vector_clear(&p->tmpcs);
    ae_vector_clear(&p->tmpci);
}


void _sactiveset_destroy(void* _p)
{
    sactiveset *p = (sactiveset*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->xc);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->h);
    ae_vector_destroy(&p->cstatus);
    ae_matrix_destroy(&p->sdensebatch);
    ae_matrix_destroy(&p->pdensebatch);
    ae_matrix_destroy(&p->idensebatch);
    ae_vector_destroy(&p->sparsebatch);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_matrix_destroy(&p->cleic);
    ae_vector_destroy(&p->mtnew);
    ae_vector_destroy(&p->mtx);
    ae_vector_destroy(&p->mtas);
    ae_vector_destroy(&p->cdtmp);
    ae_vector_destroy(&p->corrtmp);
    ae_vector_destroy(&p->unitdiagonal);
    _snnlssolver_destroy(&p->solver);
    ae_vector_destroy(&p->scntmp);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmpfeas);
    ae_matrix_destroy(&p->tmpm0);
    ae_vector_destroy(&p->rctmps);
    ae_vector_destroy(&p->rctmpg);
    ae_vector_destroy(&p->rctmprightpart);
    ae_matrix_destroy(&p->rctmpdense0);
    ae_matrix_destroy(&p->rctmpdense1);
    ae_vector_destroy(&p->rctmpisequality);
    ae_vector_destroy(&p->rctmpconstraintidx);
    ae_vector_destroy(&p->rctmplambdas);
    ae_matrix_destroy(&p->tmpbasis);
    ae_vector_destroy(&p->tmpnormestimates);
    ae_vector_destroy(&p->tmpreciph);
    ae_vector_destroy(&p->tmpprodp);
    ae_vector_destroy(&p->tmpprods);
    ae_vector_destroy(&p->tmpcp);
    ae_vector_destroy(&p->tmpcs);
    ae_vector_destroy(&p->tmpci);
}


#endif
#if defined(AE_COMPILE_QQPSOLVER) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
This function initializes QQPSettings structure with default settings.

Newly created structure MUST be initialized by default settings  -  or  by
copy of the already initialized structure.

  -- ALGLIB --
     Copyright 14.05.2011 by Bochkanov Sergey
*************************************************************************/
void qqploaddefaults(ae_int_t n, qqpsettings* s, ae_state *_state)
{


    s->epsg = 0.0;
    s->epsf = 0.0;
    s->epsx = 1.0E-6;
    s->maxouterits = 0;
    s->cgphase = ae_true;
    s->cnphase = ae_true;
    s->cgminits = 5;
    s->cgmaxits = ae_maxint(s->cgminits, ae_round((double)1+0.33*(double)n, _state), _state);
    s->sparsesolver = 0;
    s->cnmaxupdates = ae_round((double)1+0.1*(double)n, _state);
}


/*************************************************************************
This function initializes QQPSettings  structure  with  copy  of  another,
already initialized structure.

  -- ALGLIB --
     Copyright 14.05.2011 by Bochkanov Sergey
*************************************************************************/
void qqpcopysettings(const qqpsettings* src,
     qqpsettings* dst,
     ae_state *_state)
{


    dst->epsg = src->epsg;
    dst->epsf = src->epsf;
    dst->epsx = src->epsx;
    dst->maxouterits = src->maxouterits;
    dst->cgphase = src->cgphase;
    dst->cnphase = src->cnphase;
    dst->cgminits = src->cgminits;
    dst->cgmaxits = src->cgmaxits;
    dst->sparsesolver = src->sparsesolver;
    dst->cnmaxupdates = src->cnmaxupdates;
}


/*************************************************************************
This function performs preallocation of internal 2D matrices. If matrix
size is less than expected, we grow to some larger value (specified by user).

It can be useful in cases when we solve many subsequent QP problems with
increasing sizes - helps to avoid multiple allocations.

INPUT PARAMETERS:
    SState      -   object which stores temporaries:
                    * uninitialized object is automatically initialized
                    * previously allocated memory is reused as much
                      as possible
    NExpected   -   if internal buffers have size enough for NExpected,
                    no preallocation happens. If size is less than NExpected,
                    buffers are preallocated up to NGrowTo*NGrowTo
    NGrowTo     -   new size
    
    
OUTPUT PARAMETERS:
    SState      -   temporary buffers, some of them are preallocated

  -- ALGLIB --
     Copyright 09.10.2017 by Bochkanov Sergey
*************************************************************************/
void qqppreallocategrowdense(qqpbuffers* sstate,
     ae_int_t nexpected,
     ae_int_t ngrowto,
     ae_state *_state)
{


    if( sstate->densea.rows<nexpected||sstate->densea.cols<nexpected )
    {
        rmatrixsetlengthatleast(&sstate->densea, ngrowto, ngrowto, _state);
    }
    if( sstate->densez.rows<nexpected||sstate->densez.cols<nexpected )
    {
        rmatrixsetlengthatleast(&sstate->densez, ngrowto, ngrowto, _state);
    }
}


/*************************************************************************
This function runs QQP solver; it returns after optimization  process  was
completed. Following QP problem is solved:

    min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin))
    
subject to boundary constraints.

IMPORTANT: UNLIKE MANY OTHER SOLVERS, THIS FUNCTION DOES NOT  REQUIRE  YOU
           TO INITIALIZE STATE OBJECT. IT CAN BE AUTOMATICALLY INITIALIZED
           DURING SOLUTION PROCESS.

INPUT PARAMETERS:
    AC          -   for dense problems given by CQM model (AKind=0) A-term
                    of CQM object contains system matrix. Other terms  are
                    unspecified and should not be referenced.
    SparseAC    -   for sparse problems (AKind=1)
    DenseAC     -   for traditional dense matrices (AKind=2)
    AKind       -   matrix term to use:
                    * 0 for dense CQM (CQMAC)
                    * 1 for sparse matrix (SparseAC)
                    * 2 for dense matrix (DenseAC)
    IsUpper     -   which triangle of  SparseAC/DenseAC  stores  matrix  -
                    upper or lower one (for dense matrices this  parameter
                    is not actual).
    BC          -   linear term, array[NC]
    BndLC       -   lower bound, array[NC]
    BndUC       -   upper bound, array[NC]
    SC          -   scale vector, array[NC]:
                    * I-th element contains scale of I-th variable,
                    * SC[I]>0
    XOriginC    -   origin term, array[NC]. Can be zero.
    NC          -   number of variables in the  original  formulation  (no
                    slack variables).
    CLEICC      -   linear equality/inequality constraints. Present version
                    of this function does NOT provide  publicly  available
                    support for linear constraints. This feature  will  be
                    introduced in the future versions of the function.
    NEC, NIC    -   number of equality/inequality constraints.
                    MUST BE ZERO IN THE CURRENT VERSION!!!
    Settings    -   QQPSettings object initialized by one of the initialization
                    functions.
    SState      -   object which stores temporaries:
                    * uninitialized object is automatically initialized
                    * previously allocated memory is reused as much
                      as possible
    XS          -   initial point, array[NC]
    
    
OUTPUT PARAMETERS:
    XS          -   last point
    TerminationType-termination type:
                    *
                    *
                    *

  -- ALGLIB --
     Copyright 14.05.2011 by Bochkanov Sergey
*************************************************************************/
void qqpoptimize(const convexquadraticmodel* cqmac,
     const sparsematrix* sparseac,
     /* Real    */ const ae_matrix* denseac,
     ae_int_t akind,
     ae_bool isupper,
     /* Real    */ const ae_vector* bc,
     /* Real    */ const ae_vector* bndlc,
     /* Real    */ const ae_vector* bnduc,
     /* Real    */ const ae_vector* sc,
     /* Real    */ const ae_vector* xoriginc,
     ae_int_t nc,
     const qqpsettings* settings,
     qqpbuffers* sstate,
     /* Real    */ ae_vector* xs,
     ae_int_t* terminationtype,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double vv;
    double d2;
    double d1;
    ae_int_t d1est;
    ae_int_t d2est;
    ae_bool needact;
    double reststp;
    double fullstp;
    double stpmax;
    double stp;
    ae_int_t stpcnt;
    ae_int_t cidx;
    double cval;
    ae_int_t cgcnt;
    ae_int_t cgmax;
    ae_int_t newtcnt;
    ae_int_t sparsesolver;
    double beta;
    ae_bool b;
    double fprev;
    double fcur;
    ae_bool problemsolved;
    ae_bool isconstrained;
    double f0;
    double f1;

    *terminationtype = 0;

    
    /*
     * Primary checks
     */
    ae_assert((akind==0||akind==1)||akind==2, "QQPOptimize: incorrect AKind", _state);
    sstate->n = nc;
    n = sstate->n;
    *terminationtype = 0;
    sstate->repinneriterationscount = 0;
    sstate->repouteriterationscount = 0;
    sstate->repncholesky = 0;
    sstate->repncupdates = 0;
    
    /*
     * Several checks
     * * matrix size
     * * scale vector
     * * consistency of bound constraints
     * * consistency of settings
     */
    if( akind==1 )
    {
        ae_assert(sparsegetnrows(sparseac, _state)==n, "QQPOptimize: rows(SparseAC)<>N", _state);
        ae_assert(sparsegetncols(sparseac, _state)==n, "QQPOptimize: cols(SparseAC)<>N", _state);
    }
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(sc->ptr.p_double[i], _state)&&ae_fp_greater(sc->ptr.p_double[i],(double)(0)), "QQPOptimize: incorrect scale", _state);
    }
    for(i=0; i<=n-1; i++)
    {
        if( ae_isfinite(bndlc->ptr.p_double[i], _state)&&ae_isfinite(bnduc->ptr.p_double[i], _state) )
        {
            if( ae_fp_greater(bndlc->ptr.p_double[i],bnduc->ptr.p_double[i]) )
            {
                *terminationtype = -3;
                return;
            }
        }
    }
    ae_assert(settings->cgphase||settings->cnphase, "QQPOptimize: both phases (CG and Newton) are inactive", _state);
    
    /*
     * Allocate data structures
     */
    rvectorsetlengthatleast(&sstate->bndl, n, _state);
    rvectorsetlengthatleast(&sstate->bndu, n, _state);
    bvectorsetlengthatleast(&sstate->havebndl, n, _state);
    bvectorsetlengthatleast(&sstate->havebndu, n, _state);
    rvectorsetlengthatleast(&sstate->xs, n, _state);
    rvectorsetlengthatleast(&sstate->xf, n, _state);
    rvectorsetlengthatleast(&sstate->xp, n, _state);
    rvectorsetlengthatleast(&sstate->gc, n, _state);
    rvectorsetlengthatleast(&sstate->cgc, n, _state);
    rvectorsetlengthatleast(&sstate->cgp, n, _state);
    rvectorsetlengthatleast(&sstate->dc, n, _state);
    rvectorsetlengthatleast(&sstate->dp, n, _state);
    rvectorsetlengthatleast(&sstate->tmp0, n, _state);
    rvectorsetlengthatleast(&sstate->tmp1, n, _state);
    rvectorsetlengthatleast(&sstate->stpbuf, 15, _state);
    sasinit(n, &sstate->sas, _state);
    
    /*
     * Scale/shift problem coefficients:
     *
     *     min { 0.5*(x-x0)'*A*(x-x0) + b'*(x-x0) }
     *
     * becomes (after transformation "x = S*y+x0")
     *
     *     min { 0.5*y'*(S*A*S)*y + (S*b)'*y
     *
     * Modified A_mod=S*A*S and b_mod=S*(b+A*x0) are
     * stored into SState.DenseA and SState.B.
     *       
     */
    rvectorsetlengthatleast(&sstate->b, n, _state);
    for(i=0; i<=n-1; i++)
    {
        sstate->b.ptr.p_double[i] = sc->ptr.p_double[i]*bc->ptr.p_double[i];
    }
    sstate->akind = -99;
    if( akind==0 )
    {
        
        /*
         * Dense QP problem - just copy and scale.
         */
        rmatrixsetlengthatleast(&sstate->densea, n, n, _state);
        cqmgeta(cqmac, &sstate->densea, _state);
        sstate->akind = 0;
        sstate->absamax = (double)(0);
        sstate->absasum = (double)(0);
        sstate->absasum2 = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = sc->ptr.p_double[i]*sstate->densea.ptr.pp_double[i][j]*sc->ptr.p_double[j];
                vv = ae_fabs(v, _state);
                sstate->densea.ptr.pp_double[i][j] = v;
                sstate->absamax = ae_maxreal(sstate->absamax, vv, _state);
                sstate->absasum = sstate->absasum+vv;
                sstate->absasum2 = sstate->absasum2+vv*vv;
            }
        }
    }
    if( akind==1 )
    {
        
        /*
         * Sparse QP problem - a bit tricky. Depending on format of the
         * input we use different strategies for copying matrix:
         * * SKS matrices are copied to SKS format
         * * anything else is copied to CRS format
         */
        sparsecopytosksbuf(sparseac, &sstate->sparsea, _state);
        if( isupper )
        {
            sparsetransposesks(&sstate->sparsea, _state);
        }
        sstate->akind = 1;
        sstate->sparseupper = ae_false;
        sstate->absamax = (double)(0);
        sstate->absasum = (double)(0);
        sstate->absasum2 = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            k = sstate->sparsea.ridx.ptr.p_int[i];
            for(j=i-sstate->sparsea.didx.ptr.p_int[i]; j<=i; j++)
            {
                v = sc->ptr.p_double[i]*sstate->sparsea.vals.ptr.p_double[k]*sc->ptr.p_double[j];
                vv = ae_fabs(v, _state);
                sstate->sparsea.vals.ptr.p_double[k] = v;
                if( i==j )
                {
                    
                    /*
                     * Diagonal terms are counted only once
                     */
                    sstate->absamax = ae_maxreal(sstate->absamax, vv, _state);
                    sstate->absasum = sstate->absasum+vv;
                    sstate->absasum2 = sstate->absasum2+vv*vv;
                }
                else
                {
                    
                    /*
                     * Offdiagonal terms are counted twice
                     */
                    sstate->absamax = ae_maxreal(sstate->absamax, vv, _state);
                    sstate->absasum = sstate->absasum+(double)2*vv;
                    sstate->absasum2 = sstate->absasum2+(double)2*vv*vv;
                }
                k = k+1;
            }
        }
    }
    if( akind==2 )
    {
        
        /*
         * Dense QP problem - just copy and scale.
         */
        rmatrixsetlengthatleast(&sstate->densea, n, n, _state);
        sstate->akind = 0;
        sstate->absamax = (double)(0);
        sstate->absasum = (double)(0);
        sstate->absasum2 = (double)(0);
        if( isupper )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=i; j<=n-1; j++)
                {
                    v = sc->ptr.p_double[i]*denseac->ptr.pp_double[i][j]*sc->ptr.p_double[j];
                    vv = ae_fabs(v, _state);
                    sstate->densea.ptr.pp_double[i][j] = v;
                    sstate->densea.ptr.pp_double[j][i] = v;
                    if( ae_fp_eq((double)(i),v) )
                    {
                        k = 1;
                    }
                    else
                    {
                        k = 2;
                    }
                    sstate->absamax = ae_maxreal(sstate->absamax, vv, _state);
                    sstate->absasum = sstate->absasum+vv*(double)k;
                    sstate->absasum2 = sstate->absasum2+vv*vv*(double)k;
                }
            }
        }
        else
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=i; j++)
                {
                    v = sc->ptr.p_double[i]*denseac->ptr.pp_double[i][j]*sc->ptr.p_double[j];
                    vv = ae_fabs(v, _state);
                    sstate->densea.ptr.pp_double[i][j] = v;
                    sstate->densea.ptr.pp_double[j][i] = v;
                    if( ae_fp_eq((double)(i),v) )
                    {
                        k = 1;
                    }
                    else
                    {
                        k = 2;
                    }
                    sstate->absamax = ae_maxreal(sstate->absamax, vv, _state);
                    sstate->absasum = sstate->absasum+vv*(double)k;
                    sstate->absasum2 = sstate->absasum2+vv*vv*(double)k;
                }
            }
        }
    }
    ae_assert(sstate->akind>=0, "QQP: integrity check failed", _state);
    
    /*
     * Load box constraints into State structure.
     *
     * We apply transformation to variables: y=(x-x_origin)/s,
     * each of the constraints is appropriately shifted/scaled.
     */
    for(i=0; i<=n-1; i++)
    {
        sstate->havebndl.ptr.p_bool[i] = ae_isfinite(bndlc->ptr.p_double[i], _state);
        if( sstate->havebndl.ptr.p_bool[i] )
        {
            sstate->bndl.ptr.p_double[i] = (bndlc->ptr.p_double[i]-xoriginc->ptr.p_double[i])/sc->ptr.p_double[i];
        }
        else
        {
            ae_assert(ae_isneginf(bndlc->ptr.p_double[i], _state), "QQPOptimize: incorrect lower bound", _state);
            sstate->bndl.ptr.p_double[i] = _state->v_neginf;
        }
        sstate->havebndu.ptr.p_bool[i] = ae_isfinite(bnduc->ptr.p_double[i], _state);
        if( sstate->havebndu.ptr.p_bool[i] )
        {
            sstate->bndu.ptr.p_double[i] = (bnduc->ptr.p_double[i]-xoriginc->ptr.p_double[i])/sc->ptr.p_double[i];
        }
        else
        {
            ae_assert(ae_isposinf(bnduc->ptr.p_double[i], _state), "QQPOptimize: incorrect upper bound", _state);
            sstate->bndu.ptr.p_double[i] = _state->v_posinf;
        }
    }
    
    /*
     * Process initial point:
     * * set it to XS-XOriginC
     * * make sure that boundary constraints are preserved by transformation
     */
    for(i=0; i<=n-1; i++)
    {
        sstate->xs.ptr.p_double[i] = (xs->ptr.p_double[i]-xoriginc->ptr.p_double[i])/sc->ptr.p_double[i];
        if( sstate->havebndl.ptr.p_bool[i]&&ae_fp_less(sstate->xs.ptr.p_double[i],sstate->bndl.ptr.p_double[i]) )
        {
            sstate->xs.ptr.p_double[i] = sstate->bndl.ptr.p_double[i];
        }
        if( sstate->havebndu.ptr.p_bool[i]&&ae_fp_greater(sstate->xs.ptr.p_double[i],sstate->bndu.ptr.p_double[i]) )
        {
            sstate->xs.ptr.p_double[i] = sstate->bndu.ptr.p_double[i];
        }
        if( sstate->havebndl.ptr.p_bool[i]&&ae_fp_eq(xs->ptr.p_double[i],bndlc->ptr.p_double[i]) )
        {
            sstate->xs.ptr.p_double[i] = sstate->bndl.ptr.p_double[i];
        }
        if( sstate->havebndu.ptr.p_bool[i]&&ae_fp_eq(xs->ptr.p_double[i],bnduc->ptr.p_double[i]) )
        {
            sstate->xs.ptr.p_double[i] = sstate->bndu.ptr.p_double[i];
        }
    }
    
    /*
     * Select sparse direct solver
     */
    if( akind==1 )
    {
        sparsesolver = settings->sparsesolver;
        if( sparsesolver==0 )
        {
            sparsesolver = 1;
        }
        if( sparseissks(&sstate->sparsea, _state) )
        {
            sparsesolver = 2;
        }
        sparsesolver = 2;
        ae_assert(sparsesolver==1||sparsesolver==2, "QQPOptimize: incorrect SparseSolver", _state);
    }
    else
    {
        sparsesolver = 0;
    }
    
    /*
     * For unconstrained problems - try to use fast approach which requires
     * just one unregularized Cholesky decomposition for solution. If it fails,
     * switch to general QQP code.
     */
    problemsolved = ae_false;
    isconstrained = ae_false;
    for(i=0; i<=n-1; i++)
    {
        isconstrained = (isconstrained||sstate->havebndl.ptr.p_bool[i])||sstate->havebndu.ptr.p_bool[i];
    }
    if( (!isconstrained&&settings->cnphase)&&akind==0 )
    {
        rmatrixsetlengthatleast(&sstate->densez, n, n, _state);
        rvectorsetlengthatleast(&sstate->tmpcn, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=i; j<=n-1; j++)
            {
                sstate->densez.ptr.pp_double[i][j] = sstate->densea.ptr.pp_double[i][j];
            }
        }
        inc(&sstate->repncholesky, _state);
        if( spdmatrixcholeskyrec(&sstate->densez, 0, n, ae_true, &sstate->tmpcn, _state) )
        {
            ae_v_move(&sstate->xf.ptr.p_double[0], 1, &sstate->xs.ptr.p_double[0], 1, ae_v_len(0,n-1));
            for(i=0; i<=n-1; i++)
            {
                sstate->dc.ptr.p_double[i] = (double)(0);
            }
            f0 = qqpsolver_projectedtargetfunction(sstate, &sstate->xf, &sstate->dc, 0.0, &sstate->tmpcn, &sstate->tmp1, _state);
            for(k=0; k<=3; k++)
            {
                rmatrixmv(n, n, &sstate->densea, 0, 0, 0, &sstate->xf, 0, &sstate->gc, 0, _state);
                ae_v_add(&sstate->gc.ptr.p_double[0], 1, &sstate->b.ptr.p_double[0], 1, ae_v_len(0,n-1));
                for(i=0; i<=n-1; i++)
                {
                    sstate->dc.ptr.p_double[i] = -sstate->gc.ptr.p_double[i];
                }
                fblscholeskysolve(&sstate->densez, 1.0, n, ae_true, &sstate->dc, &sstate->tmpcn, _state);
                f1 = qqpsolver_projectedtargetfunction(sstate, &sstate->xf, &sstate->dc, 1.0, &sstate->tmpcn, &sstate->tmp1, _state);
                if( ae_fp_greater_eq(f1,f0) )
                {
                    break;
                }
                ae_v_add(&sstate->xf.ptr.p_double[0], 1, &sstate->dc.ptr.p_double[0], 1, ae_v_len(0,n-1));
                f0 = f1;
            }
            *terminationtype = 2;
            problemsolved = ae_true;
        }
    }
    
    /*
     * Attempt to solve problem with fast approach failed, use generic QQP
     */
    if( !problemsolved )
    {
        
        /*
         * Prepare "active set" structure
         */
        sassetbc(&sstate->sas, &sstate->bndl, &sstate->bndu, _state);
        if( !sasstartoptimization(&sstate->sas, &sstate->xs, _state) )
        {
            *terminationtype = -3;
            return;
        }
        
        /*
         * Main loop.
         *
         * Following variables are used:
         * * GC stores current gradient (unconstrained)
         * * CGC stores current gradient (constrained)
         * * DC stores current search direction
         * * CGP stores constrained gradient at previous point
         *   (zero on initial entry)
         * * DP stores previous search direction
         *   (zero on initial entry)
         */
        cgmax = settings->cgminits;
        sstate->repinneriterationscount = 0;
        sstate->repouteriterationscount = 0;
        for(;;)
        {
            if( settings->maxouterits>0&&sstate->repouteriterationscount>=settings->maxouterits )
            {
                *terminationtype = 5;
                break;
            }
            if( sstate->repouteriterationscount>0 )
            {
                
                /*
                 * Check EpsF- and EpsX-based stopping criteria.
                 * Because problem was already scaled, we do not scale step before checking its length.
                 * NOTE: these checks are performed only after at least one outer iteration was made.
                 */
                if( ae_fp_greater(settings->epsf,(double)(0)) )
                {
                    
                    /*
                     * NOTE 1: here we rely on the fact that ProjectedTargetFunction() ignore D when Stp=0
                     * NOTE 2: code below handles situation when update increases function value instead
                     *         of decreasing it.
                     */
                    fprev = qqpsolver_projectedtargetfunction(sstate, &sstate->xp, &sstate->dc, 0.0, &sstate->tmp0, &sstate->tmp1, _state);
                    fcur = qqpsolver_projectedtargetfunction(sstate, &sstate->sas.xc, &sstate->dc, 0.0, &sstate->tmp0, &sstate->tmp1, _state);
                    if( ae_fp_less_eq(fprev-fcur,settings->epsf*ae_maxreal(ae_fabs(fprev, _state), ae_maxreal(ae_fabs(fcur, _state), 1.0, _state), _state)) )
                    {
                        *terminationtype = 1;
                        break;
                    }
                }
                if( ae_fp_greater(settings->epsx,(double)(0)) )
                {
                    v = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+ae_sqr(sstate->xp.ptr.p_double[i]-sstate->sas.xc.ptr.p_double[i], _state);
                    }
                    if( ae_fp_less_eq(ae_sqrt(v, _state),settings->epsx) )
                    {
                        *terminationtype = 2;
                        break;
                    }
                }
            }
            inc(&sstate->repouteriterationscount, _state);
            ae_v_move(&sstate->xp.ptr.p_double[0], 1, &sstate->sas.xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
            if( !settings->cgphase )
            {
                cgmax = 0;
            }
            for(i=0; i<=n-1; i++)
            {
                sstate->cgp.ptr.p_double[i] = 0.0;
                sstate->dp.ptr.p_double[i] = 0.0;
            }
            for(cgcnt=0; cgcnt<=cgmax-1; cgcnt++)
            {
                
                /*
                 * Calculate unconstrained gradient GC for "extended" QP problem
                 * Determine active set, current constrained gradient CGC.
                 * Check gradient-based stopping condition.
                 *
                 * NOTE: because problem was scaled, we do not have to apply scaling
                 *       to gradient before checking stopping condition.
                 */
                qqpsolver_targetgradient(sstate, &sstate->sas.xc, &sstate->gc, _state);
                sasreactivateconstraints(&sstate->sas, &sstate->gc, _state);
                ae_v_move(&sstate->cgc.ptr.p_double[0], 1, &sstate->gc.ptr.p_double[0], 1, ae_v_len(0,n-1));
                sasconstraineddirection(&sstate->sas, &sstate->cgc, _state);
                v = ae_v_dotproduct(&sstate->cgc.ptr.p_double[0], 1, &sstate->cgc.ptr.p_double[0], 1, ae_v_len(0,n-1));
                if( ae_fp_less_eq(ae_sqrt(v, _state),settings->epsg) )
                {
                    *terminationtype = 4;
                    break;
                }
                
                /*
                 * Prepare search direction DC and explore it.
                 *
                 * We try to use CGP/DP to prepare conjugate gradient step,
                 * but we resort to steepest descent step (Beta=0) in case
                 * we are at I-th boundary, but DP[I]<>0.
                 *
                 * Such approach allows us to ALWAYS have feasible DC, with
                 * guaranteed compatibility with both feasible area and current
                 * active set.
                 *
                 * Automatic CG reset performed every time DP is incompatible
                 * with current active set and/or feasible area. We also
                 * perform reset every QuickQPRestartCG iterations.
                 */
                ae_v_moveneg(&sstate->dc.ptr.p_double[0], 1, &sstate->cgc.ptr.p_double[0], 1, ae_v_len(0,n-1));
                v = 0.0;
                vv = 0.0;
                b = ae_false;
                for(i=0; i<=n-1; i++)
                {
                    v = v+sstate->cgc.ptr.p_double[i]*sstate->cgc.ptr.p_double[i];
                    vv = vv+sstate->cgp.ptr.p_double[i]*sstate->cgp.ptr.p_double[i];
                    b = b||((sstate->havebndl.ptr.p_bool[i]&&ae_fp_eq(sstate->sas.xc.ptr.p_double[i],sstate->bndl.ptr.p_double[i]))&&ae_fp_neq(sstate->dp.ptr.p_double[i],(double)(0)));
                    b = b||((sstate->havebndu.ptr.p_bool[i]&&ae_fp_eq(sstate->sas.xc.ptr.p_double[i],sstate->bndu.ptr.p_double[i]))&&ae_fp_neq(sstate->dp.ptr.p_double[i],(double)(0)));
                }
                b = b||ae_fp_eq(vv,(double)(0));
                b = b||cgcnt%qqpsolver_quickqprestartcg==0;
                if( !b )
                {
                    beta = v/vv;
                }
                else
                {
                    beta = 0.0;
                }
                ae_v_addd(&sstate->dc.ptr.p_double[0], 1, &sstate->dp.ptr.p_double[0], 1, ae_v_len(0,n-1), beta);
                sasconstraineddirection(&sstate->sas, &sstate->dc, _state);
                sasexploredirection(&sstate->sas, &sstate->dc, &stpmax, &cidx, &cval, _state);
                
                /*
                 * Build quadratic model of F along descent direction:
                 *
                 *     F(xc+alpha*D) = D2*alpha^2 + D1*alpha
                 *
                 * Terminate algorithm if needed.
                 *
                 * NOTE: we do not maintain constant term D0
                 */
                qqpsolver_quadraticmodel(sstate, &sstate->sas.xc, &sstate->dc, &sstate->gc, &d1, &d1est, &d2, &d2est, &sstate->tmp0, _state);
                if( ae_fp_eq(d1,(double)(0))&&ae_fp_eq(d2,(double)(0)) )
                {
                    
                    /*
                     * D1 and D2 are exactly zero, success.
                     * After this if-then we assume that D is non-zero.
                     */
                    *terminationtype = 4;
                    break;
                }
                if( d1est>=0 )
                {
                    
                    /*
                     * Numerical noise is too large, it means that we are close
                     * to minimum - and that further improvement is impossible.
                     *
                     * After this if-then we assume that D1 is definitely negative
                     * (even under presence of numerical errors).
                     */
                    *terminationtype = 7;
                    break;
                }
                if( d2est<=0&&cidx<0 )
                {
                    
                    /*
                     * Function is unbounded from below:
                     * * D1<0 (verified by previous block)
                     * * D2Est<=0, which means that either D2<0 - or it can not
                     *   be reliably distinguished from zero.
                     * * step is unconstrained
                     *
                     * If these conditions are true, we abnormally terminate QP
                     * algorithm with return code -4
                     */
                    *terminationtype = -4;
                    break;
                }
                
                /*
                 * Perform step along DC.
                 *
                 * In this block of code we maintain two step length:
                 * * RestStp -  restricted step, maximum step length along DC which does
                 *              not violate constraints
                 * * FullStp -  step length along DC which minimizes quadratic function
                 *              without taking constraints into account. If problem is
                 *              unbounded from below without constraints, FullStp is
                 *              forced to be RestStp.
                 *
                 * So, if function is convex (D2>0):
                 * * FullStp = -D1/(2*D2)
                 * * RestStp = restricted FullStp
                 * * 0<=RestStp<=FullStp
                 *
                 * If function is non-convex, but bounded from below under constraints:
                 * * RestStp = step length subject to constraints
                 * * FullStp = RestStp
                 *
                 * After RestStp and FullStp are initialized, we generate several trial
                 * steps which are different multiples of RestStp and FullStp.
                 */
                if( d2est>0 )
                {
                    ae_assert(ae_fp_less(d1,(double)(0)), "QQPOptimize: internal error", _state);
                    fullstp = -d1/((double)2*d2);
                    needact = ae_fp_greater_eq(fullstp,stpmax);
                    if( needact )
                    {
                        ae_assert(sstate->stpbuf.cnt>=3, "QQPOptimize: StpBuf overflow", _state);
                        reststp = stpmax;
                        stp = reststp;
                        sstate->stpbuf.ptr.p_double[0] = reststp*(double)4;
                        sstate->stpbuf.ptr.p_double[1] = fullstp;
                        sstate->stpbuf.ptr.p_double[2] = fullstp/(double)4;
                        stpcnt = 3;
                    }
                    else
                    {
                        reststp = fullstp;
                        stp = fullstp;
                        stpcnt = 0;
                    }
                }
                else
                {
                    ae_assert(cidx>=0, "QQPOptimize: internal error", _state);
                    ae_assert(sstate->stpbuf.cnt>=2, "QQPOptimize: StpBuf overflow", _state);
                    reststp = stpmax;
                    fullstp = stpmax;
                    stp = reststp;
                    needact = ae_true;
                    sstate->stpbuf.ptr.p_double[0] = (double)4*reststp;
                    stpcnt = 1;
                }
                qqpsolver_findbeststepandmove(sstate, &sstate->sas, &sstate->dc, stp, needact, cidx, cval, &sstate->stpbuf, stpcnt, &sstate->activated, &sstate->tmp0, &sstate->tmp1, _state);
                
                /*
                 * Update CG information.
                 */
                ae_v_move(&sstate->dp.ptr.p_double[0], 1, &sstate->dc.ptr.p_double[0], 1, ae_v_len(0,n-1));
                ae_v_move(&sstate->cgp.ptr.p_double[0], 1, &sstate->cgc.ptr.p_double[0], 1, ae_v_len(0,n-1));
                
                /*
                 * Update iterations counter
                 */
                sstate->repinneriterationscount = sstate->repinneriterationscount+1;
            }
            if( *terminationtype!=0 )
            {
                break;
            }
            cgmax = settings->cgmaxits;
            
            /*
             * Generate YIdx - reordering of variables for constrained Newton phase.
             * Free variables come first, fixed are last ones.
             */
            newtcnt = 0;
            for(;;)
            {
                
                /*
                 * Skip iteration if constrained Newton is turned off.
                 */
                if( !settings->cnphase )
                {
                    break;
                }
                
                /*
                 * At the first iteration   - build Cholesky decomposition of Hessian.
                 * At subsequent iterations - refine Hessian by adding new constraints.
                 *
                 * Loop is terminated in following cases:
                 * * Hessian is not positive definite subject to current constraints
                 *   (termination during initial decomposition)
                 * * there were no new constraints being activated
                 *   (termination during update)
                 * * all constraints were activated during last step
                 *   (termination during update)
                 * * CNMaxUpdates were performed on matrix
                 *   (termination during update)
                 */
                if( newtcnt==0 )
                {
                    
                    /*
                     * Perform initial Newton step. If Cholesky decomposition fails,
                     * increase number of CG iterations to CGMaxIts - it should help
                     * us to find set of constraints which will make matrix positive
                     * definite.
                     */
                    b = qqpsolver_cnewtonbuild(sstate, sparsesolver, &sstate->repncholesky, _state);
                    if( b )
                    {
                        cgmax = settings->cgminits;
                    }
                }
                else
                {
                    b = qqpsolver_cnewtonupdate(sstate, settings, &sstate->repncupdates, _state);
                }
                if( !b )
                {
                    break;
                }
                inc(&newtcnt, _state);
                
                /*
                 * Calculate gradient GC.
                 */
                qqpsolver_targetgradient(sstate, &sstate->sas.xc, &sstate->gc, _state);
                
                /*
                 * Bound-constrained Newton step
                 */
                for(i=0; i<=n-1; i++)
                {
                    sstate->dc.ptr.p_double[i] = sstate->gc.ptr.p_double[i];
                }
                if( !qqpsolver_cnewtonstep(sstate, settings, &sstate->dc, _state) )
                {
                    break;
                }
                qqpsolver_quadraticmodel(sstate, &sstate->sas.xc, &sstate->dc, &sstate->gc, &d1, &d1est, &d2, &d2est, &sstate->tmp0, _state);
                if( d1est>=0 )
                {
                    
                    /*
                     * We are close to minimum, derivative is nearly zero, break Newton iteration
                     */
                    break;
                }
                if( d2est>0 )
                {
                    
                    /*
                     * Positive definite matrix, we can perform Newton step
                     */
                    ae_assert(ae_fp_less(d1,(double)(0)), "QQPOptimize: internal error", _state);
                    fullstp = -d1/((double)2*d2);
                    sasexploredirection(&sstate->sas, &sstate->dc, &stpmax, &cidx, &cval, _state);
                    needact = ae_fp_greater_eq(fullstp,stpmax);
                    if( needact )
                    {
                        ae_assert(sstate->stpbuf.cnt>=3, "QQPOptimize: StpBuf overflow", _state);
                        reststp = stpmax;
                        stp = reststp;
                        sstate->stpbuf.ptr.p_double[0] = reststp*(double)4;
                        sstate->stpbuf.ptr.p_double[1] = fullstp;
                        sstate->stpbuf.ptr.p_double[2] = fullstp/(double)4;
                        stpcnt = 3;
                    }
                    else
                    {
                        reststp = fullstp;
                        stp = fullstp;
                        stpcnt = 0;
                    }
                    qqpsolver_findbeststepandmove(sstate, &sstate->sas, &sstate->dc, stp, needact, cidx, cval, &sstate->stpbuf, stpcnt, &sstate->activated, &sstate->tmp0, &sstate->tmp1, _state);
                }
                else
                {
                    
                    /*
                     * Matrix is semi-definite or indefinite, but regularized
                     * Cholesky succeeded and gave us descent direction in DC.
                     *
                     * We will investigate it and try to perform descent step:
                     * * first, we explore direction:
                     *   * if it is unbounded, we stop algorithm with
                     *     appropriate termination code -4.
                     *   * if StpMax=0, we break Newton phase and return to
                     *     CG phase - constraint geometry is complicated near
                     *     current point, so it is better to use simpler algo.
                     * * second, we check that bounded step decreases function;
                     *   if not, we again skip to CG phase
                     * * finally, we use FindBestStep...() function to choose
                     *   between bounded step and projection of full-length step
                     *   (latter may give additional decrease in
                     */
                    sasexploredirection(&sstate->sas, &sstate->dc, &stpmax, &cidx, &cval, _state);
                    if( cidx<0 )
                    {
                        
                        /*
                         * Function is unbounded from below:
                         * * D1<0 (verified by previous block)
                         * * D2Est<=0, which means that either D2<0 - or it can not
                         *   be reliably distinguished from zero.
                         * * step is unconstrained
                         *
                         * If these conditions are true, we abnormally terminate QP
                         * algorithm with return code -4
                         */
                        *terminationtype = -4;
                        break;
                    }
                    if( ae_fp_eq(stpmax,(double)(0)) )
                    {
                        
                        /*
                         * Resort to CG phase.
                         * Increase number of CG iterations.
                         */
                        cgmax = settings->cgmaxits;
                        break;
                    }
                    ae_assert(ae_fp_greater(stpmax,(double)(0)), "QQPOptimize: internal error", _state);
                    f0 = qqpsolver_projectedtargetfunction(sstate, &sstate->sas.xc, &sstate->dc, 0.0, &sstate->tmp0, &sstate->tmp1, _state);
                    f1 = qqpsolver_projectedtargetfunction(sstate, &sstate->sas.xc, &sstate->dc, stpmax, &sstate->tmp0, &sstate->tmp1, _state);
                    if( ae_fp_greater_eq(f1,f0) )
                    {
                        
                        /*
                         * Descent direction does not actually decrease function value.
                         * Resort to CG phase
                         * Increase number of CG iterations.
                         */
                        cgmax = settings->cgmaxits;
                        break;
                    }
                    ae_assert(sstate->stpbuf.cnt>=3, "QQPOptimize: StpBuf overflow", _state);
                    reststp = stpmax;
                    stp = reststp;
                    sstate->stpbuf.ptr.p_double[0] = reststp*(double)4;
                    sstate->stpbuf.ptr.p_double[1] = 1.00;
                    sstate->stpbuf.ptr.p_double[2] = 0.25;
                    stpcnt = 3;
                    qqpsolver_findbeststepandmove(sstate, &sstate->sas, &sstate->dc, stp, ae_true, cidx, cval, &sstate->stpbuf, stpcnt, &sstate->activated, &sstate->tmp0, &sstate->tmp1, _state);
                }
            }
            if( *terminationtype!=0 )
            {
                break;
            }
        }
        sasstopoptimization(&sstate->sas, _state);
        ae_v_move(&sstate->xf.ptr.p_double[0], 1, &sstate->sas.xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    }
    
    /*
     * Stop optimization and unpack results.
     *
     * Add XOriginC to XS and make sure that boundary constraints are
     * both (a) satisfied, (b) preserved. Former means that "shifted"
     * point is feasible, while latter means that point which was exactly
     * at the boundary before shift will be exactly at the boundary
     * after shift.
     */
    for(i=0; i<=n-1; i++)
    {
        xs->ptr.p_double[i] = sc->ptr.p_double[i]*sstate->xf.ptr.p_double[i]+xoriginc->ptr.p_double[i];
        if( sstate->havebndl.ptr.p_bool[i]&&ae_fp_less(xs->ptr.p_double[i],bndlc->ptr.p_double[i]) )
        {
            xs->ptr.p_double[i] = bndlc->ptr.p_double[i];
        }
        if( sstate->havebndu.ptr.p_bool[i]&&ae_fp_greater(xs->ptr.p_double[i],bnduc->ptr.p_double[i]) )
        {
            xs->ptr.p_double[i] = bnduc->ptr.p_double[i];
        }
        if( sstate->havebndl.ptr.p_bool[i]&&ae_fp_eq(sstate->xf.ptr.p_double[i],sstate->bndl.ptr.p_double[i]) )
        {
            xs->ptr.p_double[i] = bndlc->ptr.p_double[i];
        }
        if( sstate->havebndu.ptr.p_bool[i]&&ae_fp_eq(sstate->xf.ptr.p_double[i],sstate->bndu.ptr.p_double[i]) )
        {
            xs->ptr.p_double[i] = bnduc->ptr.p_double[i];
        }
    }
}


/*************************************************************************
Target function at point PROJ(X+Stp*D), where PROJ(.) is a projection into
feasible set.

NOTE: if Stp=0, D is not referenced at all. Thus,  there  is  no  need  to
      fill it by some meaningful values for Stp=0.

This subroutine uses temporary buffers  Tmp0/1,  which  are  automatically
resized if needed.

  -- ALGLIB --
     Copyright 21.12.2013 by Bochkanov Sergey
*************************************************************************/
static double qqpsolver_projectedtargetfunction(const qqpbuffers* sstate,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* d,
     double stp,
     /* Real    */ ae_vector* tmp0,
     /* Real    */ ae_vector* tmp1,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    double v;
    double result;


    n = sstate->n;
    rvectorsetlengthatleast(tmp0, n, _state);
    rvectorsetlengthatleast(tmp1, n, _state);
    
    /*
     * Calculate projected point
     */
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_neq(stp,(double)(0)) )
        {
            v = x->ptr.p_double[i]+stp*d->ptr.p_double[i];
        }
        else
        {
            v = x->ptr.p_double[i];
        }
        if( sstate->havebndl.ptr.p_bool[i]&&ae_fp_less(v,sstate->bndl.ptr.p_double[i]) )
        {
            v = sstate->bndl.ptr.p_double[i];
        }
        if( sstate->havebndu.ptr.p_bool[i]&&ae_fp_greater(v,sstate->bndu.ptr.p_double[i]) )
        {
            v = sstate->bndu.ptr.p_double[i];
        }
        tmp0->ptr.p_double[i] = v;
    }
    
    /*
     * Function value at the Tmp0:
     *
     * f(x) = 0.5*x'*A*x + b'*x
     */
    result = 0.0;
    for(i=0; i<=n-1; i++)
    {
        result = result+sstate->b.ptr.p_double[i]*tmp0->ptr.p_double[i];
    }
    if( sstate->akind==0 )
    {
        
        /*
         * Dense matrix A
         */
        result = result+0.5*rmatrixsyvmv(n, &sstate->densea, 0, 0, ae_true, tmp0, 0, tmp1, _state);
    }
    else
    {
        
        /*
         * sparse matrix A
         */
        ae_assert(sstate->akind==1, "QQPOptimize: unexpected AKind in ProjectedTargetFunction", _state);
        result = result+0.5*sparsevsmv(&sstate->sparsea, sstate->sparseupper, tmp0, _state);
    }
    return result;
}


/*************************************************************************
Gradient of the target function:

    f(x) = 0.5*x'*A*x + b'*x
    
which is equal to

    grad = A*x + b

Here:
* x is array[N]
* A is array[N,N]
* b is array[N]

INPUT PARAMETERS:
    SState  -   structure which stores function terms (not modified)
    X       -   location
    G       -   possibly preallocated buffer

OUTPUT PARAMETERS:
    G       -   array[N], gradient
            
  -- ALGLIB --
     Copyright 21.12.2013 by Bochkanov Sergey
*************************************************************************/
static void qqpsolver_targetgradient(const qqpbuffers* sstate,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{
    ae_int_t n;


    n = sstate->n;
    rvectorsetlengthatleast(g, n, _state);
    if( sstate->akind==0 )
    {
        
        /*
         * Dense matrix A
         */
        rmatrixsymv(n, 1.0, &sstate->densea, 0, 0, ae_true, x, 0, 0.0, g, 0, _state);
    }
    else
    {
        
        /*
         * Sparse matrix A
         */
        ae_assert(sstate->akind==1, "QQPOptimize: unexpected AKind in TargetGradient", _state);
        sparsesmv(&sstate->sparsea, sstate->sparseupper, x, g, _state);
    }
    ae_v_add(&g->ptr.p_double[0], 1, &sstate->b.ptr.p_double[0], 1, ae_v_len(0,n-1));
}


/*************************************************************************
First and second derivatives  of  the  "extended"  target  function  along
specified direction. Target  function  is  called  "extended"  because  of
additional slack variables and has form:

    f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b)
    
with gradient

    grad = A*x + b + penaltyfactor*C'*(C*x-b)
    
Quadratic model has form

    F(x0+alpha*D) = D2*alpha^2 + D1*alpha

INPUT PARAMETERS:
    SState  -   structure which is used to obtain quadratic term of the model
    X       -   current point, array[N]
    D       -   direction across which derivatives are calculated, array[N]
    G       -   gradient at current point (pre-calculated by caller), array[N]

OUTPUT PARAMETERS:
    D1      -   linear coefficient
    D1Est   -   estimate of D1 sign,  accounting  for  possible  numerical
                errors:
                * >0    means "almost surely positive"
                * <0    means "almost surely negative"
                * =0    means "pessimistic estimate  of  numerical  errors
                        in D1 is larger than magnitude of D1 itself; it is
                        impossible to reliably distinguish D1 from zero".
    D2      -   quadratic coefficient
    D2Est   -   estimate of D2 sign,  accounting  for  possible  numerical
                errors:
                * >0    means "almost surely positive"
                * <0    means "almost surely negative"
                * =0    means "pessimistic estimate  of  numerical  errors
                        in D2 is larger than magnitude of D2 itself; it is
                        impossible to reliably distinguish D2 from zero".
            
  -- ALGLIB --
     Copyright 14.05.2014 by Bochkanov Sergey
*************************************************************************/
static void qqpsolver_quadraticmodel(const qqpbuffers* sstate,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* d,
     /* Real    */ const ae_vector* g,
     double* d1,
     ae_int_t* d1est,
     double* d2,
     ae_int_t* d2est,
     /* Real    */ ae_vector* tmp0,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    double v;
    double mx;
    double mb;
    double md;

    *d1 = 0.0;
    *d1est = 0;
    *d2 = 0.0;
    *d2est = 0;

    n = sstate->n;
    
    /*
     * Maximums
     */
    mx = 0.0;
    md = 0.0;
    mb = 0.0;
    for(i=0; i<=n-1; i++)
    {
        mx = ae_maxreal(mx, ae_fabs(x->ptr.p_double[i], _state), _state);
        md = ae_maxreal(md, ae_fabs(d->ptr.p_double[i], _state), _state);
    }
    for(i=0; i<=n-1; i++)
    {
        mb = ae_maxreal(mb, ae_fabs(sstate->b.ptr.p_double[i], _state), _state);
    }
    
    /*
     * D2
     */
    if( sstate->akind==0 )
    {
        
        /*
         * Dense matrix A
         */
        *d2 = 0.5*rmatrixsyvmv(n, &sstate->densea, 0, 0, ae_true, d, 0, tmp0, _state);
    }
    else
    {
        
        /*
         * Sparse matrix A
         */
        ae_assert(sstate->akind==1, "QQPOptimize: unexpected AKind in TargetGradient", _state);
        *d2 = 0.5*sparsevsmv(&sstate->sparsea, sstate->sparseupper, d, _state);
    }
    v = ae_v_dotproduct(&d->ptr.p_double[0], 1, &g->ptr.p_double[0], 1, ae_v_len(0,n-1));
    *d1 = v;
    
    /*
     * Error estimates
     */
    estimateparabolicmodel(sstate->absasum, sstate->absasum2, mx, mb, md, *d1, *d2, d1est, d2est, _state);
}


/*************************************************************************
This function accepts quadratic model of the form

    f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b)
    
and list of possible steps along direction D. It chooses  best  step  (one
which achieves minimum value of the target  function)  and  moves  current
point (given by SAS object) to the new location. Step is  bounded  subject
to boundary constraints.

Candidate steps are divided into two groups:
* "default" step, which is always performed when no candidate steps LONGER
  THAN THE DEFAULT  ONE  is  given.  This  candidate  MUST  reduce  target
  function value; it is  responsibility  of  caller  to   provide  default
  candidate which reduces target function.
* "additional candidates", which may be shorter or longer than the default
  step. Candidates which are shorter that the default  step  are  ignored;
  candidates which are longer than the "default" step are tested.
  
The idea is that we ALWAYS try "default" step, and it is responsibility of
the caller to provide us with something which is worth trying.  This  step
may activate some constraint - that's why we  stopped  at  "default"  step
size. However, we may also try longer steps which may activate  additional
constraints and further reduce function value.

INPUT PARAMETERS:
    SState  -   structure which stores model
    SAS     -   active set structure which stores current point in SAS.XC
    D       -   direction for step
    Stp     -   step length for "default" candidate
    NeedAct -   whether   default  candidate  activates  some  constraint;
                if NeedAct  is True,  constraint  given  by  CIdc/CVal  is
                GUARANTEED to be activated in the final point.
    CIdx    -   if NeedAct is True, stores index of the constraint to activate
    CVal    -   if NeedAct is True, stores constrained value;
                SAS.XC[CIdx] is forced to be equal to CVal.
    AddSteps-   array[AddStepsCnt] of additional steps:
                * AddSteps[]<=Stp are ignored
                * AddSteps[]>Stp are tried
    Activated-  possibly preallocated buffer; previously allocated memory
                will be reused.
    Tmp0/1   -  possibly preallocated buffers; previously allocated memory
                will be reused.
    
OUTPUT PARAMETERS:
    SAS     -   SAS.XC is set to new point;  if  there  was  a  constraint
                specified  by  NeedAct/CIdx/CVal,  it  will  be  activated
                (other constraints may be activated too, but this  one  is
                guaranteed to be active in the final point).
    Activated-  elements of this array are set to True, if I-th constraint
                as inactive at previous point, but become  active  in  the
                new one.
                Situations when we deactivate xi>=0 and activate xi<=1 are
                considered as activation of previously inactive constraint
            
  -- ALGLIB --
     Copyright 14.05.2014 by Bochkanov Sergey
*************************************************************************/
static void qqpsolver_findbeststepandmove(const qqpbuffers* sstate,
     sactiveset* sas,
     /* Real    */ const ae_vector* d,
     double stp,
     ae_bool needact,
     ae_int_t cidx,
     double cval,
     /* Real    */ const ae_vector* addsteps,
     ae_int_t addstepscnt,
     /* Boolean */ ae_vector* activated,
     /* Real    */ ae_vector* tmp0,
     /* Real    */ ae_vector* tmp1,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t k;
    double v;
    double stpbest;
    double fbest;
    double fcand;


    n = sstate->n;
    rvectorsetlengthatleast(tmp0, n, _state);
    bvectorsetlengthatleast(activated, n, _state);
    
    /*
     * Calculate initial step, store to Tmp0
     *
     * NOTE: Tmp0 is guaranteed to be feasible w.r.t. boundary constraints
     */
    for(i=0; i<=n-1; i++)
    {
        v = sas->xc.ptr.p_double[i]+stp*d->ptr.p_double[i];
        if( sstate->havebndl.ptr.p_bool[i]&&ae_fp_less(v,sstate->bndl.ptr.p_double[i]) )
        {
            v = sstate->bndl.ptr.p_double[i];
        }
        if( sstate->havebndu.ptr.p_bool[i]&&ae_fp_greater(v,sstate->bndu.ptr.p_double[i]) )
        {
            v = sstate->bndu.ptr.p_double[i];
        }
        tmp0->ptr.p_double[i] = v;
    }
    if( needact )
    {
        tmp0->ptr.p_double[cidx] = cval;
    }
    
    /*
     * Try additional steps, if AddStepsCnt>0
     */
    if( addstepscnt>0 )
    {
        
        /*
         * Find best step
         */
        stpbest = stp;
        fbest = qqpsolver_projectedtargetfunction(sstate, &sas->xc, d, stpbest, tmp0, tmp1, _state);
        for(k=0; k<=addstepscnt-1; k++)
        {
            if( ae_fp_greater(addsteps->ptr.p_double[k],stp) )
            {
                fcand = qqpsolver_projectedtargetfunction(sstate, &sas->xc, d, addsteps->ptr.p_double[k], tmp0, tmp1, _state);
                if( ae_fp_less(fcand,fbest) )
                {
                    fbest = fcand;
                    stpbest = addsteps->ptr.p_double[k];
                }
            }
        }
        
        /*
         * Prepare best step
         *
         * NOTE: because only AddSteps[]>Stp were checked,
         *       this step will activate constraint CIdx.
         */
        for(i=0; i<=n-1; i++)
        {
            v = sas->xc.ptr.p_double[i]+stpbest*d->ptr.p_double[i];
            if( sstate->havebndl.ptr.p_bool[i]&&ae_fp_less(v,sstate->bndl.ptr.p_double[i]) )
            {
                v = sstate->bndl.ptr.p_double[i];
            }
            if( sstate->havebndu.ptr.p_bool[i]&&ae_fp_greater(v,sstate->bndu.ptr.p_double[i]) )
            {
                v = sstate->bndu.ptr.p_double[i];
            }
            tmp0->ptr.p_double[i] = v;
        }
        if( needact )
        {
            tmp0->ptr.p_double[cidx] = cval;
        }
    }
    
    /*
     * Fill Activated array by information about activated constraints.
     * Perform step
     */
    for(i=0; i<=n-1; i++)
    {
        activated->ptr.p_bool[i] = ae_false;
        v = tmp0->ptr.p_double[i];
        if( ae_fp_eq(v,sas->xc.ptr.p_double[i]) )
        {
            continue;
        }
        if( sstate->havebndl.ptr.p_bool[i]&&ae_fp_eq(v,sstate->bndl.ptr.p_double[i]) )
        {
            activated->ptr.p_bool[i] = ae_true;
        }
        if( sstate->havebndu.ptr.p_bool[i]&&ae_fp_eq(v,sstate->bndu.ptr.p_double[i]) )
        {
            activated->ptr.p_bool[i] = ae_true;
        }
    }
    sasmoveto(sas, tmp0, needact, cidx, cval, _state);
}


/*************************************************************************
This function prepares data for  constrained  Newton  step  for  penalized
quadratic model of the form

    f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b)
    
where A can be dense or sparse, and model is considered subject to equality
constraints specified by SState.SAS.XC  object.  Constraint  is considered
active if XC[i] is exactly BndL[i] or BndU[i],  i.e.  we  ignore  internal
list of constraints monitored by SAS object. Our own  set  of  constraints
includes all  constraints  stored  by  SAS,  but  also  may  include  some
constraints which are inactive in SAS.

"Preparation" means that Cholesky decomposition of  the  effective  system
matrix is performed, and we can  perform  constrained  Newton  step.

This function works as black box. It uses fields of SState which are marked
as "Variables for constrained Newton phase", and only  this  function  and
its friends know about these variables. Everyone else should use:
* CNewtonBuild() to prepare initial Cholesky decomposition for step
* CNewtonStep() to perform constrained Newton step
* CNewtonUpdate() to update Cholesky matrix  after  point  was  moved  and
  constraints were updated. In some cases it  is  possible to  efficiently
  re-calculate Cholesky decomposition if you know which  constraints  were
  activated. If efficient  re-calculation  is  impossible,  this  function
  returns False.

INPUT PARAMETERS:
    SState  -   structure which stores model and temporaries for CN phase;
                in particular, SAS.XC stores current point.
    SparseSolver-which sparse solver to use for sparse model; ignored  for
                dense QP. Can be:
                * 2 -   SKS-based Cholesky
    NCholesky-  counter which is incremented after Cholesky (successful or
                failed one)
    
OUTPUT PARAMETERS:
    NCholesky-  possibly updated counter
    
RESULT:
    True, if Cholesky decomposition was successfully performed.
    False, if a) matrix was semi-definite or indefinite, or b)  particular
    combination of matrix type (sparse) and constraints  (general  linear)
    is not supported.
    
NOTE: this function may routinely return False, for indefinite matrices or
      for sparse problems with general linear constraints. You  should  be
      able to handle such situations.
            
  -- ALGLIB --
     Copyright 14.05.2014 by Bochkanov Sergey
*************************************************************************/
static ae_bool qqpsolver_cnewtonbuild(qqpbuffers* sstate,
     ae_int_t sparsesolver,
     ae_int_t* ncholesky,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    ae_bool b;
    ae_int_t ridx0;
    ae_int_t ridx1;
    ae_int_t nfree;
    ae_bool result;


    result = ae_false;
    
    /*
     * Fetch often used fields
     */
    n = sstate->n;
    
    /*
     * 1. Set CNModelAge to zero
     * 2. Generate YIdx - reordering of variables such that free variables
     *    come first and are ordered by ascending, fixed are last ones and
     *    have no particular ordering.
     *
     * This step is same for dense and sparse problems.
     */
    sstate->cnmodelage = 0;
    ivectorsetlengthatleast(&sstate->yidx, n, _state);
    ridx0 = 0;
    ridx1 = n-1;
    for(i=0; i<=n-1; i++)
    {
        sstate->yidx.ptr.p_int[i] = -1;
    }
    for(i=0; i<=n-1; i++)
    {
        ae_assert(!sstate->havebndl.ptr.p_bool[i]||ae_fp_greater_eq(sstate->sas.xc.ptr.p_double[i],sstate->bndl.ptr.p_double[i]), "CNewtonBuild: internal error", _state);
        ae_assert(!sstate->havebndu.ptr.p_bool[i]||ae_fp_less_eq(sstate->sas.xc.ptr.p_double[i],sstate->bndu.ptr.p_double[i]), "CNewtonBuild: internal error", _state);
        b = ae_false;
        b = b||(sstate->havebndl.ptr.p_bool[i]&&ae_fp_eq(sstate->sas.xc.ptr.p_double[i],sstate->bndl.ptr.p_double[i]));
        b = b||(sstate->havebndu.ptr.p_bool[i]&&ae_fp_eq(sstate->sas.xc.ptr.p_double[i],sstate->bndu.ptr.p_double[i]));
        if( b )
        {
            sstate->yidx.ptr.p_int[ridx1] = i;
            ridx1 = ridx1-1;
        }
        else
        {
            sstate->yidx.ptr.p_int[ridx0] = i;
            ridx0 = ridx0+1;
        }
    }
    ae_assert(ridx0==ridx1+1, "CNewtonBuild: internal error", _state);
    nfree = ridx0;
    sstate->nfree = nfree;
    if( nfree==0 )
    {
        return result;
    }
    
    /*
     * Constrained Newton matrix: dense version
     */
    if( sstate->akind==0 )
    {
        rmatrixsetlengthatleast(&sstate->densez, n, n, _state);
        rvectorsetlengthatleast(&sstate->tmpcn, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=i; j<=n-1; j++)
            {
                sstate->densez.ptr.pp_double[i][j] = sstate->densea.ptr.pp_double[i][j];
            }
        }
        for(i=1; i<=nfree-1; i++)
        {
            ae_assert(sstate->yidx.ptr.p_int[i]>sstate->yidx.ptr.p_int[i-1], "CNewtonBuild: integrity check failed", _state);
        }
        for(i=0; i<=nfree-1; i++)
        {
            k = sstate->yidx.ptr.p_int[i];
            for(j=i; j<=nfree-1; j++)
            {
                sstate->densez.ptr.pp_double[i][j] = sstate->densez.ptr.pp_double[k][sstate->yidx.ptr.p_int[j]];
            }
        }
        rvectorsetlengthatleast(&sstate->regdiag, n, _state);
        for(i=0; i<=nfree-1; i++)
        {
            v = 0.0;
            for(j=0; j<=i-1; j++)
            {
                v = v+ae_fabs(sstate->densez.ptr.pp_double[j][i], _state);
            }
            for(j=i; j<=nfree-1; j++)
            {
                v = v+ae_fabs(sstate->densez.ptr.pp_double[i][j], _state);
            }
            if( ae_fp_eq(v,(double)(0)) )
            {
                v = 1.0;
            }
            sstate->regdiag.ptr.p_double[i] = qqpsolver_regz*v;
        }
        for(i=0; i<=nfree-1; i++)
        {
            sstate->densez.ptr.pp_double[i][i] = sstate->densez.ptr.pp_double[i][i]+sstate->regdiag.ptr.p_double[i];
        }
        inc(ncholesky, _state);
        if( !spdmatrixcholeskyrec(&sstate->densez, 0, nfree, ae_true, &sstate->tmpcn, _state) )
        {
            return result;
        }
        for(i=nfree-1; i>=0; i--)
        {
            ae_v_move(&sstate->tmpcn.ptr.p_double[i], 1, &sstate->densez.ptr.pp_double[i][i], 1, ae_v_len(i,nfree-1));
            k = sstate->yidx.ptr.p_int[i];
            for(j=k; j<=n-1; j++)
            {
                sstate->densez.ptr.pp_double[k][j] = (double)(0);
            }
            for(j=i; j<=nfree-1; j++)
            {
                sstate->densez.ptr.pp_double[k][sstate->yidx.ptr.p_int[j]] = sstate->tmpcn.ptr.p_double[j];
            }
        }
        for(i=nfree; i<=n-1; i++)
        {
            k = sstate->yidx.ptr.p_int[i];
            sstate->densez.ptr.pp_double[k][k] = 1.0;
            for(j=k+1; j<=n-1; j++)
            {
                sstate->densez.ptr.pp_double[k][j] = (double)(0);
            }
        }
        result = ae_true;
        return result;
    }
    
    /*
     * Constrained Newton matrix: sparse version
     */
    if( sstate->akind==1 )
    {
        ae_assert(sparsesolver==2, "CNewtonBuild: internal error", _state);
        
        /*
         * Copy sparse A to Z and fill rows/columns corresponding to active
         * constraints by zeros. Diagonal elements corresponding to active
         * constraints are filled by unit values.
         */
        sparsecopytosksbuf(&sstate->sparsea, &sstate->sparsecca, _state);
        rvectorsetlengthatleast(&sstate->tmpcn, n, _state);
        for(i=0; i<=n-1; i++)
        {
            sstate->tmpcn.ptr.p_double[i] = (double)(0);
        }
        for(i=nfree; i<=n-1; i++)
        {
            sstate->tmpcn.ptr.p_double[sstate->yidx.ptr.p_int[i]] = (double)(1);
        }
        for(i=0; i<=n-1; i++)
        {
            k = sstate->sparsecca.ridx.ptr.p_int[i];
            for(j=i-sstate->sparsecca.didx.ptr.p_int[i]; j<=i; j++)
            {
                if( ae_fp_neq(sstate->tmpcn.ptr.p_double[i],(double)(0))||ae_fp_neq(sstate->tmpcn.ptr.p_double[j],(double)(0)) )
                {
                    
                    /*
                     * I-th or J-th variable is in active set (constrained)
                     */
                    if( i==j )
                    {
                        sstate->sparsecca.vals.ptr.p_double[k] = 1.0;
                    }
                    else
                    {
                        sstate->sparsecca.vals.ptr.p_double[k] = 0.0;
                    }
                }
                k = k+1;
            }
        }
        
        /*
         * Perform sparse Cholesky
         */
        inc(ncholesky, _state);
        if( !sparsecholeskyskyline(&sstate->sparsecca, n, sstate->sparseupper, _state) )
        {
            return result;
        }
        result = ae_true;
        return result;
    }
    
    /*
     * Unexpected :)
     */
    ae_assert(ae_false, "CNewtonBuild: internal error", _state);
    return result;
}


/*************************************************************************
This   function  updates  equality-constrained   Cholesky   matrix   after
activation of the  new  equality  constraints.  Matrix  being  updated  is
quadratic term of the function below

    f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b)
    
where A can be dense or sparse.

This  function  uses  YIdx[]  array  (set by CNewtonBuild()  function)  to
distinguish between active and inactive constraints.

This function works as black box. It uses fields of SState which are marked
as "Variables for constrained Newton phase", and only  this  function  and
its friends know about these variables. Everyone else should use:
* CNewtonBuild() to prepare initial Cholesky decomposition for step
* CNewtonStep() to perform constrained Newton step
* CNewtonUpdate() to update Cholesky matrix  after  point  was  moved  and
  constraints were updated. In some cases it  is  possible to  efficiently
  re-calculate Cholesky decomposition if you know which  constraints  were
  activated. If efficient  re-calculation  is  impossible,  this  function
  returns False.

INPUT PARAMETERS:
    SState  -   structure which stores model and temporaries for CN phase;
                in particular, SAS.XC stores current point.
    Settings -  QQPSettings object which was  initialized  by  appropriate
                construction function.
    NCUpdates-  counter which is incremented after each update (one update
                means one variable being fixed)
    
OUTPUT PARAMETERS:
    NCUpdates-  possibly updated counter
    
RESULT:
    True, if Cholesky decomposition was successfully performed.
    False, if a) model age was too high, or b) particular  combination  of
    matrix type (sparse) and constraints (general linear) is not supported
    
NOTE: this function may routinely return False.
      You should be able to handle such situations.
            
  -- ALGLIB --
     Copyright 14.05.2014 by Bochkanov Sergey
*************************************************************************/
static ae_bool qqpsolver_cnewtonupdate(qqpbuffers* sstate,
     const qqpsettings* settings,
     ae_int_t* ncupdates,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nfree;
    ae_int_t ntofix;
    ae_bool b;
    ae_int_t ridx0;
    ae_int_t ridx1;
    ae_int_t i;
    ae_int_t k;
    ae_bool result;


    result = ae_false;
    
    /*
     * Cholesky updates for sparse problems are not supported
     */
    if( sstate->akind==1 )
    {
        return result;
    }
    
    /*
     * Fetch often used fields
     */
    n = sstate->n;
    nfree = sstate->nfree;
    
    /*
     * Determine variables to fix and move them to YIdx[NFree-NToFix:NFree-1]
     * Exit if CNModelAge increased too much.
     */
    ivectorsetlengthatleast(&sstate->tmpcni, n, _state);
    ridx0 = 0;
    ridx1 = nfree-1;
    for(i=0; i<=nfree-1; i++)
    {
        sstate->tmpcni.ptr.p_int[i] = -1;
    }
    for(k=0; k<=nfree-1; k++)
    {
        i = sstate->yidx.ptr.p_int[k];
        ae_assert(!sstate->havebndl.ptr.p_bool[i]||ae_fp_greater_eq(sstate->sas.xc.ptr.p_double[i],sstate->bndl.ptr.p_double[i]), "CNewtonUpdate: internal error", _state);
        ae_assert(!sstate->havebndu.ptr.p_bool[i]||ae_fp_less_eq(sstate->sas.xc.ptr.p_double[i],sstate->bndu.ptr.p_double[i]), "CNewtonUpdate: internal error", _state);
        b = ae_false;
        b = b||(sstate->havebndl.ptr.p_bool[i]&&ae_fp_eq(sstate->sas.xc.ptr.p_double[i],sstate->bndl.ptr.p_double[i]));
        b = b||(sstate->havebndu.ptr.p_bool[i]&&ae_fp_eq(sstate->sas.xc.ptr.p_double[i],sstate->bndu.ptr.p_double[i]));
        if( b )
        {
            sstate->tmpcni.ptr.p_int[ridx1] = i;
            ridx1 = ridx1-1;
        }
        else
        {
            sstate->tmpcni.ptr.p_int[ridx0] = i;
            ridx0 = ridx0+1;
        }
    }
    ae_assert(ridx0==ridx1+1, "CNewtonUpdate: internal error", _state);
    ntofix = nfree-ridx0;
    if( ntofix==0||ntofix==nfree )
    {
        return result;
    }
    if( sstate->cnmodelage+ntofix>settings->cnmaxupdates )
    {
        return result;
    }
    for(i=0; i<=nfree-1; i++)
    {
        sstate->yidx.ptr.p_int[i] = sstate->tmpcni.ptr.p_int[i];
    }
    
    /*
     * Constrained Newton matrix: dense version.
     */
    if( sstate->akind==0 )
    {
        
        /*
         * Update Cholesky matrix with SPDMatrixCholeskyUpdateFixBuf()
         */
        bvectorsetlengthatleast(&sstate->tmpcnb, n, _state);
        for(i=0; i<=n-1; i++)
        {
            sstate->tmpcnb.ptr.p_bool[i] = ae_false;
        }
        for(i=nfree-ntofix; i<=nfree-1; i++)
        {
            sstate->tmpcnb.ptr.p_bool[sstate->yidx.ptr.p_int[i]] = ae_true;
        }
        spdmatrixcholeskyupdatefixbuf(&sstate->densez, n, ae_true, &sstate->tmpcnb, &sstate->tmpcn, _state);
        
        /*
         * Update information stored in State and exit
         */
        sstate->nfree = nfree-ntofix;
        sstate->cnmodelage = sstate->cnmodelage+ntofix;
        *ncupdates = *ncupdates+ntofix;
        result = ae_true;
        return result;
    }
    
    /*
     * Unexpected :)
     */
    ae_assert(ae_false, "CNewtonUpdate: internal error", _state);
    return result;
}


/*************************************************************************
This   function prepares equality-constrained Newton step using previously
calculated constrained Cholesky matrix of the problem

    f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b)
    
where A can be dense or sparse.

As  input,  this  function  accepts  gradient  at the current location. As
output, it returns step vector (replaces gradient).

This function works as black box. It uses fields of SState which are marked
as "Variables for constrained Newton phase", and only  this  function  and
its friends know about these variables. Everyone else should use:
* CNewtonBuild() to prepare initial Cholesky decomposition for step
* CNewtonStep() to perform constrained Newton step
* CNewtonUpdate() to update Cholesky matrix  after  point  was  moved  and
  constraints were updated. In some cases it  is  possible to  efficiently
  re-calculate Cholesky decomposition if you know which  constraints  were
  activated. If efficient  re-calculation  is  impossible,  this  function
  returns False.

INPUT PARAMETERS:
    SState  -   structure which stores model and temporaries for CN phase;
                in particular, SAS.XC stores current point.
    Settings -  QQPSettings object which was  initialized  by  appropriate
                construction function.
    GC       -  array[N], gradient of the target function
    
OUTPUT PARAMETERS:
    GC       -  array[N], step vector (on success)
    
RESULT:
    True, if step was successfully calculated.
    False, if step calculation failed:
    a) gradient was exactly zero,
    b) gradient norm was smaller than EpsG (stopping condition)
    c) all variables were equality-constrained
    
NOTE: this function may routinely return False.
      You should be able to handle such situations.
            
  -- ALGLIB --
     Copyright 14.05.2014 by Bochkanov Sergey
*************************************************************************/
static ae_bool qqpsolver_cnewtonstep(qqpbuffers* sstate,
     const qqpsettings* settings,
     /* Real    */ ae_vector* gc,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t nfree;
    double v;
    ae_bool result;


    result = ae_false;
    n = sstate->n;
    nfree = sstate->nfree;
    for(i=nfree; i<=n-1; i++)
    {
        gc->ptr.p_double[sstate->yidx.ptr.p_int[i]] = 0.0;
    }
    v = ae_v_dotproduct(&gc->ptr.p_double[0], 1, &gc->ptr.p_double[0], 1, ae_v_len(0,n-1));
    if( ae_fp_less_eq(ae_sqrt(v, _state),settings->epsg) )
    {
        return result;
    }
    for(i=0; i<=n-1; i++)
    {
        gc->ptr.p_double[i] = -gc->ptr.p_double[i];
    }
    if( sstate->akind==0 )
    {
        
        /*
         * Dense Newton step.
         * Use straightforward Cholesky solver.
         */
        fblscholeskysolve(&sstate->densez, 1.0, n, ae_true, gc, &sstate->tmpcn, _state);
        result = ae_true;
        return result;
    }
    if( sstate->akind==1 )
    {
        
        /*
         * Sparse Newton step.
         *
         * We have T*T' = L*L' = U'*U (depending on specific triangle stored in SparseCCA).
         */
        if( sstate->sparseupper )
        {
            sparsetrsv(&sstate->sparsecca, sstate->sparseupper, ae_false, 1, gc, _state);
            sparsetrsv(&sstate->sparsecca, sstate->sparseupper, ae_false, 0, gc, _state);
        }
        else
        {
            sparsetrsv(&sstate->sparsecca, sstate->sparseupper, ae_false, 0, gc, _state);
            sparsetrsv(&sstate->sparsecca, sstate->sparseupper, ae_false, 1, gc, _state);
        }
        result = ae_true;
        return result;
    }
    ae_assert(ae_false, "CNewtonStep: internal error", _state);
    return result;
}


void _qqpsettings_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    qqpsettings *p = (qqpsettings*)_p;
    ae_touch_ptr((void*)p);
}


void _qqpsettings_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    qqpsettings       *dst = (qqpsettings*)_dst;
    const qqpsettings *src = (const qqpsettings*)_src;
    dst->epsg = src->epsg;
    dst->epsf = src->epsf;
    dst->epsx = src->epsx;
    dst->maxouterits = src->maxouterits;
    dst->cgphase = src->cgphase;
    dst->cnphase = src->cnphase;
    dst->cgminits = src->cgminits;
    dst->cgmaxits = src->cgmaxits;
    dst->cnmaxupdates = src->cnmaxupdates;
    dst->sparsesolver = src->sparsesolver;
}


void _qqpsettings_clear(void* _p)
{
    qqpsettings *p = (qqpsettings*)_p;
    ae_touch_ptr((void*)p);
}


void _qqpsettings_destroy(void* _p)
{
    qqpsettings *p = (qqpsettings*)_p;
    ae_touch_ptr((void*)p);
}


void _qqpbuffers_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    qqpbuffers *p = (qqpbuffers*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_init(&p->densea, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparsea, _state, make_automatic);
    ae_vector_init(&p->b, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->havebndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->havebndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->xs, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cgc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cgp, 0, DT_REAL, _state, make_automatic);
    _sactiveset_init(&p->sas, _state, make_automatic);
    ae_vector_init(&p->activated, 0, DT_BOOL, _state, make_automatic);
    ae_matrix_init(&p->densez, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparsecca, _state, make_automatic);
    ae_vector_init(&p->yidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->regdiag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->regx0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpcn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpcni, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->tmpcnb, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->stpbuf, 0, DT_REAL, _state, make_automatic);
    _sparsebuffers_init(&p->sbuf, _state, make_automatic);
}


void _qqpbuffers_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    qqpbuffers       *dst = (qqpbuffers*)_dst;
    const qqpbuffers *src = (const qqpbuffers*)_src;
    dst->n = src->n;
    dst->akind = src->akind;
    ae_matrix_init_copy(&dst->densea, &src->densea, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsea, &src->sparsea, _state, make_automatic);
    dst->sparseupper = src->sparseupper;
    dst->absamax = src->absamax;
    dst->absasum = src->absasum;
    dst->absasum2 = src->absasum2;
    ae_vector_init_copy(&dst->b, &src->b, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    ae_vector_init_copy(&dst->havebndl, &src->havebndl, _state, make_automatic);
    ae_vector_init_copy(&dst->havebndu, &src->havebndu, _state, make_automatic);
    ae_vector_init_copy(&dst->xs, &src->xs, _state, make_automatic);
    ae_vector_init_copy(&dst->xf, &src->xf, _state, make_automatic);
    ae_vector_init_copy(&dst->gc, &src->gc, _state, make_automatic);
    ae_vector_init_copy(&dst->xp, &src->xp, _state, make_automatic);
    ae_vector_init_copy(&dst->dc, &src->dc, _state, make_automatic);
    ae_vector_init_copy(&dst->dp, &src->dp, _state, make_automatic);
    ae_vector_init_copy(&dst->cgc, &src->cgc, _state, make_automatic);
    ae_vector_init_copy(&dst->cgp, &src->cgp, _state, make_automatic);
    _sactiveset_init_copy(&dst->sas, &src->sas, _state, make_automatic);
    ae_vector_init_copy(&dst->activated, &src->activated, _state, make_automatic);
    dst->nfree = src->nfree;
    dst->cnmodelage = src->cnmodelage;
    ae_matrix_init_copy(&dst->densez, &src->densez, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsecca, &src->sparsecca, _state, make_automatic);
    ae_vector_init_copy(&dst->yidx, &src->yidx, _state, make_automatic);
    ae_vector_init_copy(&dst->regdiag, &src->regdiag, _state, make_automatic);
    ae_vector_init_copy(&dst->regx0, &src->regx0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpcn, &src->tmpcn, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpcni, &src->tmpcni, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpcnb, &src->tmpcnb, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->stpbuf, &src->stpbuf, _state, make_automatic);
    _sparsebuffers_init_copy(&dst->sbuf, &src->sbuf, _state, make_automatic);
    dst->repinneriterationscount = src->repinneriterationscount;
    dst->repouteriterationscount = src->repouteriterationscount;
    dst->repncholesky = src->repncholesky;
    dst->repncupdates = src->repncupdates;
}


void _qqpbuffers_clear(void* _p)
{
    qqpbuffers *p = (qqpbuffers*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_clear(&p->densea);
    _sparsematrix_clear(&p->sparsea);
    ae_vector_clear(&p->b);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->havebndl);
    ae_vector_clear(&p->havebndu);
    ae_vector_clear(&p->xs);
    ae_vector_clear(&p->xf);
    ae_vector_clear(&p->gc);
    ae_vector_clear(&p->xp);
    ae_vector_clear(&p->dc);
    ae_vector_clear(&p->dp);
    ae_vector_clear(&p->cgc);
    ae_vector_clear(&p->cgp);
    _sactiveset_clear(&p->sas);
    ae_vector_clear(&p->activated);
    ae_matrix_clear(&p->densez);
    _sparsematrix_clear(&p->sparsecca);
    ae_vector_clear(&p->yidx);
    ae_vector_clear(&p->regdiag);
    ae_vector_clear(&p->regx0);
    ae_vector_clear(&p->tmpcn);
    ae_vector_clear(&p->tmpcni);
    ae_vector_clear(&p->tmpcnb);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmp1);
    ae_vector_clear(&p->stpbuf);
    _sparsebuffers_clear(&p->sbuf);
}


void _qqpbuffers_destroy(void* _p)
{
    qqpbuffers *p = (qqpbuffers*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_destroy(&p->densea);
    _sparsematrix_destroy(&p->sparsea);
    ae_vector_destroy(&p->b);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->havebndl);
    ae_vector_destroy(&p->havebndu);
    ae_vector_destroy(&p->xs);
    ae_vector_destroy(&p->xf);
    ae_vector_destroy(&p->gc);
    ae_vector_destroy(&p->xp);
    ae_vector_destroy(&p->dc);
    ae_vector_destroy(&p->dp);
    ae_vector_destroy(&p->cgc);
    ae_vector_destroy(&p->cgp);
    _sactiveset_destroy(&p->sas);
    ae_vector_destroy(&p->activated);
    ae_matrix_destroy(&p->densez);
    _sparsematrix_destroy(&p->sparsecca);
    ae_vector_destroy(&p->yidx);
    ae_vector_destroy(&p->regdiag);
    ae_vector_destroy(&p->regx0);
    ae_vector_destroy(&p->tmpcn);
    ae_vector_destroy(&p->tmpcni);
    ae_vector_destroy(&p->tmpcnb);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmp1);
    ae_vector_destroy(&p->stpbuf);
    _sparsebuffers_destroy(&p->sbuf);
}


#endif
#if defined(AE_COMPILE_QPDENSEAULSOLVER) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
This function initializes QPDENSEAULSettings structure with default settings.

Newly created structure MUST be initialized by default settings  -  or  by
copy of the already initialized structure.

  -- ALGLIB --
     Copyright 14.05.2011 by Bochkanov Sergey
*************************************************************************/
void qpdenseaulloaddefaults(ae_int_t nmain,
     qpdenseaulsettings* s,
     ae_state *_state)
{


    s->epsx = 1.0E-6;
    s->outerits = 5;
    s->rho = 100.0;
}


/*************************************************************************
This function runs Dense-AUL solver; it returns after optimization process
was completed. Following QP problem is solved:

    min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin))
    
subject to combination of box and general linear dense/sparse constraints.

INPUT PARAMETERS:
    DenseA      -   for dense problems (AKind=0), A-term of CQM object
                    contains system matrix. Other terms are unspecified
                    and should not be referenced.
    SparseA     -   for sparse problems (AKind=1), CRS format
    AKind       -   sparse matrix format:
                    * 0 for dense matrix
                    * 1 for sparse matrix
    SparseUpper -   which triangle of SparseAC stores matrix  -  upper  or
                    lower one (for dense matrices this  parameter  is  not
                    actual).
    B           -   linear term, array[N]
    BndL        -   lower bound, array[N]
    BndU        -   upper bound, array[N]
    S           -   scale vector, array[NC]:
                    * I-th element contains scale of I-th variable,
                    * SC[I]>0
    XOrigin     -   origin term, array[NC]. Can be zero.
    N           -   number of variables in the  original  formulation  (no
                    slack variables).
    CLEIC       -   dense linear equality/inequality constraints. Equality
                    constraints come first.
    NEC, NIC    -   number of dense equality/inequality constraints.
    SCLEIC      -   sparse linear equality/inequality constraints. Equality
                    constraints come first.
    SNEC, SNIC  -   number of sparse equality/inequality constraints.
    RenormLC    -   whether constraints should be renormalized (recommended)
                    or used "as is".
    Settings    -   QPDENSEAULSettings object initialized by one of the initialization
                    functions.
    State       -   object which stores temporaries
    XS          -   initial point, array[NC]
    
    
OUTPUT PARAMETERS:
    XS          -   last point
    TerminationType-termination type:
                    *
                    *
                    *

  -- ALGLIB --
     Copyright 2017 by Bochkanov Sergey
*************************************************************************/
void qpdenseauloptimize(const convexquadraticmodel* a,
     const sparsematrix* sparsea,
     ae_int_t akind,
     ae_bool sparseaupper,
     /* Real    */ const ae_vector* b,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t nn,
     /* Real    */ const ae_matrix* cleic,
     ae_int_t dnec,
     ae_int_t dnic,
     const sparsematrix* scleic,
     ae_int_t snec,
     ae_int_t snic,
     ae_bool renormlc,
     const qpdenseaulsettings* settings,
     qpdenseaulbuffers* state,
     /* Real    */ ae_vector* xs,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* laglc,
     ae_int_t* terminationtype,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double vv;
    double rho;
    double epsx;
    ae_int_t outeridx;
    ae_int_t nmain;
    ae_int_t nslack;
    ae_int_t ntotal;
    ae_int_t ktotal;
    double maxrho;
    double feaserr;
    double feaserrprev;
    double requestedfeasdecrease;
    ae_int_t goodcounter;
    ae_int_t stagnationcounter;
    ae_int_t nectotal;
    ae_int_t nictotal;
    ae_int_t nicwork;
    ae_int_t kwork;
    ae_int_t nwork;
    ae_bool allowwseviction;
    ae_bool workingsetextended;
    double targetscale;

    *terminationtype = 0;

    nmain = nn;
    nslack = dnic+snic;
    ntotal = nmain+nslack;
    nectotal = dnec+snec;
    nictotal = dnic+snic;
    ktotal = dnec+dnic+snec+snic;
    rho = settings->rho;
    epsx = settings->epsx;
    requestedfeasdecrease = 0.33;
    maxrho = 1.0E12;
    if( ae_fp_less_eq(epsx,(double)(0)) )
    {
        epsx = 1.0E-9;
    }
    
    /*
     * Integrity checks
     */
    if( snec+snic>0 )
    {
        ae_assert(scleic->matrixtype==1, "QPDENSEAULOptimize: unexpected sparse matrix format", _state);
        ae_assert(scleic->m==snec+snic, "QPDENSEAULOptimize: unexpected sparse matrix size", _state);
        ae_assert(scleic->n==nmain+1, "QPDENSEAULOptimize: unexpected sparse matrix size", _state);
    }
    
    /*
     * Prepare
     */
    state->repinneriterationscount = 0;
    state->repouteriterationscount = 0;
    state->repncholesky = 0;
    state->repnmv = 0;
    state->repnwrkchanges = 0;
    state->repnwrk0 = 0;
    state->repnwrk1 = 0;
    state->repnwrkf = 0;
    *terminationtype = 0;
    ivectorsetlengthatleast(&state->cidx, ktotal, _state);
    rvectorsetlengthatleast(&state->nulc, ktotal, _state);
    rvectorsetlengthatleast(&state->nulcest, ktotal, _state);
    rvectorsetlengthatleast(&state->exb, ntotal, _state);
    rvectorsetlengthatleast(&state->exxc, ntotal, _state);
    rvectorsetlengthatleast(&state->exxorigin, ntotal, _state);
    rvectorsetlengthatleast(&state->exbndl, ntotal, _state);
    rvectorsetlengthatleast(&state->exbndu, ntotal, _state);
    rvectorsetlengthatleast(&state->exscale, ntotal, _state);
    rvectorsetlengthatleast(&state->tmp0, ntotal, _state);
    rvectorsetlengthatleast(&state->nicerr, nictotal, _state);
    ivectorsetlengthatleast(&state->nicnact, nictotal, _state);
    
    /*
     * Allocate Lagrange multipliers, fill by default values (zeros)
     */
    rvectorsetlengthatleast(lagbc, nmain, _state);
    rvectorsetlengthatleast(laglc, ktotal, _state);
    for(i=0; i<=nmain-1; i++)
    {
        lagbc->ptr.p_double[i] = 0.0;
    }
    for(i=0; i<=ktotal-1; i++)
    {
        laglc->ptr.p_double[i] = 0.0;
    }
    
    /*
     * Prepare scaled/shifted model in dense format - input parameters
     * are converted and stored in State.SclSftA/B/HasBndL/HasBndU/BndL/BndU/CLEIC/XC/CScales
     */
    qpdenseaulsolver_scaleshiftoriginalproblem(a, sparsea, akind, sparseaupper, b, bndl, bndu, s, xorigin, nmain, cleic, dnec, dnic, scleic, snec, snic, renormlc, state, xs, _state);
    
    /*
     * Normalize model in such way that norm(A)~1 (very roughly)
     *
     * We have two lower bounds for sigma_max(A):
     * * first estimate is provided by Frobenius norm, it is equal to ANorm/NMain
     * * second estimate is provided by max(CAC)
     *
     * We select largest one of these estimates, because using just one
     * of them is prone to different failure modes. Then, we divide A and B
     * by this estimate.
     */
    targetscale = qpdenseaulsolver_normalizequadraticterm(&state->sclsfta, &state->sclsftb, nmain, &state->sclsftcleic, nectotal, nictotal, renormlc, &state->tmp2, _state);
    
    /*
     * Select working set of inequality constraints.
     *
     * Although it is possible to process all inequality constraints
     * at once, in one large batch, some QP problems have NIC>>N constraints,
     * but only minor fraction of them is inactive in the solution.
     *
     * Because algorithm running time is O((N+NEC+NIC)^3), we can
     * save a lot of time if we process only those inequality constraints
     * which need activation. Generally, NEC<N, and only O(N) inequality
     * constraints are active in the solution.
     *
     * We can do so by solving problem first without general inequality
     * constraints at all (box and general equality constraints are added),
     * and by iteratively adding more and more inequality constraints in
     * order to get feasible solution. Such set of inequality constraints
     * is called "working set".
     *
     * NOTE: such approach works reliably only for convex QP problems; non-convex
     *       QP problem can be unbounded when some constraints are dropped.
     *
     * NOTE: we can also remove some constraints from working set, but eviction
     *       can be performed only limited amount of times (at most once); if
     *       constraint is added to working set second time, it is never removed.
     *
     * NOTE: we do not perform constraint eviction on non-convex problems
     */
    qpdenseaulsolver_selectinitialworkingset(&state->sclsfta, nmain, &state->sclsftcleic, nectotal, nictotal, &state->tmp0, &state->tmp2, &nicwork, &allowwseviction, _state);
    kwork = nectotal+nicwork;
    nwork = nmain+nicwork;
    state->repnwrk0 = nicwork;
    for(i=0; i<=nicwork-1; i++)
    {
        state->nicnact.ptr.p_int[i] = 1;
    }
    for(i=nicwork; i<=nictotal-1; i++)
    {
        state->nicnact.ptr.p_int[i] = 0;
    }
    for(i=0; i<=ktotal-1; i++)
    {
        state->cidx.ptr.p_int[i] = i;
    }
    
    /*
     * Perform outer iteration
     */
    for(i=0; i<=ktotal-1; i++)
    {
        state->nulc.ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=ntotal-1; i++)
    {
        state->exscale.ptr.p_double[i] = 1.0;
        state->exxorigin.ptr.p_double[i] = 0.0;
    }
    qpdenseaulsolver_generateexinitialpoint(&state->sclsftxc, nmain, nslack, &state->exxc, _state);
    goodcounter = 0;
    stagnationcounter = 0;
    feaserr = ae_maxrealnumber;
    for(outeridx=0; outeridx<=settings->outerits-1; outeridx++)
    {
        
        /*
         * Repeat loop until working set stabilizes.
         */
        do
        {
            
            /*
             * Preallocate space for ExA and for QQP solver; we do not allocate
             * array[NTotal,NTotal] from the start because NTotal can be much
             * larger than NMain for problems with large amount of inequality
             * constraints, and we usually need NWork=O(NMain).
             *
             * NOTE: for the sake of simplicity, 1-dimensional arrays were
             *       preallocated to the maximum size required (NTotal).
             */
            if( state->exa.rows<nwork||state->exa.cols<nwork )
            {
                i = nwork+nwork/3+1;
                rmatrixsetlengthatleast(&state->exa, i, i, _state);
            }
            qqppreallocategrowdense(&state->qqpbuf, nwork, i, _state);
            
            /*
             * Generate penalized quadratic model
             */
            qpdenseaulsolver_generateexmodel(&state->sclsfta, &state->sclsftb, nmain, &state->sclsftbndl, &state->sclsfthasbndl, &state->sclsftbndu, &state->sclsfthasbndu, &state->sclsftcleic, nectotal, nicwork, &state->nulc, rho, &state->exa, &state->exb, &state->exbndl, &state->exbndu, &state->tmp2, _state);
            
            /*
             * Solve extended QP problem subject to current working set of general
             * inequality constraints.
             */
            qqploaddefaults(nwork, &state->qqpsettingsuser, _state);
            state->qqpsettingsuser.maxouterits = 50;
            state->qqpsettingsuser.epsg = 0.0;
            state->qqpsettingsuser.epsf = 0.0;
            state->qqpsettingsuser.epsx = 0.01*epsx;
            state->qqpsettingsuser.cnphase = ae_true;
            qqpoptimize(&state->dummycqm, &state->dummysparse, &state->exa, 2, ae_true, &state->exb, &state->exbndl, &state->exbndu, &state->exscale, &state->exxorigin, nwork, &state->qqpsettingsuser, &state->qqpbuf, &state->exxc, &k, _state);
            state->repncholesky = state->repncholesky+state->qqpbuf.repncholesky;
            
            /*
             * Evaluate violation of constraints
             */
            for(i=0; i<=nictotal-1; i++)
            {
                v = ae_v_dotproduct(&state->sclsftcleic.ptr.pp_double[nectotal+i][0], 1, &state->exxc.ptr.p_double[0], 1, ae_v_len(0,nmain-1));
                v = v-state->sclsftcleic.ptr.pp_double[nectotal+i][nmain];
                state->nicerr.ptr.p_double[i] = v;
            }
            
            /*
             * Working set expansion:
             * * select limited amount of most violated constraints
             * * perform permutation of non-work constraints such that
             *   candidate constraint is first the list (update XC and NuLC)
             * * increase working set size by 1
             * * increase activation count for new constraint by 1 (this count
             *   is used later by working set eviction phase)
             * * repeat
             *
             * NOTE: we use selection sort algorithm because its O(NAdded*NWork) cost
             *       is still comparable to the cost of constraints evaluation
             */
            workingsetextended = ae_false;
            i = 0;
            while(ae_fp_less((double)(i),(double)1+qpdenseaulsolver_expansionratio*(double)nmain)&&nicwork<nictotal)
            {
                
                /*
                 * Select most violated constraint
                 */
                k = nicwork;
                for(j=nicwork; j<=nictotal-1; j++)
                {
                    if( ae_fp_greater(state->nicerr.ptr.p_double[j],state->nicerr.ptr.p_double[k]) )
                    {
                        k = j;
                    }
                }
                
                /*
                 * If violation is positive, add it
                 */
                if( ae_fp_greater(state->nicerr.ptr.p_double[k],(double)(0)) )
                {
                    swaprows(&state->sclsftcleic, nectotal+nicwork, nectotal+k, -1, _state);
                    swapelements(&state->nicerr, nicwork, k, _state);
                    swapelementsi(&state->nicnact, nicwork, k, _state);
                    swapelementsi(&state->cidx, nectotal+nicwork, nectotal+k, _state);
                    swapelements(&state->cscales, nectotal+nicwork, nectotal+k, _state);
                    state->exxc.ptr.p_double[nmain+nicwork] = 0.0;
                    state->nulc.ptr.p_double[nectotal+nicwork] = 0.0;
                    state->nicnact.ptr.p_int[nicwork] = state->nicnact.ptr.p_int[nicwork]+1;
                    inc(&nicwork, _state);
                    inc(&nwork, _state);
                    inc(&kwork, _state);
                    inc(&i, _state);
                    workingsetextended = ae_true;
                }
                else
                {
                    break;
                }
            }
            
            /*
             * Working set eviction:
             * * select constraints which are (1) far away from the
             *   boundary, AND (2) has less than two activation attempts
             *   (if constraint is regularly activated/deactivated, we keep
             *   it in the working set no matter what)
             * * remove such constraints from the working set one by one
             */
            if( allowwseviction )
            {
                for(k=nicwork-1; k>=0; k--)
                {
                    if( ae_fp_less(state->nicerr.ptr.p_double[k],qpdenseaulsolver_evictionlevel)&&state->nicnact.ptr.p_int[k]<=1 )
                    {
                        swaprows(&state->sclsftcleic, nectotal+nicwork-1, nectotal+k, -1, _state);
                        swapelementsi(&state->cidx, nectotal+nicwork-1, nectotal+k, _state);
                        swapelements(&state->cscales, nectotal+nicwork-1, nectotal+k, _state);
                        swapelements(&state->nicerr, nicwork-1, k, _state);
                        swapelementsi(&state->nicnact, nicwork-1, k, _state);
                        swapelements(&state->exxc, nmain+nicwork-1, nmain+k, _state);
                        swapelements(&state->nulc, nectotal+nicwork-1, nectotal+k, _state);
                        dec(&nicwork, _state);
                        dec(&nwork, _state);
                        dec(&kwork, _state);
                    }
                }
            }
            
            /*
             * Report working set statistics
             */
            if( state->repnwrk1==0 )
            {
                state->repnwrk1 = nicwork;
            }
            state->repnwrkf = nicwork;
            if( workingsetextended )
            {
                inc(&state->repnwrkchanges, _state);
            }
        }
        while(workingsetextended);
        
        /*
         * Estimate Lagrange multipliers using alternative algorithm
         */
        ae_v_move(&state->nulcest.ptr.p_double[0], 1, &state->nulc.ptr.p_double[0], 1, ae_v_len(0,kwork-1));
        qpdenseaulsolver_updatelagrangemultipliers(&state->sclsfta, &state->sclsftb, nmain, &state->sclsftbndl, &state->sclsfthasbndl, &state->sclsftbndu, &state->sclsfthasbndu, &state->sclsftcleic, nectotal, nicwork, &state->exxc, &state->nulcest, state, _state);
        
        /*
         * Update XC and Lagrange multipliers
         */
        feaserrprev = feaserr;
        feaserr = (double)(0);
        for(i=0; i<=kwork-1; i++)
        {
            
            /*
             * Calculate I-th feasibility error in V using formula for distance
             * between point and line (here we calculate actual distance between
             * XN and hyperplane Ci'*XN=Bi, which is different from error Ci'*XN-Bi).
             */
            v = (double)(0);
            vv = (double)(0);
            for(j=0; j<=nmain-1; j++)
            {
                v = v+state->sclsftcleic.ptr.pp_double[i][j]*state->exxc.ptr.p_double[j];
                vv = vv+ae_sqr(state->sclsftcleic.ptr.pp_double[i][j], _state);
            }
            if( i>=nectotal )
            {
                v = v+state->exxc.ptr.p_double[nmain+(i-nectotal)];
                vv = vv+ae_sqr((double)(1), _state);
            }
            v = v-state->sclsftcleic.ptr.pp_double[i][nmain];
            vv = coalesce(vv, (double)(1), _state);
            v = v/ae_sqrt(vv, _state);
            
            /*
             * Calculate magnitude of Lagrangian update (and Lagrangian parameters themselves)
             */
            feaserr = feaserr+ae_sqr(v, _state);
            state->nulc.ptr.p_double[i] = state->nulcest.ptr.p_double[i];
        }
        feaserr = ae_sqrt(feaserr, _state);
        if( ae_fp_less(feaserr,epsx) )
        {
            inc(&goodcounter, _state);
        }
        else
        {
            goodcounter = 0;
        }
        if( ae_fp_greater(feaserr,feaserrprev*requestedfeasdecrease) )
        {
            inc(&stagnationcounter, _state);
        }
        else
        {
            stagnationcounter = 0;
        }
        if( goodcounter>=2 )
        {
            break;
        }
        if( stagnationcounter>=2 )
        {
            rho = ae_minreal(rho*10.0, maxrho, _state);
        }
        else
        {
            rho = ae_minreal(rho*1.41, maxrho, _state);
        }
    }
    
    /*
     * Convert Lagrange multipliers from internal format to one expected
     * by caller:
     * * reorder multipliers for linear constraints
     * * compute residual from gradient+linearconstraints
     * * compute multipliers for box constraints from residual
     * * rescale everything
     */
    for(i=0; i<=nectotal+nicwork-1; i++)
    {
        laglc->ptr.p_double[state->cidx.ptr.p_int[i]] = -state->nulc.ptr.p_double[i]*targetscale/state->cscales.ptr.p_double[i];
    }
    rvectorsetlengthatleast(&state->tmpg, nmain, _state);
    for(i=0; i<=nmain-1; i++)
    {
        v = state->sclsftb.ptr.p_double[i];
        for(j=0; j<=nmain-1; j++)
        {
            v = v+state->sclsfta.ptr.pp_double[i][j]*state->exxc.ptr.p_double[j];
        }
        state->tmpg.ptr.p_double[i] = v;
    }
    rmatrixgemv(nmain, nectotal+nicwork, -1.0, &state->sclsftcleic, 0, 0, 1, &state->nulc, 0, 1.0, &state->tmpg, 0, _state);
    for(i=0; i<=nmain-1; i++)
    {
        if( (state->sclsfthasbndl.ptr.p_bool[i]&&ae_fp_eq(state->exxc.ptr.p_double[i],state->sclsftbndl.ptr.p_double[i]))||(state->sclsfthasbndu.ptr.p_bool[i]&&ae_fp_eq(state->exxc.ptr.p_double[i],state->sclsftbndu.ptr.p_double[i])) )
        {
            lagbc->ptr.p_double[i] = -state->tmpg.ptr.p_double[i];
        }
    }
    for(i=0; i<=nmain-1; i++)
    {
        lagbc->ptr.p_double[i] = lagbc->ptr.p_double[i]*targetscale/s->ptr.p_double[i];
    }
    
    /*
     * Unpack results.
     *
     * Add XOrigin to XC and make sure that boundary constraints are
     * satisfied.
     */
    for(i=0; i<=nmain-1; i++)
    {
        
        /*
         * Unscale/unshift
         */
        xs->ptr.p_double[i] = s->ptr.p_double[i]*state->exxc.ptr.p_double[i]+xorigin->ptr.p_double[i];
        
        /*
         * Make sure that point is feasible w.r.t. box constraints.
         * Enforce box constraints which were active in the scaled/shifted solution.
         */
        if( state->sclsfthasbndl.ptr.p_bool[i] )
        {
            if( ae_fp_less(xs->ptr.p_double[i],bndl->ptr.p_double[i]) )
            {
                xs->ptr.p_double[i] = bndl->ptr.p_double[i];
            }
            if( ae_fp_eq(state->exxc.ptr.p_double[i],state->sclsftbndl.ptr.p_double[i]) )
            {
                xs->ptr.p_double[i] = bndl->ptr.p_double[i];
            }
        }
        if( state->sclsfthasbndu.ptr.p_bool[i] )
        {
            if( ae_fp_greater(xs->ptr.p_double[i],bndu->ptr.p_double[i]) )
            {
                xs->ptr.p_double[i] = bndu->ptr.p_double[i];
            }
            if( ae_fp_eq(state->exxc.ptr.p_double[i],state->sclsftbndu.ptr.p_double[i]) )
            {
                xs->ptr.p_double[i] = bndu->ptr.p_double[i];
            }
        }
    }
    *terminationtype = 2;
}


/*************************************************************************
This function generates box-constrained QP problem, which is penalized and
augmented formulation of original linearly constrained problem

  -- ALGLIB --
     Copyright 23.02.2017 by Bochkanov Sergey
*************************************************************************/
static void qpdenseaulsolver_generateexmodel(/* Real    */ const ae_matrix* sclsfta,
     /* Real    */ const ae_vector* sclsftb,
     ae_int_t nmain,
     /* Real    */ const ae_vector* sclsftbndl,
     /* Boolean */ const ae_vector* sclsfthasbndl,
     /* Real    */ const ae_vector* sclsftbndu,
     /* Boolean */ const ae_vector* sclsfthasbndu,
     /* Real    */ const ae_matrix* sclsftcleic,
     ae_int_t sclsftnec,
     ae_int_t sclsftnic,
     /* Real    */ const ae_vector* nulc,
     double rho,
     /* Real    */ ae_matrix* exa,
     /* Real    */ ae_vector* exb,
     /* Real    */ ae_vector* exbndl,
     /* Real    */ ae_vector* exbndu,
     /* Real    */ ae_matrix* tmp2,
     ae_state *_state)
{
    ae_int_t nslack;
    ae_int_t ntotal;
    ae_int_t i;
    ae_int_t j;
    double v;


    nslack = sclsftnic;
    ntotal = nmain+nslack;
    
    /*
     * Integrity check for properly preallocated storage
     */
    ae_assert(exa->rows>=ntotal&&exa->cols>=ntotal, "QPDenseAUL.GenerateExModel - integrity check failed", _state);
    ae_assert((exb->cnt>=ntotal&&exbndl->cnt>=ntotal)&&exbndu->cnt>=ntotal, "QPDenseAUL.GenerateExModel - integrity check failed", _state);
    
    /*
     * Primary quadratic term
     */
    for(i=0; i<=ntotal-1; i++)
    {
        for(j=i; j<=ntotal-1; j++)
        {
            exa->ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(i=0; i<=nmain-1; i++)
    {
        for(j=i; j<=nmain-1; j++)
        {
            exa->ptr.pp_double[i][j] = sclsfta->ptr.pp_double[i][j];
        }
    }
    
    /*
     * Primary linear term
     */
    for(i=0; i<=ntotal-1; i++)
    {
        exb->ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=nmain-1; i++)
    {
        exb->ptr.p_double[i] = sclsftb->ptr.p_double[i];
    }
    
    /*
     * Box constraints - move primary, add slack
     */
    for(i=0; i<=nmain-1; i++)
    {
        if( sclsfthasbndl->ptr.p_bool[i] )
        {
            exbndl->ptr.p_double[i] = sclsftbndl->ptr.p_double[i];
        }
        else
        {
            exbndl->ptr.p_double[i] = _state->v_neginf;
        }
        if( sclsfthasbndu->ptr.p_bool[i] )
        {
            exbndu->ptr.p_double[i] = sclsftbndu->ptr.p_double[i];
        }
        else
        {
            exbndu->ptr.p_double[i] = _state->v_posinf;
        }
    }
    for(i=nmain; i<=ntotal-1; i++)
    {
        exbndl->ptr.p_double[i] = (double)(0);
        exbndu->ptr.p_double[i] = _state->v_posinf;
    }
    
    /*
     * Handle equality constraints:
     * * modify quadratic term
     * * modify linear term
     * * add Lagrangian term
     */
    rmatrixsetlengthatleast(tmp2, sclsftnec+sclsftnic, ntotal, _state);
    for(i=0; i<=sclsftnec+sclsftnic-1; i++)
    {
        
        /*
         * Given constraint row ci and right hand side ri,
         * I-th quadratic constraint adds penalty term
         *
         *     0.5*Rho*(ci'*x-ri)^2 =
         *     = 0.5*Rho*(ci'*x-ri)^T*(ci'*x-ri) =
         *     = 0.5*Rho*(x'*ci-ri')*(ci'*x-ri) =
         *     = 0.5*Rho*(x'*ci*ci'*x - ri'*ci'*x - x'*ci*ri + ri'*ri )
         *     = 0.5*Rho*(x'*(ci*ci')*x - 2*ri*(ci'*x) + ri^2 )
         *
         * Thus, quadratic term is updated by
         *
         *     0.5*Rho*(ci*ci')
         *
         * (with actual update to ExA being performed without 0.5
         * multiplier because entire matrix is post-multipliead by 0.5)
         * and linear term receives update
         *
         *     -Rho*ri*ci
         *
         * Similaryly, lagrangian term is -NUi*(ci'*x-ri),
         * so linear term is updated by
         *
         *     -NUi*ci
         *
         * Because our model does not take into account constant term,
         * we calculate just quadratic and linear terms.
         */
        ae_v_move(&tmp2->ptr.pp_double[i][0], 1, &sclsftcleic->ptr.pp_double[i][0], 1, ae_v_len(0,nmain-1));
        for(j=nmain; j<=ntotal-1; j++)
        {
            tmp2->ptr.pp_double[i][j] = (double)(0);
        }
        if( i>=sclsftnec )
        {
            tmp2->ptr.pp_double[i][nmain+i-sclsftnec] = 1.0;
        }
        v = -rho*sclsftcleic->ptr.pp_double[i][nmain];
        ae_v_addd(&exb->ptr.p_double[0], 1, &tmp2->ptr.pp_double[i][0], 1, ae_v_len(0,ntotal-1), v);
        v = -nulc->ptr.p_double[i];
        ae_v_addd(&exb->ptr.p_double[0], 1, &tmp2->ptr.pp_double[i][0], 1, ae_v_len(0,ntotal-1), v);
    }
    rmatrixsyrk(ntotal, sclsftnec+sclsftnic, rho, tmp2, 0, 0, 2, 1.0, exa, 0, 0, ae_true, _state);
}


/*************************************************************************
This function generates initial point for  "extended"  box-constrained  QP
problem.

  -- ALGLIB --
     Copyright 23.02.2017 by Bochkanov Sergey
*************************************************************************/
static void qpdenseaulsolver_generateexinitialpoint(/* Real    */ const ae_vector* sclsftxc,
     ae_int_t nmain,
     ae_int_t nslack,
     /* Real    */ ae_vector* exxc,
     ae_state *_state)
{
    ae_int_t ntotal;
    ae_int_t i;


    ntotal = nmain+nslack;
    for(i=0; i<=ntotal-1; i++)
    {
        exxc->ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=nmain-1; i++)
    {
        exxc->ptr.p_double[i] = sclsftxc->ptr.p_double[i];
    }
}


/*************************************************************************
This function estimates Lagrange multipliers for scaled-shifted QP problem
(here "scaled-shifted"  means  that we  performed  variable  scaling   and
subtracted origin) given by quadratic term A, linear term B, box constraints
and linear constraint matrix.

It is assumed that all linear constraints are equality  ones,  with  first
NEC ones being constraints without slack  variables,  and  next  NIC  ones
having slack variables. The only inequality constraints we  have  are  box
ones, with first NMain ones being "general" box constraints, and next  NIC
ones being non-negativity constraints (not specified explicitly).

We also make use of the current point XC, which is used to determine active
box constraints.

Actual QP problem size is NMain+NIC, but   some  parameters   have   lower
dimensionality.

Parameters sizes are:
* A is assumed to be array[NMain,NMain]
* B is assumed to be array[NMain]
* BndL, BndU are array[NMain]
* CLEIC is array[NEC+NIC,NMain+1] (last item in a row containts right part)
* ExXC is array[NMain+NIC], holds current point
* NuLCEst is array[NEC+NIC], holds initial values of Lagrange coeffs

On exit NuLCEst is updated with new estimate of Lagrange multipliers.

  -- ALGLIB --
     Copyright 23.02.2017 by Bochkanov Sergey
*************************************************************************/
static void qpdenseaulsolver_updatelagrangemultipliers(/* Real    */ const ae_matrix* sclsfta,
     /* Real    */ const ae_vector* sclsftb,
     ae_int_t nmain,
     /* Real    */ const ae_vector* sclsftbndl,
     /* Boolean */ const ae_vector* sclsfthasbndl,
     /* Real    */ const ae_vector* sclsftbndu,
     /* Boolean */ const ae_vector* sclsfthasbndu,
     /* Real    */ const ae_matrix* sclsftcleic,
     ae_int_t sclsftnec,
     ae_int_t sclsftnic,
     /* Real    */ const ae_vector* exxc,
     /* Real    */ ae_vector* nulcest,
     qpdenseaulbuffers* buffers,
     ae_state *_state)
{
    ae_int_t nslack;
    ae_int_t ntotal;
    ae_int_t ktotal;
    ae_int_t nqrrows;
    ae_int_t nqrcols;
    ae_int_t i;
    ae_int_t j;
    double lambdareg;
    double mxdiag;
    double v;
    ae_bool isactive;


    nslack = sclsftnic;
    ntotal = nmain+nslack;
    ktotal = sclsftnec+sclsftnic;
    
    /*
     * Given current point ExXC, we can determine active and inactive
     * constraints. After we drop inactive inequality constraints, we
     * have equality-only constrained QP problem, with mix of general
     * linear equality constraints and "simple" constraints Xi=Ci.
     *
     * Problem min(0.5*x'*A*x + b'*x) s.t. C*x=d (general linear
     * constraints) can be solved by explicitly writing out Lagrange
     * equations:
     *
     *     [ A  C' ] [ X ]   [ -b]
     *     [       ] [   ] = [   ]
     *     [ C     ] [ L ]   [ d ]
     *
     * or
     *
     *         [ X ]
     *     A1* [   ] = b1
     *         [ L ]
     *
     * where X stands for solution itself, and L stands for Lagrange
     * multipliers. It can be easily solved with direct linear solver.
     * However, such formulation does not account for "simple" equality
     * constraints on variables. It is possible to include "simple"
     * constraints into "general" ones (i.e. append (0 ... 0 -1 0 ... 0)'
     * to the constraint matrix), but it will increase problem
     * size.
     *
     * Another approach is to use initial values of X and L (X0 and L0)
     * as starting point, and to solve for "offset" from (X0, L0):
     *
     *        [ X0+X1 ]
     *     A1*[       ] = b1
     *        [ L0+L1 ]
     *
     * or 
     *
     *        [ X1 ]           [ X0 ]
     *     A1*[    ] = b1 - A1*[    ]
     *        [ L1 ]           [ L0 ]
     *
     * In such formulation components of X1 which correspond to active
     * constraints on variables are "frozen" at value 0 (because we have
     * equality constraint, offset from constrained value have to be zero).
     *
     * Thus, we can rewrite corresponding columns of A1 with zeros - and
     * use this space to store (0 ... 0 -1 0 ... 0)', which is used to
     * account for Lagrange multipliers for "simple" constraints.
     */
    nqrcols = ntotal+ktotal;
    nqrrows = nqrcols;
    rvectorsetlengthatleast(&buffers->qrsv0, nqrcols, _state);
    rvectorsetlengthatleast(&buffers->qrsvx1, nqrcols, _state);
    for(i=0; i<=ntotal-1; i++)
    {
        buffers->qrsv0.ptr.p_double[i] = exxc->ptr.p_double[i];
    }
    for(i=0; i<=ktotal-1; i++)
    {
        buffers->qrsv0.ptr.p_double[ntotal+i] = nulcest->ptr.p_double[i];
    }
    rmatrixsetlengthatleast(&buffers->qrkkt, nqrcols+nqrcols, nqrcols+1, _state);
    rvectorsetlengthatleast(&buffers->qrrightpart, nqrcols+nqrcols, _state);
    lambdareg = 1.0E-8;
    for(;;)
    {
        
        /*
         * Initialize matrix A1 and right part b1 with zeros
         */
        for(i=0; i<=buffers->qrkkt.rows-1; i++)
        {
            for(j=0; j<=buffers->qrkkt.cols-1; j++)
            {
                buffers->qrkkt.ptr.pp_double[i][j] = (double)(0);
            }
            buffers->qrrightpart.ptr.p_double[i] = (double)(0);
        }
        
        /*
         * Append quadratic term (note: we implicitly add NSlack zeros to
         * A and b).
         */
        mxdiag = (double)(0);
        for(i=0; i<=nmain-1; i++)
        {
            for(j=0; j<=nmain-1; j++)
            {
                buffers->qrkkt.ptr.pp_double[i][j] = sclsfta->ptr.pp_double[i][j];
            }
            buffers->qrrightpart.ptr.p_double[i] = -sclsftb->ptr.p_double[i];
            mxdiag = ae_maxreal(mxdiag, ae_fabs(sclsfta->ptr.pp_double[i][i], _state), _state);
        }
        mxdiag = coalesce(mxdiag, (double)(1), _state);
        
        /*
         * Append general linear constraints
         */
        for(i=0; i<=ktotal-1; i++)
        {
            for(j=0; j<=nmain-1; j++)
            {
                buffers->qrkkt.ptr.pp_double[ntotal+i][j] = -sclsftcleic->ptr.pp_double[i][j];
                buffers->qrkkt.ptr.pp_double[j][ntotal+i] = -sclsftcleic->ptr.pp_double[i][j];
            }
            if( i>=sclsftnec )
            {
                buffers->qrkkt.ptr.pp_double[ntotal+i][nmain+(i-sclsftnec)] = (double)(-1);
                buffers->qrkkt.ptr.pp_double[nmain+(i-sclsftnec)][ntotal+i] = (double)(-1);
            }
            buffers->qrrightpart.ptr.p_double[ntotal+i] = -sclsftcleic->ptr.pp_double[i][nmain];
        }
        
        /*
         * Append regularizer to the bottom of the matrix
         * (it will be factored in during QR decomposition)
         */
        if( ae_fp_greater(lambdareg,(double)(0)) )
        {
            nqrrows = nqrcols+nqrcols;
            for(i=0; i<=nqrcols-1; i++)
            {
                buffers->qrkkt.ptr.pp_double[nqrcols+i][i] = lambdareg*mxdiag;
            }
        }
        
        /*
         * Subtract reference point (X0,L0) from the system
         */
        for(i=0; i<=nqrcols-1; i++)
        {
            v = ae_v_dotproduct(&buffers->qrkkt.ptr.pp_double[i][0], 1, &buffers->qrsv0.ptr.p_double[0], 1, ae_v_len(0,nqrcols-1));
            buffers->qrrightpart.ptr.p_double[i] = buffers->qrrightpart.ptr.p_double[i]-v;
        }
        
        /*
         * Handle active "simple" equality constraints
         */
        for(i=0; i<=ntotal-1; i++)
        {
            isactive = ae_false;
            if( i<nmain&&((sclsfthasbndl->ptr.p_bool[i]&&ae_fp_eq(exxc->ptr.p_double[i],sclsftbndl->ptr.p_double[i]))||(sclsfthasbndu->ptr.p_bool[i]&&ae_fp_eq(exxc->ptr.p_double[i],sclsftbndu->ptr.p_double[i]))) )
            {
                isactive = ae_true;
            }
            if( i>=nmain&&ae_fp_eq(exxc->ptr.p_double[i],0.0) )
            {
                isactive = ae_true;
            }
            if( !isactive )
            {
                continue;
            }
            for(j=0; j<=nqrrows-1; j++)
            {
                buffers->qrkkt.ptr.pp_double[j][i] = (double)(0);
            }
            buffers->qrkkt.ptr.pp_double[i][i] = (double)(-1);
        }
        
        /*
         * Solve via QR decomposition:
         * * append right part to the system matrix
         * * perform QR decomposition of the extended matrix (right part is implicitly
         *   multiplied by Q during decomposition; believe me, it works!)
         * * check condition number, increase regularization value if necessary and retry
         * * solve triangular system, break iteration
         */
        for(i=0; i<=nqrrows-1; i++)
        {
            buffers->qrkkt.ptr.pp_double[i][nqrcols] = buffers->qrrightpart.ptr.p_double[i];
        }
        rmatrixqr(&buffers->qrkkt, nqrrows, nqrcols+1, &buffers->qrtau, _state);
        if( ae_fp_less_eq(rmatrixtrrcond1(&buffers->qrkkt, nqrcols, ae_true, ae_false, _state),(double)1000*ae_machineepsilon) )
        {
            lambdareg = coalesce((double)10*lambdareg, 1.0E-13, _state);
            continue;
        }
        for(i=nqrcols-1; i>=0; i--)
        {
            v = buffers->qrkkt.ptr.pp_double[i][nqrcols];
            for(j=i+1; j<=nqrcols-1; j++)
            {
                v = v-buffers->qrkkt.ptr.pp_double[i][j]*buffers->qrsvx1.ptr.p_double[j];
            }
            buffers->qrsvx1.ptr.p_double[i] = v/buffers->qrkkt.ptr.pp_double[i][i];
        }
        break;
    }
    
    /*
     * Update Lagrange coefficients
     */
    for(i=0; i<=ktotal-1; i++)
    {
        nulcest->ptr.p_double[i] = buffers->qrsv0.ptr.p_double[ntotal+i]+buffers->qrsvx1.ptr.p_double[ntotal+i];
    }
}


/*************************************************************************
This function generates scaled (by S) and shifted (by XC) reformulation of
the original problem.

INPUT PARAMETERS:
    DenseA      -   for dense problems (AKind=0), A-term of CQM object
                    contains system matrix. Other terms are unspecified
                    and should not be referenced.
    SparseA     -   for sparse problems (AKind=1), CRS format
    AKind       -   sparse matrix format:
                    * 0 for dense matrix
                    * 1 for sparse matrix
    SparseUpper -   which triangle of SparseAC stores matrix  -  upper  or
                    lower one (for dense matrices this  parameter  is  not
                    actual).
    B           -   linear term, array[N]
    BndL        -   lower bound, array[N]
    BndU        -   upper bound, array[N]
    S           -   scale vector, array[NC]:
                    * I-th element contains scale of I-th variable,
                    * SC[I]>0
    XOrigin     -   origin term, array[NC]. Can be zero.
    N           -   number of variables in the  original  formulation  (no
                    slack variables).
    CLEIC       -   dense linear equality/inequality constraints. Equality
                    constraints come first.
    NEC, NIC    -   number of dense equality/inequality constraints.
    SCLEIC      -   sparse linear equality/inequality constraints. Equality
                    constraints come first.
    SNEC, SNIC  -   number of sparse equality/inequality constraints.
    RenormLC    -   whether constraints should be renormalized (recommended)
                    or used "as is".
    Settings    -   QPDENSEAULSettings object initialized by one of the initialization
                    functions.
    State       -   object which stores temporaries
    XS          -   initial point, array[NC]

    
On output, following fields of the State structure are modified:
* SclSftA       -   array[NMain,NMain], quadratic term, both triangles
* SclSftB       -   array[NMain], linear term
* SclSftXC      -   array[NMain], initial point
* SclSftHasBndL,
  SclSftHasBndU,
  SclSftBndL,
  SclSftBndU    -   array[NMain], lower/upper bounds
* SclSftCLEIC   -   array[KTotal,NMain+1], general linear constraints

NOTE: State.Tmp2 is used to store temporary array[NMain,NMain]

  -- ALGLIB --
     Copyright 01.10.2017 by Bochkanov Sergey
*************************************************************************/
static void qpdenseaulsolver_scaleshiftoriginalproblem(const convexquadraticmodel* a,
     const sparsematrix* sparsea,
     ae_int_t akind,
     ae_bool sparseaupper,
     /* Real    */ const ae_vector* b,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t nmain,
     /* Real    */ const ae_matrix* cleic,
     ae_int_t dnec,
     ae_int_t dnic,
     const sparsematrix* scleic,
     ae_int_t snec,
     ae_int_t snic,
     ae_bool renormlc,
     qpdenseaulbuffers* state,
     /* Real    */ ae_vector* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t j0;
    ae_int_t j1;
    double v;
    double vv;
    ae_int_t ktotal;


    ae_assert(akind==0||akind==1, "QPDENSEAULOptimize: unexpected AKind", _state);
    ktotal = dnec+dnic+snec+snic;
    rmatrixsetlengthatleast(&state->sclsfta, nmain, nmain, _state);
    rvectorsetlengthatleast(&state->sclsftb, nmain, _state);
    rvectorsetlengthatleast(&state->sclsftxc, nmain, _state);
    rvectorsetlengthatleast(&state->sclsftbndl, nmain, _state);
    rvectorsetlengthatleast(&state->sclsftbndu, nmain, _state);
    bvectorsetlengthatleast(&state->sclsfthasbndl, nmain, _state);
    bvectorsetlengthatleast(&state->sclsfthasbndu, nmain, _state);
    rmatrixsetlengthatleast(&state->sclsftcleic, ktotal, nmain+1, _state);
    rvectorsetlengthatleast(&state->cscales, ktotal, _state);
    if( akind==0 )
    {
        
        /*
         * Extract dense A and scale
         */
        cqmgeta(a, &state->tmp2, _state);
        for(i=0; i<=nmain-1; i++)
        {
            for(j=0; j<=nmain-1; j++)
            {
                state->sclsfta.ptr.pp_double[i][j] = (double)(0);
            }
        }
        for(i=0; i<=nmain-1; i++)
        {
            for(j=i; j<=nmain-1; j++)
            {
                v = state->tmp2.ptr.pp_double[i][j]*s->ptr.p_double[i]*s->ptr.p_double[j];
                state->sclsfta.ptr.pp_double[i][j] = v;
                state->sclsfta.ptr.pp_double[j][i] = v;
            }
        }
    }
    if( akind==1 )
    {
        
        /*
         * Extract sparse A and scale
         */
        ae_assert(sparsea->matrixtype==1, "QPDENSEAULOptimize: unexpected sparse matrix format", _state);
        ae_assert(sparsea->m==nmain, "QPDENSEAULOptimize: unexpected sparse matrix size", _state);
        ae_assert(sparsea->n==nmain, "QPDENSEAULOptimize: unexpected sparse matrix size", _state);
        for(i=0; i<=nmain-1; i++)
        {
            for(j=0; j<=nmain-1; j++)
            {
                state->sclsfta.ptr.pp_double[i][j] = (double)(0);
            }
        }
        if( sparseaupper )
        {
            for(i=0; i<=nmain-1; i++)
            {
                if( sparsea->didx.ptr.p_int[i]!=sparsea->uidx.ptr.p_int[i] )
                {
                    state->sclsfta.ptr.pp_double[i][i] = sparsea->vals.ptr.p_double[sparsea->didx.ptr.p_int[i]]*s->ptr.p_double[i]*s->ptr.p_double[i];
                }
                j0 = sparsea->uidx.ptr.p_int[i];
                j1 = sparsea->ridx.ptr.p_int[i+1]-1;
                for(j=j0; j<=j1; j++)
                {
                    k = sparsea->idx.ptr.p_int[j];
                    v = sparsea->vals.ptr.p_double[j]*s->ptr.p_double[i]*s->ptr.p_double[k];
                    state->sclsfta.ptr.pp_double[i][k] = v;
                    state->sclsfta.ptr.pp_double[k][i] = v;
                }
            }
        }
        else
        {
            for(i=0; i<=nmain-1; i++)
            {
                if( sparsea->didx.ptr.p_int[i]!=sparsea->uidx.ptr.p_int[i] )
                {
                    state->sclsfta.ptr.pp_double[i][i] = sparsea->vals.ptr.p_double[sparsea->didx.ptr.p_int[i]]*s->ptr.p_double[i]*s->ptr.p_double[i];
                }
                j0 = sparsea->ridx.ptr.p_int[i];
                j1 = sparsea->didx.ptr.p_int[i]-1;
                for(j=j0; j<=j1; j++)
                {
                    k = sparsea->idx.ptr.p_int[j];
                    v = sparsea->vals.ptr.p_double[j]*s->ptr.p_double[i]*s->ptr.p_double[k];
                    state->sclsfta.ptr.pp_double[i][k] = v;
                    state->sclsfta.ptr.pp_double[k][i] = v;
                }
            }
        }
    }
    for(i=0; i<=nmain-1; i++)
    {
        state->sclsftb.ptr.p_double[i] = b->ptr.p_double[i]*s->ptr.p_double[i];
        state->sclsftxc.ptr.p_double[i] = (xs->ptr.p_double[i]-xorigin->ptr.p_double[i])/s->ptr.p_double[i];
        state->sclsfthasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->sclsfthasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
        state->sclsftbndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->sclsftbndu.ptr.p_double[i] = bndu->ptr.p_double[i];
    }
    scaleshiftbcinplace(s, xorigin, &state->sclsftbndl, &state->sclsftbndu, nmain, _state);
    for(i=0; i<=ktotal-1; i++)
    {
        for(j=0; j<=nmain; j++)
        {
            state->sclsftcleic.ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(i=0; i<=dnec-1; i++)
    {
        for(j=0; j<=nmain-1; j++)
        {
            v = cleic->ptr.pp_double[i][j]*s->ptr.p_double[j];
            state->sclsftcleic.ptr.pp_double[i][j] = v;
        }
        state->sclsftcleic.ptr.pp_double[i][nmain] = cleic->ptr.pp_double[i][nmain];
    }
    for(i=0; i<=dnic-1; i++)
    {
        for(j=0; j<=nmain-1; j++)
        {
            v = cleic->ptr.pp_double[dnec+i][j]*s->ptr.p_double[j];
            state->sclsftcleic.ptr.pp_double[dnec+snec+i][j] = v;
        }
        state->sclsftcleic.ptr.pp_double[dnec+snec+i][nmain] = cleic->ptr.pp_double[dnec+i][nmain];
    }
    for(i=0; i<=snec-1; i++)
    {
        
        /*
         * Because constraints are sparse, everything is a bit tricky -
         * it is possible that N-th element of the row is zero and not
         * stored; it is also possible that entire row is empty.
         */
        j0 = scleic->ridx.ptr.p_int[i];
        j1 = scleic->ridx.ptr.p_int[i+1]-1;
        if( j1>=j0&&scleic->idx.ptr.p_int[j1]==nmain )
        {
            state->sclsftcleic.ptr.pp_double[dnec+i][nmain] = scleic->vals.ptr.p_double[j1];
            j1 = j1-1;
        }
        for(j=j0; j<=j1; j++)
        {
            k = scleic->idx.ptr.p_int[j];
            v = scleic->vals.ptr.p_double[j]*s->ptr.p_double[k];
            state->sclsftcleic.ptr.pp_double[dnec+i][k] = v;
        }
    }
    for(i=0; i<=snic-1; i++)
    {
        
        /*
         * Because constraints are sparse, everything is a bit tricky -
         * it is possible that N-th element of the row is zero and not
         * stored; it is also possible that entire row is empty.
         */
        j0 = scleic->ridx.ptr.p_int[snec+i];
        j1 = scleic->ridx.ptr.p_int[snec+i+1]-1;
        if( j1>=j0&&scleic->idx.ptr.p_int[j1]==nmain )
        {
            state->sclsftcleic.ptr.pp_double[dnec+snec+dnic+i][nmain] = scleic->vals.ptr.p_double[j1];
            j1 = j1-1;
        }
        for(j=j0; j<=j1; j++)
        {
            k = scleic->idx.ptr.p_int[j];
            v = scleic->vals.ptr.p_double[j]*s->ptr.p_double[k];
            state->sclsftcleic.ptr.pp_double[dnec+snec+dnic+i][k] = v;
        }
    }
    if( renormlc&&ktotal>0 )
    {
        
        /*
         * Normalize linear constraints in such way that they have unit norm
         * (after variable scaling)
         */
        for(i=0; i<=ktotal-1; i++)
        {
            vv = 0.0;
            for(j=0; j<=nmain-1; j++)
            {
                v = state->sclsftcleic.ptr.pp_double[i][j];
                vv = vv+v*v;
            }
            vv = ae_sqrt(vv, _state);
            state->cscales.ptr.p_double[i] = vv;
            if( ae_fp_greater(vv,(double)(0)) )
            {
                vv = (double)1/vv;
                for(j=0; j<=nmain; j++)
                {
                    state->sclsftcleic.ptr.pp_double[i][j] = state->sclsftcleic.ptr.pp_double[i][j]*vv;
                }
            }
        }
    }
    else
    {
        
        /*
         * Load unit scales
         */
        for(i=0; i<=ktotal-1; i++)
        {
            state->cscales.ptr.p_double[i] = 1.0;
        }
    }
    for(i=0; i<=ktotal-1; i++)
    {
        
        /*
         * Apply XOrigin
         */
        v = 0.0;
        for(j=0; j<=nmain-1; j++)
        {
            v = v+state->sclsftcleic.ptr.pp_double[i][j]*(xorigin->ptr.p_double[j]/s->ptr.p_double[j]);
        }
        state->sclsftcleic.ptr.pp_double[i][nmain] = state->sclsftcleic.ptr.pp_double[i][nmain]-v;
    }
}


/*************************************************************************
Normalize model in such way that norm(A)~1 (very roughly)

We have two lower bounds for sigma_max(A):
* first estimate is provided by Frobenius norm, it is equal to ANorm/NMain
* second estimate is provided by max(CAC)

We select largest one of these estimates, because using just one
of them is prone to different failure modes. Then, we divide A and B
by this estimate.

INPUT PARAMETERS:
    A   -       array[N,N], quadratic term, full triangle is given
    B   -       array[N], linear term
    N   -       problem size
    CLEIC-      array[NEC+NIC,N+1], linear equality/inequality constraints
    NEC -       number of equality constraints
    NIC -       number of inequality constraints
    UseCLEIC-   additional normalization of A in such way that CLEIC*A*CLEIC'~1:
                * if False, CLEIC is ignored
                * if True, CLEIC rows MUST have unit norm (we check it)
    Tmp2-       additional buffer, possibly preallocated
    
OUTPUT PARAMETERS:
    A, B - appropriately rescaled by 1/SCL
    
RESULT:
    multiplier SCL

  -- ALGLIB --
     Copyright 01.10.2017 by Bochkanov Sergey
*************************************************************************/
static double qpdenseaulsolver_normalizequadraticterm(/* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ const ae_matrix* cleic,
     ae_int_t nec,
     ae_int_t nic,
     ae_bool usecleic,
     /* Real    */ ae_matrix* tmp2,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double anorm;
    double maxcac;
    double v;
    double vv;
    ae_int_t ktotal;
    ae_int_t nmain;
    double result;


    nmain = n;
    ktotal = nec+nic;
    anorm = (double)(0);
    for(i=0; i<=nmain-1; i++)
    {
        for(j=0; j<=nmain-1; j++)
        {
            anorm = anorm+ae_sqr(a->ptr.pp_double[i][j], _state);
        }
    }
    anorm = ae_sqrt(anorm, _state);
    if( usecleic&&ktotal>0 )
    {
        
        /*
         * Calculate max(|diag(C*A*C')|), where C is constraint matrix
         */
        rmatrixsetlengthatleast(tmp2, ktotal, nmain, _state);
        rmatrixgemm(ktotal, nmain, nmain, 1.0, cleic, 0, 0, 0, a, 0, 0, 0, 0.0, tmp2, 0, 0, _state);
        maxcac = 0.0;
        for(i=0; i<=ktotal-1; i++)
        {
            v = (double)(0);
            vv = (double)(0);
            for(j=0; j<=nmain-1; j++)
            {
                v = v+tmp2->ptr.pp_double[i][j]*cleic->ptr.pp_double[i][j];
                vv = vv+ae_sqr(cleic->ptr.pp_double[i][j], _state);
            }
            ae_assert(ae_fp_less(ae_fabs(vv-(double)1, _state),1.0E-9)||ae_fp_eq(vv,(double)(0)), "DENSE-AUL: integrity check failed", _state);
            maxcac = ae_maxreal(maxcac, ae_fabs(v, _state), _state);
        }
    }
    else
    {
        maxcac = (double)(0);
    }
    result = coalesce(ae_maxreal(maxcac, anorm/(double)nmain, _state), (double)(1), _state);
    v = (double)1/result;
    for(i=0; i<=nmain-1; i++)
    {
        for(j=0; j<=nmain-1; j++)
        {
            a->ptr.pp_double[i][j] = a->ptr.pp_double[i][j]*v;
        }
    }
    for(i=0; i<=nmain-1; i++)
    {
        b->ptr.p_double[i] = b->ptr.p_double[i]*v;
    }
    return result;
}


/*************************************************************************
This function selects initial working set of general inequality constraints
for QP problem:
* for non-convex QP problems    -   NICWork=NIC is returned
* otherwise                     -   NICWork=0 is returned (we have to
                                    determine working set iteratively)

INPUT PARAMETERS:
    A           -   array[NMain], quadratic term, full matrix is stored
    NMain       -   number of variables in the "original" QP problem
    CLEIC       -   array[NEC+NIC,NMain+1], constraint matrix
    NEC         -   number of equality constraints
    NIC         -   number of inequality constraints

OUTPUT PARAMETERS:
    NICWork     -   recommended size of working set; in current version
                    either all (NICWork=NIC) or none (NICWork=0) constraints
                    are included.
    AllowWSEviction-whether problem properties allow eviction of constraints
                    from working set or not. Non-convex problems do not
                    allow eviction, convex ones do.

  -- ALGLIB --
     Copyright 02.10.2017 by Bochkanov Sergey
*************************************************************************/
static void qpdenseaulsolver_selectinitialworkingset(/* Real    */ const ae_matrix* a,
     ae_int_t nmain,
     /* Real    */ const ae_matrix* cleic,
     ae_int_t nec,
     ae_int_t nic,
     /* Real    */ ae_vector* tmp0,
     /* Real    */ ae_matrix* tmp2,
     ae_int_t* nicwork,
     ae_bool* allowwseviction,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    *nicwork = 0;
    *allowwseviction = ae_false;

    rmatrixsetlengthatleast(tmp2, nmain, nmain, _state);
    rvectorsetlengthatleast(tmp0, nmain, _state);
    for(i=0; i<=nmain-1; i++)
    {
        for(j=i; j<=nmain-1; j++)
        {
            tmp2->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
    if( !spdmatrixcholeskyrec(tmp2, 0, nmain, ae_true, tmp0, _state) )
    {
        
        /*
         * Matrix is indefinite.
         *
         * We have to select full working set, otherwise algorithm may fail
         * because problem with reduced working set can be unbounded from below.
         */
        *nicwork = nic;
        *allowwseviction = ae_false;
    }
    else
    {
        
        /*
         * Positive definite matrix.
         *
         * We can select zero initial working set and expand it later.
         */
        *nicwork = 0;
        *allowwseviction = ae_true;
    }
}


void _qpdenseaulsettings_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    qpdenseaulsettings *p = (qpdenseaulsettings*)_p;
    ae_touch_ptr((void*)p);
}


void _qpdenseaulsettings_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    qpdenseaulsettings       *dst = (qpdenseaulsettings*)_dst;
    const qpdenseaulsettings *src = (const qpdenseaulsettings*)_src;
    dst->epsx = src->epsx;
    dst->outerits = src->outerits;
    dst->rho = src->rho;
}


void _qpdenseaulsettings_clear(void* _p)
{
    qpdenseaulsettings *p = (qpdenseaulsettings*)_p;
    ae_touch_ptr((void*)p);
}


void _qpdenseaulsettings_destroy(void* _p)
{
    qpdenseaulsettings *p = (qpdenseaulsettings*)_p;
    ae_touch_ptr((void*)p);
}


void _qpdenseaulbuffers_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    qpdenseaulbuffers *p = (qpdenseaulbuffers*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->nulc, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->sclsfta, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sclsftb, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sclsfthasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->sclsfthasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->sclsftbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sclsftbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sclsftxc, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->sclsftcleic, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->cscales, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->exa, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->exb, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->exxc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->exbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->exbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->exscale, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->exxorigin, 0, DT_REAL, _state, make_automatic);
    _qqpsettings_init(&p->qqpsettingsuser, _state, make_automatic);
    _qqpbuffers_init(&p->qqpbuf, _state, make_automatic);
    ae_vector_init(&p->nulcest, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpg, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmp2, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->modelg, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->deltax, 0, DT_REAL, _state, make_automatic);
    _convexquadraticmodel_init(&p->dummycqm, _state, make_automatic);
    _sparsematrix_init(&p->dummysparse, _state, make_automatic);
    ae_matrix_init(&p->qrkkt, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->qrrightpart, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->qrtau, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->qrsv0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->qrsvx1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nicerr, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nicnact, 0, DT_INT, _state, make_automatic);
}


void _qpdenseaulbuffers_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    qpdenseaulbuffers       *dst = (qpdenseaulbuffers*)_dst;
    const qpdenseaulbuffers *src = (const qpdenseaulbuffers*)_src;
    ae_vector_init_copy(&dst->nulc, &src->nulc, _state, make_automatic);
    ae_matrix_init_copy(&dst->sclsfta, &src->sclsfta, _state, make_automatic);
    ae_vector_init_copy(&dst->sclsftb, &src->sclsftb, _state, make_automatic);
    ae_vector_init_copy(&dst->sclsfthasbndl, &src->sclsfthasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->sclsfthasbndu, &src->sclsfthasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->sclsftbndl, &src->sclsftbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->sclsftbndu, &src->sclsftbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->sclsftxc, &src->sclsftxc, _state, make_automatic);
    ae_matrix_init_copy(&dst->sclsftcleic, &src->sclsftcleic, _state, make_automatic);
    ae_vector_init_copy(&dst->cidx, &src->cidx, _state, make_automatic);
    ae_vector_init_copy(&dst->cscales, &src->cscales, _state, make_automatic);
    ae_matrix_init_copy(&dst->exa, &src->exa, _state, make_automatic);
    ae_vector_init_copy(&dst->exb, &src->exb, _state, make_automatic);
    ae_vector_init_copy(&dst->exxc, &src->exxc, _state, make_automatic);
    ae_vector_init_copy(&dst->exbndl, &src->exbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->exbndu, &src->exbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->exscale, &src->exscale, _state, make_automatic);
    ae_vector_init_copy(&dst->exxorigin, &src->exxorigin, _state, make_automatic);
    _qqpsettings_init_copy(&dst->qqpsettingsuser, &src->qqpsettingsuser, _state, make_automatic);
    _qqpbuffers_init_copy(&dst->qqpbuf, &src->qqpbuf, _state, make_automatic);
    ae_vector_init_copy(&dst->nulcest, &src->nulcest, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpg, &src->tmpg, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmp2, &src->tmp2, _state, make_automatic);
    ae_vector_init_copy(&dst->modelg, &src->modelg, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    ae_vector_init_copy(&dst->deltax, &src->deltax, _state, make_automatic);
    _convexquadraticmodel_init_copy(&dst->dummycqm, &src->dummycqm, _state, make_automatic);
    _sparsematrix_init_copy(&dst->dummysparse, &src->dummysparse, _state, make_automatic);
    ae_matrix_init_copy(&dst->qrkkt, &src->qrkkt, _state, make_automatic);
    ae_vector_init_copy(&dst->qrrightpart, &src->qrrightpart, _state, make_automatic);
    ae_vector_init_copy(&dst->qrtau, &src->qrtau, _state, make_automatic);
    ae_vector_init_copy(&dst->qrsv0, &src->qrsv0, _state, make_automatic);
    ae_vector_init_copy(&dst->qrsvx1, &src->qrsvx1, _state, make_automatic);
    ae_vector_init_copy(&dst->nicerr, &src->nicerr, _state, make_automatic);
    ae_vector_init_copy(&dst->nicnact, &src->nicnact, _state, make_automatic);
    dst->repinneriterationscount = src->repinneriterationscount;
    dst->repouteriterationscount = src->repouteriterationscount;
    dst->repncholesky = src->repncholesky;
    dst->repnwrkchanges = src->repnwrkchanges;
    dst->repnwrk0 = src->repnwrk0;
    dst->repnwrk1 = src->repnwrk1;
    dst->repnwrkf = src->repnwrkf;
    dst->repnmv = src->repnmv;
}


void _qpdenseaulbuffers_clear(void* _p)
{
    qpdenseaulbuffers *p = (qpdenseaulbuffers*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->nulc);
    ae_matrix_clear(&p->sclsfta);
    ae_vector_clear(&p->sclsftb);
    ae_vector_clear(&p->sclsfthasbndl);
    ae_vector_clear(&p->sclsfthasbndu);
    ae_vector_clear(&p->sclsftbndl);
    ae_vector_clear(&p->sclsftbndu);
    ae_vector_clear(&p->sclsftxc);
    ae_matrix_clear(&p->sclsftcleic);
    ae_vector_clear(&p->cidx);
    ae_vector_clear(&p->cscales);
    ae_matrix_clear(&p->exa);
    ae_vector_clear(&p->exb);
    ae_vector_clear(&p->exxc);
    ae_vector_clear(&p->exbndl);
    ae_vector_clear(&p->exbndu);
    ae_vector_clear(&p->exscale);
    ae_vector_clear(&p->exxorigin);
    _qqpsettings_clear(&p->qqpsettingsuser);
    _qqpbuffers_clear(&p->qqpbuf);
    ae_vector_clear(&p->nulcest);
    ae_vector_clear(&p->tmpg);
    ae_vector_clear(&p->tmp0);
    ae_matrix_clear(&p->tmp2);
    ae_vector_clear(&p->modelg);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->deltax);
    _convexquadraticmodel_clear(&p->dummycqm);
    _sparsematrix_clear(&p->dummysparse);
    ae_matrix_clear(&p->qrkkt);
    ae_vector_clear(&p->qrrightpart);
    ae_vector_clear(&p->qrtau);
    ae_vector_clear(&p->qrsv0);
    ae_vector_clear(&p->qrsvx1);
    ae_vector_clear(&p->nicerr);
    ae_vector_clear(&p->nicnact);
}


void _qpdenseaulbuffers_destroy(void* _p)
{
    qpdenseaulbuffers *p = (qpdenseaulbuffers*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->nulc);
    ae_matrix_destroy(&p->sclsfta);
    ae_vector_destroy(&p->sclsftb);
    ae_vector_destroy(&p->sclsfthasbndl);
    ae_vector_destroy(&p->sclsfthasbndu);
    ae_vector_destroy(&p->sclsftbndl);
    ae_vector_destroy(&p->sclsftbndu);
    ae_vector_destroy(&p->sclsftxc);
    ae_matrix_destroy(&p->sclsftcleic);
    ae_vector_destroy(&p->cidx);
    ae_vector_destroy(&p->cscales);
    ae_matrix_destroy(&p->exa);
    ae_vector_destroy(&p->exb);
    ae_vector_destroy(&p->exxc);
    ae_vector_destroy(&p->exbndl);
    ae_vector_destroy(&p->exbndu);
    ae_vector_destroy(&p->exscale);
    ae_vector_destroy(&p->exxorigin);
    _qqpsettings_destroy(&p->qqpsettingsuser);
    _qqpbuffers_destroy(&p->qqpbuf);
    ae_vector_destroy(&p->nulcest);
    ae_vector_destroy(&p->tmpg);
    ae_vector_destroy(&p->tmp0);
    ae_matrix_destroy(&p->tmp2);
    ae_vector_destroy(&p->modelg);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->deltax);
    _convexquadraticmodel_destroy(&p->dummycqm);
    _sparsematrix_destroy(&p->dummysparse);
    ae_matrix_destroy(&p->qrkkt);
    ae_vector_destroy(&p->qrrightpart);
    ae_vector_destroy(&p->qrtau);
    ae_vector_destroy(&p->qrsv0);
    ae_vector_destroy(&p->qrsvx1);
    ae_vector_destroy(&p->nicerr);
    ae_vector_destroy(&p->nicnact);
}


#endif
#if defined(AE_COMPILE_VIPMSOLVER) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
Initializes QP-IPM state and prepares it to receive quadratic/linear terms
and constraints.

The solver is configured to work internally with dense NxN  factorization,
no matter what exactly is passed - dense or sparse matrices.

INPUT PARAMETERS:
    State       -   solver  state  to  be configured; previously allocated
                    memory is reused as much as possible
    S           -   scale vector, array[N]:
                    * I-th element contains scale of I-th variable,
                    * S[I]>0
    XOrigin     -   origin term, array[N]. Can be zero. The solver solves
                    problem of the form
                    
                    >
                    > min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin))
                    >
                    
                    The terms A and b (as well as constraints) will be
                    specified later with separate calls.
    N           -   vars count, N>=1
    Normalize   -   apply normalization to target/constraints or not:
                    * if True, then target and constraints are normalized
                      to unit scale
                    * if False, it is assumed that the caller has performed
                      normalization.
                    The latter is recommended for presolved problems.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void vipminitdense(vipmstate* state,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t n,
     ae_bool normalize,
     ae_state *_state)
{


    ae_assert(n>=1, "VIPMInitDense: N<1", _state);
    ae_assert(isfinitevector(s, n, _state), "VIPMInitDense: S contains infinite or NaN elements", _state);
    ae_assert(isfinitevector(xorigin, n, _state), "VIPMInitDense: XOrigin contains infinite or NaN elements", _state);
    vipmsolver_vipminit(state, s, xorigin, n, n, 0, normalize, _state);
}


/*************************************************************************
Initializes QP-IPM state and prepares it to receive quadratic/linear terms
and constraints.

The solver is configured to work internally with dense NxN problem divided
into two distinct parts - "main" and slack one:
* dense quadratic term  is  a  NMain*NMain  matrix  (NMain<=N),  quadratic
  coefficients are zero for variables outside of [0,NMain) range)
* linear term is general vector of length N
* linear constraints have special structure for variable with  indexes  in
  [NMain,N) range: at most one element per column can be nonzero.

This mode is intended for problems arising during SL1QP nonlinear programming.

INPUT PARAMETERS:
    State       -   solver  state  to  be configured; previously allocated
                    memory is reused as much as possible
    S           -   scale vector, array[N]:
                    * I-th element contains scale of I-th variable,
                    * S[I]>0
    XOrigin     -   origin term, array[N]. Can be zero. The solver solves
                    problem of the form
                    
                    >
                    > min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin))
                    >
                    
                    The terms A and b (as well as constraints) will be
                    specified later with separate calls.
    NMain       -   number of "main" variables, 1<=NMain<=N
    N           -   total number of variables including slack ones
    Normalize   -   apply normalization to target/constraints or not:
                    * if True, then target and constraints are normalized
                      to unit scale
                    * if False, it is assumed that the caller has performed
                      normalization.
                    The latter is recommended for presolved problems.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void vipminitdensewithslacks(vipmstate* state,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t nmain,
     ae_int_t n,
     ae_bool normalize,
     ae_state *_state)
{


    ae_assert(nmain>=1, "VIPMInitDense: NMain<1", _state);
    ae_assert(n>=1, "VIPMInitDense: N<1", _state);
    ae_assert(nmain<=n, "VIPMInitDense: NMain>N", _state);
    ae_assert(isfinitevector(s, n, _state), "VIPMInitDense: S contains infinite or NaN elements", _state);
    ae_assert(isfinitevector(xorigin, n, _state), "VIPMInitDense: XOrigin contains infinite or NaN elements", _state);
    vipmsolver_vipminit(state, s, xorigin, n, nmain, 0, normalize, _state);
}


/*************************************************************************
Initializes QP-IPM state and prepares it to receive quadratic/linear terms
and constraints.

The  solver  is configured  to  work  internally  with  sparse (N+M)x(N+M)
factorization no matter what exactly is passed - dense or sparse matrices.
Dense quadratic term will be sparsified prior to storage.

INPUT PARAMETERS:
    State       -   solver  state  to  be configured; previously allocated
                    memory is reused as much as possible
    S           -   scale vector, array[N]:
                    * I-th element contains scale of I-th variable,
                    * S[I]>0
    XOrigin     -   origin term, array[N]. Can be zero. The solver solves
                    problem of the form
                    >
                    > min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin))
                    >
                    The terms A and b (as well as constraints) will be
                    specified later with separate calls.
    N           -   total number of variables, N>=1
    Normalize   -   apply normalization to target/constraints or not:
                    * if True, then target and constraints are normalized
                      to unit scale
                    * if False, it is assumed that the caller has performed
                      normalization.
                    The latter is recommended for presolved problems.
    
This optimization mode assumes that no slack variables is present.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void vipminitsparse(vipmstate* state,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t n,
     ae_bool normalize,
     ae_state *_state)
{


    ae_assert(n>=1, "VIPMInitSparse: N<1", _state);
    ae_assert(isfinitevector(s, n, _state), "VIPMInitSparse: S contains infinite or NaN elements", _state);
    ae_assert(isfinitevector(xorigin, n, _state), "VIPMInitSparse: XOrigin contains infinite or NaN elements", _state);
    vipmsolver_vipminit(state, s, xorigin, n, n, 1, normalize, _state);
}


/*************************************************************************
Sets linear/quadratic terms for QP-IPM solver

If you initialized solver with VIMPInitDenseWithSlacks(), NMain below is a
number of non-slack variables. In other cases, NMain=N.

INPUT PARAMETERS:
    State               -   instance initialized with one of the initialization
                            functions
    DenseH              -   if HKind=0: array[NMain,NMain], dense quadratic term
                            (either upper or lower triangle)
    SparseH             -   if HKind=1: array[NMain,NMain], sparse quadratic term
                            (either upper or lower triangle)
    HKind               -   0 or 1, quadratic term format
    IsUpper             -   whether dense/sparse H contains lower or upper
                            triangle of the quadratic term
    C                   -   array[N], linear term

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void vipmsetquadraticlinear(vipmstate* state,
     /* Real    */ const ae_matrix* denseh,
     const sparsematrix* sparseh,
     ae_int_t hkind,
     ae_bool isupper,
     /* Real    */ const ae_vector* c,
     ae_state *_state)
{
    ae_int_t nmain;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t j0;
    ae_int_t j1;
    double v;
    double vv;
    ae_int_t nnz;
    ae_int_t offs;


    nmain = state->nmain;
    n = state->n;
    ae_assert(hkind==0||hkind==1, "VIPMSetQuadraticLinear: incorrect HKind", _state);
    ae_assert(isfinitevector(c, n, _state), "VIPMSetQuadraticLinear: C contains infinite or NaN elements", _state);
    ae_assert(state->factorizationtype==0||state->factorizationtype==1, "VIPMSetQuadraticLinear: unexpected factorization type", _state);
    
    /*
     * Set problem info, reset factorization flag
     */
    state->islinear = ae_false;
    state->factorizationpresent = ae_false;
    state->factorizationpoweredup = ae_false;
    
    /*
     * Linear term
     */
    rvectorsetlengthatleast(&state->c, n, _state);
    rvectorcopy(n, c, 0, &state->c, 0, _state);
    
    /*
     * Quadratic term and normalization
     *
     * NOTE: we perform integrity check for inifinities/NANs by
     *       computing sum of all matrix elements and checking its
     *       value for being finite. It is a bit faster than checking
     *       each element individually.
     */
    state->hkind = -1;
    state->targetscale = 1.0;
    if( state->factorizationtype==0 )
    {
        
        /*
         * Quadratic term is stored in dense format: either copy dense
         * term of densify sparse one
         */
        state->hkind = 0;
        rmatrixsetlengthatleast(&state->denseh, nmain, nmain, _state);
        if( hkind==0 )
        {
            
            /*
             * Copy dense quadratic term
             */
            if( isupper )
            {
                rmatrixtranspose(nmain, nmain, denseh, 0, 0, &state->denseh, 0, 0, _state);
            }
            else
            {
                rmatrixcopy(nmain, nmain, denseh, 0, 0, &state->denseh, 0, 0, _state);
            }
        }
        if( hkind==1 )
        {
            
            /*
             * Extract sparse quadratic term
             */
            ae_assert(sparseh->matrixtype==1, "VIPMSetQuadraticLinear: unexpected sparse matrix format", _state);
            ae_assert(sparseh->m==nmain, "VIPMSetQuadraticLinear: unexpected sparse matrix size", _state);
            ae_assert(sparseh->n==nmain, "VIPMSetQuadraticLinear: unexpected sparse matrix size", _state);
            for(i=0; i<=nmain-1; i++)
            {
                for(j=0; j<=i; j++)
                {
                    state->denseh.ptr.pp_double[i][j] = (double)(0);
                }
            }
            for(i=0; i<=nmain-1; i++)
            {
                
                /*
                 * diagonal element
                 */
                if( sparseh->didx.ptr.p_int[i]!=sparseh->uidx.ptr.p_int[i] )
                {
                    state->denseh.ptr.pp_double[i][i] = sparseh->vals.ptr.p_double[sparseh->didx.ptr.p_int[i]];
                }
                
                /*
                 * Off-diagonal elements
                 */
                if( isupper )
                {
                    
                    /*
                     * superdiagonal elements are moved to subdiagonal part
                     */
                    j0 = sparseh->uidx.ptr.p_int[i];
                    j1 = sparseh->ridx.ptr.p_int[i+1]-1;
                    for(j=j0; j<=j1; j++)
                    {
                        state->denseh.ptr.pp_double[sparseh->idx.ptr.p_int[j]][i] = sparseh->vals.ptr.p_double[j];
                    }
                }
                else
                {
                    
                    /*
                     * subdiagonal elements are moved to subdiagonal part
                     */
                    j0 = sparseh->ridx.ptr.p_int[i];
                    j1 = sparseh->didx.ptr.p_int[i]-1;
                    for(j=j0; j<=j1; j++)
                    {
                        state->denseh.ptr.pp_double[i][sparseh->idx.ptr.p_int[j]] = sparseh->vals.ptr.p_double[j];
                    }
                }
            }
        }
        vv = (double)(0);
        for(i=0; i<=nmain-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                vv = vv+state->denseh.ptr.pp_double[i][j];
            }
        }
        ae_assert(ae_isfinite(vv, _state), "VIPMSetQuadraticLinear: DenseH contains infinite or NaN values!", _state);
        scaledenseqpinplace(&state->denseh, ae_false, nmain, &state->c, n, &state->scl, _state);
        if( state->normalize )
        {
            state->targetscale = normalizedenseqpinplace(&state->denseh, ae_false, nmain, &state->c, n, _state);
        }
        state->isdiagonalh = ae_false;
    }
    if( state->factorizationtype==1 )
    {
        ae_assert(nmain==n, "VIPMSetQuadraticLinear: critical integrity check failed, NMain!=N", _state);
        
        /*
         * Quadratic term is stored in sparse format: either sparsify dense
         * term or copy the sparse one
         */
        state->hkind = 1;
        state->sparseh.matrixtype = 1;
        state->sparseh.m = n;
        state->sparseh.n = n;
        if( hkind==0 )
        {
            
            /*
             * Sparsify dense term
             */
            nnz = 0;
            for(i=0; i<=n-1; i++)
            {
                nnz = nnz+1;
                if( isupper )
                {
                    j0 = i+1;
                    j1 = n-1;
                }
                else
                {
                    j0 = 0;
                    j1 = i-1;
                }
                for(j=j0; j<=j1; j++)
                {
                    if( denseh->ptr.pp_double[i][j]!=(double)0 )
                    {
                        nnz = nnz+1;
                    }
                }
            }
            ivectorsetlengthatleast(&state->sparseh.ridx, n+1, _state);
            ivectorsetlengthatleast(&state->sparseh.idx, nnz, _state);
            rvectorsetlengthatleast(&state->sparseh.vals, nnz, _state);
            state->sparseh.ridx.ptr.p_int[0] = 0;
            offs = 0;
            vv = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                
                /*
                 * Off-diagonal elements are copied only when nonzero
                 */
                if( !isupper )
                {
                    for(j=0; j<=i-1; j++)
                    {
                        if( denseh->ptr.pp_double[i][j]!=(double)0 )
                        {
                            v = denseh->ptr.pp_double[i][j];
                            state->sparseh.idx.ptr.p_int[offs] = j;
                            state->sparseh.vals.ptr.p_double[offs] = v;
                            vv = vv+v;
                            offs = offs+1;
                        }
                    }
                }
                
                /*
                 * Diagonal element is always copied
                 */
                v = denseh->ptr.pp_double[i][i];
                state->sparseh.idx.ptr.p_int[offs] = i;
                state->sparseh.vals.ptr.p_double[offs] = v;
                vv = vv+v;
                offs = offs+1;
                
                /*
                 * Off-diagonal elements are copied only when nonzero
                 */
                if( isupper )
                {
                    for(j=i+1; j<=n-1; j++)
                    {
                        if( denseh->ptr.pp_double[i][j]!=(double)0 )
                        {
                            v = denseh->ptr.pp_double[i][j];
                            state->sparseh.idx.ptr.p_int[offs] = j;
                            state->sparseh.vals.ptr.p_double[offs] = v;
                            vv = vv+v;
                            offs = offs+1;
                        }
                    }
                }
                
                /*
                 * Finalize row
                 */
                state->sparseh.ridx.ptr.p_int[i+1] = offs;
            }
            ae_assert(ae_isfinite(vv, _state), "VIPMSetQuadraticLinear: DenseH contains infinite or NaN values!", _state);
            ae_assert(offs==nnz, "VIPMSetQuadraticLinear: integrity check failed", _state);
            sparsecreatecrsinplace(&state->sparseh, _state);
        }
        if( hkind==1 )
        {
            
            /*
             * Copy sparse quadratic term, but make sure that we have diagonal elements
             * present (we add diagonal if it is not present)
             */
            ae_assert(sparseh->matrixtype==1, "VIPMSetQuadraticLinear: unexpected sparse matrix format", _state);
            ae_assert(sparseh->m==n, "VIPMSetQuadraticLinear: unexpected sparse matrix size", _state);
            ae_assert(sparseh->n==n, "VIPMSetQuadraticLinear: unexpected sparse matrix size", _state);
            ivectorsetlengthatleast(&state->sparseh.ridx, n+1, _state);
            ivectorsetlengthatleast(&state->sparseh.idx, sparseh->ridx.ptr.p_int[n]+n, _state);
            rvectorsetlengthatleast(&state->sparseh.vals, sparseh->ridx.ptr.p_int[n]+n, _state);
            state->sparseh.ridx.ptr.p_int[0] = 0;
            offs = 0;
            vv = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                
                /*
                 * Copy subdiagonal elements (if needed)
                 */
                if( !isupper )
                {
                    j0 = sparseh->ridx.ptr.p_int[i];
                    j1 = sparseh->didx.ptr.p_int[i]-1;
                    for(k=j0; k<=j1; k++)
                    {
                        v = sparseh->vals.ptr.p_double[k];
                        state->sparseh.idx.ptr.p_int[offs] = sparseh->idx.ptr.p_int[k];
                        state->sparseh.vals.ptr.p_double[offs] = v;
                        vv = vv+v;
                        offs = offs+1;
                    }
                }
                
                /*
                 * Diagonal element is always copied
                 */
                v = (double)(0);
                if( sparseh->uidx.ptr.p_int[i]!=sparseh->didx.ptr.p_int[i] )
                {
                    v = sparseh->vals.ptr.p_double[sparseh->didx.ptr.p_int[i]];
                }
                state->sparseh.idx.ptr.p_int[offs] = i;
                state->sparseh.vals.ptr.p_double[offs] = v;
                vv = vv+v;
                offs = offs+1;
                
                /*
                 * Copy superdiagonal elements (if needed)
                 */
                if( isupper )
                {
                    j0 = sparseh->uidx.ptr.p_int[i];
                    j1 = sparseh->ridx.ptr.p_int[i+1]-1;
                    for(k=j0; k<=j1; k++)
                    {
                        v = sparseh->vals.ptr.p_double[k];
                        state->sparseh.idx.ptr.p_int[offs] = sparseh->idx.ptr.p_int[k];
                        state->sparseh.vals.ptr.p_double[offs] = v;
                        vv = vv+v;
                        offs = offs+1;
                    }
                }
                
                /*
                 * Finalize row
                 */
                state->sparseh.ridx.ptr.p_int[i+1] = offs;
            }
            ae_assert(ae_isfinite(vv, _state), "VIPMSetQuadraticLinear: SparseH contains infinite or NaN values!", _state);
            ae_assert(offs<=state->sparseh.vals.cnt&&offs<=state->sparseh.idx.cnt, "VIPMSetQuadraticLinear: integrity check failed", _state);
            sparsecreatecrsinplace(&state->sparseh, _state);
        }
        if( isupper )
        {
            sparsecopytransposecrsbuf(&state->sparseh, &state->tmpsparse0, _state);
            sparsecopybuf(&state->tmpsparse0, &state->sparseh, _state);
        }
        
        /*
         * Finalize
         */
        scalesparseqpinplace(&state->scl, n, &state->sparseh, &state->tmpr2, &state->dummyr, 0, &state->c, _state);
        if( state->normalize )
        {
            state->targetscale = normalizesparseqpinplace(&state->sparseh, ae_false, &state->tmpr2, &state->dummyr, 0, &state->c, n, _state);
        }
        state->isdiagonalh = state->sparseh.ridx.ptr.p_int[n]==n;
    }
    ae_assert(state->hkind>=0, "VIPMSetQuadraticLinear: integrity check failed", _state);
}


/*************************************************************************
Sets constraints for QP-IPM solver

INPUT PARAMETERS:
    State               -   instance initialized with one of the initialization
                            functions
    BndL, BndU          -   lower and upper bound. BndL[] can be -INF,
                            BndU[] can be +INF.
    SparseA             -   sparse constraint matrix, CRS format
    MSparse             -   number of sparse constraints
    DenseA              -   array[MDense,N], dense part of the constraints
    MDense              -   number of dense constraints
    CL, CU              -   lower and upper bounds for constraints, first
                            MSparse are bounds for sparse part, following
                            MDense ones are bounds for dense part,
                            MSparse+MDense in total.
                            -INF <= CL[I] <= CU[I] <= +INF.
                            
This function throws exception if constraints have inconsistent bounds, i.e.
either BndL[I]>BndU[I] or CL[I]>CU[I]. In all other cases it succeeds.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void vipmsetconstraints(vipmstate* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     const sparsematrix* sparsea,
     ae_int_t msparse,
     /* Real    */ const ae_matrix* densea,
     ae_int_t mdense,
     /* Real    */ const ae_vector* cl,
     /* Real    */ const ae_vector* cu,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t n;
    ae_int_t nmain;
    ae_int_t nslack;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t k;
    ae_int_t offsmain;
    ae_int_t offscombined;
    double vs;
    double v;


    n = state->n;
    nmain = state->nmain;
    nslack = n-nmain;
    ae_assert(mdense>=0, "VIPMSetConstraints: MDense<0", _state);
    ae_assert(msparse>=0, "VIPMSetConstraints: MSparse<0", _state);
    ae_assert(apservisfinitematrix(densea, mdense, n, _state), "VIPMSetConstraints: DenseA contains infinite or NaN values!", _state);
    ae_assert(msparse==0||sparsea->matrixtype==1, "VIPMSetConstraints: non-CRS constraint matrix!", _state);
    ae_assert(msparse==0||(sparsea->m==msparse&&sparsea->n==n), "VIPMSetConstraints: constraint matrix has incorrect size", _state);
    ae_assert(cl->cnt>=mdense+msparse, "VIPMSetConstraints: CL is too short!", _state);
    ae_assert(cu->cnt>=mdense+msparse, "VIPMSetConstraints: CU is too short!", _state);
    
    /*
     * Reset factorization flag
     */
    state->factorizationpresent = ae_false;
    state->factorizationpoweredup = ae_false;
    
    /*
     * Box constraints
     */
    rvectorsetlengthatleast(&state->bndl, n, _state);
    rvectorsetlengthatleast(&state->bndu, n, _state);
    rvectorsetlengthatleast(&state->rawbndl, n, _state);
    rvectorsetlengthatleast(&state->rawbndu, n, _state);
    bvectorsetlengthatleast(&state->hasbndl, n, _state);
    bvectorsetlengthatleast(&state->hasbndu, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
        ae_assert(!((state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_greater(bndl->ptr.p_double[i],bndu->ptr.p_double[i])), "VIPMInitDenseQuadratic: inconsistent range for box constraints", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        state->rawbndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->rawbndu.ptr.p_double[i] = bndu->ptr.p_double[i];
    }
    scaleshiftbcinplace(&state->scl, &state->xorigin, &state->bndl, &state->bndu, n, _state);
    
    /*
     * Linear constraints (full matrices)
     */
    m = mdense+msparse;
    rvectorsetlengthatleast(&state->b, m, _state);
    rvectorsetlengthatleast(&state->r, m, _state);
    rvectorsetlengthatleast(&state->ascales, m, _state);
    bvectorsetlengthatleast(&state->aflips, m, _state);
    bvectorsetlengthatleast(&state->hasr, m, _state);
    rmatrixsetlengthatleast(&state->denseafull, mdense, n, _state);
    if( msparse>0 )
    {
        sparsecopytocrsbuf(sparsea, &state->sparseafull, _state);
    }
    if( mdense>0 )
    {
        rmatrixcopy(mdense, n, densea, 0, 0, &state->denseafull, 0, 0, _state);
    }
    for(i=0; i<=m-1; i++)
    {
        ae_assert(ae_isfinite(cl->ptr.p_double[i], _state)||ae_isneginf(cl->ptr.p_double[i], _state), "VIPMInitDenseQuadratic: CL is not finite number or -INF", _state);
        ae_assert(ae_isfinite(cu->ptr.p_double[i], _state)||ae_isposinf(cu->ptr.p_double[i], _state), "VIPMInitDenseQuadratic: CU is not finite number or +INF", _state);
        
        /*
         * Store range
         */
        if( ae_isfinite(cl->ptr.p_double[i], _state)||ae_isfinite(cu->ptr.p_double[i], _state) )
        {
            
            /*
             * Non-degenerate constraint, at least one of bounds is present
             */
            if( ae_isfinite(cl->ptr.p_double[i], _state) )
            {
                ae_assert(!ae_isfinite(cu->ptr.p_double[i], _state)||ae_fp_greater_eq(cu->ptr.p_double[i],cl->ptr.p_double[i]), "VIPMInitDenseQuadratic: inconsistent range (right-hand side) for linear constraint", _state);
                if( ae_isfinite(cu->ptr.p_double[i], _state) )
                {
                    
                    /*
                     * We have both CL and CU, i.e. CL <= A*x <= CU.
                     *
                     * It can be either equality constraint (no slacks) or range constraint
                     * (two pairs of slacks variables).
                     *
                     * Try to arrange things in such a way that |CU|>=|CL| (it can be done
                     * by multiplication by -1 and boundaries swap).
                     * 
                     * Having |CU|>=|CL| will allow us to drop huge irrelevant bound CU,
                     * if we find it irrelevant during computations. Due to limitations
                     * of our slack variable substitution, it can be done only for CU.
                     */
                    if( ae_fp_greater_eq(ae_fabs(cu->ptr.p_double[i], _state),ae_fabs(cl->ptr.p_double[i], _state)) )
                    {
                        state->b.ptr.p_double[i] = cl->ptr.p_double[i];
                        state->r.ptr.p_double[i] = cu->ptr.p_double[i]-cl->ptr.p_double[i];
                        state->hasr.ptr.p_bool[i] = ae_true;
                        state->aflips.ptr.p_bool[i] = ae_false;
                        vs = (double)(1);
                    }
                    else
                    {
                        state->b.ptr.p_double[i] = -cu->ptr.p_double[i];
                        state->r.ptr.p_double[i] = cu->ptr.p_double[i]-cl->ptr.p_double[i];
                        state->hasr.ptr.p_bool[i] = ae_true;
                        state->aflips.ptr.p_bool[i] = ae_true;
                        vs = (double)(-1);
                    }
                }
                else
                {
                    
                    /*
                     * Only lower bound: CL <= A*x.
                     *
                     * One pair of slack variables added.
                     */
                    state->b.ptr.p_double[i] = cl->ptr.p_double[i];
                    state->r.ptr.p_double[i] = _state->v_posinf;
                    state->hasr.ptr.p_bool[i] = ae_false;
                    state->aflips.ptr.p_bool[i] = ae_false;
                    vs = (double)(1);
                }
            }
            else
            {
                
                /*
                 * Only upper bound: A*x <= CU
                 *
                 * One pair of slack variables added.
                 */
                state->b.ptr.p_double[i] = -cu->ptr.p_double[i];
                state->r.ptr.p_double[i] = _state->v_posinf;
                state->hasr.ptr.p_bool[i] = ae_false;
                state->aflips.ptr.p_bool[i] = ae_true;
                vs = (double)(-1);
            }
        }
        else
        {
            
            /*
             * Degenerate constraint -inf <= Ax <= +inf.
             * Generate dummy formulation.
             */
            state->b.ptr.p_double[i] = (double)(-1);
            state->r.ptr.p_double[i] = (double)(2);
            state->hasr.ptr.p_bool[i] = ae_true;
            state->aflips.ptr.p_bool[i] = ae_false;
            vs = (double)(0);
        }
        
        /*
         * Store matrix row and its scaling coefficient
         */
        if( i<msparse )
        {
            j0 = state->sparseafull.ridx.ptr.p_int[i];
            j1 = state->sparseafull.ridx.ptr.p_int[i+1]-1;
            for(j=j0; j<=j1; j++)
            {
                state->sparseafull.vals.ptr.p_double[j] = state->sparseafull.vals.ptr.p_double[j]*vs;
            }
        }
        else
        {
            for(j=0; j<=n-1; j++)
            {
                state->denseafull.ptr.pp_double[i-msparse][j] = state->denseafull.ptr.pp_double[i-msparse][j]*vs;
            }
        }
        state->ascales.ptr.p_double[i] = vs;
    }
    scaleshiftmixedbrlcinplace(&state->scl, &state->xorigin, n, &state->sparseafull, msparse, &state->denseafull, mdense, &state->b, &state->r, _state);
    if( state->normalize )
    {
        normalizemixedbrlcinplace(&state->sparseafull, msparse, &state->denseafull, mdense, &state->b, &state->r, n, ae_true, &state->tmp0, ae_true, _state);
        for(i=0; i<=m-1; i++)
        {
            state->ascales.ptr.p_double[i] = state->ascales.ptr.p_double[i]*state->tmp0.ptr.p_double[i];
        }
    }
    state->mdense = mdense;
    state->msparse = msparse;
    
    /*
     * Separate main and slack parts of the constraint matrices
     */
    ivectorsetlengthatleast(&state->tmpi, nslack, _state);
    for(i=0; i<=nslack-1; i++)
    {
        state->tmpi.ptr.p_int[i] = 0;
    }
    state->combinedaslack.m = mdense+msparse;
    state->combinedaslack.n = nslack;
    ivectorsetlengthatleast(&state->combinedaslack.ridx, mdense+msparse+1, _state);
    ivectorsetlengthatleast(&state->combinedaslack.idx, nslack, _state);
    rvectorsetlengthatleast(&state->combinedaslack.vals, nslack, _state);
    state->combinedaslack.ridx.ptr.p_int[0] = 0;
    state->sparseamain.m = msparse;
    state->sparseamain.n = nmain;
    if( msparse>0 )
    {
        ivectorsetlengthatleast(&state->sparseamain.ridx, msparse+1, _state);
        ivectorsetlengthatleast(&state->sparseamain.idx, sparsea->ridx.ptr.p_int[msparse], _state);
        rvectorsetlengthatleast(&state->sparseamain.vals, sparsea->ridx.ptr.p_int[msparse], _state);
        state->sparseamain.ridx.ptr.p_int[0] = 0;
        for(i=0; i<=msparse-1; i++)
        {
            offsmain = state->sparseamain.ridx.ptr.p_int[i];
            offscombined = state->combinedaslack.ridx.ptr.p_int[i];
            j0 = state->sparseafull.ridx.ptr.p_int[i];
            j1 = state->sparseafull.ridx.ptr.p_int[i+1]-1;
            for(j=j0; j<=j1; j++)
            {
                v = state->sparseafull.vals.ptr.p_double[j];
                k = state->sparseafull.idx.ptr.p_int[j];
                if( k<nmain )
                {
                    state->sparseamain.idx.ptr.p_int[offsmain] = k;
                    state->sparseamain.vals.ptr.p_double[offsmain] = v;
                    offsmain = offsmain+1;
                }
                else
                {
                    ae_assert(state->tmpi.ptr.p_int[k-nmain]==0, "VIPMSetConstraints: slack column contains more than one nonzero element", _state);
                    state->combinedaslack.idx.ptr.p_int[offscombined] = k-nmain;
                    state->combinedaslack.vals.ptr.p_double[offscombined] = v;
                    state->tmpi.ptr.p_int[k-nmain] = state->tmpi.ptr.p_int[k-nmain]+1;
                    offscombined = offscombined+1;
                }
            }
            state->sparseamain.ridx.ptr.p_int[i+1] = offsmain;
            state->combinedaslack.ridx.ptr.p_int[i+1] = offscombined;
        }
    }
    sparsecreatecrsinplace(&state->sparseamain, _state);
    if( mdense>0 )
    {
        rmatrixsetlengthatleast(&state->denseamain, mdense, nmain, _state);
        rmatrixcopy(mdense, nmain, &state->denseafull, 0, 0, &state->denseamain, 0, 0, _state);
        for(i=0; i<=mdense-1; i++)
        {
            offscombined = state->combinedaslack.ridx.ptr.p_int[msparse+i];
            for(k=nmain; k<=n-1; k++)
            {
                if( state->denseafull.ptr.pp_double[i][k]!=(double)0 )
                {
                    ae_assert(state->tmpi.ptr.p_int[k-nmain]==0, "VIPMSetConstraints: slack column contains more than one nonzero element", _state);
                    state->combinedaslack.idx.ptr.p_int[offscombined] = k-nmain;
                    state->combinedaslack.vals.ptr.p_double[offscombined] = state->denseafull.ptr.pp_double[i][k];
                    state->tmpi.ptr.p_int[k-nmain] = state->tmpi.ptr.p_int[k-nmain]+1;
                    offscombined = offscombined+1;
                }
            }
            state->combinedaslack.ridx.ptr.p_int[msparse+i+1] = offscombined;
        }
    }
    sparsecreatecrsinplace(&state->combinedaslack, _state);
}


/*************************************************************************
Sets stopping criteria for QP-IPM solver.

You can set all epsilon-values to one small value, about 1.0E-6.

INPUT PARAMETERS:
    State               -   instance initialized with one of the initialization
                            functions
    EpsP                -   maximum primal error allowed in the  solution,
                            EpsP>=0. Zero will be  automatically  replaced
                            by recommended default value,  which is  equal
                            to 10*Sqrt(Epsilon) in the current version
    EpsD                -   maximum  dual  error allowed in the  solution,
                            EpsP>=0. Zero will be  automatically  replaced
                            by recommended default value,  which is  equal
                            to 10*Sqrt(Epsilon) in the current version
    EpsGap              -   maximum  duality gap allowed in the  solution,
                            EpsP>=0. Zero will be  automatically  replaced
                            by recommended default value,  which is  equal
                            to 10*Sqrt(Epsilon) in the current version

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void vipmsetcond(vipmstate* state,
     double epsp,
     double epsd,
     double epsgap,
     ae_state *_state)
{
    double sml;


    ae_assert(ae_isfinite(epsp, _state)&&ae_fp_greater_eq(epsp,(double)(0)), "VIPMSetCond: EpsP is infinite or negative", _state);
    ae_assert(ae_isfinite(epsd, _state)&&ae_fp_greater_eq(epsd,(double)(0)), "VIPMSetCond: EpsD is infinite or negative", _state);
    ae_assert(ae_isfinite(epsgap, _state)&&ae_fp_greater_eq(epsgap,(double)(0)), "VIPMSetCond: EpsP is infinite or negative", _state);
    sml = ae_sqrt(ae_machineepsilon, _state);
    state->epsp = coalesce(epsp, sml, _state);
    state->epsd = coalesce(epsd, sml, _state);
    state->epsgap = coalesce(epsgap, sml, _state);
}


/*************************************************************************
Solve QP problem.

INPUT PARAMETERS:
    State               -   solver instance
    DropBigBounds       -   If True, algorithm may drop box and linear constraints
                            with huge bound values that destabilize algorithm.
    
OUTPUT PARAMETERS:
    XS                  -   array[N], solution
    LagBC               -   array[N], Lagrange multipliers for box constraints
    LagLC               -   array[M], Lagrange multipliers for linear constraints
    TerminationType     -   completion code, positive values for success,
                            negative for failures (XS constrains best point
                            found so far):
                            * -2    the task is either unbounded or infeasible;
                                    the IPM solver has difficulty distinguishing between these two.
                            * +1    stopping criteria are met
                            * +7    stopping criteria are too stringent

RESULT:

This function ALWAYS returns something  meaningful in XS, LagBC, LagLC - 
either solution or the best point so far, even for negative TerminationType.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void vipmoptimize(vipmstate* state,
     ae_bool dropbigbounds,
     /* Real    */ ae_vector* xs,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* laglc,
     ae_int_t* terminationtype,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t iteridx;
    double mu;
    double muaff;
    double sigma;
    double alphaaffp;
    double alphaaffd;
    double alphap;
    double alphad;
    ae_int_t primalstagnationcnt;
    ae_int_t dualstagnationcnt;
    double regeps;
    double regeps0;
    double regeps1;
    double dampeps;
    double safedampeps;
    double modeps;
    double maxdampeps;
    double regfree;
    double dampfree;
    ae_int_t droppedbounds;
    double primalxscale;
    double errp2;
    double errd2;
    double errpinf;
    double errdinf;
    double preverrp2;
    double preverrd2;
    double errgap;
    double eprimal;
    double edual;
    double egap;
    double mumin;
    double mustop;
    double y0nrm;
    double bady;
    double mxprimal;
    double mxdeltaprimal;
    ae_int_t bestiteridx;
    double besterr;
    double bestegap;
    double besteprimal;
    double bestedual;
    ae_bool loadbest;

    *terminationtype = 0;

    n = state->n;
    m = state->mdense+state->msparse;
    state->dotrace = ae_is_trace_enabled("IPM");
    state->dodetailedtrace = state->dotrace&&ae_is_trace_enabled("IPM.DETAILED");
    
    /*
     * Prepare outputs
     */
    rsetallocv(n, 0.0, xs, _state);
    rsetallocv(n, 0.0, lagbc, _state);
    rsetallocv(m, 0.0, laglc, _state);
    
    /*
     * Some integrity checks:
     * * we need PrimalStagnationLen<DualStagnationLen in order to be able to correctly
     *   detect infeasible instances (stagnated dual error is present in both infeasible
     *   and unbounded instances, so we should check for primal stagnation a few iters
     *   before checking for dual stagnation)
     */
    ae_assert(vipmsolver_primalstagnationlen<vipmsolver_dualstagnationlen, "VIPM: critical integrity failure - incorrect configuration parameters", _state);
    
    /*
     * Trace output (if needed)
     */
    if( state->dotrace )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("// IPM SOLVER STARTED                                                                             //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
    }
    
    /*
     * Prepare regularization coefficients:
     * * RegEps - one that is applied to initial (5N+5M)x(5N+5M) KKT system. This one has to be
     *   small because it perturbs solution returned by the algorithm. Essential in order to
     *   avoid stalling at extremely large points.
     * * ModEps - small modification applied to LDLT decomposition in order to preserve sign
     *   of diagonal elements
     * * DampEps - damping coefficient for damped Newton step. Comes along with SafeDampEps 
     *   (threshold value when some safeguards are turned off in order to preserve convergence
     *   speed) and MaxDampEps - threshold value when we consider problem overregularized and stop.
     * * DampFree - additional damping coefficient for free variables
     */
    regfree = ae_pow(ae_machineepsilon, 0.75, _state);
    dampfree = (double)(0);
    regeps0 = ae_sqrt(ae_machineepsilon, _state);
    regeps1 = (double)10*ae_machineepsilon;
    regeps = regeps0;
    modeps = ((double)100+ae_sqrt((double)(n), _state))*ae_machineepsilon;
    dampeps = (double)10*ae_machineepsilon;
    safedampeps = ae_sqrt(ae_machineepsilon, _state);
    maxdampeps = ae_sqrt(ae_sqrt(ae_machineepsilon, _state), _state);
    
    /*
     * Set up initial state
     */
    state->repiterationscount = 0;
    state->repncholesky = 0;
    mustop = ((double)100+ae_sqrt((double)(n), _state))*ae_machineepsilon;
    mumin = 0.01*mustop;
    vipmsolver_vipmpowerup(state, regfree, _state);
    vipmsolver_varsinitfrom(&state->best, &state->current, _state);
    vipmsolver_varsinitbyzero(&state->zerovars, n, m, _state);
    vipmsolver_varsinitbyzero(&state->deltaaff, n, m, _state);
    vipmsolver_varsinitbyzero(&state->deltacorr, n, m, _state);
    bestiteridx = -1;
    besterr = ae_maxrealnumber;
    bestegap = ae_maxrealnumber;
    besteprimal = ae_maxrealnumber;
    bestedual = ae_maxrealnumber;
    vipmsolver_traceprogress(state, 0.0, 0.0, 0.0, 0.0, 0.0, _state);
    y0nrm = (double)(0);
    y0nrm = ae_maxreal(y0nrm, rmaxabsv(m, &state->current.y, _state), _state);
    y0nrm = ae_maxreal(y0nrm, rmaxabsv(m, &state->current.v, _state), _state);
    y0nrm = ae_maxreal(y0nrm, rmaxabsv(m, &state->current.q, _state), _state);
    y0nrm = ae_maxreal(y0nrm, rmaxabsv(n, &state->current.z, _state), _state);
    y0nrm = ae_maxreal(y0nrm, rmaxabsv(n, &state->current.s, _state), _state);
    
    /*
     * Start iteration
     */
    loadbest = ae_true;
    primalstagnationcnt = 0;
    dualstagnationcnt = 0;
    *terminationtype = 7;
    errp2 = ae_maxrealnumber;
    errd2 = ae_maxrealnumber;
    for(iteridx=0; iteridx<=vipmsolver_maxipmits-1; iteridx++)
    {
        regeps = rcase2(iteridx<vipmsolver_phase0length, regeps0, regeps1, _state);
        
        /*
         * Trace beginning
         */
        if( state->dotrace )
        {
            ae_trace("=== PREDICTOR-CORRECTOR STEP %2d ====================================================================\n",
                (int)(iteridx));
        }
        
        /*
         * Check regularization status, terminate if overregularized
         */
        if( ae_fp_greater_eq(dampeps,maxdampeps) )
        {
            if( state->dotrace )
            {
                ae_trace("> tried to increase regularization parameter, but it is too large\n");
                ae_trace("> it is likely that stopping conditions are too stringent, stopping at the best point found so far\n");
            }
            *terminationtype = 7;
            break;
        }
        
        /*
         * Precompute factorization
         *
         * NOTE: we use "solver" regularization coefficient at this moment
         */
        if( !vipmsolver_vipmprecomputenewtonfactorization(state, &state->current, regeps, modeps, dampeps, dampfree, _state) )
        {
            
            /*
             * KKT factorization failed.
             * Increase regularization parameter and skip this iteration.
             */
            dampeps = (double)10*dampeps;
            if( state->dotrace )
            {
                ae_trace("> LDLT factorization failed due to rounding errors\n");
                ae_trace("> increasing damping coefficient to %0.2e, skipping iteration\n",
                    (double)(dampeps));
            }
            continue;
        }
        
        /*
         * Compute Mu
         */
        mu = vipmsolver_varscomputemu(state, &state->current, _state);
        
        /*
         * Compute affine scaling step for Mehrotra's predictor-corrector algorithm
         */
        if( !vipmsolver_vipmcomputestepdirection(state, &state->current, 0.0, &state->zerovars, &state->deltaaff, regeps, ae_fp_greater_eq(dampeps,safedampeps), _state) )
        {
            
            /*
             * Affine scaling step failed due to numerical errors.
             * Increase regularization parameter and skip this iteration.
             */
            dampeps = (double)10*dampeps;
            if( state->dotrace )
            {
                ae_trace("> affine scaling step failed to decrease residual due to rounding errors\n");
                ae_trace("> increasing damping coefficient to %0.2e, skipping iteration\n",
                    (double)(dampeps));
            }
            continue;
        }
        vipmsolver_vipmcomputesteplength(state, &state->current, &state->deltaaff, vipmsolver_steplengthdecay, ae_false, &alphaaffp, &alphaaffd, _state);
        
        /*
         * Compute MuAff and centering parameter
         */
        vipmsolver_varsinitfrom(&state->trial, &state->current, _state);
        vipmsolver_varsaddstep(&state->trial, &state->deltaaff, alphaaffp, alphaaffd, _state);
        muaff = vipmsolver_varscomputemu(state, &state->trial, _state);
        sigma = ae_minreal(ae_pow((muaff+mumin)/(mu+mumin), (double)(3), _state), 1.0, _state);
        ae_assert(ae_isfinite(sigma, _state)&&ae_fp_less_eq(sigma,(double)(1)), "VIPMOptimize: critical integrity check failed for Sigma (infinite or greater than 1)", _state);
        
        /*
         * Compute corrector step
         */
        if( !vipmsolver_vipmcomputestepdirection(state, &state->current, sigma*mu+mumin, &state->deltaaff, &state->deltacorr, regeps, ae_fp_greater_eq(dampeps,safedampeps), _state) )
        {
            
            /*
             * Affine scaling step failed due to numerical errors.
             * Increase regularization parameter and skip this iteration.
             */
            dampeps = (double)10*dampeps;
            if( state->dotrace )
            {
                ae_trace("> corrector step failed to decrease residual due to rounding errors\n");
                ae_trace("> increasing damping coefficient to %0.2e, skipping iteration\n",
                    (double)(dampeps));
            }
            continue;
        }
        vipmsolver_vipmcomputesteplength(state, &state->current, &state->deltacorr, vipmsolver_steplengthdecay, ae_false, &alphap, &alphad, _state);
        if( ae_fp_greater_eq((double)(iteridx),vipmsolver_minitersbeforesafeguards)&&(ae_fp_less_eq(alphap,vipmsolver_badsteplength)||ae_fp_less_eq(alphad,vipmsolver_badsteplength)) )
        {
            
            /*
             * Affine scaling step failed due to numerical errors.
             * Increase regularization parameter and skip this iteration.
             */
            dampeps = (double)10*dampeps;
            if( state->dotrace )
            {
                ae_trace("> step length is too short, suspecting rounding errors\n");
                ae_trace("> increasing damping coefficient to %0.2e, skipping iteration\n",
                    (double)(dampeps));
            }
            continue;
        }
        
        /*
         * Perform a step
         */
        vipmsolver_runintegritychecks(state, &state->current, &state->deltacorr, alphap, alphad, _state);
        vipmsolver_vipmperformstep(state, alphap, alphad, _state);
        vipmsolver_traceprogress(state, mu, muaff, sigma, alphap, alphad, _state);
        
        /*
         * Check for excessive bounds (one that are so large that they are both irrelevant
         * and destabilizing due to their magnitude)
         */
        if( dropbigbounds&&iteridx>=vipmsolver_minitersbeforedroppingbounds )
        {
            ae_assert(ae_fp_less_eq((double)10*vipmsolver_bigconstrmag,(double)1/vipmsolver_bigconstrxtol), "VIPMOptimize: integrity check failed (incorrect BigConstr settings)", _state);
            droppedbounds = 0;
            
            /*
             * Determine variable and step scales.
             * Both quantities are bounded from below by 1.0
             */
            mxprimal = 1.0;
            mxprimal = ae_maxreal(mxprimal, rmaxabsv(n, &state->current.x, _state), _state);
            mxprimal = ae_maxreal(mxprimal, rmaxabsv(n, &state->current.g, _state), _state);
            mxprimal = ae_maxreal(mxprimal, rmaxabsv(n, &state->current.t, _state), _state);
            mxprimal = ae_maxreal(mxprimal, rmaxabsv(m, &state->current.w, _state), _state);
            mxprimal = ae_maxreal(mxprimal, rmaxabsv(m, &state->current.p, _state), _state);
            mxdeltaprimal = 1.0;
            mxdeltaprimal = ae_maxreal(mxdeltaprimal, alphap*rmaxabsv(n, &state->deltacorr.x, _state), _state);
            mxdeltaprimal = ae_maxreal(mxdeltaprimal, alphap*rmaxabsv(n, &state->deltacorr.g, _state), _state);
            mxdeltaprimal = ae_maxreal(mxdeltaprimal, alphap*rmaxabsv(n, &state->deltacorr.t, _state), _state);
            mxdeltaprimal = ae_maxreal(mxdeltaprimal, alphap*rmaxabsv(m, &state->deltacorr.w, _state), _state);
            mxdeltaprimal = ae_maxreal(mxdeltaprimal, alphap*rmaxabsv(m, &state->deltacorr.p, _state), _state);
            
            /*
             * If changes in primal variables are small enough, try dropping too large bounds
             */
            if( ae_fp_less(mxdeltaprimal,mxprimal*vipmsolver_bigconstrxtol) )
            {
                
                /*
                 * Drop irrelevant box constraints
                 */
                primalxscale = 1.0;
                primalxscale = ae_maxreal(primalxscale, rmaxabsv(n, &state->current.x, _state), _state);
                for(i=0; i<=n-1; i++)
                {
                    if( (state->hasbndl.ptr.p_bool[i]&&state->hasgz.ptr.p_bool[i])&&ae_fp_greater(ae_fabs(state->bndl.ptr.p_double[i], _state),vipmsolver_bigconstrmag*primalxscale) )
                    {
                        state->hasgz.ptr.p_bool[i] = ae_false;
                        state->current.g.ptr.p_double[i] = (double)(0);
                        state->current.z.ptr.p_double[i] = (double)(0);
                        state->cntgz = state->cntgz-1;
                        inc(&droppedbounds, _state);
                    }
                    if( (state->hasbndu.ptr.p_bool[i]&&state->hasts.ptr.p_bool[i])&&ae_fp_greater(ae_fabs(state->bndu.ptr.p_double[i], _state),vipmsolver_bigconstrmag*primalxscale) )
                    {
                        state->hasts.ptr.p_bool[i] = ae_false;
                        state->current.t.ptr.p_double[i] = (double)(0);
                        state->current.s.ptr.p_double[i] = (double)(0);
                        state->cntts = state->cntts-1;
                        inc(&droppedbounds, _state);
                    }
                }
                
                /*
                 * Drop irrelevant linear constraints. Due to specifics of the solver
                 * we can drop only right part part of b<=Ax<=b+r.
                 *
                 * We can't drop b<=A from b<=A<=b+r because it impossible with our choice of
                 * slack variables. Usually we do not need to do so because we reorder constraints
                 * during initialization in such a way that |b+r|>|b| and because typical
                 * applications do not have excessively large lower AND upper bound (user may
                 * specify large value for 'absent' bound, but usually he does not mark both bounds as absent).
                 */
                vipmsolver_multiplygeax(state, 1.0, &state->current.x, 0, 0.0, &state->tmpax, 0, _state);
                primalxscale = 1.0;
                primalxscale = ae_maxreal(primalxscale, rmaxabsv(n, &state->current.x, _state), _state);
                primalxscale = ae_maxreal(primalxscale, rmaxabsv(m, &state->tmpax, _state), _state);
                for(i=0; i<=m-1; i++)
                {
                    if( ((state->hasr.ptr.p_bool[i]&&state->haspq.ptr.p_bool[i])&&ae_fp_greater(ae_fabs(state->b.ptr.p_double[i]+state->r.ptr.p_double[i], _state),vipmsolver_bigconstrmag*primalxscale))&&ae_fp_less(ae_fabs(state->b.ptr.p_double[i], _state),vipmsolver_bigconstrmag*primalxscale) )
                    {
                        ae_assert(state->haswv.ptr.p_bool[i]&&state->haspq.ptr.p_bool[i], "VIPMOptimize: unexpected integrity check failure (4y64)", _state);
                        state->haspq.ptr.p_bool[i] = ae_false;
                        state->current.p.ptr.p_double[i] = (double)(0);
                        state->current.q.ptr.p_double[i] = (double)(0);
                        state->cntpq = state->cntpq-1;
                        inc(&droppedbounds, _state);
                    }
                }
                
                /*
                 * Trace output
                 */
                if( droppedbounds>0 )
                {
                    if( state->dotrace )
                    {
                        ae_trace("[NOTICE] detected %0d irrelevant constraints with huge bounds, X converged to values well below them, dropping...\n",
                            (int)(droppedbounds));
                    }
                }
            }
        }
        
        /*
         * Check stopping criteria
         * * primal and dual stagnation are checked only when following criteria are met:
         *   1) Mu is smaller than 1 (we already converged close enough)
         *   2) we performed more than MinItersBeforeStagnation iterations
         */
        preverrp2 = errp2;
        preverrd2 = errd2;
        vipmsolver_computeerrors(state, &errp2, &errd2, &errpinf, &errdinf, &errgap, _state);
        mu = vipmsolver_varscomputemu(state, &state->current, _state);
        egap = errgap;
        eprimal = errpinf;
        edual = errdinf;
        if( ae_fp_less(rmax3(egap, eprimal, edual, _state),besterr) )
        {
            
            /*
             * Save best point found so far
             */
            vipmsolver_varsinitfrom(&state->best, &state->current, _state);
            bestiteridx = iteridx;
            besterr = rmax3(egap, eprimal, edual, _state);
            bestegap = egap;
            besteprimal = eprimal;
            bestedual = edual;
        }
        if( bestiteridx>0&&iteridx>bestiteridx+vipmsolver_minitersbeforeeworststagnation )
        {
            if( state->dotrace )
            {
                ae_trace("> worst of primal/dual/gap errors stagnated for %0d its, stopping at the best point found so far\n",
                    (int)(vipmsolver_minitersbeforeeworststagnation));
            }
            break;
        }
        if( ((ae_fp_less_eq(egap,state->epsgap)&&ae_fp_greater_eq(errp2,vipmsolver_stagnationdelta*preverrp2))&&ae_fp_greater_eq(errpinf,vipmsolver_primalinfeasible1))&&iteridx>=vipmsolver_minitersbeforestagnation )
        {
            inc(&primalstagnationcnt, _state);
            if( primalstagnationcnt>=vipmsolver_primalstagnationlen )
            {
                if( state->dotrace )
                {
                    ae_trace("> primal error stagnated for %0d its, stopping at the best point found so far\n",
                        (int)(vipmsolver_primalstagnationlen));
                }
                break;
            }
        }
        else
        {
            primalstagnationcnt = 0;
        }
        if( ((ae_fp_less_eq(egap,state->epsgap)&&ae_fp_greater_eq(errd2,vipmsolver_stagnationdelta*preverrd2))&&ae_fp_greater_eq(errdinf,vipmsolver_dualinfeasible1))&&iteridx>=vipmsolver_minitersbeforestagnation )
        {
            inc(&dualstagnationcnt, _state);
            if( dualstagnationcnt>=vipmsolver_dualstagnationlen )
            {
                if( state->dotrace )
                {
                    ae_trace("> dual error stagnated for %0d its, stopping at the best point found so far\n",
                        (int)(vipmsolver_dualstagnationlen));
                }
                break;
            }
        }
        else
        {
            dualstagnationcnt = 0;
        }
        if( ae_fp_less_eq(mu,mustop)&&iteridx>=vipmsolver_itersfortoostringentcond )
        {
            if( state->dotrace )
            {
                ae_trace("> stopping conditions are too stringent, stopping at the best point found so far\n");
            }
            *terminationtype = 7;
            break;
        }
        if( (ae_fp_less_eq(egap,state->epsgap)&&ae_fp_less_eq(eprimal,state->epsp))&&ae_fp_less_eq(edual,state->epsd) )
        {
            if( state->dotrace )
            {
                ae_trace("> stopping criteria are met\n");
            }
            *terminationtype = 1;
            loadbest = ae_false;
            break;
        }
        bady = vipmsolver_bigy;
        bady = ae_maxreal(bady, vipmsolver_ygrowth*y0nrm, _state);
        bady = ae_maxreal(bady, vipmsolver_ygrowth*rmaxabsv(n, &state->current.x, _state), _state);
        bady = ae_maxreal(bady, vipmsolver_ygrowth*rmaxabsv(n, &state->current.g, _state), _state);
        bady = ae_maxreal(bady, vipmsolver_ygrowth*rmaxabsv(n, &state->current.t, _state), _state);
        bady = ae_maxreal(bady, vipmsolver_ygrowth*rmaxabsv(m, &state->current.w, _state), _state);
        bady = ae_maxreal(bady, vipmsolver_ygrowth*rmaxabsv(m, &state->current.p, _state), _state);
        if( ae_fp_greater_eq(rmaxabsv(m, &state->current.y, _state),bady)&&iteridx>=vipmsolver_minitersbeforeinfeasible )
        {
            if( state->dotrace )
            {
                ae_trace("> |Y| increased beyond %0.1e, stopping at the best point found so far\n",
                    (double)(bady));
            }
            break;
        }
    }
    
    /*
     * Load best point, perform some checks
     */
    if( loadbest )
    {
        
        /*
         * Load best point
         *
         * NOTE: TouchReal() is used to avoid spurious compiler warnings about 'set but unused'
         */
        if( state->dotrace )
        {
            ae_trace("> the best point so far is one from iteration %0d\n",
                (int)(bestiteridx));
        }
        vipmsolver_varsinitfrom(&state->current, &state->best, _state);
        touchreal(&besteprimal, _state);
        touchreal(&bestedual, _state);
        touchreal(&bestegap, _state);
        
        /*
         * If no error flags were set yet, check solution quality
         */
        bady = vipmsolver_bigy;
        bady = ae_maxreal(bady, vipmsolver_ygrowth*y0nrm, _state);
        bady = ae_maxreal(bady, vipmsolver_ygrowth*rmaxabsv(n, &state->current.x, _state), _state);
        bady = ae_maxreal(bady, vipmsolver_ygrowth*rmaxabsv(n, &state->current.g, _state), _state);
        bady = ae_maxreal(bady, vipmsolver_ygrowth*rmaxabsv(n, &state->current.t, _state), _state);
        bady = ae_maxreal(bady, vipmsolver_ygrowth*rmaxabsv(m, &state->current.w, _state), _state);
        bady = ae_maxreal(bady, vipmsolver_ygrowth*rmaxabsv(m, &state->current.p, _state), _state);
        if( *terminationtype>0&&ae_fp_greater_eq(rmaxabsv(m, &state->current.y, _state),bady) )
        {
            *terminationtype = -2;
            if( state->dotrace )
            {
                ae_trace("> |Y| increased beyond %0.1e, declaring infeasibility/unboundedness\n",
                    (double)(bady));
            }
        }
        if( *terminationtype>0&&ae_fp_greater_eq(besteprimal,vipmsolver_primalinfeasible1) )
        {
            *terminationtype = -2;
            if( state->dotrace )
            {
                ae_trace("> primal error at the best point is too high, declaring infeasibility/unboundedness\n");
            }
        }
        if( *terminationtype>0&&ae_fp_greater_eq(bestedual,vipmsolver_dualinfeasible1) )
        {
            *terminationtype = -2;
            if( state->dotrace )
            {
                ae_trace("> dual error at the best point is too high, declaring infeasibility/unboundedness\n");
            }
        }
    }
    
    /*
     * Output
     */
    vipmsolver_multiplyhx(state, &state->current.x, &state->tmp0, _state);
    raddv(n, 1.0, &state->c, &state->tmp0, _state);
    vipmsolver_multiplygeatx(state, -1.0, &state->current.y, 0, 1.0, &state->tmp0, 0, _state);
    for(i=0; i<=n-1; i++)
    {
        if( state->isfrozen.ptr.p_bool[i] )
        {
            
            /*
             * I-th variable is frozen, use its frozen value.
             * By the definition, I-th Lagrangian multiplier is an I-th component of Lagrangian gradient
             */
            xs->ptr.p_double[i] = state->current.x.ptr.p_double[i];
            lagbc->ptr.p_double[i] = -state->tmp0.ptr.p_double[i];
        }
        else
        {
            xs->ptr.p_double[i] = state->current.x.ptr.p_double[i];
            lagbc->ptr.p_double[i] = 0.0;
            if( state->hasgz.ptr.p_bool[i] )
            {
                lagbc->ptr.p_double[i] = lagbc->ptr.p_double[i]-state->current.z.ptr.p_double[i];
            }
            if( state->hasts.ptr.p_bool[i] )
            {
                lagbc->ptr.p_double[i] = lagbc->ptr.p_double[i]+state->current.s.ptr.p_double[i];
            }
        }
    }
    for(i=0; i<=m-1; i++)
    {
        laglc->ptr.p_double[i] = -state->current.y.ptr.p_double[i];
    }
    
    /*
     * Unscale point and Lagrange multipliers
     */
    unscaleunshiftpointbc(&state->scl, &state->xorigin, &state->rawbndl, &state->rawbndu, &state->bndl, &state->bndu, &state->hasbndl, &state->hasbndu, xs, n, _state);
    for(i=0; i<=n-1; i++)
    {
        lagbc->ptr.p_double[i] = lagbc->ptr.p_double[i]*state->targetscale/state->scl.ptr.p_double[i];
    }
    for(i=0; i<=m-1; i++)
    {
        laglc->ptr.p_double[i] = laglc->ptr.p_double[i]*state->targetscale/coalesce(state->ascales.ptr.p_double[i], 1.0, _state);
    }
}


/*************************************************************************
Allocates place for variables of IPM and fills by zeros.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_varsinitbyzero(vipmvars* vstate,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state)
{


    ae_assert(n>=1, "VarsInitByZero: N<1", _state);
    ae_assert(m>=0, "VarsInitByZero: M<0", _state);
    vstate->n = n;
    vstate->m = m;
    rsetallocv(n, 0.0, &vstate->x, _state);
    rsetallocv(n, 0.0, &vstate->g, _state);
    rsetallocv(n, 0.0, &vstate->t, _state);
    rsetallocv(n, 0.0, &vstate->z, _state);
    rsetallocv(n, 0.0, &vstate->s, _state);
    rsetallocv(m, 0.0, &vstate->y, _state);
    rsetallocv(m, 0.0, &vstate->w, _state);
    rsetallocv(m, 0.0, &vstate->p, _state);
    rsetallocv(m, 0.0, &vstate->v, _state);
    rsetallocv(m, 0.0, &vstate->q, _state);
}


/*************************************************************************
Allocates place for variables of IPM and fills them by values of
the source

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_varsinitfrom(vipmvars* vstate,
     const vipmvars* vsrc,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;


    n = vsrc->n;
    m = vsrc->m;
    ae_assert(n>=1, "VarsInitFrom: N<1", _state);
    ae_assert(m>=0, "VarsInitFrom: M<0", _state);
    vstate->n = n;
    vstate->m = m;
    rcopyallocv(n, &vsrc->x, &vstate->x, _state);
    rcopyallocv(n, &vsrc->g, &vstate->g, _state);
    rcopyallocv(n, &vsrc->t, &vstate->t, _state);
    rcopyallocv(n, &vsrc->z, &vstate->z, _state);
    rcopyallocv(n, &vsrc->s, &vstate->s, _state);
    rcopyallocv(m, &vsrc->y, &vstate->y, _state);
    rcopyallocv(m, &vsrc->w, &vstate->w, _state);
    rcopyallocv(m, &vsrc->p, &vstate->p, _state);
    rcopyallocv(m, &vsrc->v, &vstate->v, _state);
    rcopyallocv(m, &vsrc->q, &vstate->q, _state);
}


/*************************************************************************
Adds to variables direction vector times step length. Different
lengths are used for primal and dual steps.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_varsaddstep(vipmvars* vstate,
     const vipmvars* vdir,
     double stpp,
     double stpd,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t m;


    n = vstate->n;
    m = vstate->m;
    ae_assert(n>=1, "VarsAddStep: N<1", _state);
    ae_assert(m>=0, "VarsAddStep: M<0", _state);
    ae_assert(n==vdir->n, "VarsAddStep: sizes mismatch", _state);
    ae_assert(m==vdir->m, "VarsAddStep: sizes mismatch", _state);
    for(i=0; i<=n-1; i++)
    {
        vstate->x.ptr.p_double[i] = vstate->x.ptr.p_double[i]+stpp*vdir->x.ptr.p_double[i];
        vstate->g.ptr.p_double[i] = vstate->g.ptr.p_double[i]+stpp*vdir->g.ptr.p_double[i];
        vstate->t.ptr.p_double[i] = vstate->t.ptr.p_double[i]+stpp*vdir->t.ptr.p_double[i];
        vstate->z.ptr.p_double[i] = vstate->z.ptr.p_double[i]+stpd*vdir->z.ptr.p_double[i];
        vstate->s.ptr.p_double[i] = vstate->s.ptr.p_double[i]+stpd*vdir->s.ptr.p_double[i];
    }
    for(i=0; i<=m-1; i++)
    {
        vstate->w.ptr.p_double[i] = vstate->w.ptr.p_double[i]+stpp*vdir->w.ptr.p_double[i];
        vstate->p.ptr.p_double[i] = vstate->p.ptr.p_double[i]+stpp*vdir->p.ptr.p_double[i];
        vstate->y.ptr.p_double[i] = vstate->y.ptr.p_double[i]+stpd*vdir->y.ptr.p_double[i];
        vstate->v.ptr.p_double[i] = vstate->v.ptr.p_double[i]+stpd*vdir->v.ptr.p_double[i];
        vstate->q.ptr.p_double[i] = vstate->q.ptr.p_double[i]+stpd*vdir->q.ptr.p_double[i];
    }
}


/*************************************************************************
Computes complementarity gap

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static double vipmsolver_varscomputecomplementaritygap(const vipmvars* vstate,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t m;
    double result;


    n = vstate->n;
    m = vstate->m;
    result = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        result = result+vstate->z.ptr.p_double[i]*vstate->g.ptr.p_double[i]+vstate->s.ptr.p_double[i]*vstate->t.ptr.p_double[i];
    }
    for(i=0; i<=m-1; i++)
    {
        result = result+vstate->v.ptr.p_double[i]*vstate->w.ptr.p_double[i]+vstate->p.ptr.p_double[i]*vstate->q.ptr.p_double[i];
    }
    return result;
}


/*************************************************************************
Computes empirical value of the barrier parameter Mu

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static double vipmsolver_varscomputemu(const vipmstate* state,
     const vipmvars* vstate,
     ae_state *_state)
{
    double result;


    result = (double)(0);
    result = result+rdotv(vstate->n, &vstate->z, &vstate->g, _state)+rdotv(vstate->n, &vstate->s, &vstate->t, _state);
    result = result+rdotv(vstate->m, &vstate->v, &vstate->w, _state)+rdotv(vstate->m, &vstate->p, &vstate->q, _state);
    result = result/coalesce((double)(state->cntgz+state->cntts+state->cntwv+state->cntpq), (double)(1), _state);
    return result;
}


/*************************************************************************
Initializes reduced sparse system.

Works only for sparse IPM.

  -- ALGLIB --
     Copyright 15.11.2021 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_reducedsysteminit(vipmreducedsparsesystem* s,
     const vipmstate* solver,
     ae_state *_state)
{
    ae_int_t ntotal;
    ae_int_t nnzmax;
    ae_int_t factldlt;
    ae_int_t permpriorityamd;
    ae_int_t memreuse;
    ae_int_t offs;
    ae_int_t rowoffs;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t sumcoldeg;
    ae_int_t sumrowdeg;
    ae_int_t colthreshold;
    ae_int_t rowthreshold;
    ae_int_t eligiblecols;
    ae_int_t eligiblerows;


    ae_assert(solver->factorizationtype==1, "ReducedSystemInit: unexpected factorization type", _state);
    ae_assert(solver->hkind==1, "ReducedSystemInit: unexpected HKind", _state);
    ntotal = solver->n+solver->mdense+solver->msparse;
    s->ntotal = ntotal;
    rallocv(ntotal, &s->effectivediag, _state);
    
    /*
     * Determine maximum amount of memory required to store sparse matrices
     */
    nnzmax = solver->sparseh.ridx.ptr.p_int[solver->n];
    if( solver->msparse>0 )
    {
        nnzmax = nnzmax+solver->sparseafull.ridx.ptr.p_int[solver->msparse];
    }
    if( solver->mdense>0 )
    {
        nnzmax = nnzmax+solver->n*solver->mdense;
    }
    nnzmax = nnzmax+ntotal;
    
    /*
     * Default DIAG and DAMP terms
     */
    rsetallocv(ntotal, 0.0, &s->diagterm, _state);
    rsetallocv(ntotal, 0.0, &s->dampterm, _state);
    
    /*
     * Prepare lower triangle of template KKT matrix (KKT system without D and E
     * terms being added to diagonals)
     */
    s->rawsystem.m = ntotal;
    s->rawsystem.n = ntotal;
    ivectorsetlengthatleast(&s->rawsystem.idx, nnzmax, _state);
    rvectorsetlengthatleast(&s->rawsystem.vals, nnzmax, _state);
    ivectorsetlengthatleast(&s->rawsystem.ridx, ntotal+1, _state);
    s->rawsystem.ridx.ptr.p_int[0] = 0;
    offs = 0;
    rowoffs = 0;
    sumcoldeg = 0;
    sumrowdeg = 0;
    isetallocv(solver->n, 0, &s->coldegrees, _state);
    isetallocv(solver->msparse+solver->mdense, 0, &s->rowdegrees, _state);
    bsetallocv(solver->n, ae_true, &s->isdiagonal, _state);
    for(i=0; i<=solver->n-1; i++)
    {
        ae_assert(solver->sparseh.didx.ptr.p_int[i]+1==solver->sparseh.uidx.ptr.p_int[i], "ReducedSystemInit: critical integrity check failed for diagonal of H", _state);
        if( !solver->isfrozen.ptr.p_bool[i] )
        {
            
            /*
             * Entire row is not frozen, but some of its entries can be.
             * Output non-frozen offdiagonal entries.
             */
            k0 = solver->sparseh.ridx.ptr.p_int[i];
            k1 = solver->sparseh.didx.ptr.p_int[i]-1;
            for(k=k0; k<=k1; k++)
            {
                j = solver->sparseh.idx.ptr.p_int[k];
                if( !solver->isfrozen.ptr.p_bool[j] )
                {
                    s->rawsystem.idx.ptr.p_int[offs] = j;
                    s->rawsystem.vals.ptr.p_double[offs] = -solver->sparseh.vals.ptr.p_double[k];
                    s->isdiagonal.ptr.p_bool[i] = ae_false;
                    s->isdiagonal.ptr.p_bool[j] = ae_false;
                    s->coldegrees.ptr.p_int[i] = s->coldegrees.ptr.p_int[i]+1;
                    s->coldegrees.ptr.p_int[j] = s->coldegrees.ptr.p_int[j]+1;
                    sumcoldeg = sumcoldeg+2;
                    offs = offs+1;
                }
            }
            
            /*
             * Output diagonal entry (it is always not frozen)
             */
            s->rawsystem.idx.ptr.p_int[offs] = i;
            s->rawsystem.vals.ptr.p_double[offs] = -solver->sparseh.vals.ptr.p_double[solver->sparseh.didx.ptr.p_int[i]];
            offs = offs+1;
        }
        else
        {
            
            /*
             * Entire row is frozen, output just -1
             */
            s->rawsystem.idx.ptr.p_int[offs] = i;
            s->rawsystem.vals.ptr.p_double[offs] = -1.0;
            offs = offs+1;
        }
        rowoffs = rowoffs+1;
        s->rawsystem.ridx.ptr.p_int[rowoffs] = offs;
    }
    for(i=0; i<=solver->msparse-1; i++)
    {
        k0 = solver->sparseafull.ridx.ptr.p_int[i];
        k1 = solver->sparseafull.ridx.ptr.p_int[i+1]-1;
        for(k=k0; k<=k1; k++)
        {
            j = solver->sparseafull.idx.ptr.p_int[k];
            if( !solver->isfrozen.ptr.p_bool[j] )
            {
                s->rawsystem.idx.ptr.p_int[offs] = j;
                s->rawsystem.vals.ptr.p_double[offs] = solver->sparseafull.vals.ptr.p_double[k];
                s->rowdegrees.ptr.p_int[i] = s->rowdegrees.ptr.p_int[i]+1;
                s->coldegrees.ptr.p_int[j] = s->coldegrees.ptr.p_int[j]+1;
                sumcoldeg = sumcoldeg+1;
                sumrowdeg = sumrowdeg+1;
                offs = offs+1;
            }
        }
        s->rawsystem.idx.ptr.p_int[offs] = rowoffs;
        s->rawsystem.vals.ptr.p_double[offs] = 0.0;
        offs = offs+1;
        rowoffs = rowoffs+1;
        s->rawsystem.ridx.ptr.p_int[rowoffs] = offs;
    }
    for(i=0; i<=solver->mdense-1; i++)
    {
        for(k=0; k<=solver->n-1; k++)
        {
            if( solver->denseafull.ptr.pp_double[i][k]!=0.0&&!solver->isfrozen.ptr.p_bool[k] )
            {
                s->rawsystem.idx.ptr.p_int[offs] = k;
                s->rawsystem.vals.ptr.p_double[offs] = solver->denseafull.ptr.pp_double[i][k];
                s->rowdegrees.ptr.p_int[solver->msparse+i] = s->rowdegrees.ptr.p_int[solver->msparse+i]+1;
                s->coldegrees.ptr.p_int[k] = s->coldegrees.ptr.p_int[k]+1;
                sumcoldeg = sumcoldeg+1;
                sumrowdeg = sumrowdeg+1;
                offs = offs+1;
            }
        }
        s->rawsystem.idx.ptr.p_int[offs] = rowoffs;
        s->rawsystem.vals.ptr.p_double[offs] = 0.0;
        offs = offs+1;
        rowoffs = rowoffs+1;
        s->rawsystem.ridx.ptr.p_int[rowoffs] = offs;
    }
    ae_assert(rowoffs==ntotal, "ReducedSystemInit: critical integrity check failed", _state);
    ae_assert(offs<=nnzmax, "ReducedSystemInit: critical integrity check failed", _state);
    sparsecreatecrsinplace(&s->rawsystem, _state);
    
    /*
     * Prepare reordering
     */
    isetallocv(ntotal, 0, &s->priorities, _state);
    colthreshold = ae_round(vipmsolver_muquasidense*(double)sumcoldeg/(double)solver->n, _state)+1;
    rowthreshold = ae_round(vipmsolver_muquasidense*(double)sumrowdeg/(double)(solver->msparse+solver->mdense+1), _state)+1;
    eligiblecols = 0;
    eligiblerows = 0;
    for(i=0; i<=solver->n-1; i++)
    {
        if( s->coldegrees.ptr.p_int[i]<=colthreshold )
        {
            eligiblecols = eligiblecols+1;
        }
    }
    for(i=0; i<=solver->mdense+solver->msparse-1; i++)
    {
        if( s->rowdegrees.ptr.p_int[i]<=rowthreshold )
        {
            eligiblerows = eligiblerows+1;
        }
    }
    if( ae_maxint(eligiblecols, eligiblerows, _state)>0&&ae_maxint(eligiblecols, eligiblerows, _state)<ntotal )
    {
        if( eligiblecols>=eligiblerows )
        {
            
            /*
             * Eliminate columns first
             */
            for(i=0; i<=solver->n-1; i++)
            {
                s->priorities.ptr.p_int[i] = icase2(s->coldegrees.ptr.p_int[i]<=colthreshold, 0, 2, _state);
            }
            for(i=solver->n; i<=ntotal-1; i++)
            {
                s->priorities.ptr.p_int[i] = icase2(s->rowdegrees.ptr.p_int[i-solver->n]<=rowthreshold, 1, 2, _state);
            }
        }
        else
        {
            
            /*
             * Eliminate rows first
             */
            for(i=0; i<=solver->n-1; i++)
            {
                s->priorities.ptr.p_int[i] = icase2(s->coldegrees.ptr.p_int[i]<=colthreshold, 1, 2, _state);
            }
            for(i=solver->n; i<=ntotal-1; i++)
            {
                s->priorities.ptr.p_int[i] = icase2(s->rowdegrees.ptr.p_int[i-solver->n]<=rowthreshold, 0, 2, _state);
            }
        }
    }
    for(i=0; i<=solver->n-1; i++)
    {
        s->priorities.ptr.p_int[i] = icase2(solver->hasbndl.ptr.p_bool[i]||solver->hasbndu.ptr.p_bool[i], s->priorities.ptr.p_int[i], 2, _state);
    }
    if( solver->dotrace )
    {
        ae_trace("> initializing KKT system; no priority ordering being applied\n");
    }
    
    /*
     * Perform factorization analysis using sparsity pattern (but not numerical values)
     */
    factldlt = 1;
    permpriorityamd = 3;
    memreuse = 1;
    if( !spsymmanalyze(&s->rawsystem, &s->priorities, (double)ntotal+1.0, 0, factldlt, permpriorityamd, memreuse, &s->analysis, _state) )
    {
        ae_assert(ae_false, "ReducedSystemInit: critical integrity check failed, symbolically degenerate KKT system encountered", _state);
    }
}


/*************************************************************************
Computes factorization of A+Diag+Damp, where A is an internally stored KKT
matrix and Diag and Damp are user-supplied diagonal terms.

The Diag term is assumed to be a "true" modification of the system, and
Damp is assumed to be a small damping factor. The difference is that the
damping factor is added during the factorization, but not accounted for
during the iterative refinement stage, i.e. we factor A+Diag+Damp, but aim
to solve A+Diag.

ModEps and BadChol are user supplied tolerances for modified Cholesky/LDLT.

Returns True on success, False on LDLT failure.

On success outputs diagonal reproduction error ErrSq, and sum of squared
diagonal elements SumSq

  -- ALGLIB --
     Copyright 15.11.2021 by Bochkanov Sergey
*************************************************************************/
static ae_bool vipmsolver_reducedsystemfactorizewithaddends(vipmreducedsparsesystem* s,
     /* Real    */ const ae_vector* diag,
     /* Real    */ const ae_vector* damp,
     double modeps,
     double badchol,
     double* sumsq,
     double* errsq,
     ae_state *_state)
{
    ae_int_t ntotal;
    ae_int_t i;
    ae_bool result;

    *sumsq = 0.0;
    *errsq = 0.0;

    ntotal = s->ntotal;
    rcopyv(ntotal, diag, &s->diagterm, _state);
    rcopyv(ntotal, damp, &s->dampterm, _state);
    for(i=0; i<=ntotal-1; i++)
    {
        s->effectivediag.ptr.p_double[i] = s->rawsystem.vals.ptr.p_double[s->rawsystem.didx.ptr.p_int[i]]+diag->ptr.p_double[i]+damp->ptr.p_double[i];
    }
    spsymmreloaddiagonal(&s->analysis, &s->effectivediag, _state);
    result = ae_true;
    spsymmsetmodificationstrategy(&s->analysis, 1, modeps, badchol, 0.0, 0.0, _state);
    if( spsymmfactorize(&s->analysis, _state) )
    {
        
        /*
         * Compute diagonal reproduction error
         */
        spsymmdiagerr(&s->analysis, sumsq, errsq, _state);
    }
    else
    {
        *sumsq = (double)(0);
        *errsq = (double)(0);
        result = ae_false;
    }
    return result;
}


/*************************************************************************
Solve reduced KKT system, replacing right part by its solution.

  -- ALGLIB --
     Copyright 15.11.2021 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_reducedsystemsolve(vipmreducedsparsesystem* s,
     ae_bool dotrace,
     /* Real    */ ae_vector* b,
     ae_state *_state)
{
    ae_int_t iteridx;
    double bnrm2;
    double relerr;
    double prevrelerr;


    
    /*
     * Perform initial solution
     */
    rcopyallocv(s->ntotal, b, &s->tmpb, _state);
    spsymmsolve(&s->analysis, b, _state);
    
    /*
     * Trace residual and relative error
     */
    bnrm2 = ae_maxreal(rdotv2(s->ntotal, &s->tmpb, _state), 1.0, _state);
    sparsesmv(&s->rawsystem, ae_false, b, &s->tmprhs, _state);
    rmuladdv(s->ntotal, b, &s->diagterm, &s->tmprhs, _state);
    rmulv(s->ntotal, -1.0, &s->tmprhs, _state);
    raddv(s->ntotal, 1.0, &s->tmpb, &s->tmprhs, _state);
    relerr = ae_sqrt(rdotv2(s->ntotal, &s->tmprhs, _state)/bnrm2, _state);
    if( dotrace )
    {
        ae_trace("> reduced system solved, res/rhs = %0.3e (initial)\n",
            (double)(relerr));
    }
    
    /*
     * Perform iterative refinement, if necessary
     */
    prevrelerr = 1.0E50;
    iteridx = 0;
    while((iteridx<vipmsolver_maxrefinementits&&ae_fp_greater(relerr,(double)10*ae_machineepsilon))&&ae_fp_less(relerr,0.5*prevrelerr))
    {
        
        /*
         * Compute correction and update solution
         */
        rcopyallocv(s->ntotal, &s->tmprhs, &s->tmpcorr, _state);
        spsymmsolve(&s->analysis, &s->tmpcorr, _state);
        raddv(s->ntotal, 1.0, &s->tmpcorr, b, _state);
        
        /*
         * Recompute residual
         */
        sparsesmv(&s->rawsystem, ae_false, b, &s->tmprhs, _state);
        rmuladdv(s->ntotal, b, &s->diagterm, &s->tmprhs, _state);
        rmulv(s->ntotal, -1.0, &s->tmprhs, _state);
        raddv(s->ntotal, 1.0, &s->tmpb, &s->tmprhs, _state);
        prevrelerr = relerr;
        relerr = ae_sqrt(rdotv2(s->ntotal, &s->tmprhs, _state)/bnrm2, _state);
        iteridx = iteridx+1;
    }
    if( dotrace&&iteridx>0 )
    {
        ae_trace("> reduced system solved, res/rhs = %0.3e (refined, %0d its)\n",
            (double)(relerr),
            (int)(iteridx));
    }
}


/*************************************************************************
Initializes QP-IPM state and prepares it to receive quadratic/linear terms
and constraints.

The solver is configured to work internally with factorization FType

INPUT PARAMETERS:
    State       -   solver  state  to  be configured; previously allocated
                    memory is reused as much as possible
    S           -   scale vector, array[N]:
                    * I-th element contains scale of I-th variable,
                    * S[I]>0
    XOrigin     -   origin term, array[N]. Can be zero. The solver solves
                    problem of the form
                    
                    >
                    > min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin))
                    >
                    
                    The terms A and b (as well as constraints) will be
                    specified later with separate calls.
    FType       -   factorization type:
                    * 0 for dense NxN factorization (normal equations)
                    * 1 for sparse (N+M)x(N+M) factorization
    Normalize   -   apply normalization to target/constraints or not:
                    * if True, then target and constraints are normalized
                      to unit scale
                    * if False, it is assumed that the caller has performed
                      normalization.
                    The latter is recommended for presolved problems.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_vipminit(vipmstate* state,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t n,
     ae_int_t nmain,
     ae_int_t ftype,
     ae_bool normalize,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t nslack;


    ae_assert(n>=1, "VIPMInit: N<1", _state);
    ae_assert(isfinitevector(s, n, _state), "VIPMInit: S contains infinite or NaN elements", _state);
    ae_assert(isfinitevector(xorigin, n, _state), "VIPMInit: XOrigin contains infinite or NaN elements", _state);
    ae_assert(ftype==0||ftype==1, "VIPMInit: unexpected FType", _state);
    ae_assert(nmain>=1, "VIPMInit: NMain<1", _state);
    ae_assert(nmain<=n, "VIPMInit: NMain>N", _state);
    nslack = n-nmain;
    
    /*
     * Problem metrics, settings and type
     */
    state->n = n;
    state->nmain = nmain;
    state->normalize = normalize;
    state->islinear = ae_true;
    state->factorizationtype = ftype;
    state->factorizationpresent = ae_false;
    state->factorizationpoweredup = ae_false;
    vipmsetcond(state, 0.0, 0.0, 0.0, _state);
    state->slacksforequalityconstraints = ae_true;
    
    /*
     * Reports
     */
    state->repiterationscount = 0;
    state->repncholesky = 0;
    
    /*
     * Trace
     */
    state->dotrace = ae_false;
    state->dodetailedtrace = ae_false;
    
    /*
     * Scale and origin
     */
    rvectorsetlengthatleast(&state->scl, n, _state);
    rvectorsetlengthatleast(&state->invscl, n, _state);
    rvectorsetlengthatleast(&state->xorigin, n, _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(s->ptr.p_double[i]>0.0, "VIPMInit: S[i] is non-positive", _state);
        state->scl.ptr.p_double[i] = s->ptr.p_double[i];
        state->invscl.ptr.p_double[i] = (double)1/s->ptr.p_double[i];
        state->xorigin.ptr.p_double[i] = xorigin->ptr.p_double[i];
    }
    state->targetscale = 1.0;
    
    /*
     * Linear and quadratic terms - default value
     */
    rvectorsetlengthatleast(&state->c, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->c.ptr.p_double[i] = (double)(0);
    }
    state->hkind = -1;
    if( ftype==0 )
    {
        
        /*
         * Dense quadratic term
         */
        rmatrixsetlengthatleast(&state->denseh, nmain, nmain, _state);
        for(i=0; i<=nmain-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                state->denseh.ptr.pp_double[i][j] = (double)(0);
            }
        }
        state->hkind = 0;
        state->isdiagonalh = ae_false;
    }
    if( ftype==1 )
    {
        
        /*
         * Sparse quadratic term
         */
        state->sparseh.matrixtype = 1;
        state->sparseh.m = n;
        state->sparseh.n = n;
        state->sparseh.ninitialized = n;
        ivectorsetlengthatleast(&state->sparseh.idx, n, _state);
        rvectorsetlengthatleast(&state->sparseh.vals, n, _state);
        ivectorsetlengthatleast(&state->sparseh.ridx, n+1, _state);
        for(i=0; i<=n-1; i++)
        {
            state->sparseh.idx.ptr.p_int[i] = i;
            state->sparseh.vals.ptr.p_double[i] = 0.0;
            state->sparseh.ridx.ptr.p_int[i] = i;
        }
        state->sparseh.ridx.ptr.p_int[n] = n;
        sparsecreatecrsinplace(&state->sparseh, _state);
        state->hkind = 1;
        state->isdiagonalh = ae_true;
    }
    ae_assert(state->hkind>=0, "VIPMInit: integrity check failed", _state);
    
    /*
     * Box constraints - default values
     */
    rvectorsetlengthatleast(&state->bndl, n, _state);
    rvectorsetlengthatleast(&state->bndu, n, _state);
    bvectorsetlengthatleast(&state->hasbndl, n, _state);
    bvectorsetlengthatleast(&state->hasbndu, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->hasbndl.ptr.p_bool[i] = ae_false;
        state->hasbndu.ptr.p_bool[i] = ae_false;
        state->bndl.ptr.p_double[i] = _state->v_neginf;
        state->bndu.ptr.p_double[i] = _state->v_posinf;
    }
    
    /*
     * Linear constraints - empty
     */
    state->mdense = 0;
    state->msparse = 0;
    state->combinedaslack.m = 0;
    state->combinedaslack.n = nslack;
    state->sparseamain.m = 0;
    state->sparseamain.n = nmain;
    sparsecreatecrsinplace(&state->sparseamain, _state);
    sparsecreatecrsinplace(&state->combinedaslack, _state);
}


/*************************************************************************
Computes target function 0.5*x'*H*x+c'*x

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static double vipmsolver_vipmtarget(const vipmstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nmain;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t j0;
    ae_int_t j1;
    double v;
    double result;


    n = state->n;
    nmain = state->nmain;
    ae_assert(state->hkind==0||state->hkind==1, "VIPMTarget: unexpected HKind", _state);
    result = (double)(0);
    
    /*
     * Dense
     */
    if( state->hkind==0 )
    {
        for(i=0; i<=nmain-1; i++)
        {
            for(j=0; j<=i-1; j++)
            {
                result = result+x->ptr.p_double[i]*state->denseh.ptr.pp_double[i][j]*x->ptr.p_double[j];
            }
            result = result+0.5*x->ptr.p_double[i]*x->ptr.p_double[i]*state->denseh.ptr.pp_double[i][i];
        }
        for(i=0; i<=n-1; i++)
        {
            result = result+state->c.ptr.p_double[i]*x->ptr.p_double[i];
        }
        return result;
    }
    
    /*
     * Sparse
     */
    if( state->hkind==1 )
    {
        for(i=0; i<=n-1; i++)
        {
            result = result+state->c.ptr.p_double[i]*x->ptr.p_double[i];
            j0 = state->sparseh.ridx.ptr.p_int[i];
            j1 = state->sparseh.didx.ptr.p_int[i]-1;
            for(k=j0; k<=j1; k++)
            {
                v = state->sparseh.vals.ptr.p_double[k];
                j = state->sparseh.idx.ptr.p_int[k];
                result = result+v*x->ptr.p_double[i]*x->ptr.p_double[j];
            }
            ae_assert(state->sparseh.uidx.ptr.p_int[i]!=state->sparseh.didx.ptr.p_int[i], "VIPMTarget: sparse diagonal not found", _state);
            v = state->sparseh.vals.ptr.p_double[state->sparseh.didx.ptr.p_int[i]];
            result = result+0.5*v*x->ptr.p_double[i]*x->ptr.p_double[i];
        }
        return result;
    }
    return result;
}


/*************************************************************************
Computes

    Y := alpha*A*x + beta*Y

where A is constraint matrix, X is user-specified source, Y is target.

Beta can be zero (in this case original contents of Y is ignored).
If Beta is nonzero, we expect that Y contains preallocated array.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_multiplygeax(const vipmstate* state,
     double alpha,
     /* Real    */ const ae_vector* x,
     ae_int_t offsx,
     double beta,
     /* Real    */ ae_vector* y,
     ae_int_t offsax,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t mdense;
    ae_int_t msparse;


    n = state->n;
    m = state->mdense+state->msparse;
    mdense = state->mdense;
    msparse = state->msparse;
    if( ae_fp_eq(beta,(double)(0)) )
    {
        rallocv(offsax+m, y, _state);
    }
    else
    {
        ae_assert(y->cnt>=offsax+m, "MultiplyGEAX: Y is too short", _state);
    }
    if( msparse>0 )
    {
        sparsegemv(&state->sparseafull, alpha, 0, x, offsx, beta, y, offsax, _state);
    }
    if( mdense>0 )
    {
        rmatrixgemv(mdense, n, alpha, &state->denseafull, 0, 0, 0, x, offsx, beta, y, offsax+msparse, _state);
    }
}


/*************************************************************************
Computes

    Y := alpha*A'*x + beta*Y

where A is constraint matrix, X is user-specified source, Y is target.

Beta can be zero, in this case we automatically reallocate target if it is
too short (but do NOT reallocate it if its size is large enough).
If Beta is nonzero, we expect that Y contains preallocated array.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_multiplygeatx(const vipmstate* state,
     double alpha,
     /* Real    */ const ae_vector* x,
     ae_int_t offsx,
     double beta,
     /* Real    */ ae_vector* y,
     ae_int_t offsy,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t mdense;
    ae_int_t msparse;


    n = state->n;
    mdense = state->mdense;
    msparse = state->msparse;
    if( ae_fp_eq(beta,(double)(0)) )
    {
        rallocv(offsy+n, y, _state);
        rsetvx(n, 0.0, y, offsy, _state);
    }
    else
    {
        ae_assert(y->cnt>=offsy+n, "MultiplyGEATX: Y is too short", _state);
        rmulvx(n, beta, y, offsy, _state);
    }
    if( msparse>0 )
    {
        sparsegemv(&state->sparseafull, alpha, 1, x, offsx, 1.0, y, offsy, _state);
    }
    if( mdense>0 )
    {
        rmatrixgemv(n, mdense, alpha, &state->denseafull, 0, 0, 1, x, offsx+msparse, 1.0, y, offsy, _state);
    }
}


/*************************************************************************
Computes H*x, does not support advanced functionality of GEAX/GEATX

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_multiplyhx(const vipmstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* hx,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nmain;
    ae_int_t i;


    n = state->n;
    nmain = state->nmain;
    rvectorsetlengthatleast(hx, n, _state);
    ae_assert(state->hkind==0||state->hkind==1, "VIPMMultiplyHX: unexpected HKind", _state);
    if( state->hkind==0 )
    {
        rmatrixsymv(nmain, 1.0, &state->denseh, 0, 0, ae_false, x, 0, 0.0, hx, 0, _state);
        for(i=nmain; i<=n-1; i++)
        {
            hx->ptr.p_double[i] = (double)(0);
        }
        for(i=0; i<=n-1; i++)
        {
            hx->ptr.p_double[i] = hx->ptr.p_double[i]+x->ptr.p_double[i]*state->diagr.ptr.p_double[i];
        }
    }
    if( state->hkind==1 )
    {
        ae_assert(state->sparseh.n==n&&state->sparseh.m==n, "VIPMMultiplyHX: sparse H has incorrect size", _state);
        if( state->isdiagonalh )
        {
            
            /*
             * H is known to be diagonal, much faster code can be used
             */
            rcopyv(n, &state->diagr, hx, _state);
            raddv(n, 1.0, &state->sparseh.vals, hx, _state);
            rmergemulv(n, x, hx, _state);
        }
        else
        {
            
            /*
             * H is a general sparse matrix, use generic sparse matrix-vector multiply
             */
            sparsesmv(&state->sparseh, ae_false, x, hx, _state);
            for(i=0; i<=n-1; i++)
            {
                hx->ptr.p_double[i] = hx->ptr.p_double[i]+x->ptr.p_double[i]*state->diagr.ptr.p_double[i];
            }
        }
    }
}


/*************************************************************************
Computes products H*x, A*x, A^T*y

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_vipmmultiply(const vipmstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     /* Real    */ ae_vector* hx,
     /* Real    */ ae_vector* ax,
     /* Real    */ ae_vector* aty,
     ae_state *_state)
{


    vipmsolver_multiplygeax(state, 1.0, x, 0, 0.0, ax, 0, _state);
    vipmsolver_multiplygeatx(state, 1.0, y, 0, 0.0, aty, 0, _state);
    vipmsolver_multiplyhx(state, x, hx, _state);
}


/*************************************************************************
This function "powers up" factorization, i.e. prepares some important
temporaries. It should be called once prior to the first call to
VIPMInitialPoint() or VIPMFactorize().

Parameters:
    RegFree     -   regularization for free variables;
                    good value sqrt(MachineEpsilon)

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_vipmpowerup(vipmstate* state,
     double regfree,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    double v;
    double vrhs;
    double priorcoeff;
    double initprimslack;
    double initdualslack;
    double maxinitialnoncentrality;
    double maxinitialimbalance;
    double maxdualslack;
    double mu0;
    double mumin;
    ae_bool success;


    ae_assert(state->factorizationtype==0||state->factorizationtype==1, "VIPMPowerUp: unexpected factorization type", _state);
    n = state->n;
    m = state->mdense+state->msparse;
    ae_assert(!state->factorizationpoweredup, "VIPMPowerUp: repeated call", _state);
    maxinitialnoncentrality = 1.0E-6;
    maxinitialimbalance = 1.0E-6;
    
    /*
     * Set up information about presence of slack variables.
     * Decide which components of X should be frozen.
     * Compute diagonal regularization matrix R.
     */
    bcopyallocv(n, &state->hasbndl, &state->hasgz, _state);
    bcopyallocv(n, &state->hasbndu, &state->hasts, _state);
    bsetallocv(n, ae_false, &state->isfrozen, _state);
    rsetallocv(n, 0.0, &state->diagr, _state);
    vipmsolver_varsinitbyzero(&state->current, n, m, _state);
    for(i=0; i<=n-1; i++)
    {
        if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            state->isfrozen.ptr.p_bool[i] = ae_true;
            state->hasgz.ptr.p_bool[i] = ae_false;
            state->hasts.ptr.p_bool[i] = ae_false;
            state->current.x.ptr.p_double[i] = state->bndl.ptr.p_double[i];
        }
        if( !state->hasbndl.ptr.p_bool[i]&&!state->hasbndu.ptr.p_bool[i] )
        {
            state->diagr.ptr.p_double[i] = regfree;
        }
    }
    ballocv(m, &state->haspq, _state);
    ballocv(m, &state->haswv, _state);
    for(i=0; i<=m-1; i++)
    {
        state->haswv.ptr.p_bool[i] = (state->slacksforequalityconstraints||!state->hasr.ptr.p_bool[i])||ae_fp_greater(state->r.ptr.p_double[i],(double)(0));
        state->haspq.ptr.p_bool[i] = state->hasr.ptr.p_bool[i]&&state->haswv.ptr.p_bool[i];
    }
    state->cntgz = 0;
    state->cntts = 0;
    state->cntwv = 0;
    state->cntpq = 0;
    for(i=0; i<=n-1; i++)
    {
        if( state->hasgz.ptr.p_bool[i] )
        {
            state->cntgz = state->cntgz+1;
        }
        if( state->hasts.ptr.p_bool[i] )
        {
            state->cntts = state->cntts+1;
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( state->haswv.ptr.p_bool[i] )
        {
            state->cntwv = state->cntwv+1;
        }
        if( state->haspq.ptr.p_bool[i] )
        {
            state->cntpq = state->cntpq+1;
        }
    }
    
    /*
     * Special initialization for sparse version
     */
    if( state->factorizationtype==1 )
    {
        vipmsolver_reducedsysteminit(&state->reducedsparsesystem, state, _state);
    }
    state->factorizationpoweredup = ae_true;
    
    /*
     * Set up initial values of primal and dual variables X and Y by solving
     * modified KKT system which tries to enforce linear constraints (ignoring
     * box constraints for a while) subject to minimization of additional prior
     * term which moves solution towards some interior point.
     *
     * Here we expect that State.Current.X contains zeros in non-fixed variables
     * and their fixed values for fixed ones.
     */
    priorcoeff = 1.0;
    success = vipmsolver_vipmfactorize(state, 0.0, &state->diagddr, 0.0, &state->diagder, priorcoeff, priorcoeff, ae_machineepsilon, ae_machineepsilon, _state);
    ae_assert(success, "VIPMInitialPoint: impossible failure of LDLT factorization", _state);
    vipmsolver_multiplyhx(state, &state->current.x, &state->tmp0, _state);
    vipmsolver_multiplygeax(state, 1.0, &state->current.x, 0, 0.0, &state->tmp1, 0, _state);
    rallocv(n+m, &state->deltaxy, _state);
    for(i=0; i<=n-1; i++)
    {
        state->deltaxy.ptr.p_double[i] = state->c.ptr.p_double[i]+state->tmp0.ptr.p_double[i];
    }
    for(i=0; i<=m-1; i++)
    {
        
        /*
         * We need to specify target right-hand sides for constraints.
         *
         * Ether zero, b or b+r is used (depending on presence of r and
         * magnitudes of b and b+r, and subject to current state of frozen
         * variables).
         */
        vrhs = state->b.ptr.p_double[i]-state->tmp1.ptr.p_double[i];
        if( state->hasr.ptr.p_bool[i] )
        {
            
            /*
             * Range constraint b<=Ax<=b+r
             */
            if( ae_fp_greater_eq(vrhs,(double)(0)) )
            {
                
                /*
                 * 0<=b<=b+r, select target at lower bound
                 */
                v = vrhs;
            }
            else
            {
                
                /*
                 * b<=0, b+r can have any sign.
                 * Select zero target if possible, if not - one with smallest absolute value.
                 */
                v = ae_minreal(vrhs+state->r.ptr.p_double[i], 0.0, _state);
            }
        }
        else
        {
            
            /*
             * Single-sided constraint Ax>=b.
             * Select zero target if possible, if not - one with smallest absolute value.
             */
            v = ae_maxreal(vrhs, 0.0, _state);
        }
        state->deltaxy.ptr.p_double[n+i] = v;
    }
    vipmsolver_solvereducedkktsystem(state, &state->deltaxy, _state);
    for(i=0; i<=n-1; i++)
    {
        if( !state->isfrozen.ptr.p_bool[i] )
        {
            state->current.x.ptr.p_double[i] = state->deltaxy.ptr.p_double[i];
        }
    }
    for(i=0; i<=m-1; i++)
    {
        state->current.y.ptr.p_double[i] = state->deltaxy.ptr.p_double[n+i];
    }
    
    /*
     * Set up slacks according to our own heuristic
     */
    initprimslack = ae_maxreal(vipmsolver_initslackval, rmaxabsv(n, &state->current.x, _state), _state);
    initdualslack = ae_maxreal(vipmsolver_initslackval, rmaxabsv(m, &state->current.y, _state), _state);
    maxdualslack = ae_maxreal((double)10*initdualslack, vipmsolver_maxdualslackval, _state);
    vipmsolver_multiplygeax(state, 1.0, &state->current.x, 0, 0.0, &state->tmpax, 0, _state);
    mu0 = 1.0;
    for(i=0; i<=n-1; i++)
    {
        if( state->hasgz.ptr.p_bool[i] )
        {
            state->current.g.ptr.p_double[i] = ae_maxreal(ae_fabs(state->current.x.ptr.p_double[i]-state->bndl.ptr.p_double[i], _state), initprimslack, _state);
            state->current.z.ptr.p_double[i] = boundval(state->current.g.ptr.p_double[i]*maxinitialimbalance, initdualslack, maxdualslack, _state);
            mu0 = ae_maxreal(mu0, state->current.g.ptr.p_double[i]*state->current.z.ptr.p_double[i], _state);
        }
        if( state->hasts.ptr.p_bool[i] )
        {
            state->current.t.ptr.p_double[i] = ae_maxreal(ae_fabs(state->current.x.ptr.p_double[i]-state->bndu.ptr.p_double[i], _state), initprimslack, _state);
            state->current.s.ptr.p_double[i] = boundval(state->current.t.ptr.p_double[i]*maxinitialimbalance, initdualslack, maxdualslack, _state);
            mu0 = ae_maxreal(mu0, state->current.t.ptr.p_double[i]*state->current.s.ptr.p_double[i], _state);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( state->haswv.ptr.p_bool[i] )
        {
            state->current.w.ptr.p_double[i] = ae_maxreal(ae_fabs(state->tmpax.ptr.p_double[i]-state->b.ptr.p_double[i], _state), initprimslack, _state);
            state->current.v.ptr.p_double[i] = boundval(ae_maxreal(state->current.w.ptr.p_double[i]*maxinitialimbalance, ae_fabs(state->current.y.ptr.p_double[i], _state), _state), vipmsolver_initslackval, maxdualslack, _state);
            mu0 = ae_maxreal(mu0, state->current.w.ptr.p_double[i]*state->current.v.ptr.p_double[i], _state);
        }
        if( state->haspq.ptr.p_bool[i] )
        {
            state->current.p.ptr.p_double[i] = ae_maxreal(ae_fabs(state->r.ptr.p_double[i]-state->current.w.ptr.p_double[i], _state), initprimslack, _state);
            state->current.q.ptr.p_double[i] = boundval(ae_maxreal(state->current.p.ptr.p_double[i]*maxinitialimbalance, ae_fabs(state->current.y.ptr.p_double[i], _state), _state), vipmsolver_initslackval, maxdualslack, _state);
            mu0 = ae_maxreal(mu0, state->current.p.ptr.p_double[i]*state->current.q.ptr.p_double[i], _state);
        }
    }
    
    /*
     * Additional shift to ensure that initial point is not too non-centered
     */
    mumin = mu0*maxinitialnoncentrality;
    for(i=0; i<=n-1; i++)
    {
        if( state->hasgz.ptr.p_bool[i]&&ae_fp_less(state->current.g.ptr.p_double[i]*state->current.z.ptr.p_double[i],mumin) )
        {
            v = ae_sqrt(mumin/(state->current.g.ptr.p_double[i]*state->current.z.ptr.p_double[i]), _state);
            state->current.g.ptr.p_double[i] = state->current.g.ptr.p_double[i]*v;
            state->current.z.ptr.p_double[i] = state->current.z.ptr.p_double[i]*v;
        }
        if( state->hasts.ptr.p_bool[i]&&ae_fp_less(state->current.t.ptr.p_double[i]*state->current.s.ptr.p_double[i],mumin) )
        {
            v = ae_sqrt(mumin/(state->current.t.ptr.p_double[i]*state->current.s.ptr.p_double[i]), _state);
            state->current.t.ptr.p_double[i] = state->current.t.ptr.p_double[i]*v;
            state->current.s.ptr.p_double[i] = state->current.s.ptr.p_double[i]*v;
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( state->haswv.ptr.p_bool[i]&&ae_fp_less(state->current.w.ptr.p_double[i]*state->current.v.ptr.p_double[i],mumin) )
        {
            v = ae_sqrt(mumin/(state->current.w.ptr.p_double[i]*state->current.v.ptr.p_double[i]), _state);
            state->current.w.ptr.p_double[i] = state->current.w.ptr.p_double[i]*v;
            state->current.v.ptr.p_double[i] = state->current.v.ptr.p_double[i]*v;
        }
        if( state->haspq.ptr.p_bool[i]&&ae_fp_less(state->current.p.ptr.p_double[i]*state->current.q.ptr.p_double[i],mumin) )
        {
            v = ae_sqrt(mumin/(state->current.p.ptr.p_double[i]*state->current.q.ptr.p_double[i]), _state);
            state->current.p.ptr.p_double[i] = state->current.p.ptr.p_double[i]*v;
            state->current.q.ptr.p_double[i] = state->current.q.ptr.p_double[i]*v;
        }
    }
    
    /*
     * Almost done
     */
    if( state->dotrace )
    {
        ae_trace("> initial point was generated\n");
    }
}


/*************************************************************************
This function performs factorization of modified KKT system

    (                        |                 )
    ( -(H+alpha0*D+alpha1*I) |       A^T       )
    (                        |                 )
    (------------------------|-----------------)
    (                        |                 )
    (           A            | beta0*E+beta1*I )
    (                        |                 )

where:
* H is an NxN quadratic term
* A is an MxN matrix of linear constraint
* alpha0, alpha1, beta0, beta1 are nonnegative scalars
* D and E are diagonal matrices with nonnegative entries (which are ignored
  if alpha0 and beta0 are zero - arrays are not referenced at all)
* I is an NxN or MxM identity matrix

Additionally, regularizing term

    (        |        )
    ( -reg*I |        )
    (        |        )
    (--------|--------)
    (        |        )
    (        | +reg*I )
    (        |        )

is added to the entire KKT system prior to factorization in order to
improve its numerical stability.

Returns True on success, False on falure of factorization (it is recommended
to increase regularization parameter and try one more time).


  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static ae_bool vipmsolver_vipmfactorize(vipmstate* state,
     double alpha0,
     /* Real    */ const ae_vector* d,
     double beta0,
     /* Real    */ const ae_vector* e,
     double alpha11,
     double beta11,
     double modeps,
     double dampeps,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nmain;
    ae_int_t nslack;
    ae_int_t m;
    ae_int_t mdense;
    ae_int_t msparse;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t ka;
    ae_int_t kb;
    ae_int_t ja;
    ae_int_t jb;
    double va;
    double vb;
    double v;
    double vv;
    double badchol;
    double sumsq;
    double errsq;
    ae_int_t t0;
    ae_bool result;


    ae_assert(ae_isfinite(alpha0, _state)&&ae_fp_greater_eq(alpha0,(double)(0)), "VIPMFactorize: Alpha0 is infinite or negative", _state);
    ae_assert(ae_isfinite(alpha11, _state)&&ae_fp_greater_eq(alpha11,(double)(0)), "VIPMFactorize: Alpha1 is infinite or negative", _state);
    ae_assert(ae_isfinite(beta0, _state)&&ae_fp_greater_eq(beta0,(double)(0)), "VIPMFactorize: Beta0 is infinite or negative", _state);
    ae_assert(ae_isfinite(beta11, _state)&&ae_fp_greater_eq(beta11,(double)(0)), "VIPMFactorize: Beta1 is infinite or negative", _state);
    ae_assert(state->factorizationtype==0||state->factorizationtype==1, "VIPMFactorize: unexpected factorization type", _state);
    ae_assert(state->factorizationpoweredup, "VIPMFactorize: critical integrity check failed (no powerup stage)", _state);
    n = state->n;
    nmain = state->nmain;
    nslack = n-nmain;
    m = state->mdense+state->msparse;
    mdense = state->mdense;
    msparse = state->msparse;
    state->factorizationpresent = ae_false;
    badchol = 1.0E50;
    result = ae_true;
    
    /*
     * Dense NxN normal equations approach
     */
    if( state->factorizationtype==0 )
    {
        
        /*
         * A problem formulation with possible slacks.
         *
         * === A FORMULATION WITHOUT FROZEN VARIABLES ===
         *
         * We have to solve following system:
         *
         *     [ -(H+Dh+Rh)         Ah'  ] [ Xh ]   [ Bh ]
         *     [          -(Dz+Rz)  Az'  ] [ Xz ] = [ Bz ]
         *     [   Ah     Az         E   ] [ Y  ]   [ By ]
         *
         * with Xh being NMain-dimensional vector, Xz being NSlack-dimensional vector, constraint
         * matrix A being divided into non-slack and slack parts Ah and Az (and Ah, in turn, being
         * divided into sparse and dense parts), Rh and Rz being diagonal regularization matrix,
         * Y being M-dimensional vector.
         *
         * NOTE: due to definition of slack variables following holds: for any diagonal matrix W
         *       a product Az*W*Az' is a diagonal matrix.
         *
         * From the second line we get
         *
         *     Xz = inv(Dz+Rz)*Az'*y - inv(Dz+Rz)*Bz
         *        = inv(Dz+Rz)*Az'*y - BzWave
         *
         * Using this value for Zx, third line gives us 
         *
         *     Y  = inv(E+Az*inv(Dz+Rz)*Az')*(By+Az*BzWave-Ah*Xh)
         *        = inv(EWave)*(ByWave-Ah*Xh)
         *        with EWave = E+Az*inv(Dz+Rz)*Az' and ByWave = By+Az*BzWave
         *
         * Finally, first line gives us 
         *
         *     Xh = -inv(H+Dh+Rh+Ah'*inv(EWave)*Ah)*(Bh-Ah'*inv(EWave)*ByWave)
         *        = -inv(HWave)*BhWave
         *        with HWave = H+Dh+Rh+Ah'*inv(EWave)*Ah and BhWave = Bh-Ah'*inv(EWave)*ByWave
         *
         * In order to prepare factorization we need to compute:
         * (a) diagonal matrices Dh, Rh, Dz and Rz (and precomputed inverse of Dz+Rz)
         * (b) EWave
         * (c) HWave
         *
         * === SPECIAL HANDLING OF FROZEN VARIABLES ===
         *
         * Frozen variables result in zero steps, i.e. zero components of Xh and Xz.
         * It could be implemented by explicit modification of KKT system (zeroing out
         * columns/rows of KKT matrix, rows of right part, putting 1's to diagonal).
         *
         * However, it is possible to do without actually modifying quadratic term and
         * constraints:
         * * freezing elements of Xz can be implemented by zeroing out corresponding
         *   columns of inv(Dz+Rz) because Az always appears in computations along with diagonal Dz+Rz.
         * * freezing elements of Xh is a bit more complex - it needs:
         *   * zeroing out columns/rows of HWave and setting up unit diagonal prior to solving for Xh
         *   * explicitly zeroing out computed elements of Xh prior to computing Y and Xz
         */
        rvectorsetlengthatleast(&state->factregdhrh, nmain, _state);
        rvectorsetlengthatleast(&state->factinvregdzrz, nslack, _state);
        for(i=0; i<=n-1; i++)
        {
            v = (double)(0);
            if( alpha0>(double)0 )
            {
                v = v+alpha0*d->ptr.p_double[i];
            }
            if( alpha11>(double)0 )
            {
                v = v+alpha11;
            }
            v = v+state->diagr.ptr.p_double[i];
            v = v+dampeps;
            ae_assert(v>(double)0, "VIPMFactorize: integrity check failed, degenerate diagonal matrix", _state);
            if( i>=nmain )
            {
                if( !state->isfrozen.ptr.p_bool[i] )
                {
                    state->factinvregdzrz.ptr.p_double[i-nmain] = (double)1/v;
                }
                else
                {
                    state->factinvregdzrz.ptr.p_double[i-nmain] = 0.0;
                }
            }
            else
            {
                state->factregdhrh.ptr.p_double[i] = v;
            }
        }
        
        /*
         * Now we are ready to compute EWave
         */
        rvectorsetlengthatleast(&state->factregewave, m, _state);
        for(i=0; i<=m-1; i++)
        {
            
            /*
             * Compute diagonal element of E
             */
            v = (double)(0);
            if( beta0>(double)0 )
            {
                v = v+beta0*e->ptr.p_double[i];
            }
            if( beta11>(double)0 )
            {
                v = v+beta11;
            }
            v = v+dampeps;
            ae_assert(v>(double)0, "VIPMFactorize: integrity check failed, degenerate diagonal matrix", _state);
            
            /*
             * Compute diagonal modification Az*inv(Dz)*Az'
             */
            k0 = state->combinedaslack.ridx.ptr.p_int[i];
            k1 = state->combinedaslack.ridx.ptr.p_int[i+1]-1;
            for(k=k0; k<=k1; k++)
            {
                vv = state->combinedaslack.vals.ptr.p_double[k];
                v = v+vv*vv*state->factinvregdzrz.ptr.p_double[state->combinedaslack.idx.ptr.p_int[k]];
            }
            
            /*
             * Save EWave
             */
            state->factregewave.ptr.p_double[i] = v;
        }
        
        /*
         * Now we are ready to compute HWave:
         * * store H
         * * add Dh
         * * add Ah'*inv(EWave)*Ah
         */
        rmatrixsetlengthatleast(&state->factdensehaug, nmain, nmain, _state);
        ae_assert(state->hkind==0, "VIPMFactorize: unexpected HKind", _state);
        rmatrixcopy(nmain, nmain, &state->denseh, 0, 0, &state->factdensehaug, 0, 0, _state);
        for(i=0; i<=nmain-1; i++)
        {
            state->factdensehaug.ptr.pp_double[i][i] = state->factdensehaug.ptr.pp_double[i][i]+state->factregdhrh.ptr.p_double[i];
        }
        if( msparse>0 )
        {
            
            /*
             * Handle sparse part of Ah in Ah'*inv(EWave)*Ah
             */
            for(i=0; i<=msparse-1; i++)
            {
                v = 1.0/state->factregewave.ptr.p_double[i];
                k0 = state->sparseamain.ridx.ptr.p_int[i];
                k1 = state->sparseamain.ridx.ptr.p_int[i+1]-1;
                for(ka=k0; ka<=k1; ka++)
                {
                    ja = state->sparseamain.idx.ptr.p_int[ka];
                    va = state->sparseamain.vals.ptr.p_double[ka];
                    for(kb=k0; kb<=ka; kb++)
                    {
                        jb = state->sparseamain.idx.ptr.p_int[kb];
                        vb = state->sparseamain.vals.ptr.p_double[kb];
                        state->factdensehaug.ptr.pp_double[ja][jb] = state->factdensehaug.ptr.pp_double[ja][jb]+v*va*vb;
                    }
                }
            }
        }
        if( mdense>0 )
        {
            
            /*
             * Handle dense part of Ah in Ah'*inv(EWave)*Ah
             */
            rmatrixsetlengthatleast(&state->tmpr2, mdense, nmain, _state);
            rmatrixcopy(mdense, nmain, &state->denseamain, 0, 0, &state->tmpr2, 0, 0, _state);
            for(i=0; i<=mdense-1; i++)
            {
                v = 1.0/ae_sqrt(state->factregewave.ptr.p_double[msparse+i], _state);
                for(j=0; j<=nmain-1; j++)
                {
                    state->tmpr2.ptr.pp_double[i][j] = v*state->tmpr2.ptr.pp_double[i][j];
                }
            }
            rmatrixsyrk(nmain, mdense, 1.0, &state->tmpr2, 0, 0, 2, 1.0, &state->factdensehaug, 0, 0, ae_false, _state);
        }
        
        /*
         * Zero out rows/cols of HWave corresponding to frozen variables, set up unit diagonal
         */
        rsetallocv(nmain, 1.0, &state->tmp0, _state);
        for(i=0; i<=nmain-1; i++)
        {
            if( state->isfrozen.ptr.p_bool[i] )
            {
                state->tmp0.ptr.p_double[i] = 0.0;
            }
        }
        for(i=0; i<=nmain-1; i++)
        {
            if( state->isfrozen.ptr.p_bool[i] )
            {
                
                /*
                 * Entire row is nullified except for diagonal element
                 */
                rsetr(i+1, 0.0, &state->factdensehaug, i, _state);
                state->factdensehaug.ptr.pp_double[i][i] = 1.0;
            }
            else
            {
                
                /*
                 * Only some components are nullified
                 */
                rmergemulvr(i+1, &state->tmp0, &state->factdensehaug, i, _state);
            }
        }
        
        /*
         * Compute Cholesky factorization of HWave
         */
        if( !spdmatrixcholesky(&state->factdensehaug, nmain, ae_false, _state) )
        {
            result = ae_false;
            return result;
        }
        v = (double)(0);
        for(i=0; i<=nmain-1; i++)
        {
            v = v+state->factdensehaug.ptr.pp_double[i][i];
        }
        if( !ae_isfinite(v, _state)||ae_fp_greater(v,badchol) )
        {
            result = ae_false;
            return result;
        }
        state->factorizationpresent = ae_true;
    }
    
    /*
     * Sparse (M+N)x(M+N) factorization
     */
    if( state->factorizationtype==1 )
    {
        t0 = 0;
        
        /*
         * Generate reduced KKT matrix
         */
        rallocv(n+m, &state->facttmpdiag, _state);
        rallocv(n+m, &state->facttmpdamp, _state);
        for(i=0; i<=n-1; i++)
        {
            vv = (double)(0);
            if( alpha0>(double)0 )
            {
                vv = vv+alpha0*d->ptr.p_double[i];
            }
            if( alpha11>(double)0 )
            {
                vv = vv+alpha11;
            }
            vv = vv+state->diagr.ptr.p_double[i];
            state->facttmpdiag.ptr.p_double[i] = -vv;
            state->facttmpdamp.ptr.p_double[i] = -dampeps;
            ae_assert(vv>(double)0, "VIPMFactorize: integrity check failed, degenerate diagonal matrix", _state);
        }
        for(i=0; i<=msparse+mdense-1; i++)
        {
            vv = (double)(0);
            if( beta0>(double)0 )
            {
                vv = vv+beta0*e->ptr.p_double[i];
            }
            if( beta11>(double)0 )
            {
                vv = vv+beta11;
            }
            state->facttmpdiag.ptr.p_double[n+i] = vv;
            state->facttmpdamp.ptr.p_double[n+i] = dampeps;
            ae_assert(vv>(double)0, "VIPMFactorize: integrity check failed, degenerate diagonal matrix", _state);
        }
        
        /*
         * Perform factorization
         * Perform additional integrity check: LDLT should reproduce diagonal of initial KKT system with good precision
         */
        if( state->dotrace )
        {
            ae_trace("--- sparse KKT factorization report ----------------------------------------------------------------\n");
            t0 = ae_tickcount();
        }
        if( !vipmsolver_reducedsystemfactorizewithaddends(&state->reducedsparsesystem, &state->facttmpdiag, &state->facttmpdamp, modeps, badchol, &sumsq, &errsq, _state) )
        {
            result = ae_false;
            return result;
        }
        if( state->dotrace )
        {
            ae_trace("> factorized in %0d ms\n",
                (int)(ae_tickcount()-t0));
        }
        if( ae_fp_greater(ae_sqrt(errsq/((double)1+sumsq), _state),ae_sqrt(ae_machineepsilon, _state)) )
        {
            if( state->dotrace )
            {
                ae_trace("LDLT-diag-err= %0.3e (diagonal reproduction error)\n",
                    (double)(ae_sqrt(errsq/((double)1+sumsq), _state)));
            }
            result = ae_false;
            return result;
        }
        state->factorizationpresent = ae_true;
        
        /*
         * Trace
         */
        if( state->dotrace )
        {
            ae_trace("diag-err     = %0.3e (diagonal reproduction error)\n",
                (double)(ae_sqrt(errsq/((double)1+sumsq), _state)));
        }
    }
    
    /*
     * Done, integrity control
     */
    ae_assert(state->factorizationpresent, "VIPMFactorize: integrity check failed", _state);
    inc(&state->repncholesky, _state);
    return result;
}


/*************************************************************************
A  low-level  function  which  solves  KKT  system  whose  regularized (!)
factorization was prepared by VIPMFactorize(). No iterative refinement  is
performed.

On input, right-hand-side is stored in DeltaXY; on output, solution replaces
DeltaXY.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_solvereducedkktsystem(vipmstate* state,
     /* Real    */ ae_vector* deltaxy,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nmain;
    ae_int_t nslack;
    ae_int_t m;
    ae_int_t mdense;
    ae_int_t msparse;
    ae_int_t i;


    ae_assert(state->factorizationpresent, "VIPMSolve: integrity check failed - factorization is not present", _state);
    ae_assert(state->factorizationtype==0||state->factorizationtype==1, "VIPMSolve: unexpected factorization type", _state);
    n = state->n;
    nmain = state->nmain;
    nslack = n-nmain;
    m = state->mdense+state->msparse;
    mdense = state->mdense;
    msparse = state->msparse;
    
    /*
     * Dense solving
     */
    if( state->factorizationtype==0 )
    {
        
        /*
         * Compute
         *
         *     BzWave = inv(Dz+Rz)*Bz
         *     ByWave = By+Az*BzWave
         *     BhWave = Bh-Ah'*inv(EWave)*ByWave
         */
        for(i=0; i<=nslack-1; i++)
        {
            deltaxy->ptr.p_double[nmain+i] = deltaxy->ptr.p_double[nmain+i]*state->factinvregdzrz.ptr.p_double[i];
        }
        sparsegemv(&state->combinedaslack, 1.0, 0, deltaxy, nmain, 1.0, deltaxy, n, _state);
        rvectorsetlengthatleast(&state->tmp1, m, _state);
        for(i=0; i<=m-1; i++)
        {
            state->tmp1.ptr.p_double[i] = deltaxy->ptr.p_double[n+i]/state->factregewave.ptr.p_double[i];
        }
        sparsegemv(&state->sparseamain, -1.0, 1, &state->tmp1, 0, 1.0, deltaxy, 0, _state);
        rmatrixgemv(nmain, mdense, -1.0, &state->denseamain, 0, 0, 1, &state->tmp1, msparse, 1.0, deltaxy, 0, _state);
        
        /*
         * Compute Xh = -inv(HWave)*BhWave.
         * Zero out components corresponding to frozen variables.
         */
        for(i=0; i<=nmain-1; i++)
        {
            deltaxy->ptr.p_double[i] = -deltaxy->ptr.p_double[i];
        }
        rmatrixtrsv(nmain, &state->factdensehaug, 0, 0, ae_false, ae_false, 0, deltaxy, 0, _state);
        rmatrixtrsv(nmain, &state->factdensehaug, 0, 0, ae_false, ae_false, 1, deltaxy, 0, _state);
        for(i=0; i<=n-1; i++)
        {
            if( state->isfrozen.ptr.p_bool[i] )
            {
                deltaxy->ptr.p_double[i] = (double)(0);
            }
        }
        
        /*
         * Compute Y = inv(EWave)*(ByWave-Ah*Xh)
         */
        sparsegemv(&state->sparseamain, -1.0, 0, deltaxy, 0, 1.0, deltaxy, n, _state);
        rmatrixgemv(mdense, nmain, -1.0, &state->denseamain, 0, 0, 0, deltaxy, 0, 1.0, deltaxy, n+msparse, _state);
        for(i=0; i<=m-1; i++)
        {
            deltaxy->ptr.p_double[n+i] = deltaxy->ptr.p_double[n+i]/state->factregewave.ptr.p_double[i];
        }
        
        /*
         * Compute Xz = -(BzWave - inv(Dz+Rz)*Az'*y)
         */
        rvectorsetlengthatleast(&state->tmp0, nslack, _state);
        for(i=0; i<=nslack-1; i++)
        {
            state->tmp0.ptr.p_double[i] = (double)(0);
        }
        sparsegemv(&state->combinedaslack, 1.0, 1, deltaxy, n, 1.0, &state->tmp0, 0, _state);
        for(i=0; i<=nslack-1; i++)
        {
            deltaxy->ptr.p_double[nmain+i] = -(deltaxy->ptr.p_double[nmain+i]-state->factinvregdzrz.ptr.p_double[i]*state->tmp0.ptr.p_double[i]);
        }
        
        /*
         * Done
         */
        return;
    }
    
    /*
     * Sparse solving
     */
    if( state->factorizationtype==1 )
    {
        vipmsolver_reducedsystemsolve(&state->reducedsparsesystem, state->dotrace, deltaxy, _state);
        for(i=0; i<=n-1; i++)
        {
            if( state->isfrozen.ptr.p_bool[i] )
            {
                deltaxy->ptr.p_double[i] = (double)(0);
            }
        }
        return;
    }
    
    /*
     *
     */
    ae_assert(ae_false, "VIPMSolve: integrity check failed - unexpected factorization", _state);
}


/*************************************************************************
Generates precomputed temporary  vectors  and  KKT  factorization  at  the
beginning of the current iteration.

This function uses representation of  KKT  system  inspired  by  Vanderbei
slack variable approach, but with additional regularization being  applied
all along computations.

On successful factorization returns True; on failure returns False - it is
recommended to increase regularization parameter and try one more time.

--- DESCRIPTION ----------------------------------------------------------

Initial KKT system proposed by Vanderbei has following structure:

    (1) -DS*deltaT - I*deltaS                       = -mu/T + s + DELTAT*DELTAS/T = -GammaS
    (2) -DZ*deltaG - I*deltaZ                       = -mu/G + z + DELTAG*DELTAZ/G = -GammaZ
    (3) -DQ*deltaP - I*deltaQ                       = -mu/P + q + DELTAP*DELTAQ/P = -GammaQ
    (4) -DW*deltaV - I*deltaW                       = -mu/V + w + DELTAV*DELTAW/V = -GammaW
    (5)  -I*deltaY - I*deltaQ + I*deltaV            = y-q+v                       =  Beta
    (6)  -H*deltaX +A'*deltaY + I*deltaZ - I*deltaS = c-A'*y-z+s+H*x              =  Sigma
    (7)   A*deltaX - I*deltaW                       = b-A*x+w                     =  Rho
    (8)   I*deltaX - I*deltaG                       = l-x+g                       =  Nu
    (9)  -I*deltaX - I*deltaT                       = -u+x+t                      = -Tau
    (10) -I*deltaW - I*deltaP                       = -r+w+p                      = -Alpha
    
where
    
    DS = diag(S/T)
    DZ = diag(Z/G)
    DQ = diag(Q/P)
    DW = diag(W/V)

This linear system is actually  symmetric  indefinite  one,  that  can  be
regularized by modifying equations (5), (6),  (7), (8), (9), (10):

    (5)       -I*deltaY - I*deltaQ + I*deltaV -REG*deltaW= y+q-v+REG*w          =  Beta
    (6) -(H+REG)*deltaX +A'*deltaY + I*deltaZ - I*deltaS = c-A'*y-z+s+(H+REG)*x =  Sigma
    (7)        A*deltaX - I*deltaW            +REG*deltaY= b-A*x+w-REG*y        =  Rho
    (8)        I*deltaX - I*deltaG            +REG*deltaZ= l-x+g-REG*z          =  Nu
    (9)       -I*deltaX - I*deltaT            +REG*deltaS= -u+x+t-REG*s         = -Tau
    (10)      -I*deltaW - I*deltaP            +REG*deltaQ= -r+w+p-REG*q         = -Alpha
    
NOTE: regularizing equations (5)-(10) seems to be beneficial because their
      coefficients are well-normalized, usually having unit scale. Contrary
      to that, equations (1)-(4) are wildly nonnormalized, and regularization
      ruins algorithm convergence.

From (1), (2), (3) and (4) we obtain

    deltaT = (GammaS-I*deltaS)/DS
    deltaG = (GammaZ-I*deltaZ)/DZ
    deltaP = (GammaQ-I*deltaQ)/DQ
    deltaV = (GammaW-I*deltaW)/DW
    
and substitute them to equations to obtain

    (5)   -I*deltaY - I*deltaQ      - (inv(DW)+REG)*deltaW =    Beta-inv(DW)*GammaW  =  BetaCap
    (8)    I*deltaX                 + (inv(DZ)+REG)*deltaZ =      Nu+inv(DZ)*GammaZ  =  NuCap
    (9)   -I*deltaX                 + (inv(DS)+REG)*deltaS =   -(Tau-inv(DS)*GammaS) = -TauCap
    (10)  -I*deltaW                 + (inv(DQ)+REG)*deltaQ = -(Alpha-inv(DQ)*GammaQ) = -AlphaCap
    (6)   A'*deltaY + I*deltaZ - I*deltaS - (H+REG)*deltaX = c-A'*y-z+s+(H+REG)*x    =  Sigma
    (7)  REG*deltaY + A*deltaX - I*deltaW                  = b-A*x+w-REG*y           =  Rho

then, we obtain (here IRI stands for Invert-Regularize-Invert)

    DQIRI  = inv(inv(DQ)+REG)
    DZIRI  = inv(inv(DZ)+REG)
    DSIRI  = inv(inv(DS)+REG)
    
    deltaQ = (I*deltaW-AlphaCap)*DQIRI
    deltaZ =    (NuCap-I*deltaX)*DZIRI
    deltaS =   (I*deltaX-TauCap)*DSIRI
    
    DWIR   = inv(DW)+REG
    
and after substitution

    (5)   -I*deltaY        - (DQIRI+DWIR)*deltaW = BetaCap-DQIRI*AlphaCap
    (6)   A'*deltaY - (H+REG+DSIRI+DZIRI)*deltaX = Sigma-DSIRI*TauCap-DZIRI*NuCap
    (7)  REG*deltaY + A*deltaX - I*deltaW        = Rho
    
finally, we obtain
    
    DE     = inv(DQIRI+DWIR)
    DER    = DE+REG
    DDR    = DSIRI+DZIRI+REG
    deltaW = -(BetaCap-DQIRI*AlphaCap+I*deltaY)*DE

and after substitution

    (6)  -(H+DDR)*deltaX +  A'*deltaY = Sigma-DSIRI*TauCap-DZIRI*NuCap
    (7)         A*deltaX + DER*deltaY = Rho-DE*(BetaCap-DQIRI*AlphaCap)

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static ae_bool vipmsolver_vipmprecomputenewtonfactorization(vipmstate* state,
     const vipmvars* v0,
     double regeps,
     double modeps,
     double dampeps,
     double dampfree,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_bool result;


    n = state->n;
    m = state->mdense+state->msparse;
    rsetallocv(n, 0.0, &state->diagdz, _state);
    rsetallocv(n, 0.0, &state->diagdzi, _state);
    rsetallocv(n, 0.0, &state->diagdziri, _state);
    rsetallocv(n, 0.0, &state->diagds, _state);
    rsetallocv(n, 0.0, &state->diagdsi, _state);
    rsetallocv(n, 0.0, &state->diagdsiri, _state);
    rsetallocv(m, 0.0, &state->diagdw, _state);
    rsetallocv(m, 0.0, &state->diagdwi, _state);
    rsetallocv(m, 0.0, &state->diagdwir, _state);
    rsetallocv(m, 0.0, &state->diagdq, _state);
    rsetallocv(m, 0.0, &state->diagdqi, _state);
    rsetallocv(m, 0.0, &state->diagdqiri, _state);
    rallocv(n, &state->diagddr, _state);
    rallocv(m, &state->diagde, _state);
    rallocv(m, &state->diagder, _state);
    
    /*
     * Handle temporary matrices arising due to box constraints
     */
    for(i=0; i<=n-1; i++)
    {
        
        /*
         * Lower bound: G*inv(Z) and Z*inv(G)
         */
        if( state->hasgz.ptr.p_bool[i] )
        {
            ae_assert(v0->g.ptr.p_double[i]>0.0&&v0->z.ptr.p_double[i]>0.0, "VIPMPrecomputeNewtonFactorization: integrity failure - G[i]<=0 or Z[i]<=0", _state);
            state->diagdz.ptr.p_double[i] = v0->z.ptr.p_double[i]/v0->g.ptr.p_double[i];
            state->diagdzi.ptr.p_double[i] = (double)1/state->diagdz.ptr.p_double[i];
            state->diagdziri.ptr.p_double[i] = (double)1/(state->diagdzi.ptr.p_double[i]+regeps);
        }
        else
        {
            ae_assert(v0->g.ptr.p_double[i]==0.0&&v0->z.ptr.p_double[i]==0.0, "VIPMPrecomputeNewtonFactorization: integrity failure - G[i]<>0 or Z[i]<>0 for absent lower bound", _state);
        }
        
        /*
         * Upper bound: T*inv(S) and S*inv(T)
         */
        if( state->hasts.ptr.p_bool[i] )
        {
            ae_assert(v0->t.ptr.p_double[i]>0.0&&v0->s.ptr.p_double[i]>0.0, "VIPMPrecomputeNewtonFactorization: integrity failure - T[i]<=0 or S[i]<=0", _state);
            state->diagds.ptr.p_double[i] = v0->s.ptr.p_double[i]/v0->t.ptr.p_double[i];
            state->diagdsi.ptr.p_double[i] = (double)1/state->diagds.ptr.p_double[i];
            state->diagdsiri.ptr.p_double[i] = (double)1/(state->diagdsi.ptr.p_double[i]+regeps);
        }
        else
        {
            ae_assert(v0->t.ptr.p_double[i]==0.0&&v0->s.ptr.p_double[i]==0.0, "VIPMPrecomputeNewtonFactorization: integrity failure - T[i]<>0 or S[i]<>0 for absent upper bound", _state);
        }
        
        /*
         * Diagonal term D
         */
        state->diagddr.ptr.p_double[i] = state->diagdziri.ptr.p_double[i]+state->diagdsiri.ptr.p_double[i]+regeps;
        if( !state->hasgz.ptr.p_bool[i]&&!state->hasts.ptr.p_bool[i] )
        {
            state->diagddr.ptr.p_double[i] = state->diagddr.ptr.p_double[i]+dampfree;
        }
    }
    
    /*
     * Handle temporary matrices arising due to linear constraints: with lower bound B[]
     * or with lower and upper bounds.
     */
    for(i=0; i<=m-1; i++)
    {
        
        /*
         * Lower bound
         */
        if( state->haswv.ptr.p_bool[i] )
        {
            ae_assert(v0->v.ptr.p_double[i]>0.0&&v0->w.ptr.p_double[i]>0.0, "VIPMPrecomputeNewtonFactorization: integrity failure - V[i]<=0 or W[i]<=0", _state);
            state->diagdw.ptr.p_double[i] = v0->w.ptr.p_double[i]/v0->v.ptr.p_double[i];
            state->diagdwi.ptr.p_double[i] = (double)1/state->diagdw.ptr.p_double[i];
            state->diagdwir.ptr.p_double[i] = state->diagdwi.ptr.p_double[i]+regeps;
        }
        else
        {
            ae_assert(v0->v.ptr.p_double[i]==0.0&&v0->w.ptr.p_double[i]==0.0, "VIPMPrecomputeNewtonFactorization: integrity failure - V[i]<>0 or W[i]<>0 for linear equality constraint", _state);
        }
        
        /*
         * Upper bound
         */
        if( state->haspq.ptr.p_bool[i] )
        {
            ae_assert(v0->p.ptr.p_double[i]>0.0&&v0->q.ptr.p_double[i]>0.0, "VIPMPrecomputeNewtonFactorization: integrity failure - P[i]<=0 or Q[i]<=0", _state);
            state->diagdq.ptr.p_double[i] = v0->q.ptr.p_double[i]/v0->p.ptr.p_double[i];
            state->diagdqi.ptr.p_double[i] = (double)1/state->diagdq.ptr.p_double[i];
            state->diagdqiri.ptr.p_double[i] = (double)1/(state->diagdqi.ptr.p_double[i]+regeps);
        }
        else
        {
            ae_assert(v0->p.ptr.p_double[i]==0.0&&v0->q.ptr.p_double[i]==0.0, "VIPMPrecomputeNewtonFactorization: integrity failure - P[i]<>0 or Q[i]<>0 for absent linear constraint", _state);
        }
        
        /*
         * Diagonal term E
         */
        if( state->haswv.ptr.p_bool[i]||state->haspq.ptr.p_bool[i] )
        {
            state->diagde.ptr.p_double[i] = (double)1/(state->diagdwir.ptr.p_double[i]+state->diagdqiri.ptr.p_double[i]);
        }
        else
        {
            state->diagde.ptr.p_double[i] = 0.0;
        }
        state->diagder.ptr.p_double[i] = state->diagde.ptr.p_double[i]+regeps;
    }
    
    /*
     * Perform factorization
     */
    result = vipmsolver_vipmfactorize(state, 1.0, &state->diagddr, 1.0, &state->diagder, 0.0, 0.0, modeps, dampeps, _state);
    return result;
}


/*************************************************************************
Solves KKT system stored in VIPMState with user-passed RHS.
Sol must be preallocated VIPMVars object whose initial values are ignored.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_solvekktsystem(vipmstate* state,
     const vipmrighthandside* rhs,
     vipmvars* sol,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;


    n = state->n;
    m = state->mdense+state->msparse;
    
    /*
     * Compute elimination temporaries
     *
     * RhsAlphaCap  = RhsAlpha - InvDQ*GammaQ
     * RhsNuCap     = RhsNu    + InvDZ*GammaZ
     * RhsTauCap    = RhsTau   - InvDS*GammaS
     * RhsBetaCap   = RhsBeta  - InvDW*GammaW
     */
    rallocv(n, &state->rhsnucap, _state);
    rallocv(n, &state->rhstaucap, _state);
    rallocv(m, &state->rhsbetacap, _state);
    rallocv(m, &state->rhsalphacap, _state);
    rcopynegmuladdv(m, &state->diagdqi, &rhs->gammaq, &rhs->alpha, &state->rhsalphacap, _state);
    rcopymuladdv(n, &state->diagdzi, &rhs->gammaz, &rhs->nu, &state->rhsnucap, _state);
    rcopynegmuladdv(n, &state->diagdsi, &rhs->gammas, &rhs->tau, &state->rhstaucap, _state);
    rcopynegmuladdv(m, &state->diagdwi, &rhs->gammaw, &rhs->beta, &state->rhsbetacap, _state);
    
    /*
     * Solve reduced KKT system
     */
    rvectorsetlengthatleast(&state->deltaxy, n+m, _state);
    for(i=0; i<=n-1; i++)
    {
        state->deltaxy.ptr.p_double[i] = rhs->sigma.ptr.p_double[i]-state->diagdziri.ptr.p_double[i]*state->rhsnucap.ptr.p_double[i]-state->diagdsiri.ptr.p_double[i]*state->rhstaucap.ptr.p_double[i];
    }
    for(i=0; i<=m-1; i++)
    {
        state->deltaxy.ptr.p_double[n+i] = rhs->rho.ptr.p_double[i]-state->diagde.ptr.p_double[i]*(state->rhsbetacap.ptr.p_double[i]-state->diagdqiri.ptr.p_double[i]*state->rhsalphacap.ptr.p_double[i]);
    }
    vipmsolver_solvereducedkktsystem(state, &state->deltaxy, _state);
    
    /*
     * Perform backsubstitution
     */
    for(i=0; i<=n-1; i++)
    {
        sol->x.ptr.p_double[i] = state->deltaxy.ptr.p_double[i];
        sol->s.ptr.p_double[i] = state->diagdsiri.ptr.p_double[i]*(sol->x.ptr.p_double[i]-state->rhstaucap.ptr.p_double[i]);
        sol->z.ptr.p_double[i] = state->diagdziri.ptr.p_double[i]*(state->rhsnucap.ptr.p_double[i]-sol->x.ptr.p_double[i]);
        sol->g.ptr.p_double[i] = state->diagdzi.ptr.p_double[i]*(rhs->gammaz.ptr.p_double[i]-sol->z.ptr.p_double[i]);
        sol->t.ptr.p_double[i] = state->diagdsi.ptr.p_double[i]*(rhs->gammas.ptr.p_double[i]-sol->s.ptr.p_double[i]);
    }
    for(i=0; i<=m-1; i++)
    {
        sol->y.ptr.p_double[i] = state->deltaxy.ptr.p_double[n+i];
        sol->w.ptr.p_double[i] = -state->diagde.ptr.p_double[i]*(state->rhsbetacap.ptr.p_double[i]-state->diagdqiri.ptr.p_double[i]*state->rhsalphacap.ptr.p_double[i]+sol->y.ptr.p_double[i]);
        sol->q.ptr.p_double[i] = state->diagdqiri.ptr.p_double[i]*(sol->w.ptr.p_double[i]-state->rhsalphacap.ptr.p_double[i]);
        sol->v.ptr.p_double[i] = state->diagdwi.ptr.p_double[i]*(rhs->gammaw.ptr.p_double[i]-sol->w.ptr.p_double[i]);
        sol->p.ptr.p_double[i] = state->diagdqi.ptr.p_double[i]*(rhs->gammaq.ptr.p_double[i]-sol->q.ptr.p_double[i]);
    }
}


/*************************************************************************
Compute VIPM step by solving KKT system.

VDResult must be preallocated VIPMVars object  whose  initial  values  are
ignored.

Returns False on failure to compute step direction with reasonable accuracy
(it is advised to terminate iterations immediately).

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static ae_bool vipmsolver_vipmcomputestepdirection(vipmstate* state,
     const vipmvars* v0,
     double muestimate,
     const vipmvars* vdestimate,
     vipmvars* vdresult,
     double reg,
     ae_bool isdampepslarge,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    double vrhsprim2;
    double vrhsdual2;
    double vrhscmpl2;
    double vresprim2;
    double vresdual2;
    double vrescmpl2;
    double vrhspriminf;
    double vrhsdualinf;
    double vrespriminf;
    double vresdualinf;
    double badres;
    double verybadres;
    double residualgrowth;
    ae_bool primaldestabilized;
    ae_bool dualdestabilized;
    ae_bool result;


    n = state->n;
    m = state->mdense+state->msparse;
    badres = 1.01;
    verybadres = 1.0E3;
    result = ae_true;
    
    /*
     * Initial solver report
     */
    if( state->dotrace )
    {
        ae_trace("--- detailed KKT solver report ---------------------------------------------------------------------\n");
    }
    
    /*
     * Solve KKT system with right-hand sides coming from primal, dual 
     * and complementary slackness conditions. Analyze solution,
     * terminate immediately if primal/dual residuals are way too high.
     */
    vipmsolver_rhscompute(state, v0, muestimate, vdestimate, &state->rhs, reg, _state);
    vrhsprim2 = vipmsolver_rhsprimal2(&state->rhs, n, m, _state);
    vrhsdual2 = vipmsolver_rhsdual2(&state->rhs, n, m, _state);
    vrhscmpl2 = vipmsolver_rhscompl2(&state->rhs, n, m, _state);
    vrhspriminf = vipmsolver_rhsprimalinf(&state->rhs, n, m, _state);
    vrhsdualinf = vipmsolver_rhsdualinf(&state->rhs, n, m, _state);
    if( state->dotrace )
    {
        ae_trace("> primal/dual/complementarity right-hand-side\n");
        ae_trace("rhs-prim     = %0.3e (2-norm)\n",
            (double)(ae_sqrt(vrhsprim2, _state)));
        ae_trace("rhs-dual     = %0.3e (2-norm)\n",
            (double)(ae_sqrt(vrhsdual2, _state)));
        ae_trace("rhs-cmpl     = %0.3e (2-norm)\n",
            (double)(ae_sqrt(vrhscmpl2, _state)));
    }
    vipmsolver_solvekktsystem(state, &state->rhs, vdresult, _state);
    vipmsolver_rhssubtract(state, &state->rhs, v0, vdresult, reg, _state);
    vresprim2 = vipmsolver_rhsprimal2(&state->rhs, n, m, _state);
    vresdual2 = vipmsolver_rhsdual2(&state->rhs, n, m, _state);
    vrescmpl2 = vipmsolver_rhscompl2(&state->rhs, n, m, _state);
    vrespriminf = vipmsolver_rhsprimalinf(&state->rhs, n, m, _state);
    vresdualinf = vipmsolver_rhsdualinf(&state->rhs, n, m, _state);
    if( state->dotrace )
    {
        ae_trace("> primal/dual/complementarity residuals compared with RHS\n");
        ae_trace("res/rhs prim = %0.3e\n",
            (double)(ae_sqrt(vresprim2/coalesce(vrhsprim2, (double)(1), _state), _state)));
        ae_trace("res/rhs dual = %0.3e\n",
            (double)(ae_sqrt(vresdual2/coalesce(vrhsdual2, (double)(1), _state), _state)));
        ae_trace("res/rhs cmpl = %0.3e\n",
            (double)(ae_sqrt(vrescmpl2/coalesce(vrhscmpl2, (double)(1), _state), _state)));
        ae_trace("res/rhs all  = %0.3e\n",
            (double)(ae_sqrt((vresprim2+vresdual2+vrescmpl2)/coalesce(vrhsprim2+vrhsdual2+vrhscmpl2, (double)(1), _state), _state)));
    }
    primaldestabilized = ae_fp_less_eq(vrhspriminf,state->epsp)&&ae_fp_greater_eq(vrespriminf,ae_maxreal(verybadres*vrhspriminf, state->epsp, _state));
    dualdestabilized = ae_fp_less_eq(vrhsdualinf,state->epsd)&&ae_fp_greater_eq(vresdualinf,ae_maxreal(verybadres*vrhsdualinf, state->epsd, _state));
    residualgrowth = ae_sqrt((vresprim2+vresdual2+vrescmpl2)/coalesce(vrhsprim2+vrhsdual2+vrhscmpl2, (double)(1), _state), _state);
    if( ((primaldestabilized||dualdestabilized)&&ae_fp_greater(residualgrowth,0.01*ae_sqrt(ae_machineepsilon, _state)))&&!isdampepslarge )
    {
        if( state->dotrace )
        {
            ae_trace("> primal/dual residual growth is too high, signaling presence of numerical errors\n");
        }
        result = ae_false;
        return result;
    }
    if( ae_fp_greater(residualgrowth,badres) )
    {
        if( state->dotrace )
        {
            ae_trace("> total residual is too high, signaling presence of numerical errors\n");
        }
        result = ae_false;
        return result;
    }
    return result;
}


/*************************************************************************
This function estimates primal and dual step lengths (subject to step
decay parameter, which should be in [0,1] range).

Current version returns same step lengths for primal and dual steps.

INPUT PARAMETERS:
    State               -   solver state
    V0                  -   current point (we ignore one stored in State.Current)
    VS                  -   step direction
    StepDecay           -   decay parameter, the step is multiplied by this
                            coefficient. 1.0 corresponds to full step
                            length being returned. Values in (0,1] range.
    SeparateStep        -   separate step for primal and dual vars
    
OUTPUT PARAMETERS:
    AlphaP              -   primal step (after applying decay coefficient)
    AlphaD              -   dual   step (after applying decay coefficient)

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_vipmcomputesteplength(const vipmstate* state,
     const vipmvars* v0,
     const vipmvars* vs,
     double stepdecay,
     ae_bool separatestep,
     double* alphap,
     double* alphad,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    double alpha;

    *alphap = 0.0;
    *alphad = 0.0;

    n = state->n;
    m = state->mdense+state->msparse;
    ae_assert(n==v0->n&&m==v0->m, "VIPMComputeStepLength: sizes mismatch", _state);
    *alphap = (double)(1);
    *alphad = (double)(1);
    for(i=0; i<=n-1; i++)
    {
        
        /*
         * Primal
         */
        if( vs->g.ptr.p_double[i]<0.0 )
        {
            *alphap = safeminposrv(v0->g.ptr.p_double[i], -vs->g.ptr.p_double[i], *alphap, _state);
        }
        if( vs->t.ptr.p_double[i]<0.0 )
        {
            *alphap = safeminposrv(v0->t.ptr.p_double[i], -vs->t.ptr.p_double[i], *alphap, _state);
        }
        
        /*
         * Dual
         */
        if( vs->z.ptr.p_double[i]<0.0 )
        {
            *alphad = safeminposrv(v0->z.ptr.p_double[i], -vs->z.ptr.p_double[i], *alphad, _state);
        }
        if( vs->s.ptr.p_double[i]<0.0 )
        {
            *alphad = safeminposrv(v0->s.ptr.p_double[i], -vs->s.ptr.p_double[i], *alphad, _state);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        
        /*
         * Primal
         */
        if( vs->w.ptr.p_double[i]<0.0 )
        {
            *alphap = safeminposrv(v0->w.ptr.p_double[i], -vs->w.ptr.p_double[i], *alphap, _state);
        }
        if( vs->p.ptr.p_double[i]<0.0 )
        {
            *alphap = safeminposrv(v0->p.ptr.p_double[i], -vs->p.ptr.p_double[i], *alphap, _state);
        }
        
        /*
         * Dual
         */
        if( vs->v.ptr.p_double[i]<0.0 )
        {
            *alphad = safeminposrv(v0->v.ptr.p_double[i], -vs->v.ptr.p_double[i], *alphad, _state);
        }
        if( vs->q.ptr.p_double[i]<0.0 )
        {
            *alphad = safeminposrv(v0->q.ptr.p_double[i], -vs->q.ptr.p_double[i], *alphad, _state);
        }
    }
    
    /*
     * Separate step or joint step?
     */
    if( separatestep )
    {
        
        /*
         * Separate step on primal/dual
         */
        *alphap = stepdecay*(*alphap);
        *alphad = stepdecay*(*alphad);
    }
    else
    {
        
        /*
         * Because we may solve QP problem, step length has to be same for primal and dual variables
         */
        alpha = ae_minreal(*alphap, *alphad, _state);
        *alphap = stepdecay*alpha;
        *alphad = stepdecay*alpha;
    }
}


/*************************************************************************
This function performs IPM step, updates  iteration  counts  and  performs
following additional checks:
* it monitors status of box/linear constraints  and  smoothly  drops  ones
  with too large bounds (a variable or linear sum is well below constraint
  bound for several iterations)

INPUT PARAMETERS:
    State               -   solver state
    AlphaP              -   primal step to perform
    AlphaD              -   dual   step to perform
    
OUTPUT PARAMETERS:

  -- ALGLIB --
     Copyright 01.08.2020 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_vipmperformstep(vipmstate* state,
     double alphap,
     double alphad,
     ae_state *_state)
{


    
    /*
     * Perform step
     */
    vipmsolver_varsaddstep(&state->current, &state->deltacorr, alphap, alphad, _state);
    
    /*
     * Update iterations count
     */
    inc(&state->repiterationscount, _state);
}


/*************************************************************************
Compute primal/dual errors and complementarity gap

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_computeerrors(vipmstate* state,
     double* errp2,
     double* errd2,
     double* errpinf,
     double* errdinf,
     double* egap,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t cntp2;
    ae_int_t cntd2;
    double v;

    *errp2 = 0.0;
    *errd2 = 0.0;
    *errpinf = 0.0;
    *errdinf = 0.0;
    *egap = 0.0;

    n = state->n;
    m = state->mdense+state->msparse;
    
    /*
     * Compute primal and dual infeasibilities
     */
    vipmsolver_vipmmultiply(state, &state->current.x, &state->current.y, &state->tmphx, &state->tmpax, &state->tmpaty, _state);
    cntp2 = 0;
    *errp2 = (double)(0);
    *errpinf = (double)(0);
    for(i=0; i<=m-1; i++)
    {
        v = state->tmpax.ptr.p_double[i]-state->current.w.ptr.p_double[i]-state->b.ptr.p_double[i];
        *errp2 = *errp2+v*v;
        *errpinf = ae_maxreal(*errpinf, ae_fabs(v, _state), _state);
        inc(&cntp2, _state);
        if( state->haspq.ptr.p_bool[i] )
        {
            v = state->current.w.ptr.p_double[i]+state->current.p.ptr.p_double[i]-state->r.ptr.p_double[i];
            *errp2 = *errp2+v*v;
            *errpinf = ae_maxreal(*errpinf, ae_fabs(v, _state), _state);
            inc(&cntp2, _state);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->hasgz.ptr.p_bool[i] )
        {
            v = state->current.x.ptr.p_double[i]-state->current.g.ptr.p_double[i]-state->bndl.ptr.p_double[i];
            *errp2 = *errp2+v*v;
            *errpinf = ae_maxreal(*errpinf, ae_fabs(v, _state), _state);
            inc(&cntp2, _state);
        }
        if( state->hasts.ptr.p_bool[i] )
        {
            v = state->current.x.ptr.p_double[i]+state->current.t.ptr.p_double[i]-state->bndu.ptr.p_double[i];
            *errp2 = *errp2+v*v;
            *errpinf = ae_maxreal(*errpinf, ae_fabs(v, _state), _state);
            inc(&cntp2, _state);
        }
    }
    *errp2 = ae_sqrt(*errp2/coalesce((double)(cntp2), (double)(1), _state), _state);
    cntd2 = 0;
    *errd2 = (double)(0);
    *errdinf = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        if( !state->isfrozen.ptr.p_bool[i] )
        {
            v = state->tmphx.ptr.p_double[i]+state->c.ptr.p_double[i]-state->tmpaty.ptr.p_double[i];
            if( state->hasgz.ptr.p_bool[i] )
            {
                v = v-state->current.z.ptr.p_double[i];
            }
            if( state->hasts.ptr.p_bool[i] )
            {
                v = v+state->current.s.ptr.p_double[i];
            }
            *errd2 = *errd2+v*v;
            *errdinf = ae_maxreal(*errdinf, ae_fabs(v, _state), _state);
            inc(&cntd2, _state);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        v = (double)(0);
        if( state->haswv.ptr.p_bool[i] )
        {
            v = state->current.y.ptr.p_double[i]-state->current.v.ptr.p_double[i];
        }
        if( state->haspq.ptr.p_bool[i] )
        {
            v = v+state->current.q.ptr.p_double[i];
        }
        *errd2 = *errd2+v*v;
        *errdinf = ae_maxreal(*errdinf, ae_fabs(v, _state), _state);
        if( state->haswv.ptr.p_bool[i]||state->haspq.ptr.p_bool[i] )
        {
            inc(&cntd2, _state);
        }
    }
    *errd2 = ae_sqrt(*errd2/coalesce((double)(cntd2), (double)(1), _state), _state);
    *egap = vipmsolver_varscomputecomplementaritygap(&state->current, _state)/(1.0+ae_fabs(vipmsolver_vipmtarget(state, &state->current.x, _state), _state));
}


/*************************************************************************
Performs integrity checks for current point and step

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_runintegritychecks(const vipmstate* state,
     const vipmvars* v0,
     const vipmvars* vd,
     double alphap,
     double alphad,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;


    n = state->n;
    m = state->mdense+state->msparse;
    ae_assert(ae_isfinite(alphap, _state)&&ae_fp_greater_eq(alphap,(double)(0)), "[VIPM]RunIntegrityChecks: bad AlphaP", _state);
    ae_assert(ae_isfinite(alphad, _state)&&ae_fp_greater_eq(alphad,(double)(0)), "[VIPM]RunIntegrityChecks: bad AlphaD", _state);
    for(i=0; i<=n-1; i++)
    {
        if( state->hasgz.ptr.p_bool[i] )
        {
            ae_assert(!state->isfrozen.ptr.p_bool[i], "[VIPM]RunIntegrityChecks: integrity failure - X[I] is frozen", _state);
            ae_assert(v0->g.ptr.p_double[i]>0.0&&v0->z.ptr.p_double[i]>0.0, "[VIPM]RunIntegrityChecks: integrity failure - G[i]<=0 or Z[i]<=0", _state);
        }
        else
        {
            ae_assert(v0->g.ptr.p_double[i]==0.0&&v0->z.ptr.p_double[i]==0.0, "[VIPM]RunIntegrityChecks: integrity failure - G[i]<>0 or Z[i]<>0 for absent lower bound", _state);
            ae_assert(vd->g.ptr.p_double[i]==0.0&&vd->z.ptr.p_double[i]==0.0, "[VIPM]RunIntegrityChecks: integrity failure - G[i]<>0 or Z[i]<>0 for absent lower bound", _state);
        }
        if( state->hasts.ptr.p_bool[i] )
        {
            ae_assert(!state->isfrozen.ptr.p_bool[i], "[VIPM]RunIntegrityChecks: integrity failure - X[I] is frozen", _state);
            ae_assert(v0->t.ptr.p_double[i]>0.0&&v0->s.ptr.p_double[i]>0.0, "[VIPM]RunIntegrityChecks: integrity failure - T[i]<=0 or S[i]<=0", _state);
        }
        else
        {
            ae_assert(v0->t.ptr.p_double[i]==0.0&&v0->s.ptr.p_double[i]==0.0, "[VIPM]RunIntegrityChecks: integrity failure - T[i]<>0 or S[i]<>0 for absent upper bound", _state);
            ae_assert(vd->t.ptr.p_double[i]==0.0&&vd->s.ptr.p_double[i]==0.0, "[VIPM]RunIntegrityChecks: integrity failure - T[i]<>0 or S[i]<>0 for absent upper bound", _state);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        ae_assert(state->haswv.ptr.p_bool[i]||!state->haspq.ptr.p_bool[i], "[VIPM]RunIntegrityChecks: inconsistent HasWV/HasPQ", _state);
        if( state->haswv.ptr.p_bool[i] )
        {
            ae_assert(v0->v.ptr.p_double[i]>0.0&&v0->w.ptr.p_double[i]>0.0, "[VIPM]RunIntegrityChecks: integrity failure - V[i]<=0 or W[i]<=0", _state);
        }
        else
        {
            ae_assert(v0->v.ptr.p_double[i]==0.0&&v0->w.ptr.p_double[i]==0.0, "[VIPM]RunIntegrityChecks: integrity failure - V[i]<>0 or W[i]<>0 for linear equality constraint", _state);
            ae_assert(vd->v.ptr.p_double[i]==0.0&&vd->w.ptr.p_double[i]==0.0, "[VIPM]RunIntegrityChecks: integrity failure - V[i]<>0 or W[i]<>0 for linear equality constraint", _state);
        }
        if( state->haspq.ptr.p_bool[i] )
        {
            ae_assert(v0->p.ptr.p_double[i]>0.0&&v0->q.ptr.p_double[i]>0.0, "[VIPM]RunIntegrityChecks: integrity failure - P[i]<=0 or Q[i]<=0", _state);
        }
        else
        {
            ae_assert(v0->p.ptr.p_double[i]==0.0&&v0->q.ptr.p_double[i]==0.0, "[VIPM]RunIntegrityChecks: integrity failure - P[i]<>0 or Q[i]<>0 for absent range of linear constraint", _state);
            ae_assert(vd->p.ptr.p_double[i]==0.0&&vd->q.ptr.p_double[i]==0.0, "[VIPM]RunIntegrityChecks: integrity failure - P[i]<>0 or Q[i]<>0 for absent range of linear constraint", _state);
        }
    }
}


/*************************************************************************
Evaluate progress so far, outputs trace data, if requested to do so.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_traceprogress(vipmstate* state,
     double mu,
     double muaff,
     double sigma,
     double alphap,
     double alphad,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    double v;
    double errp2;
    double errd2;
    double errpinf;
    double errdinf;
    double errgap;


    n = state->n;
    m = state->mdense+state->msparse;
    if( !state->dotrace )
    {
        return;
    }
    
    /*
     * Print high-level information
     */
    vipmsolver_computeerrors(state, &errp2, &errd2, &errpinf, &errdinf, &errgap, _state);
    ae_trace("--- step report ------------------------------------------------------------------------------------\n");
    ae_trace("> step information\n");
    ae_trace("mu_init = %0.3e    (at the beginning)\n",
        (double)(mu));
    ae_trace("mu_aff  = %0.3e    (by affine scaling step)\n",
        (double)(muaff));
    ae_trace("sigma   = %0.3e    (centering parameter)\n",
        (double)(sigma));
    ae_trace("alphaP  = %0.3e    (primal step)\n",
        (double)(alphap));
    ae_trace("alphaD  = %0.3e    (dual   step)\n",
        (double)(alphad));
    ae_trace("mu_cur  = %0.3e    (after the step)\n",
        (double)(vipmsolver_varscomputemu(state, &state->current, _state)));
    ae_trace("> errors\n");
    ae_trace("errP    = %0.3e    (primal infeasibility, inf-norm)\n",
        (double)(errpinf));
    ae_trace("errD    = %0.3e    (dual infeasibility,   inf-norm)\n",
        (double)(errdinf));
    ae_trace("errGap  = %0.3e    (complementarity gap)\n",
        (double)(errgap));
    ae_trace("> current point information (inf-norm)\n");
    ae_trace("|X|=%8.1e,  |G|=%8.1e,  |T|=%8.1e,  |W|=%8.1e,  |P|=%8.1e\n",
        (double)(rmaxabsv(n, &state->current.x, _state)),
        (double)(rmaxabsv(n, &state->current.g, _state)),
        (double)(rmaxabsv(n, &state->current.t, _state)),
        (double)(rmaxabsv(m, &state->current.w, _state)),
        (double)(rmaxabsv(m, &state->current.p, _state)));
    ae_trace("|Y|=%8.1e,  |Z|=%8.1e,  |S|=%8.1e,  |V|=%8.1e,  |Q|=%8.1e\n",
        (double)(rmaxabsv(m, &state->current.y, _state)),
        (double)(rmaxabsv(n, &state->current.z, _state)),
        (double)(rmaxabsv(n, &state->current.s, _state)),
        (double)(rmaxabsv(m, &state->current.v, _state)),
        (double)(rmaxabsv(m, &state->current.q, _state)));
    
    /*
     * Print variable stats, if required
     */
    if( state->dotrace )
    {
        ae_trace("--- variable statistics ----------------------------------------------------------------------------\n");
        ae_trace("> smallest values for nonnegative vars\n");
        ae_trace("primal:       minG=%8.1e  minT=%8.1e  minW=%8.1e  minP=%8.1e\n",
            (double)(vipmsolver_minnz(&state->current.g, n, _state)),
            (double)(vipmsolver_minnz(&state->current.t, n, _state)),
            (double)(vipmsolver_minnz(&state->current.w, m, _state)),
            (double)(vipmsolver_minnz(&state->current.p, m, _state)));
        ae_trace("dual:         minZ=%8.1e  minS=%8.1e  minV=%8.1e  minQ=%8.1e\n",
            (double)(vipmsolver_minnz(&state->current.z, n, _state)),
            (double)(vipmsolver_minnz(&state->current.s, n, _state)),
            (double)(vipmsolver_minnz(&state->current.v, m, _state)),
            (double)(vipmsolver_minnz(&state->current.q, m, _state)));
        ae_trace("> min and max complementary slackness\n");
        ae_trace("min:            GZ=%8.1e    TS=%8.1e    WV=%8.1e    PQ=%8.1e\n",
            (double)(vipmsolver_minprodnz(&state->current.g, &state->current.z, n, _state)),
            (double)(vipmsolver_minprodnz(&state->current.t, &state->current.s, n, _state)),
            (double)(vipmsolver_minprodnz(&state->current.w, &state->current.v, m, _state)),
            (double)(vipmsolver_minprodnz(&state->current.p, &state->current.q, m, _state)));
        ae_trace("max:            GZ=%8.1e    TS=%8.1e    WV=%8.1e    PQ=%8.1e\n",
            (double)(vipmsolver_maxprodnz(&state->current.g, &state->current.z, n, _state)),
            (double)(vipmsolver_maxprodnz(&state->current.t, &state->current.s, n, _state)),
            (double)(vipmsolver_maxprodnz(&state->current.w, &state->current.v, m, _state)),
            (double)(vipmsolver_maxprodnz(&state->current.p, &state->current.q, m, _state)));
    }
    
    /*
     * Detailed output (all variables values, not suited for high-dimensional problems)
     */
    if( state->dodetailedtrace )
    {
        vipmsolver_vipmmultiply(state, &state->current.x, &state->current.y, &state->tmphx, &state->tmpax, &state->tmpaty, _state);
        rsetallocv(n, 0.0, &state->tmplaggrad, _state);
        for(i=0; i<=n-1; i++)
        {
            if( !state->isfrozen.ptr.p_bool[i] )
            {
                v = state->tmphx.ptr.p_double[i]+state->c.ptr.p_double[i]-state->tmpaty.ptr.p_double[i];
                if( state->hasgz.ptr.p_bool[i] )
                {
                    v = v-state->current.z.ptr.p_double[i];
                }
                if( state->hasts.ptr.p_bool[i] )
                {
                    v = v+state->current.s.ptr.p_double[i];
                }
                state->tmplaggrad.ptr.p_double[i] = v;
            }
        }
        ae_trace("--- printing raw data (prior to applying variable scales and shifting by XOrigin) ------------------\n");
        ae_trace("X (raw)         = ");
        tracevectorunscaledunshiftedautoprec(&state->current.x, n, &state->scl, ae_true, &state->xorigin, ae_true, _state);
        ae_trace("\n");
        ae_trace("--- printing scaled data (after applying variable scales and shifting by XOrigin) ------------------\n");
        ae_trace("> reporting X, Lagrangian gradient\n");
        ae_trace("Xnew            = ");
        tracevectorautoprec(&state->current.x, 0, n, _state);
        ae_trace("\n");
        ae_trace("Lag-grad        = ");
        tracevectorautoprec(&state->tmplaggrad, 0, n, _state);
        ae_trace("\n");
        ae_trace("--- printing new point -----------------------------------------------------------------------------\n");
        ae_trace("> primal slacks and dual multipliers for box constraints\n");
        ae_trace("G (L prim slck) = ");
        tracevectorautoprec(&state->current.g, 0, n, _state);
        ae_trace("\n");
        ae_trace("Z (L dual mult) = ");
        tracevectorautoprec(&state->current.z, 0, n, _state);
        ae_trace("\n");
        ae_trace("T (U prim slck) = ");
        tracevectorautoprec(&state->current.t, 0, n, _state);
        ae_trace("\n");
        ae_trace("S (U dual mult) = ");
        tracevectorautoprec(&state->current.s, 0, n, _state);
        ae_trace("\n");
        ae_trace("> primal slacks and dual multipliers for linear constraints, B/R stand for B<=Ax<=B+R\n");
        ae_trace("Y (lag mult)    = ");
        tracevectorautoprec(&state->current.y, 0, m, _state);
        ae_trace("\n");
        ae_trace("W (B prim slck) = ");
        tracevectorautoprec(&state->current.w, 0, m, _state);
        ae_trace("\n");
        ae_trace("V (B dual mult) = ");
        tracevectorautoprec(&state->current.v, 0, m, _state);
        ae_trace("\n");
        ae_trace("P (R prim slck) = ");
        tracevectorautoprec(&state->current.p, 0, m, _state);
        ae_trace("\n");
        ae_trace("Q (R dual mult) = ");
        tracevectorautoprec(&state->current.q, 0, m, _state);
        ae_trace("\n");
    }
    ae_trace("\n");
}


/*************************************************************************
Compute right-hand side for KKT system.

INPUT PARAMETERS:
    State           -   IPM state
    V0              -   current point (used to compute RHS)
    MuEstimate      -   estimate of Mu (can be zero)
    DirEstimate     -   estimate of delta's (can be zero)
    
OUTPUT PARAMETERS:
    Rhs             -   RHS

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_rhscompute(vipmstate* state,
     const vipmvars* v0,
     double muestimate,
     const vipmvars* direstimate,
     vipmrighthandside* rhs,
     double reg,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;


    n = state->n;
    m = state->mdense+state->msparse;
    
    /*
     * Allocate
     */
    rvectorsetlengthatleast(&rhs->sigma, n, _state);
    rvectorsetlengthatleast(&rhs->nu, n, _state);
    rvectorsetlengthatleast(&rhs->tau, n, _state);
    rvectorsetlengthatleast(&rhs->gammaz, n, _state);
    rvectorsetlengthatleast(&rhs->gammas, n, _state);
    rvectorsetlengthatleast(&rhs->gammaw, m, _state);
    rvectorsetlengthatleast(&rhs->gammaq, m, _state);
    rsetallocv(m, 0.0, &rhs->beta, _state);
    rsetallocv(m, 0.0, &rhs->rho, _state);
    rsetallocv(m, 0.0, &rhs->alpha, _state);
    
    /*
     * Compute products H*x, A*x, A^T*y
     * We compute these products in one location for the sake of simplicity.
     */
    vipmsolver_vipmmultiply(state, &v0->x, &v0->y, &state->tmphx, &state->tmpax, &state->tmpaty, _state);
    
    /*
     * Compute right-hand side:
     * Rho      = b - A*x + w
     * Nu       = l - x + g
     * Tau      = u - x - t
     * Alpha    = r - w - p
     * Sigma    = c - A^T*y - z + s + (H+REG)*x
     * Beta     = y + q - v
     */
    for(i=0; i<=m-1; i++)
    {
        rhs->rho.ptr.p_double[i] = state->b.ptr.p_double[i]-state->tmpax.ptr.p_double[i]-reg*v0->y.ptr.p_double[i];
        if( state->haswv.ptr.p_bool[i] )
        {
            
            /*
             * Inequality/range constraint
             */
            rhs->rho.ptr.p_double[i] = rhs->rho.ptr.p_double[i]+v0->w.ptr.p_double[i];
        }
        else
        {
            
            /*
             * Equality constraint without slack variables, W[i]=0
             */
            ae_assert(v0->w.ptr.p_double[i]==0.0, "RhsCompute: W[i]<>0 for linear equality constraint", _state);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->hasgz.ptr.p_bool[i] )
        {
            
            /*
             * Lower bound is present
             */
            rhs->nu.ptr.p_double[i] = state->bndl.ptr.p_double[i]-v0->x.ptr.p_double[i]+v0->g.ptr.p_double[i]-reg*v0->z.ptr.p_double[i];
        }
        else
        {
            
            /*
             * Lower bound is absent, g[i] = 0
             */
            ae_assert(v0->g.ptr.p_double[i]==0.0, "RhsCompute: G[i]<>0 for absent constraint", _state);
            rhs->nu.ptr.p_double[i] = (double)(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->hasts.ptr.p_bool[i] )
        {
            
            /*
             * Upper bound is present
             */
            rhs->tau.ptr.p_double[i] = state->bndu.ptr.p_double[i]-v0->x.ptr.p_double[i]-v0->t.ptr.p_double[i]+reg*v0->s.ptr.p_double[i];
        }
        else
        {
            
            /*
             * Upper bound is absent, t[i] = 0
             */
            ae_assert(v0->t.ptr.p_double[i]==0.0, "RhsCompute: T[i]<>0 for absent constraint", _state);
            rhs->tau.ptr.p_double[i] = (double)(0);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( state->haspq.ptr.p_bool[i] )
        {
            rhs->alpha.ptr.p_double[i] = state->r.ptr.p_double[i]-v0->w.ptr.p_double[i]-v0->p.ptr.p_double[i]+reg*v0->q.ptr.p_double[i];
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( !state->isfrozen.ptr.p_bool[i] )
        {
            rhs->sigma.ptr.p_double[i] = state->c.ptr.p_double[i]-state->tmpaty.ptr.p_double[i]+state->tmphx.ptr.p_double[i]+reg*v0->x.ptr.p_double[i];
            if( state->hasgz.ptr.p_bool[i] )
            {
                rhs->sigma.ptr.p_double[i] = rhs->sigma.ptr.p_double[i]-v0->z.ptr.p_double[i];
            }
            if( state->hasts.ptr.p_bool[i] )
            {
                rhs->sigma.ptr.p_double[i] = rhs->sigma.ptr.p_double[i]+v0->s.ptr.p_double[i];
            }
        }
        else
        {
            rhs->sigma.ptr.p_double[i] = (double)(0);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( state->haswv.ptr.p_bool[i] )
        {
            rhs->beta.ptr.p_double[i] = rhs->beta.ptr.p_double[i]+v0->y.ptr.p_double[i]-v0->v.ptr.p_double[i]+reg*v0->w.ptr.p_double[i];
        }
        if( state->haspq.ptr.p_bool[i] )
        {
            rhs->beta.ptr.p_double[i] = rhs->beta.ptr.p_double[i]+v0->q.ptr.p_double[i];
        }
    }
    
    /*
     * Compute right-hand side:
     * GammaZ   = mu*inv(G)*e - z - inv(G)*DELTAG*deltaZ
     * GammaW   = mu*inv(V)*e - w - inv(V)*DELTAV*deltaW
     * GammaS   = mu*inv(T)*e - s - inv(T)*DELTAT*deltaS
     * GammaQ   = mu*inv(P)*e - q - inv(P)*DELTAP*deltaQ
     */
    for(i=0; i<=n-1; i++)
    {
        if( state->hasgz.ptr.p_bool[i] )
        {
            ae_assert(v0->g.ptr.p_double[i]>0.0, "RhsCompute: G[i]<=0", _state);
            rhs->gammaz.ptr.p_double[i] = muestimate/v0->g.ptr.p_double[i]-v0->z.ptr.p_double[i]-direstimate->g.ptr.p_double[i]*direstimate->z.ptr.p_double[i]/v0->g.ptr.p_double[i];
        }
        else
        {
            ae_assert(v0->g.ptr.p_double[i]==0.0, "RhsCompute: G[i]<>0 for absent constraint", _state);
            ae_assert(v0->z.ptr.p_double[i]==0.0, "RhsCompute: Z[i]<>0 for absent constraint", _state);
            rhs->gammaz.ptr.p_double[i] = (double)(0);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( state->haswv.ptr.p_bool[i] )
        {
            
            /*
             * Inequality/range constraint
             */
            ae_assert(v0->v.ptr.p_double[i]>0.0, "RhsCompute: V[i]<=0", _state);
            rhs->gammaw.ptr.p_double[i] = muestimate/v0->v.ptr.p_double[i]-v0->w.ptr.p_double[i]-direstimate->v.ptr.p_double[i]*direstimate->w.ptr.p_double[i]/v0->v.ptr.p_double[i];
        }
        else
        {
            
            /*
             * Equality constraint
             */
            ae_assert(v0->v.ptr.p_double[i]==0.0, "RhsCompute: V[i]<>0 for equality constraint", _state);
            ae_assert(v0->w.ptr.p_double[i]==0.0, "RhsCompute: W[i]<>0 for equality constraint", _state);
            rhs->gammaw.ptr.p_double[i] = (double)(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->hasts.ptr.p_bool[i] )
        {
            
            /*
             * Upper bound is present
             */
            ae_assert(v0->t.ptr.p_double[i]>0.0, "RhsCompute: T[i]<=0", _state);
            rhs->gammas.ptr.p_double[i] = muestimate/v0->t.ptr.p_double[i]-v0->s.ptr.p_double[i]-direstimate->t.ptr.p_double[i]*direstimate->s.ptr.p_double[i]/v0->t.ptr.p_double[i];
        }
        else
        {
            
            /*
             * Upper bound is absent
             */
            ae_assert(v0->t.ptr.p_double[i]==0.0, "RhsCompute: T[i]<>0 for absent constraint", _state);
            ae_assert(v0->s.ptr.p_double[i]==0.0, "RhsCompute: S[i]<>0 for absent constraint", _state);
            rhs->gammas.ptr.p_double[i] = (double)(0);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( state->haspq.ptr.p_bool[i] )
        {
            ae_assert(v0->p.ptr.p_double[i]>0.0, "RhsCompute: P[i]<=0", _state);
            rhs->gammaq.ptr.p_double[i] = muestimate/v0->p.ptr.p_double[i]-v0->q.ptr.p_double[i]-direstimate->p.ptr.p_double[i]*direstimate->q.ptr.p_double[i]/v0->p.ptr.p_double[i];
        }
        else
        {
            ae_assert(v0->p.ptr.p_double[i]==0.0, "RhsCompute: P[i]<>0 for absent range", _state);
            ae_assert(v0->q.ptr.p_double[i]==0.0, "RhsCompute: Q[i]<>0 for absent range", _state);
            rhs->gammaq.ptr.p_double[i] = (double)(0);
        }
    }
}


/*************************************************************************
Subtracts KKT*cand from already computed RHS.

A pair of RhsCompute/RhsSubtract calls results in  residual  being  loaded
into the RHS structure.

INPUT PARAMETERS:
    State           -   IPM state
    V0              -   current point (used to compute RHS)
    MuEstimate      -   estimate of Mu (can be zero)
    DirEstimate     -   estimate of delta's (can be zero)
    ResidualFrom    -   whether we want to compute RHS or residual computed
                        using VDCandidate
    VDCandidate     -   solution candidate
    
OUTPUT PARAMETERS:
    Rhs             -   either RHS or residual RHS-KKT*Cand

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void vipmsolver_rhssubtract(vipmstate* state,
     vipmrighthandside* rhs,
     const vipmvars* v0,
     const vipmvars* vdcandidate,
     double reg,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;


    n = state->n;
    m = state->mdense+state->msparse;
    vipmsolver_vipmmultiply(state, &vdcandidate->x, &vdcandidate->y, &state->tmphx, &state->tmpax, &state->tmpaty, _state);
    
    /*
     * Residual for Rho, Nu, Tau, Alpha, Sigma, Beta
     */
    for(i=0; i<=m-1; i++)
    {
        if( state->haswv.ptr.p_bool[i] )
        {
            rhs->rho.ptr.p_double[i] = rhs->rho.ptr.p_double[i]-(state->tmpax.ptr.p_double[i]-vdcandidate->w.ptr.p_double[i]+reg*vdcandidate->y.ptr.p_double[i]);
        }
        else
        {
            rhs->rho.ptr.p_double[i] = rhs->rho.ptr.p_double[i]-(state->tmpax.ptr.p_double[i]+reg*vdcandidate->y.ptr.p_double[i]);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->hasgz.ptr.p_bool[i] )
        {
            rhs->nu.ptr.p_double[i] = rhs->nu.ptr.p_double[i]-(vdcandidate->x.ptr.p_double[i]-vdcandidate->g.ptr.p_double[i]+reg*vdcandidate->z.ptr.p_double[i]);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->hasts.ptr.p_bool[i] )
        {
            rhs->tau.ptr.p_double[i] = rhs->tau.ptr.p_double[i]-(vdcandidate->x.ptr.p_double[i]+vdcandidate->t.ptr.p_double[i]-reg*vdcandidate->s.ptr.p_double[i]);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( state->haspq.ptr.p_bool[i] )
        {
            rhs->alpha.ptr.p_double[i] = rhs->alpha.ptr.p_double[i]-(vdcandidate->w.ptr.p_double[i]+vdcandidate->p.ptr.p_double[i]-reg*vdcandidate->q.ptr.p_double[i]);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( !state->isfrozen.ptr.p_bool[i] )
        {
            rhs->sigma.ptr.p_double[i] = rhs->sigma.ptr.p_double[i]-(state->tmpaty.ptr.p_double[i]-state->tmphx.ptr.p_double[i]-reg*vdcandidate->x.ptr.p_double[i]);
            if( state->hasgz.ptr.p_bool[i] )
            {
                rhs->sigma.ptr.p_double[i] = rhs->sigma.ptr.p_double[i]-vdcandidate->z.ptr.p_double[i];
            }
            if( state->hasts.ptr.p_bool[i] )
            {
                rhs->sigma.ptr.p_double[i] = rhs->sigma.ptr.p_double[i]+vdcandidate->s.ptr.p_double[i];
            }
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( state->haswv.ptr.p_bool[i] )
        {
            rhs->beta.ptr.p_double[i] = rhs->beta.ptr.p_double[i]-(-vdcandidate->y.ptr.p_double[i]+vdcandidate->v.ptr.p_double[i]-reg*vdcandidate->w.ptr.p_double[i]);
        }
        if( state->haspq.ptr.p_bool[i] )
        {
            rhs->beta.ptr.p_double[i] = rhs->beta.ptr.p_double[i]+vdcandidate->q.ptr.p_double[i];
        }
    }
    
    /*
     * Residual for GammaZ, GammaW, GammaS, GammaQ
     */
    for(i=0; i<=n-1; i++)
    {
        if( state->hasgz.ptr.p_bool[i] )
        {
            rhs->gammaz.ptr.p_double[i] = rhs->gammaz.ptr.p_double[i]-(v0->z.ptr.p_double[i]/v0->g.ptr.p_double[i]*vdcandidate->g.ptr.p_double[i]+vdcandidate->z.ptr.p_double[i]);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( state->haswv.ptr.p_bool[i] )
        {
            rhs->gammaw.ptr.p_double[i] = rhs->gammaw.ptr.p_double[i]-(v0->w.ptr.p_double[i]/v0->v.ptr.p_double[i]*vdcandidate->v.ptr.p_double[i]+vdcandidate->w.ptr.p_double[i]);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->hasts.ptr.p_bool[i] )
        {
            rhs->gammas.ptr.p_double[i] = rhs->gammas.ptr.p_double[i]-(v0->s.ptr.p_double[i]/v0->t.ptr.p_double[i]*vdcandidate->t.ptr.p_double[i]+vdcandidate->s.ptr.p_double[i]);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( state->haspq.ptr.p_bool[i] )
        {
            rhs->gammaq.ptr.p_double[i] = rhs->gammaq.ptr.p_double[i]-(v0->q.ptr.p_double[i]/v0->p.ptr.p_double[i]*vdcandidate->p.ptr.p_double[i]+vdcandidate->q.ptr.p_double[i]);
        }
    }
}


/*************************************************************************
Computes sum of squared primal terms of RHS

INPUT PARAMETERS:
    Rhs             -   RHS structure
    N, M            -   problem metrics
    
RESULT:
    sum(sqr()) computed over primal terms (Rho, Nu, Tau, Alpha)

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double vipmsolver_rhsprimal2(const vipmrighthandside* rhs,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state)
{
    double result;


    result = (double)(0);
    result = result+rdotv2(m, &rhs->rho, _state);
    result = result+rdotv2(n, &rhs->nu, _state);
    result = result+rdotv2(n, &rhs->tau, _state);
    result = result+rdotv2(m, &rhs->alpha, _state);
    return result;
}


/*************************************************************************
Computes sum of squared dual terms of RHS

INPUT PARAMETERS:
    Rhs             -   RHS structure
    N, M            -   problem metrics
    
RESULT:
    sum(sqr()) computed over dual terms (Sigma, Beta)

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double vipmsolver_rhsdual2(const vipmrighthandside* rhs,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state)
{
    double result;


    result = (double)(0);
    result = result+rdotv2(n, &rhs->sigma, _state);
    result = result+rdotv2(m, &rhs->beta, _state);
    return result;
}


/*************************************************************************
Computes inf-norm of primal terms of RHS

INPUT PARAMETERS:
    Rhs             -   RHS structure
    N, M            -   problem metrics
    
RESULT:
    max(abs()) computed over primal terms (Rho, Nu, Tau, Alpha)

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double vipmsolver_rhsprimalinf(const vipmrighthandside* rhs,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state)
{
    double result;


    result = (double)(0);
    result = ae_maxreal(result, rmaxabsv(m, &rhs->rho, _state), _state);
    result = ae_maxreal(result, rmaxabsv(n, &rhs->nu, _state), _state);
    result = ae_maxreal(result, rmaxabsv(n, &rhs->tau, _state), _state);
    result = ae_maxreal(result, rmaxabsv(m, &rhs->alpha, _state), _state);
    return result;
}


/*************************************************************************
Computes inf-norm of dual terms of RHS

INPUT PARAMETERS:
    Rhs             -   RHS structure
    N, M            -   problem metrics
    
RESULT:
    max(abs()) computed over dual terms (Sigma, Beta)

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double vipmsolver_rhsdualinf(const vipmrighthandside* rhs,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state)
{
    double result;


    result = (double)(0);
    result = ae_maxreal(result, rmaxabsv(n, &rhs->sigma, _state), _state);
    result = ae_maxreal(result, rmaxabsv(m, &rhs->beta, _state), _state);
    return result;
}


/*************************************************************************
Computes maximum over complementarity slackness terms of RHS

INPUT PARAMETERS:
    Rhs             -   RHS structure
    N, M            -   problem metrics
    
RESULT:
    max(abs()) computed over complementarity terms (GammaZ, GammaS, GammaW, GammaQ)

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double vipmsolver_rhscompl2(const vipmrighthandside* rhs,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state)
{
    double result;


    result = (double)(0);
    result = result+rdotv2(n, &rhs->gammaz, _state);
    result = result+rdotv2(n, &rhs->gammas, _state);
    result = result+rdotv2(m, &rhs->gammaw, _state);
    result = result+rdotv2(m, &rhs->gammaq, _state);
    return result;
}


/*************************************************************************
Computes minimum nonzero value of the vector. Returns 0 if all components
are nonpositive.

INPUT PARAMETERS:
    X               -   vector
    N               -   length

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double vipmsolver_minnz(/* Real    */ const ae_vector* x,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool nz;
    double result;


    result = (double)(0);
    nz = ae_false;
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater(x->ptr.p_double[i],(double)(0)) )
        {
            if( !nz )
            {
                result = x->ptr.p_double[i];
                nz = ae_true;
            }
            else
            {
                result = ae_minreal(result, x->ptr.p_double[i], _state);
            }
        }
    }
    return result;
}


/*************************************************************************
Computes minimum product of nonzero components.
Returns 0 if all components are nonpositive.

INPUT PARAMETERS:
    X               -   vector
    Y               -   vector
    N               -   length

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double vipmsolver_minprodnz(/* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool nz;
    double result;


    result = (double)(0);
    nz = ae_false;
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater(x->ptr.p_double[i],(double)(0))&&ae_fp_greater(y->ptr.p_double[i],(double)(0)) )
        {
            if( !nz )
            {
                result = x->ptr.p_double[i]*y->ptr.p_double[i];
                nz = ae_true;
            }
            else
            {
                result = ae_minreal(result, x->ptr.p_double[i]*y->ptr.p_double[i], _state);
            }
        }
    }
    return result;
}


/*************************************************************************
Computes maximum product of nonzero components.
Returns 0 if all components are nonpositive.

INPUT PARAMETERS:
    X               -   vector
    Y               -   vector
    N               -   length

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double vipmsolver_maxprodnz(/* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool nz;
    double result;


    result = (double)(0);
    nz = ae_false;
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater(x->ptr.p_double[i],(double)(0))&&ae_fp_greater(y->ptr.p_double[i],(double)(0)) )
        {
            if( !nz )
            {
                result = x->ptr.p_double[i]*y->ptr.p_double[i];
                nz = ae_true;
            }
            else
            {
                result = ae_maxreal(result, x->ptr.p_double[i]*y->ptr.p_double[i], _state);
            }
        }
    }
    return result;
}


void _vipmvars_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    vipmvars *p = (vipmvars*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->w, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->t, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->p, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->y, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->z, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->v, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->q, 0, DT_REAL, _state, make_automatic);
}


void _vipmvars_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    vipmvars       *dst = (vipmvars*)_dst;
    const vipmvars *src = (const vipmvars*)_src;
    dst->n = src->n;
    dst->m = src->m;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    ae_vector_init_copy(&dst->w, &src->w, _state, make_automatic);
    ae_vector_init_copy(&dst->t, &src->t, _state, make_automatic);
    ae_vector_init_copy(&dst->p, &src->p, _state, make_automatic);
    ae_vector_init_copy(&dst->y, &src->y, _state, make_automatic);
    ae_vector_init_copy(&dst->z, &src->z, _state, make_automatic);
    ae_vector_init_copy(&dst->v, &src->v, _state, make_automatic);
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->q, &src->q, _state, make_automatic);
}


void _vipmvars_clear(void* _p)
{
    vipmvars *p = (vipmvars*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->g);
    ae_vector_clear(&p->w);
    ae_vector_clear(&p->t);
    ae_vector_clear(&p->p);
    ae_vector_clear(&p->y);
    ae_vector_clear(&p->z);
    ae_vector_clear(&p->v);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->q);
}


void _vipmvars_destroy(void* _p)
{
    vipmvars *p = (vipmvars*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->g);
    ae_vector_destroy(&p->w);
    ae_vector_destroy(&p->t);
    ae_vector_destroy(&p->p);
    ae_vector_destroy(&p->y);
    ae_vector_destroy(&p->z);
    ae_vector_destroy(&p->v);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->q);
}


void _vipmreducedsparsesystem_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    vipmreducedsparsesystem *p = (vipmreducedsparsesystem*)_p;
    ae_touch_ptr((void*)p);
    _sparsematrix_init(&p->rawsystem, _state, make_automatic);
    ae_vector_init(&p->effectivediag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->isdiagonal, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->rowdegrees, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->coldegrees, 0, DT_INT, _state, make_automatic);
    _spcholanalysis_init(&p->analysis, _state, make_automatic);
    ae_vector_init(&p->priorities, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->diagterm, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dampterm, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpb, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmprhs, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpcorr, 0, DT_REAL, _state, make_automatic);
}


void _vipmreducedsparsesystem_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    vipmreducedsparsesystem       *dst = (vipmreducedsparsesystem*)_dst;
    const vipmreducedsparsesystem *src = (const vipmreducedsparsesystem*)_src;
    _sparsematrix_init_copy(&dst->rawsystem, &src->rawsystem, _state, make_automatic);
    ae_vector_init_copy(&dst->effectivediag, &src->effectivediag, _state, make_automatic);
    ae_vector_init_copy(&dst->isdiagonal, &src->isdiagonal, _state, make_automatic);
    ae_vector_init_copy(&dst->rowdegrees, &src->rowdegrees, _state, make_automatic);
    ae_vector_init_copy(&dst->coldegrees, &src->coldegrees, _state, make_automatic);
    dst->ntotal = src->ntotal;
    _spcholanalysis_init_copy(&dst->analysis, &src->analysis, _state, make_automatic);
    ae_vector_init_copy(&dst->priorities, &src->priorities, _state, make_automatic);
    ae_vector_init_copy(&dst->diagterm, &src->diagterm, _state, make_automatic);
    ae_vector_init_copy(&dst->dampterm, &src->dampterm, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpb, &src->tmpb, _state, make_automatic);
    ae_vector_init_copy(&dst->tmprhs, &src->tmprhs, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpcorr, &src->tmpcorr, _state, make_automatic);
}


void _vipmreducedsparsesystem_clear(void* _p)
{
    vipmreducedsparsesystem *p = (vipmreducedsparsesystem*)_p;
    ae_touch_ptr((void*)p);
    _sparsematrix_clear(&p->rawsystem);
    ae_vector_clear(&p->effectivediag);
    ae_vector_clear(&p->isdiagonal);
    ae_vector_clear(&p->rowdegrees);
    ae_vector_clear(&p->coldegrees);
    _spcholanalysis_clear(&p->analysis);
    ae_vector_clear(&p->priorities);
    ae_vector_clear(&p->diagterm);
    ae_vector_clear(&p->dampterm);
    ae_vector_clear(&p->tmpb);
    ae_vector_clear(&p->tmprhs);
    ae_vector_clear(&p->tmpcorr);
}


void _vipmreducedsparsesystem_destroy(void* _p)
{
    vipmreducedsparsesystem *p = (vipmreducedsparsesystem*)_p;
    ae_touch_ptr((void*)p);
    _sparsematrix_destroy(&p->rawsystem);
    ae_vector_destroy(&p->effectivediag);
    ae_vector_destroy(&p->isdiagonal);
    ae_vector_destroy(&p->rowdegrees);
    ae_vector_destroy(&p->coldegrees);
    _spcholanalysis_destroy(&p->analysis);
    ae_vector_destroy(&p->priorities);
    ae_vector_destroy(&p->diagterm);
    ae_vector_destroy(&p->dampterm);
    ae_vector_destroy(&p->tmpb);
    ae_vector_destroy(&p->tmprhs);
    ae_vector_destroy(&p->tmpcorr);
}


void _vipmrighthandside_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    vipmrighthandside *p = (vipmrighthandside*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->sigma, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->beta, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rho, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tau, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->alpha, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gammaz, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gammas, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gammaw, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gammaq, 0, DT_REAL, _state, make_automatic);
}


void _vipmrighthandside_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    vipmrighthandside       *dst = (vipmrighthandside*)_dst;
    const vipmrighthandside *src = (const vipmrighthandside*)_src;
    ae_vector_init_copy(&dst->sigma, &src->sigma, _state, make_automatic);
    ae_vector_init_copy(&dst->beta, &src->beta, _state, make_automatic);
    ae_vector_init_copy(&dst->rho, &src->rho, _state, make_automatic);
    ae_vector_init_copy(&dst->nu, &src->nu, _state, make_automatic);
    ae_vector_init_copy(&dst->tau, &src->tau, _state, make_automatic);
    ae_vector_init_copy(&dst->alpha, &src->alpha, _state, make_automatic);
    ae_vector_init_copy(&dst->gammaz, &src->gammaz, _state, make_automatic);
    ae_vector_init_copy(&dst->gammas, &src->gammas, _state, make_automatic);
    ae_vector_init_copy(&dst->gammaw, &src->gammaw, _state, make_automatic);
    ae_vector_init_copy(&dst->gammaq, &src->gammaq, _state, make_automatic);
}


void _vipmrighthandside_clear(void* _p)
{
    vipmrighthandside *p = (vipmrighthandside*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->sigma);
    ae_vector_clear(&p->beta);
    ae_vector_clear(&p->rho);
    ae_vector_clear(&p->nu);
    ae_vector_clear(&p->tau);
    ae_vector_clear(&p->alpha);
    ae_vector_clear(&p->gammaz);
    ae_vector_clear(&p->gammas);
    ae_vector_clear(&p->gammaw);
    ae_vector_clear(&p->gammaq);
}


void _vipmrighthandside_destroy(void* _p)
{
    vipmrighthandside *p = (vipmrighthandside*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->sigma);
    ae_vector_destroy(&p->beta);
    ae_vector_destroy(&p->rho);
    ae_vector_destroy(&p->nu);
    ae_vector_destroy(&p->tau);
    ae_vector_destroy(&p->alpha);
    ae_vector_destroy(&p->gammaz);
    ae_vector_destroy(&p->gammas);
    ae_vector_destroy(&p->gammaw);
    ae_vector_destroy(&p->gammaq);
}


void _vipmstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    vipmstate *p = (vipmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->scl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->invscl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xorigin, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->c, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->denseh, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparseh, _state, make_automatic);
    ae_vector_init(&p->diagr, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_matrix_init(&p->denseafull, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->denseamain, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparseafull, _state, make_automatic);
    _sparsematrix_init(&p->sparseamain, _state, make_automatic);
    _sparsematrix_init(&p->combinedaslack, _state, make_automatic);
    ae_vector_init(&p->ascales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->aflips, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->b, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->r, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasr, 0, DT_BOOL, _state, make_automatic);
    _vipmvars_init(&p->current, _state, make_automatic);
    _vipmvars_init(&p->best, _state, make_automatic);
    _vipmvars_init(&p->trial, _state, make_automatic);
    _vipmvars_init(&p->deltaaff, _state, make_automatic);
    _vipmvars_init(&p->deltacorr, _state, make_automatic);
    ae_vector_init(&p->isfrozen, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasgz, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasts, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->haswv, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->haspq, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->diagdz, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagdzi, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagdziri, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagds, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagdsi, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagdsiri, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagdw, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagdwi, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagdwir, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagdq, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagdqi, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagdqiri, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagddr, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagde, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagder, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->factdensehaug, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->factregdhrh, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->factinvregdzrz, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->factregewave, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->facttmpdiag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->facttmpdamp, 0, DT_REAL, _state, make_automatic);
    _vipmreducedsparsesystem_init(&p->reducedsparsesystem, _state, make_automatic);
    _vipmrighthandside_init(&p->rhs, _state, make_automatic);
    ae_vector_init(&p->rhsalphacap, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rhsbetacap, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rhsnucap, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rhstaucap, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->deltaxy, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmphx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpax, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpaty, 0, DT_REAL, _state, make_automatic);
    _vipmvars_init(&p->zerovars, _state, make_automatic);
    ae_vector_init(&p->dummyr, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpy, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp2, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpr2, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmplaggrad, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpi, 0, DT_INT, _state, make_automatic);
    _sparsematrix_init(&p->tmpsparse0, _state, make_automatic);
}


void _vipmstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    vipmstate       *dst = (vipmstate*)_dst;
    const vipmstate *src = (const vipmstate*)_src;
    dst->slacksforequalityconstraints = src->slacksforequalityconstraints;
    dst->normalize = src->normalize;
    dst->n = src->n;
    dst->nmain = src->nmain;
    dst->epsp = src->epsp;
    dst->epsd = src->epsd;
    dst->epsgap = src->epsgap;
    dst->islinear = src->islinear;
    ae_vector_init_copy(&dst->scl, &src->scl, _state, make_automatic);
    ae_vector_init_copy(&dst->invscl, &src->invscl, _state, make_automatic);
    ae_vector_init_copy(&dst->xorigin, &src->xorigin, _state, make_automatic);
    dst->targetscale = src->targetscale;
    ae_vector_init_copy(&dst->c, &src->c, _state, make_automatic);
    ae_matrix_init_copy(&dst->denseh, &src->denseh, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparseh, &src->sparseh, _state, make_automatic);
    ae_vector_init_copy(&dst->diagr, &src->diagr, _state, make_automatic);
    dst->hkind = src->hkind;
    dst->isdiagonalh = src->isdiagonalh;
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    ae_vector_init_copy(&dst->rawbndl, &src->rawbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->rawbndu, &src->rawbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_matrix_init_copy(&dst->denseafull, &src->denseafull, _state, make_automatic);
    ae_matrix_init_copy(&dst->denseamain, &src->denseamain, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparseafull, &src->sparseafull, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparseamain, &src->sparseamain, _state, make_automatic);
    _sparsematrix_init_copy(&dst->combinedaslack, &src->combinedaslack, _state, make_automatic);
    ae_vector_init_copy(&dst->ascales, &src->ascales, _state, make_automatic);
    ae_vector_init_copy(&dst->aflips, &src->aflips, _state, make_automatic);
    ae_vector_init_copy(&dst->b, &src->b, _state, make_automatic);
    ae_vector_init_copy(&dst->r, &src->r, _state, make_automatic);
    ae_vector_init_copy(&dst->hasr, &src->hasr, _state, make_automatic);
    dst->mdense = src->mdense;
    dst->msparse = src->msparse;
    _vipmvars_init_copy(&dst->current, &src->current, _state, make_automatic);
    _vipmvars_init_copy(&dst->best, &src->best, _state, make_automatic);
    _vipmvars_init_copy(&dst->trial, &src->trial, _state, make_automatic);
    _vipmvars_init_copy(&dst->deltaaff, &src->deltaaff, _state, make_automatic);
    _vipmvars_init_copy(&dst->deltacorr, &src->deltacorr, _state, make_automatic);
    ae_vector_init_copy(&dst->isfrozen, &src->isfrozen, _state, make_automatic);
    ae_vector_init_copy(&dst->hasgz, &src->hasgz, _state, make_automatic);
    ae_vector_init_copy(&dst->hasts, &src->hasts, _state, make_automatic);
    ae_vector_init_copy(&dst->haswv, &src->haswv, _state, make_automatic);
    ae_vector_init_copy(&dst->haspq, &src->haspq, _state, make_automatic);
    dst->cntgz = src->cntgz;
    dst->cntts = src->cntts;
    dst->cntwv = src->cntwv;
    dst->cntpq = src->cntpq;
    dst->repiterationscount = src->repiterationscount;
    dst->repncholesky = src->repncholesky;
    dst->dotrace = src->dotrace;
    dst->dodetailedtrace = src->dodetailedtrace;
    dst->factorizationtype = src->factorizationtype;
    dst->factorizationpoweredup = src->factorizationpoweredup;
    dst->factorizationpresent = src->factorizationpresent;
    ae_vector_init_copy(&dst->diagdz, &src->diagdz, _state, make_automatic);
    ae_vector_init_copy(&dst->diagdzi, &src->diagdzi, _state, make_automatic);
    ae_vector_init_copy(&dst->diagdziri, &src->diagdziri, _state, make_automatic);
    ae_vector_init_copy(&dst->diagds, &src->diagds, _state, make_automatic);
    ae_vector_init_copy(&dst->diagdsi, &src->diagdsi, _state, make_automatic);
    ae_vector_init_copy(&dst->diagdsiri, &src->diagdsiri, _state, make_automatic);
    ae_vector_init_copy(&dst->diagdw, &src->diagdw, _state, make_automatic);
    ae_vector_init_copy(&dst->diagdwi, &src->diagdwi, _state, make_automatic);
    ae_vector_init_copy(&dst->diagdwir, &src->diagdwir, _state, make_automatic);
    ae_vector_init_copy(&dst->diagdq, &src->diagdq, _state, make_automatic);
    ae_vector_init_copy(&dst->diagdqi, &src->diagdqi, _state, make_automatic);
    ae_vector_init_copy(&dst->diagdqiri, &src->diagdqiri, _state, make_automatic);
    ae_vector_init_copy(&dst->diagddr, &src->diagddr, _state, make_automatic);
    ae_vector_init_copy(&dst->diagde, &src->diagde, _state, make_automatic);
    ae_vector_init_copy(&dst->diagder, &src->diagder, _state, make_automatic);
    ae_matrix_init_copy(&dst->factdensehaug, &src->factdensehaug, _state, make_automatic);
    ae_vector_init_copy(&dst->factregdhrh, &src->factregdhrh, _state, make_automatic);
    ae_vector_init_copy(&dst->factinvregdzrz, &src->factinvregdzrz, _state, make_automatic);
    ae_vector_init_copy(&dst->factregewave, &src->factregewave, _state, make_automatic);
    ae_vector_init_copy(&dst->facttmpdiag, &src->facttmpdiag, _state, make_automatic);
    ae_vector_init_copy(&dst->facttmpdamp, &src->facttmpdamp, _state, make_automatic);
    _vipmreducedsparsesystem_init_copy(&dst->reducedsparsesystem, &src->reducedsparsesystem, _state, make_automatic);
    _vipmrighthandside_init_copy(&dst->rhs, &src->rhs, _state, make_automatic);
    ae_vector_init_copy(&dst->rhsalphacap, &src->rhsalphacap, _state, make_automatic);
    ae_vector_init_copy(&dst->rhsbetacap, &src->rhsbetacap, _state, make_automatic);
    ae_vector_init_copy(&dst->rhsnucap, &src->rhsnucap, _state, make_automatic);
    ae_vector_init_copy(&dst->rhstaucap, &src->rhstaucap, _state, make_automatic);
    ae_vector_init_copy(&dst->deltaxy, &src->deltaxy, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphx, &src->tmphx, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpax, &src->tmpax, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpaty, &src->tmpaty, _state, make_automatic);
    _vipmvars_init_copy(&dst->zerovars, &src->zerovars, _state, make_automatic);
    ae_vector_init_copy(&dst->dummyr, &src->dummyr, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpy, &src->tmpy, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp2, &src->tmp2, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpr2, &src->tmpr2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmplaggrad, &src->tmplaggrad, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpi, &src->tmpi, _state, make_automatic);
    _sparsematrix_init_copy(&dst->tmpsparse0, &src->tmpsparse0, _state, make_automatic);
}


void _vipmstate_clear(void* _p)
{
    vipmstate *p = (vipmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->scl);
    ae_vector_clear(&p->invscl);
    ae_vector_clear(&p->xorigin);
    ae_vector_clear(&p->c);
    ae_matrix_clear(&p->denseh);
    _sparsematrix_clear(&p->sparseh);
    ae_vector_clear(&p->diagr);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->rawbndl);
    ae_vector_clear(&p->rawbndu);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_matrix_clear(&p->denseafull);
    ae_matrix_clear(&p->denseamain);
    _sparsematrix_clear(&p->sparseafull);
    _sparsematrix_clear(&p->sparseamain);
    _sparsematrix_clear(&p->combinedaslack);
    ae_vector_clear(&p->ascales);
    ae_vector_clear(&p->aflips);
    ae_vector_clear(&p->b);
    ae_vector_clear(&p->r);
    ae_vector_clear(&p->hasr);
    _vipmvars_clear(&p->current);
    _vipmvars_clear(&p->best);
    _vipmvars_clear(&p->trial);
    _vipmvars_clear(&p->deltaaff);
    _vipmvars_clear(&p->deltacorr);
    ae_vector_clear(&p->isfrozen);
    ae_vector_clear(&p->hasgz);
    ae_vector_clear(&p->hasts);
    ae_vector_clear(&p->haswv);
    ae_vector_clear(&p->haspq);
    ae_vector_clear(&p->diagdz);
    ae_vector_clear(&p->diagdzi);
    ae_vector_clear(&p->diagdziri);
    ae_vector_clear(&p->diagds);
    ae_vector_clear(&p->diagdsi);
    ae_vector_clear(&p->diagdsiri);
    ae_vector_clear(&p->diagdw);
    ae_vector_clear(&p->diagdwi);
    ae_vector_clear(&p->diagdwir);
    ae_vector_clear(&p->diagdq);
    ae_vector_clear(&p->diagdqi);
    ae_vector_clear(&p->diagdqiri);
    ae_vector_clear(&p->diagddr);
    ae_vector_clear(&p->diagde);
    ae_vector_clear(&p->diagder);
    ae_matrix_clear(&p->factdensehaug);
    ae_vector_clear(&p->factregdhrh);
    ae_vector_clear(&p->factinvregdzrz);
    ae_vector_clear(&p->factregewave);
    ae_vector_clear(&p->facttmpdiag);
    ae_vector_clear(&p->facttmpdamp);
    _vipmreducedsparsesystem_clear(&p->reducedsparsesystem);
    _vipmrighthandside_clear(&p->rhs);
    ae_vector_clear(&p->rhsalphacap);
    ae_vector_clear(&p->rhsbetacap);
    ae_vector_clear(&p->rhsnucap);
    ae_vector_clear(&p->rhstaucap);
    ae_vector_clear(&p->deltaxy);
    ae_vector_clear(&p->tmphx);
    ae_vector_clear(&p->tmpax);
    ae_vector_clear(&p->tmpaty);
    _vipmvars_clear(&p->zerovars);
    ae_vector_clear(&p->dummyr);
    ae_vector_clear(&p->tmpy);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmp1);
    ae_vector_clear(&p->tmp2);
    ae_matrix_clear(&p->tmpr2);
    ae_vector_clear(&p->tmplaggrad);
    ae_vector_clear(&p->tmpi);
    _sparsematrix_clear(&p->tmpsparse0);
}


void _vipmstate_destroy(void* _p)
{
    vipmstate *p = (vipmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->scl);
    ae_vector_destroy(&p->invscl);
    ae_vector_destroy(&p->xorigin);
    ae_vector_destroy(&p->c);
    ae_matrix_destroy(&p->denseh);
    _sparsematrix_destroy(&p->sparseh);
    ae_vector_destroy(&p->diagr);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->rawbndl);
    ae_vector_destroy(&p->rawbndu);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_matrix_destroy(&p->denseafull);
    ae_matrix_destroy(&p->denseamain);
    _sparsematrix_destroy(&p->sparseafull);
    _sparsematrix_destroy(&p->sparseamain);
    _sparsematrix_destroy(&p->combinedaslack);
    ae_vector_destroy(&p->ascales);
    ae_vector_destroy(&p->aflips);
    ae_vector_destroy(&p->b);
    ae_vector_destroy(&p->r);
    ae_vector_destroy(&p->hasr);
    _vipmvars_destroy(&p->current);
    _vipmvars_destroy(&p->best);
    _vipmvars_destroy(&p->trial);
    _vipmvars_destroy(&p->deltaaff);
    _vipmvars_destroy(&p->deltacorr);
    ae_vector_destroy(&p->isfrozen);
    ae_vector_destroy(&p->hasgz);
    ae_vector_destroy(&p->hasts);
    ae_vector_destroy(&p->haswv);
    ae_vector_destroy(&p->haspq);
    ae_vector_destroy(&p->diagdz);
    ae_vector_destroy(&p->diagdzi);
    ae_vector_destroy(&p->diagdziri);
    ae_vector_destroy(&p->diagds);
    ae_vector_destroy(&p->diagdsi);
    ae_vector_destroy(&p->diagdsiri);
    ae_vector_destroy(&p->diagdw);
    ae_vector_destroy(&p->diagdwi);
    ae_vector_destroy(&p->diagdwir);
    ae_vector_destroy(&p->diagdq);
    ae_vector_destroy(&p->diagdqi);
    ae_vector_destroy(&p->diagdqiri);
    ae_vector_destroy(&p->diagddr);
    ae_vector_destroy(&p->diagde);
    ae_vector_destroy(&p->diagder);
    ae_matrix_destroy(&p->factdensehaug);
    ae_vector_destroy(&p->factregdhrh);
    ae_vector_destroy(&p->factinvregdzrz);
    ae_vector_destroy(&p->factregewave);
    ae_vector_destroy(&p->facttmpdiag);
    ae_vector_destroy(&p->facttmpdamp);
    _vipmreducedsparsesystem_destroy(&p->reducedsparsesystem);
    _vipmrighthandside_destroy(&p->rhs);
    ae_vector_destroy(&p->rhsalphacap);
    ae_vector_destroy(&p->rhsbetacap);
    ae_vector_destroy(&p->rhsnucap);
    ae_vector_destroy(&p->rhstaucap);
    ae_vector_destroy(&p->deltaxy);
    ae_vector_destroy(&p->tmphx);
    ae_vector_destroy(&p->tmpax);
    ae_vector_destroy(&p->tmpaty);
    _vipmvars_destroy(&p->zerovars);
    ae_vector_destroy(&p->dummyr);
    ae_vector_destroy(&p->tmpy);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmp1);
    ae_vector_destroy(&p->tmp2);
    ae_matrix_destroy(&p->tmpr2);
    ae_vector_destroy(&p->tmplaggrad);
    ae_vector_destroy(&p->tmpi);
    _sparsematrix_destroy(&p->tmpsparse0);
}


#endif
#if defined(AE_COMPILE_IPM2SOLVER) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
Initializes QP-IPM state and prepares it to receive quadratic/linear terms
and constraints.

INPUT PARAMETERS:
    State       -   solver  state  to  be configured; previously allocated
                    memory is reused as much as possible
    S           -   scale vector, array[N]:
                    * I-th element contains scale of I-th variable,
                    * S[I]>0
    XOrigin     -   origin term, array[N]. Can be zero. The solver solves
                    problem of the form
                    >
                    > min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin))
                    >
                    The terms A and b (as well as constraints) will be
                    specified later with separate calls.
    N           -   total number of variables, N>=1
    TargetScale -   target scale:
                    * >=0
                    * recommended to be max(nrm2(C),nrm2(H+corr))
                    * if zero, autodetected by performing several iterations
                      of a subspace eigensolver

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
void ipm2init(ipm2state* state,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t nuser,
     /* Real    */ const ae_matrix* denseh,
     const sparsematrix* sparseh,
     ae_int_t hkind,
     ae_bool isupper,
     /* Real    */ const ae_matrix* ccorr,
     /* Real    */ const ae_vector* dcorr,
     ae_int_t kcorr,
     /* Real    */ const ae_vector* c,
     double targetscale,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     const sparsematrix* sparsea,
     ae_int_t msparse,
     /* Real    */ const ae_matrix* densea,
     ae_int_t mdense,
     /* Real    */ const ae_vector* cl,
     /* Real    */ const ae_vector* cu,
     ae_bool uniformlcscale,
     ae_bool skipnormalization,
     ae_state *_state)
{
    ae_int_t ntotal;
    ae_int_t naug;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t j0;
    ae_int_t j1;
    double v;
    double vv;
    ae_int_t nnz;
    ae_int_t offs;


    ae_assert(nuser>=1, "IPM2Init: N<1", _state);
    ae_assert(isfinitevector(s, nuser, _state), "IPM2Init: S contains infinite or NaN elements", _state);
    ae_assert(isfinitevector(xorigin, nuser, _state), "IPM2Init: XOrigin contains infinite or NaN elements", _state);
    ae_assert(hkind==0||hkind==1, "IPM2Init: incorrect HKind", _state);
    ae_assert(isfinitevector(c, nuser, _state), "IPM2Init: C contains infinite or NaN elements", _state);
    ae_assert(mdense>=0, "IPM2Init: MDense<0", _state);
    ae_assert(msparse>=0, "IPM2Init: MSparse<0", _state);
    ae_assert(apservisfinitematrix(densea, mdense, nuser, _state), "IPM2Init: DenseA contains infinite or NaN values!", _state);
    ae_assert(msparse==0||sparsea->matrixtype==1, "IPM2Init: non-CRS constraint matrix!", _state);
    ae_assert(msparse==0||(sparsea->m==msparse&&sparsea->n==nuser), "IPM2Init: constraint matrix has incorrect size", _state);
    ae_assert(cl->cnt>=mdense+msparse, "IPM2Init: CL is too short!", _state);
    ae_assert(cu->cnt>=mdense+msparse, "IPM2Init: CU is too short!", _state);
    ae_assert(kcorr>=0, "IPM2Init: KCorr<0", _state);
    ae_assert(kcorr==0||ccorr->rows>=kcorr, "IPM2Init: CCorr size is not KCorr*N, rows count is too small", _state);
    ae_assert(kcorr==0||ccorr->cols>=nuser, "IPM2Init: CCorr size is not KCorr*N, cols count is too small", _state);
    ae_assert(kcorr==0||dcorr->cnt>=kcorr, "IPM2Init: DCorr size is not [KCorr]", _state);
    for(i=0; i<=kcorr-1; i++)
    {
        ae_assert(ae_fp_eq(ae_fabs(dcorr->ptr.p_double[i], _state),(double)(1)), "IPM2Init: |DCorr[I]|<>1", _state);
    }
    ae_assert(ae_isfinite(targetscale, _state)&&ae_fp_greater_eq(targetscale,(double)(0)), "IPM2Init: TargetScale<0 or is not finite number", _state);
    
    /*
     * Problem metrics, settings and type
     */
    ntotal = nuser+kcorr+msparse+mdense;
    naug = nuser+kcorr;
    state->nuser = nuser;
    state->naug = naug;
    state->ntotal = ntotal;
    state->islinear = ae_false;
    state->hasuserpermutation = ae_false;
    ipm2setcond(state, 0.0, 0.0, 0.0, _state);
    state->maxipmits = ipm2solver_defipmits;
    
    /*
     * Reports
     */
    state->repiterationscount = 0;
    state->repncholesky = 0;
    
    /*
     * Trace
     */
    state->dotrace = ae_false;
    state->dotimers = ae_false;
    state->dodetailedtrace = ae_false;
    
    /*
     * Scale and origin
     */
    rsetallocv(ntotal, 1.0, &state->sclx, _state);
    rsetallocv(ntotal, 1.0, &state->invsclx, _state);
    rsetallocv(ntotal, 0.0, &state->xoriginx, _state);
    for(i=0; i<=nuser-1; i++)
    {
        ae_assert(s->ptr.p_double[i]>0.0, "IPM2Init: S[i] is non-positive", _state);
        state->sclx.ptr.p_double[i] = s->ptr.p_double[i];
        state->invsclx.ptr.p_double[i] = (double)1/s->ptr.p_double[i];
        state->xoriginx.ptr.p_double[i] = xorigin->ptr.p_double[i];
    }
    
    /*
     * Extended linear term
     */
    rsetallocv(ntotal, 0.0, &state->ce, _state);
    rcopyv(nuser, c, &state->ce, _state);
    
    /*
     * Generate sparse lower triangular representation of the quadratic term H and dense
     * corrector CCorr/DCorr. Apply scaling and normalization.
     */
    state->tmplowerh.matrixtype = 1;
    state->tmplowerh.m = nuser;
    state->tmplowerh.n = nuser;
    iallocv(nuser+1, &state->tmplowerh.ridx, _state);
    if( hkind==0 )
    {
        
        /*
         * Sparsify dense term, generate leading N*N part of HX
         */
        nnz = 0;
        for(i=0; i<=nuser-1; i++)
        {
            nnz = nnz+1;
            if( isupper )
            {
                j0 = i+1;
                j1 = nuser-1;
            }
            else
            {
                j0 = 0;
                j1 = i-1;
            }
            for(j=j0; j<=j1; j++)
            {
                if( denseh->ptr.pp_double[i][j]!=(double)0 )
                {
                    nnz = nnz+1;
                }
            }
        }
        iallocv(nnz+nuser, &state->tmplowerh.idx, _state);
        rallocv(nnz+nuser, &state->tmplowerh.vals, _state);
        state->tmplowerh.ridx.ptr.p_int[0] = 0;
        offs = 0;
        vv = (double)(0);
        for(i=0; i<=state->nuser-1; i++)
        {
            
            /*
             * Off-diagonal elements are copied only when nonzero
             */
            if( !isupper )
            {
                for(j=0; j<=i-1; j++)
                {
                    if( denseh->ptr.pp_double[i][j]!=(double)0 )
                    {
                        v = denseh->ptr.pp_double[i][j];
                        state->tmplowerh.idx.ptr.p_int[offs] = j;
                        state->tmplowerh.vals.ptr.p_double[offs] = v;
                        vv = vv+v;
                        offs = offs+1;
                    }
                }
            }
            
            /*
             * Diagonal element is always copied
             */
            v = denseh->ptr.pp_double[i][i];
            state->tmplowerh.idx.ptr.p_int[offs] = i;
            state->tmplowerh.vals.ptr.p_double[offs] = v;
            vv = vv+v;
            offs = offs+1;
            
            /*
             * Off-diagonal elements are copied only when nonzero
             */
            if( isupper )
            {
                for(j=i+1; j<=nuser-1; j++)
                {
                    if( denseh->ptr.pp_double[i][j]!=(double)0 )
                    {
                        v = denseh->ptr.pp_double[i][j];
                        state->tmplowerh.idx.ptr.p_int[offs] = j;
                        state->tmplowerh.vals.ptr.p_double[offs] = v;
                        vv = vv+v;
                        offs = offs+1;
                    }
                }
            }
            
            /*
             * Finalize row
             */
            state->tmplowerh.ridx.ptr.p_int[i+1] = offs;
        }
        ae_assert(ae_isfinite(vv, _state), "VIPMSetQuadraticLinear: DenseH contains infinite or NaN values!", _state);
        ae_assert(offs==nnz, "VIPMSetQuadraticLinear: integrity check failed", _state);
        ae_assert(offs<=state->tmplowerh.vals.cnt&&offs<=state->tmplowerh.idx.cnt, "IPM2Init: integrity check failed", _state);
        sparsecreatecrsinplace(&state->tmplowerh, _state);
    }
    if( hkind==1 )
    {
        
        /*
         * Copy sparse quadratic term, but make sure that we have diagonal elements
         * present (we add diagonal if it is not present)
         */
        ae_assert(sparseh->matrixtype==1, "VIPMSetQuadraticLinear: unexpected sparse matrix format", _state);
        ae_assert(sparseh->m==nuser, "VIPMSetQuadraticLinear: unexpected sparse matrix size", _state);
        ae_assert(sparseh->n==nuser, "VIPMSetQuadraticLinear: unexpected sparse matrix size", _state);
        iallocv(sparseh->ridx.ptr.p_int[nuser]+nuser, &state->tmplowerh.idx, _state);
        rallocv(sparseh->ridx.ptr.p_int[nuser]+nuser, &state->tmplowerh.vals, _state);
        state->tmplowerh.ridx.ptr.p_int[0] = 0;
        offs = 0;
        vv = (double)(0);
        for(i=0; i<=state->nuser-1; i++)
        {
            
            /*
             * Copy subdiagonal elements (if needed)
             */
            if( !isupper )
            {
                j0 = sparseh->ridx.ptr.p_int[i];
                j1 = sparseh->didx.ptr.p_int[i]-1;
                for(k=j0; k<=j1; k++)
                {
                    v = sparseh->vals.ptr.p_double[k];
                    state->tmplowerh.idx.ptr.p_int[offs] = sparseh->idx.ptr.p_int[k];
                    state->tmplowerh.vals.ptr.p_double[offs] = v;
                    vv = vv+v;
                    offs = offs+1;
                }
            }
            
            /*
             * Diagonal element is always copied
             */
            v = (double)(0);
            if( sparseh->uidx.ptr.p_int[i]!=sparseh->didx.ptr.p_int[i] )
            {
                v = sparseh->vals.ptr.p_double[sparseh->didx.ptr.p_int[i]];
            }
            state->tmplowerh.idx.ptr.p_int[offs] = i;
            state->tmplowerh.vals.ptr.p_double[offs] = v;
            vv = vv+v;
            offs = offs+1;
            
            /*
             * Copy superdiagonal elements (if needed)
             */
            if( isupper )
            {
                j0 = sparseh->uidx.ptr.p_int[i];
                j1 = sparseh->ridx.ptr.p_int[i+1]-1;
                for(k=j0; k<=j1; k++)
                {
                    v = sparseh->vals.ptr.p_double[k];
                    state->tmplowerh.idx.ptr.p_int[offs] = sparseh->idx.ptr.p_int[k];
                    state->tmplowerh.vals.ptr.p_double[offs] = v;
                    vv = vv+v;
                    offs = offs+1;
                }
            }
            
            /*
             * Finalize row
             */
            state->tmplowerh.ridx.ptr.p_int[i+1] = offs;
        }
        ae_assert(ae_isfinite(vv, _state), "IPM2Init: SparseH contains infinite or NaN values!", _state);
        ae_assert(offs<=state->tmplowerh.vals.cnt&&offs<=state->tmplowerh.idx.cnt, "IPM2Init: integrity check failed", _state);
        sparsecreatecrsinplace(&state->tmplowerh, _state);
    }
    if( isupper )
    {
        sparsecopytransposecrsbuf(&state->tmplowerh, &state->tmpsparse0, _state);
        sparsecopybuf(&state->tmpsparse0, &state->tmplowerh, _state);
    }
    if( kcorr>0 )
    {
        for(i=0; i<=kcorr-1; i++)
        {
            ae_assert(ae_fp_eq(ae_fabs(dcorr->ptr.p_double[i], _state),1.0), "IPM2: DCorr[i] is neither 1 nor -1", _state);
        }
        rcopyallocm(kcorr, state->nuser, ccorr, &state->tmpccorr, _state);
        rcopyallocv(kcorr, dcorr, &state->tmpdcorr, _state);
    }
    scalesparseqpinplace(&state->sclx, state->nuser, &state->tmplowerh, &state->tmpccorr, &state->tmpdcorr, kcorr, &state->ce, _state);
    if( !skipnormalization )
    {
        if( ae_fp_eq(targetscale,(double)(0)) )
        {
            state->targetscale = normalizesparseqpinplace(&state->tmplowerh, ae_false, &state->tmpccorr, &state->tmpdcorr, kcorr, &state->ce, state->nuser, _state);
            state->targetscale = coalesce(state->targetscale, 1.0, _state);
        }
        else
        {
            v = (double)1/targetscale;
            rmulv(state->tmplowerh.ridx.ptr.p_int[state->nuser], v, &state->tmplowerh.vals, _state);
            rmulv(ntotal, v, &state->ce, _state);
            for(k=0; k<=kcorr-1; k++)
            {
                rmulr(state->nuser, ae_sqrt(v, _state), &state->tmpccorr, k, _state);
            }
            state->targetscale = targetscale;
        }
    }
    else
    {
        state->targetscale = 1.0;
    }
    
    /*
     * Extended quadratic term: copy, extend
     *
     * NOTE: we perform integrity check for inifinities/NANs by
     *       computing sum of all matrix elements and checking its
     *       value for being finite. It is a bit faster than checking
     *       each element individually.
     */
    state->sparsehe.matrixtype = 1;
    state->sparsehe.m = ntotal;
    state->sparsehe.n = ntotal;
    iallocv(ntotal+1, &state->sparsehe.ridx, _state);
    ae_assert(state->tmplowerh.matrixtype==1, "VIPMSetQuadraticLinear: unexpected sparse matrix format", _state);
    ae_assert(state->tmplowerh.m==nuser, "VIPMSetQuadraticLinear: unexpected sparse matrix size", _state);
    ae_assert(state->tmplowerh.n==nuser, "VIPMSetQuadraticLinear: unexpected sparse matrix size", _state);
    iallocv(state->tmplowerh.ridx.ptr.p_int[nuser]+kcorr*(nuser+1)+(state->ntotal-nuser), &state->sparsehe.idx, _state);
    rallocv(state->tmplowerh.ridx.ptr.p_int[nuser]+kcorr*(nuser+1)+(state->ntotal-nuser), &state->sparsehe.vals, _state);
    state->sparsehe.ridx.ptr.p_int[0] = 0;
    offs = 0;
    vv = (double)(0);
    for(i=0; i<=state->nuser-1; i++)
    {
        
        /*
         * Copy subdiagonal elements (if needed)
         */
        j0 = state->tmplowerh.ridx.ptr.p_int[i];
        j1 = state->tmplowerh.didx.ptr.p_int[i]-1;
        for(k=j0; k<=j1; k++)
        {
            v = state->tmplowerh.vals.ptr.p_double[k];
            state->sparsehe.idx.ptr.p_int[offs] = state->tmplowerh.idx.ptr.p_int[k];
            state->sparsehe.vals.ptr.p_double[offs] = v;
            vv = vv+v;
            offs = offs+1;
        }
        
        /*
         * The diagonal element is always copied
         */
        v = (double)(0);
        if( state->tmplowerh.uidx.ptr.p_int[i]!=state->tmplowerh.didx.ptr.p_int[i] )
        {
            v = state->tmplowerh.vals.ptr.p_double[state->tmplowerh.didx.ptr.p_int[i]];
        }
        state->sparsehe.idx.ptr.p_int[offs] = i;
        state->sparsehe.vals.ptr.p_double[offs] = v;
        vv = vv+v;
        offs = offs+1;
        
        /*
         * Finalize row
         */
        state->sparsehe.ridx.ptr.p_int[i+1] = offs;
    }
    ae_assert(ae_isfinite(vv, _state), "IPM2Init: SparseH contains infinite or NaN values!", _state);
    for(i=state->nuser; i<=state->naug-1; i++)
    {
        
        /*
         * Augmentation row
         */
        offs = state->sparsehe.ridx.ptr.p_int[i];
        for(j=0; j<=nuser-1; j++)
        {
            if( state->tmpccorr.ptr.pp_double[i-state->nuser][j]!=0.0 )
            {
                state->sparsehe.idx.ptr.p_int[offs] = j;
                state->sparsehe.vals.ptr.p_double[offs] = state->tmpccorr.ptr.pp_double[i-state->nuser][j];
                offs = offs+1;
            }
        }
        
        /*
         * Diagonal element
         */
        state->sparsehe.idx.ptr.p_int[offs] = i;
        state->sparsehe.vals.ptr.p_double[offs] = -state->tmpdcorr.ptr.p_double[i-state->nuser];
        offs = offs+1;
        
        /*
         * Finalize row
         */
        state->sparsehe.ridx.ptr.p_int[i+1] = offs;
    }
    for(i=state->naug; i<=state->ntotal-1; i++)
    {
        
        /*
         * Extend with zeros
         */
        state->sparsehe.idx.ptr.p_int[offs] = i;
        state->sparsehe.vals.ptr.p_double[offs] = 0.0;
        offs = offs+1;
        state->sparsehe.ridx.ptr.p_int[i+1] = offs;
    }
    ae_assert(offs<=state->sparsehe.vals.cnt&&offs<=state->sparsehe.idx.cnt, "IPM2Init: integrity check failed", _state);
    sparsecreatecrsinplace(&state->sparsehe, _state);
    state->isdiagonalh = state->sparsehe.ridx.ptr.p_int[state->ntotal]==state->ntotal;
    
    /*
     * Primary part of the box constraints
     */
    rcopyallocv(state->nuser, bndl, &state->rawbndl, _state);
    rcopyallocv(state->nuser, bndu, &state->rawbndu, _state);
    rsetallocv(state->ntotal, _state->v_neginf, &state->bndle, _state);
    rsetallocv(state->ntotal, _state->v_posinf, &state->bndue, _state);
    bsetallocv(state->ntotal, ae_false, &state->hasbndle, _state);
    bsetallocv(state->ntotal, ae_false, &state->hasbndue, _state);
    for(i=0; i<=state->nuser-1; i++)
    {
        state->hasbndle.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->hasbndue.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
        ae_assert(!((state->hasbndle.ptr.p_bool[i]&&state->hasbndue.ptr.p_bool[i])&&ae_fp_greater(bndl->ptr.p_double[i],bndu->ptr.p_double[i])), "IPM2Init: inconsistent range for box constraints", _state);
        state->bndle.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->bndue.ptr.p_double[i] = bndu->ptr.p_double[i];
    }
    scaleshiftbcinplace(&state->sclx, &state->xoriginx, &state->bndle, &state->bndue, state->ntotal, _state);
    
    /*
     * Linear constraints (sparsified):
     * * copy to TmpA, normalize here
     * * extend with slack variables, save to RawAE
     * * append constraint bounds to the extended box constraints
     */
    m = mdense+msparse;
    state->mraw = m;
    for(i=0; i<=m-1; i++)
    {
        ae_assert(ae_isfinite(cl->ptr.p_double[i], _state)||ae_isneginf(cl->ptr.p_double[i], _state), "IPM2: CL is not finite number or -INF", _state);
        ae_assert(ae_isfinite(cu->ptr.p_double[i], _state)||ae_isposinf(cu->ptr.p_double[i], _state), "IPM2: CU is not finite number or +INF", _state);
    }
    state->mraw = m;
    state->tmpa.m = m;
    state->tmpa.n = state->ntotal;
    iallocv(m+1, &state->tmpa.ridx, _state);
    state->tmpa.ridx.ptr.p_int[0] = 0;
    for(i=0; i<=msparse-1; i++)
    {
        offs = state->tmpa.ridx.ptr.p_int[i];
        igrowv(offs+state->ntotal, &state->tmpa.idx, _state);
        rgrowv(offs+state->ntotal, &state->tmpa.vals, _state);
        j0 = sparsea->ridx.ptr.p_int[i];
        j1 = sparsea->ridx.ptr.p_int[i+1]-1;
        for(j=j0; j<=j1; j++)
        {
            state->tmpa.idx.ptr.p_int[offs] = sparsea->idx.ptr.p_int[j];
            state->tmpa.vals.ptr.p_double[offs] = sparsea->vals.ptr.p_double[j];
            offs = offs+1;
        }
        state->tmpa.ridx.ptr.p_int[i+1] = offs;
    }
    for(i=0; i<=mdense-1; i++)
    {
        offs = state->tmpa.ridx.ptr.p_int[msparse+i];
        igrowv(offs+state->ntotal, &state->tmpa.idx, _state);
        rgrowv(offs+state->ntotal, &state->tmpa.vals, _state);
        for(k=0; k<=state->nuser-1; k++)
        {
            if( densea->ptr.pp_double[i][k]!=(double)0 )
            {
                state->tmpa.idx.ptr.p_int[offs] = k;
                state->tmpa.vals.ptr.p_double[offs] = densea->ptr.pp_double[i][k];
                offs = offs+1;
            }
        }
        state->tmpa.ridx.ptr.p_int[msparse+i+1] = offs;
    }
    sparsecreatecrsinplace(&state->tmpa, _state);
    rcopyallocv(m, cl, &state->tmpal, _state);
    rcopyallocv(m, cu, &state->tmpau, _state);
    scaleshiftsparselcinplace(&state->sclx, &state->xoriginx, state->ntotal, &state->tmpa, m, &state->tmpal, &state->tmpau, _state);
    if( !skipnormalization )
    {
        if( uniformlcscale )
        {
            normalizesparselcinplaceuniform(&state->tmpa, m, &state->tmpal, &state->tmpau, state->ntotal, &state->ascales, ae_true, _state);
        }
        else
        {
            normalizesparselcinplace(&state->tmpa, m, &state->tmpal, &state->tmpau, state->ntotal, ae_true, &state->ascales, ae_true, _state);
        }
    }
    else
    {
        rsetallocv(m, 1.0, &state->ascales, _state);
    }
    for(i=0; i<=m-1; i++)
    {
        if( state->ascales.ptr.p_double[i]==0.0 )
        {
            state->ascales.ptr.p_double[i] = 1.0;
        }
    }
    state->rawae.m = m;
    state->rawae.n = state->ntotal;
    iallocv(m+1, &state->rawae.ridx, _state);
    state->rawae.ridx.ptr.p_int[0] = 0;
    for(i=0; i<=m-1; i++)
    {
        
        /*
         * Copy I-th row of TmpA to RawAE, extend with slacks
         */
        offs = state->rawae.ridx.ptr.p_int[i];
        igrowv(offs+state->ntotal, &state->rawae.idx, _state);
        rgrowv(offs+state->ntotal, &state->rawae.vals, _state);
        j0 = state->tmpa.ridx.ptr.p_int[i];
        j1 = state->tmpa.ridx.ptr.p_int[i+1]-1;
        for(j=j0; j<=j1; j++)
        {
            state->rawae.idx.ptr.p_int[offs] = state->tmpa.idx.ptr.p_int[j];
            state->rawae.vals.ptr.p_double[offs] = state->tmpa.vals.ptr.p_double[j];
            offs = offs+1;
        }
        state->rawae.idx.ptr.p_int[offs] = state->naug+i;
        state->rawae.vals.ptr.p_double[offs] = -1.0;
        offs = offs+1;
        state->rawae.ridx.ptr.p_int[i+1] = offs;
        
        /*
         * Extend box constraints
         */
        state->bndle.ptr.p_double[state->naug+i] = state->tmpal.ptr.p_double[i];
        state->bndue.ptr.p_double[state->naug+i] = state->tmpau.ptr.p_double[i];
        state->hasbndle.ptr.p_bool[state->naug+i] = ae_isfinite(state->tmpal.ptr.p_double[i], _state);
        state->hasbndue.ptr.p_bool[state->naug+i] = ae_isfinite(state->tmpau.ptr.p_double[i], _state);
    }
    sparsecreatecrsinplace(&state->rawae, _state);
    
    /*
     * Global regularized, default state (zero)
     */
    state->greg = 0.0;
    
    /*
     * Finite version of box constraints - infinities replaced by very big numbers.
     * Allows us to avoid conditional checks during KKT system generation.
     */
    rallocv(state->ntotal, &state->bndlef, _state);
    rallocv(state->ntotal, &state->bnduef, _state);
    for(i=0; i<=state->ntotal-1; i++)
    {
        state->bndlef.ptr.p_double[i] = rcase2(ae_isfinite(state->bndle.ptr.p_double[i], _state), state->bndle.ptr.p_double[i], -0.001*ae_maxrealnumber, _state);
        state->bnduef.ptr.p_double[i] = rcase2(ae_isfinite(state->bndue.ptr.p_double[i], _state), state->bndue.ptr.p_double[i], 0.001*ae_maxrealnumber, _state);
    }
}


/*************************************************************************
Sets global regularization for X and Y and origin

INPUT PARAMETERS:
    State               -   instance initialized with one of the initialization
                            functions
    RegXY               -   >=0, global regularization
    OriginX             -   array[NUser]
    OriginY             -   array[MSparse+MDense]

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
void ipm2setreg(ipm2state* state,
     double regxy,
     /* Real    */ const ae_vector* originx,
     /* Real    */ const ae_vector* originy,
     ae_state *_state)
{


    ae_assert(ae_isfinite(regxy, _state)&&ae_fp_greater_eq(regxy,(double)(0)), "IPM2SetReg: RegXY is infinite or negative", _state);
    ae_assert(originx->cnt>=state->nuser, "IPM2SetReg: OriginX is too short", _state);
    ae_assert(originy->cnt>=state->mraw, "IPM2SetReg: OriginY is too short", _state);
    ae_assert(isfinitevector(originx, state->nuser, _state), "IPM2SetReg: OriginX contains infinite values", _state);
    ae_assert(isfinitevector(originy, state->mraw, _state), "IPM2SetReg: OriginY contains infinite values", _state);
    state->greg = regxy;
    rcopyallocv(state->nuser, originx, &state->gregoriginxuser, _state);
    rcopyallocv(state->mraw, originy, &state->gregoriginyuser, _state);
}


/*************************************************************************
Sets stopping criteria for QP-IPM solver.

You can set all epsilon-values to one small value, about 1.0E-6.

INPUT PARAMETERS:
    State               -   instance initialized with one of the initialization
                            functions
    EpsP                -   maximum primal error allowed in the  solution,
                            EpsP>=0. Zero will be  automatically  replaced
                            by recommended default value,  which is  equal
                            to 10*Sqrt(Epsilon) in the current version
    EpsD                -   maximum  dual  error allowed in the  solution,
                            EpsP>=0. Zero will be  automatically  replaced
                            by recommended default value,  which is  equal
                            to 10*Sqrt(Epsilon) in the current version
    EpsGap              -   maximum  duality gap allowed in the  solution,
                            EpsP>=0. Zero will be  automatically  replaced
                            by recommended default value,  which is  equal
                            to 10*Sqrt(Epsilon) in the current version

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
void ipm2setcond(ipm2state* state,
     double epsp,
     double epsd,
     double epsgap,
     ae_state *_state)
{
    double sml;


    ae_assert(ae_isfinite(epsp, _state)&&ae_fp_greater_eq(epsp,(double)(0)), "IPM2SetCond: EpsP is infinite or negative", _state);
    ae_assert(ae_isfinite(epsd, _state)&&ae_fp_greater_eq(epsd,(double)(0)), "IPM2SetCond: EpsD is infinite or negative", _state);
    ae_assert(ae_isfinite(epsgap, _state)&&ae_fp_greater_eq(epsgap,(double)(0)), "IPM2SetCond: EpsP is infinite or negative", _state);
    sml = ae_sqrt(ae_machineepsilon, _state);
    state->epsp = coalesce(epsp, sml, _state);
    state->epsd = coalesce(epsd, sml, _state);
    state->epsgap = coalesce(epsgap, sml, _state);
}


/*************************************************************************
Sets maximum iterations count for IPM.

INPUT PARAMETERS:
    State               -   instance initialized with one of the initialization
                            functions
    MaxIts              -   maximum iterations count;
                            recommended in 100..200.
                            Zero means default value (200).

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
void ipm2setmaxits(ipm2state* state, ae_int_t maxits, ae_state *_state)
{


    ae_assert(maxits>=0, "IPM2SetMaxIts: MaxIts<0", _state);
    state->maxipmits = maxits;
}


/*************************************************************************
This function proposes user ordering for a KKT system, as accepted by
IPM2SetOrdering().

This function accepts linear constraints matrix and it assumes that the
quadratic term is diagonal (a placeholder parameter is used to ensure that
the caller is aware of the fact). Only sparsity patter is used, not specific
coefficient values.

It returns proposed ordering. An instance of IPM2State structure is passed
to be used as a temporary buffer; its initial state is invalidated. However,
large internal temporaries are left intact and memory allocated by them can
be reused later.

INPUT PARAMETERS:
    State               -   uninitialized or initialized instance
    N                   -   variables count
    IsDiagonalQ         -   must be true to confirm that the caller is aware
                            of absence of support for non-diagonal quadratic
                            terms
    HasBndL             -   array[N], I-th element is true if bound is finite
    HasBndU             -   array[N], I-th element is true if bound is finite
    C                   -   if M>0, sparse constraints in CRS format
    CL, CU              -   array[M], lower and upper bounds for linear
                            constrains
    M                   -   >=0, linear constraints count
    P                   -   potentially preallocated buffer, array[N+M],
                            resized if needed
    
OUTPUT PARAMETERS:
    P                   -   array[N+M] that maps original indexes of a
                            KKT matrix (between 0 and N-1 for variables,
                            between N and N+M-1 for constraints) to
                            permuted indexes
                            
NOTE: the function assumes that all variables are boxed
                            
NOTE: the optimizer also allows to work with QP problems with low rank
      correction applied to the quadratic term.
      
      This function and IPM2SetOrdering() assume that independently of
      modification actual sparsity pattern its rows are assumed to be dense
      and are moved to the end. So, these rows are not included into the
      analysis. It allows us to reuse the same ordering for multiple problems
      with varying correction sizes but the same sparsity pattern for the
      rest of problem.

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
void ipm2proposeordering(ipm2state* state,
     ae_int_t n,
     ae_bool isdiagonalq,
     /* Boolean */ const ae_vector* hasbndl,
     /* Boolean */ const ae_vector* hasbndu,
     const sparsematrix* c,
     /* Real    */ const ae_vector* cl,
     /* Real    */ const ae_vector* cu,
     ae_int_t m,
     /* Integer */ ae_vector* p,
     ae_state *_state)
{
    ae_int_t offs;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t k;
    ae_int_t colthreshold;
    ae_int_t rowthreshold;
    ae_int_t sumcoldeg;
    ae_int_t sumrowdeg;
    ae_int_t groupcols;
    ae_int_t grouprows;
    ae_int_t groupequality;
    ae_int_t groupfree;
    ae_int_t densegroup;


    ae_assert(isdiagonalq, "IPM2ProposeOrdering: does not support non-diagonal Q", _state);
    
    /*
     * Allocate sparse matrix
     */
    sumcoldeg = 0;
    sumrowdeg = 0;
    isetallocv(n, 0, &state->reducedsystem.coldegrees, _state);
    isetallocv(m, 0, &state->reducedsystem.rowdegrees, _state);
    state->reducedsystem.compactkkt.matrixtype = 1;
    state->reducedsystem.compactkkt.m = n+m;
    state->reducedsystem.compactkkt.n = n+m;
    iallocv(state->reducedsystem.compactkkt.m+1, &state->reducedsystem.compactkkt.ridx, _state);
    state->reducedsystem.compactkkt.ridx.ptr.p_int[0] = 0;
    for(i=0; i<=n-1; i++)
    {
        offs = state->reducedsystem.compactkkt.ridx.ptr.p_int[i];
        igrowv(offs+1, &state->reducedsystem.compactkkt.idx, _state);
        rgrowv(offs+1, &state->reducedsystem.compactkkt.vals, _state);
        state->reducedsystem.compactkkt.idx.ptr.p_int[offs] = i;
        state->reducedsystem.compactkkt.vals.ptr.p_double[offs] = 1.0;
        offs = offs+1;
        state->reducedsystem.compactkkt.ridx.ptr.p_int[i+1] = offs;
    }
    for(i=0; i<=m-1; i++)
    {
        offs = state->reducedsystem.compactkkt.ridx.ptr.p_int[n+i];
        igrowv(offs+n+1, &state->reducedsystem.compactkkt.idx, _state);
        rgrowv(offs+n+1, &state->reducedsystem.compactkkt.vals, _state);
        j0 = c->ridx.ptr.p_int[i];
        j1 = c->ridx.ptr.p_int[i+1]-1;
        for(j=j0; j<=j1; j++)
        {
            k = c->idx.ptr.p_int[j];
            state->reducedsystem.compactkkt.idx.ptr.p_int[offs] = k;
            state->reducedsystem.compactkkt.vals.ptr.p_double[offs] = 1.0;
            state->reducedsystem.rowdegrees.ptr.p_int[i] = state->reducedsystem.rowdegrees.ptr.p_int[i]+1;
            state->reducedsystem.coldegrees.ptr.p_int[k] = state->reducedsystem.coldegrees.ptr.p_int[k]+1;
            sumrowdeg = sumrowdeg+1;
            sumcoldeg = sumcoldeg+1;
            offs = offs+1;
        }
        state->reducedsystem.compactkkt.idx.ptr.p_int[offs] = n+i;
        state->reducedsystem.compactkkt.vals.ptr.p_double[offs] = 1.0;
        offs = offs+1;
        state->reducedsystem.compactkkt.ridx.ptr.p_int[n+i+1] = offs;
    }
    sparsecreatecrsinplace(&state->reducedsystem.compactkkt, _state);
    
    /*
     * Determine elimination priorities
     */
    colthreshold = ae_round(ipm2solver_muquasidense*(double)sumcoldeg/(double)n, _state)+1;
    rowthreshold = ae_round(ipm2solver_muquasidense*(double)sumrowdeg/coalesce((double)(m), (double)(1), _state), _state)+1;
    if( ae_fp_less_eq((double)sumcoldeg/(double)n,(double)sumrowdeg/coalesce((double)(m), (double)(1), _state)) )
    {
        groupcols = 0;
        grouprows = 1;
    }
    else
    {
        groupcols = 1;
        grouprows = 0;
    }
    groupequality = 2;
    groupfree = 3;
    densegroup = ae_maxint(groupfree, groupequality, _state);
    isetallocv(n+m, 0, &state->reducedsystem.compactpriorities, _state);
    for(i=0; i<=n-1; i++)
    {
        state->reducedsystem.compactpriorities.ptr.p_int[i] = groupcols;
        if( !hasbndl->ptr.p_bool[i]&&!hasbndu->ptr.p_bool[i] )
        {
            state->reducedsystem.compactpriorities.ptr.p_int[i] = groupfree;
        }
        if( state->reducedsystem.coldegrees.ptr.p_int[i]>colthreshold )
        {
            state->reducedsystem.compactpriorities.ptr.p_int[i] = densegroup;
        }
    }
    for(i=0; i<=m-1; i++)
    {
        state->reducedsystem.compactpriorities.ptr.p_int[n+i] = grouprows;
        if( (ae_isfinite(cl->ptr.p_double[i], _state)&&ae_isfinite(cu->ptr.p_double[i], _state))&&ae_fp_less(cl->ptr.p_double[i]-cu->ptr.p_double[i],(double)1000*ae_sqrt(ae_machineepsilon, _state)*rmaxabs3(cl->ptr.p_double[i], cu->ptr.p_double[i], 1.0, _state)) )
        {
            state->reducedsystem.compactpriorities.ptr.p_int[n+i] = groupequality;
        }
        if( state->reducedsystem.rowdegrees.ptr.p_int[i]>rowthreshold )
        {
            state->reducedsystem.compactpriorities.ptr.p_int[n+i] = densegroup;
        }
    }
    if( !spsymmanalyze(&state->reducedsystem.compactkkt, &state->reducedsystem.compactpriorities, ipm2solver_mupromote, densegroup, ipm2solver_spsymmfacttype, 3, 1, &state->reducedsystem.analysis, _state) )
    {
        ae_assert(ae_false, "ReducedSystemPowerUp: critical integrity check failed, symbolically degenerate KKT system encountered", _state);
    }
    icopyallocv(n+m, &state->reducedsystem.analysis.fillinperm, p, _state);
}


/*************************************************************************
Sets KKT system ordering to that produced by IPM2ProposeOrdering().

INPUT PARAMETERS:
    State               -   instance initialized with one of the initialization
                            functions
    P                   -   array[N+M], ordering

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
void ipm2setordering(ipm2state* state,
     /* Integer */ const ae_vector* p,
     ae_state *_state)
{


    icopyallocv(state->nuser+state->mraw, p, &state->userpermutation, _state);
    state->hasuserpermutation = ae_true;
}


/*************************************************************************
Solve QP problem.

INPUT PARAMETERS:
    State               -   solver instance
    DropBigBounds       -   If True, algorithm may drop box and linear constraints
                            with huge bound values that destabilize algorithm.
    
OUTPUT PARAMETERS:
    XS                  -   array[N], solution
    LagBC               -   array[N], Lagrange multipliers for box constraints
    LagLC               -   array[M], Lagrange multipliers for linear constraints
    TerminationType     -   completion code, positive values for success,
                            negative for failures (XS constrains best point
                            found so far):
                            * -2    the task is either unbounded or infeasible;
                                    the IPM solver has difficulty distinguishing between these two.
                            * +1    stopping criteria are met
                            * +7    stopping criteria are too stringent

RESULT:

This function ALWAYS returns something  meaningful in XS, LagBC, LagLC - 
either solution or the best point so far, even for negative TerminationType.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void ipm2optimize(ipm2state* state,
     ae_bool dropbigbounds,
     /* Real    */ ae_vector* xs,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* laglc,
     ae_int_t* terminationtype,
     ae_state *_state)
{
    ae_int_t nuser;
    ae_int_t ntotal;
    ae_int_t m;
    ae_int_t i;
    double regfree;
    double dampeps;
    double maxdampeps;
    double dampepsmu;
    double dampepszs;
    ae_int_t iteridx;
    double mu0;
    double mu1;
    double alpha;
    double alphaprev;
    double alphac;
    ae_int_t primalstagnationcnt;
    ae_int_t dualstagnationcnt;
    double errp2;
    double errd2;
    double errpinf;
    double errdinf;
    double preverrp2;
    double preverrd2;
    double errgap;
    double eprimal;
    double edual;
    double egap;
    double y0nrm;
    double bady;
    ae_int_t bestiteridx;
    double besterr;
    double bestegap;
    double besteprimal;
    double bestedual;
    ae_bool loadbest;
    double mustop;

    *terminationtype = 0;

    nuser = state->nuser;
    ntotal = state->ntotal;
    m = state->mraw;
    state->dotrace = ae_is_trace_enabled("IPM");
    state->dotimers = state->dotrace||ae_is_trace_enabled("TIMERS.IPM");
    state->dodetailedtrace = state->dotrace&&ae_is_trace_enabled("IPM.DETAILED");
    
    /*
     * Prepare outputs
     */
    rsetallocv(nuser, 0.0, xs, _state);
    rsetallocv(nuser, 0.0, lagbc, _state);
    rsetallocv(m, 0.0, laglc, _state);
    
    /*
     * Some integrity checks:
     * * we need PrimalStagnationLen<DualStagnationLen in order to be able to correctly
     *   detect infeasible instances (stagnated dual error is present in both infeasible
     *   and unbounded instances, so we should check for primal stagnation a few iters
     *   before checking for dual stagnation)
     */
    ae_assert(ipm2solver_primalstagnationlen<ipm2solver_dualstagnationlen, "IPM2: critical integrity failure - incorrect configuration parameters", _state);
    
    /*
     * Trace output (if needed), setup timers
     */
    if( state->dotimers )
    {
        stimerinit(&state->timertotal, _state);
        stimerinit(&state->timeranalyze, _state);
        stimerinit(&state->timerfactorize, _state);
        stimerinit(&state->timerspsymmsolve, _state);
        stimerinit(&state->timerothersolve, _state);
        stimerstart(&state->timertotal, _state);
    }
    if( state->dotrace||state->dotimers )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("// IPM SOLVER STARTED                                                                             //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
    }
    
    /*
     * Prepare regularization coefficients:
     * * DampEps - damping coefficient for damped Newton step. Comes along with SafeDampEps 
     *   (threshold value when some safeguards are turned off in order to preserve convergence
     *   speed) and MaxDampEps - threshold value when we consider problem overregularized and stop.
     */
    regfree = ae_pow(ae_machineepsilon, 0.75, _state);
    dampepsmu = ae_machineepsilon;
    dampepszs = ae_machineepsilon;
    dampeps = (double)10*ae_machineepsilon;
    maxdampeps = ae_sqrt(ae_sqrt(ae_machineepsilon, _state), _state);
    
    /*
     * Set up initial state
     */
    state->repiterationscount = 0;
    state->repncholesky = 0;
    mustop = (double)100*ae_machineepsilon;
    ipm2solver_ipm2powerup(state, regfree, _state);
    ipm2solver_varsinitfrom(&state->best, &state->current, _state);
    ipm2solver_varsinitbyzero(&state->delta, ntotal, m, _state);
    ipm2solver_varsinitbyzero(&state->corr, ntotal, m, _state);
    bestiteridx = -1;
    besterr = ae_maxrealnumber;
    bestegap = ae_maxrealnumber;
    besteprimal = ae_maxrealnumber;
    bestedual = ae_maxrealnumber;
    ipm2solver_traceprogress(state, 0.0, 0.0, 0.0, _state);
    y0nrm = (double)(0);
    y0nrm = ae_maxreal(y0nrm, rmaxabsv(m, &state->current.y, _state), _state);
    y0nrm = ae_maxreal(y0nrm, rmaxabsv(ntotal, &state->current.z, _state), _state);
    y0nrm = ae_maxreal(y0nrm, rmaxabsv(ntotal, &state->current.s, _state), _state);
    
    /*
     * Start iteration
     */
    loadbest = ae_true;
    primalstagnationcnt = 0;
    dualstagnationcnt = 0;
    *terminationtype = 7;
    errp2 = ae_maxrealnumber;
    errd2 = ae_maxrealnumber;
    alphaprev = 0.0;
    ipm2solver_ipm2multiply(state, &state->current.x, &state->current.y, &state->currenthx, &state->currentax, &state->currentaty, _state);
    mu0 = ipm2solver_varscomputemu(state, &state->current, _state);
    ipm2solver_rhscomputeprimdual(state, &state->current, &state->currenthx, &state->currentax, &state->currentaty, &state->rhstarget, _state);
    for(iteridx=0; iteridx<=state->maxipmits-1; iteridx++)
    {
        
        /*
         * Trace beginning
         */
        if( state->dotrace )
        {
            ae_trace("=== PREDICTOR-CORRECTOR STEP %2d ====================================================================\n",
                (int)(iteridx));
        }
        
        /*
         * Check regularization status, terminate if overregularized
         */
        if( ae_fp_greater_eq(dampeps,maxdampeps) )
        {
            if( state->dotrace )
            {
                ae_trace("> tried to increase regularization parameter, but it is too large\n");
                ae_trace("> it is likely that stopping conditions are too stringent, stopping at the best point found so far\n");
            }
            *terminationtype = 7;
            break;
        }
        
        /*
         * Precompute factorization
         */
        if( !ipm2solver_reducedsystemcomputefactorization(&state->reducedsystem, state, &state->current, dampepsmu, dampepszs, dampeps, dampeps, _state) )
        {
            
            /*
             * KKT factorization failed.
             * Increase regularization parameter and skip this iteration.
             */
            dampeps = (double)10*dampeps;
            if( state->dotrace )
            {
                ae_trace("> LDLT factorization failed due to rounding errors\n");
                ae_trace("> increasing DampEps coefficient to %0.2e, skipping iteration\n",
                    (double)(dampeps));
            }
            continue;
        }
        
        /*
         * Mehrotra predictor-corrector.
         * Prepare initial predictor step
         */
        ipm2solver_rhsrecomputegammapredictor(state, &state->current, 0.0, &state->rhstarget, _state);
        if( !ipm2solver_reducedsystemsolve(&state->reducedsystem, state, &state->current, &state->rhstarget, &state->delta, _state) )
        {
            
            /*
             * Initial affine scaling step failed due to numerical errors.
             * Increase regularization parameter and skip this iteration.
             */
            dampeps = (double)10*dampeps;
            if( state->dotrace )
            {
                ae_trace("> initial affine scaling step failed to decrease residual due to rounding errors\n");
                ae_trace("> increasing DampEps coefficient to %0.2e, skipping iteration\n",
                    (double)(dampeps));
            }
            continue;
        }
        ipm2solver_ipm2computesteplength(state, &state->current, &state->delta, &alpha, _state);
        mu1 = ipm2solver_varscomputemutrial(state, &state->current, &state->delta, alpha, _state);
        
        /*
         * Perform corrector step
         */
        ipm2solver_rhsrecomputegammamehrotra(state, &state->current, ae_pow(mu1/(mu0+ae_machineepsilon), (double)(3), _state)*mu0, &state->delta, &state->rhstarget, _state);
        if( !ipm2solver_reducedsystemsolve(&state->reducedsystem, state, &state->current, &state->rhstarget, &state->corr, _state) )
        {
            dampeps = (double)10*dampeps;
            if( state->dotrace )
            {
                ae_trace("> corrector failed to decrease residual due to rounding errors\n");
                ae_trace("> increasing DampEps coefficient to %0.2e, skipping iteration\n",
                    (double)(dampeps));
            }
            continue;
        }
        
        /*
         * Finally, computing a step length and making a step
         */
        ipm2solver_ipm2computesteplength(state, &state->current, &state->corr, &alphac, _state);
        if( ae_fp_greater_eq((double)(iteridx),ipm2solver_minitersbeforesafeguards)&&(ae_fp_less_eq(alphac,ipm2solver_badsteplength)&&ae_fp_greater(alphaprev,ipm2solver_badsteplength)) )
        {
            
            /*
             * Previous step was good, but this step is too short, likely to fail due to numerical errors.
             * Increase regularization parameter and skip this iteration.
             */
            dampeps = (double)10*dampeps;
            if( state->dotrace )
            {
                ae_trace("> step length is too short (after having a good step before), suspecting rounding errors\n");
                ae_trace("> increasing DampEps coefficient to %0.2e, skipping iteration\n",
                    (double)(dampeps));
            }
            continue;
        }
        ipm2solver_varsaddstep(&state->current, &state->corr, alphac, _state);
        inc(&state->repiterationscount, _state);
        alphaprev = alphac;
        ipm2solver_traceprogress(state, mu0, alpha, alphac, _state);
        ipm2solver_ipm2multiply(state, &state->current.x, &state->current.y, &state->currenthx, &state->currentax, &state->currentaty, _state);
        ipm2solver_rhscomputeprimdual(state, &state->current, &state->currenthx, &state->currentax, &state->currentaty, &state->rhstarget, _state);
        mu0 = ipm2solver_varscomputemu(state, &state->current, _state);
        
        /*
         * Check stopping criteria
         * * primal and dual stagnation are checked only when following criteria are met:
         *   1) Mu is smaller than 1 (we already converged close enough)
         *   2) we performed more than MinItersBeforeStagnation iterations
         */
        preverrp2 = errp2;
        preverrd2 = errd2;
        ipm2solver_computeerrors(state, &state->rhstarget, &state->currenthx, &state->currentax, &state->currentaty, &errp2, &errd2, &errpinf, &errdinf, &errgap, _state);
        egap = errgap;
        eprimal = errpinf;
        edual = errdinf;
        if( ae_fp_less(rmax3(egap, eprimal, edual, _state),besterr) )
        {
            
            /*
             * Save best point found so far
             */
            ipm2solver_varsinitfrom(&state->best, &state->current, _state);
            bestiteridx = iteridx;
            besterr = rmax3(egap, eprimal, edual, _state);
            bestegap = egap;
            besteprimal = eprimal;
            bestedual = edual;
        }
        if( bestiteridx>0&&iteridx>bestiteridx+ipm2solver_minitersbeforeeworststagnation )
        {
            if( state->dotrace )
            {
                ae_trace("> worst of primal/dual/gap errors stagnated for %0d its, stopping at the best point found so far\n",
                    (int)(ipm2solver_minitersbeforeeworststagnation));
            }
            break;
        }
        if( ((ae_fp_less_eq(egap,state->epsgap)&&ae_fp_greater_eq(errp2,ipm2solver_stagnationdelta*preverrp2))&&ae_fp_greater_eq(errpinf,ipm2solver_primalinfeasible1))&&iteridx>=ipm2solver_minitersbeforestagnation )
        {
            inc(&primalstagnationcnt, _state);
            if( primalstagnationcnt>=ipm2solver_primalstagnationlen )
            {
                if( state->dotrace )
                {
                    ae_trace("> primal error stagnated for %0d its, stopping at the best point found so far\n",
                        (int)(ipm2solver_primalstagnationlen));
                }
                break;
            }
        }
        else
        {
            primalstagnationcnt = 0;
        }
        if( ((ae_fp_less_eq(egap,state->epsgap)&&ae_fp_greater_eq(errd2,ipm2solver_stagnationdelta*preverrd2))&&ae_fp_greater_eq(errdinf,ipm2solver_dualinfeasible1))&&iteridx>=ipm2solver_minitersbeforestagnation )
        {
            inc(&dualstagnationcnt, _state);
            if( dualstagnationcnt>=ipm2solver_dualstagnationlen )
            {
                if( state->dotrace )
                {
                    ae_trace("> dual error stagnated for %0d its, stopping at the best point found so far\n",
                        (int)(ipm2solver_dualstagnationlen));
                }
                break;
            }
        }
        else
        {
            dualstagnationcnt = 0;
        }
        if( ae_fp_less_eq(mu0,mustop)&&iteridx>=ipm2solver_itersfortoostringentcond )
        {
            if( state->dotrace )
            {
                ae_trace("> stopping conditions are too stringent, stopping at the best point found so far\n");
            }
            *terminationtype = 7;
            break;
        }
        if( (ae_fp_less_eq(egap,state->epsgap)&&ae_fp_less_eq(eprimal,state->epsp))&&ae_fp_less_eq(edual,state->epsd) )
        {
            if( state->dotrace )
            {
                ae_trace("> stopping criteria are met\n");
            }
            *terminationtype = 1;
            loadbest = ae_false;
            break;
        }
        bady = ipm2solver_bigy;
        bady = ae_maxreal(bady, ipm2solver_ygrowth*y0nrm, _state);
        bady = ae_maxreal(bady, ipm2solver_ygrowth*rmaxabsv(ntotal, &state->current.x, _state), _state);
        bady = ae_maxreal(bady, ipm2solver_ygrowth*rmaxabsv(ntotal, &state->current.g, _state), _state);
        bady = ae_maxreal(bady, ipm2solver_ygrowth*rmaxabsv(ntotal, &state->current.t, _state), _state);
        if( ae_fp_greater_eq(rmaxabsv(m, &state->current.y, _state),bady)&&iteridx>=ipm2solver_minitersbeforeinfeasible )
        {
            if( state->dotrace )
            {
                ae_trace("> |Y| increased beyond %0.1e, stopping at the best point found so far\n",
                    (double)(bady));
            }
            break;
        }
    }
    
    /*
     * Load best point, perform some checks
     */
    if( loadbest )
    {
        
        /*
         * Load best point
         *
         * NOTE: TouchReal() is used to avoid spurious compiler warnings about 'set but unused'
         */
        if( state->dotrace )
        {
            ae_trace("> the best point so far is one from iteration %0d\n",
                (int)(bestiteridx));
        }
        ipm2solver_varsinitfrom(&state->current, &state->best, _state);
        touchreal(&besteprimal, _state);
        touchreal(&bestedual, _state);
        touchreal(&bestegap, _state);
        
        /*
         * If no error flags were set yet, check solution quality
         */
        bady = ipm2solver_bigy;
        bady = ae_maxreal(bady, ipm2solver_ygrowth*y0nrm, _state);
        bady = ae_maxreal(bady, ipm2solver_ygrowth*rmaxabsv(ntotal, &state->current.x, _state), _state);
        bady = ae_maxreal(bady, ipm2solver_ygrowth*rmaxabsv(ntotal, &state->current.g, _state), _state);
        bady = ae_maxreal(bady, ipm2solver_ygrowth*rmaxabsv(ntotal, &state->current.t, _state), _state);
        if( *terminationtype>0&&ae_fp_greater_eq(rmaxabsv(m, &state->current.y, _state),bady) )
        {
            *terminationtype = -2;
            if( state->dotrace )
            {
                ae_trace("> |Y| increased beyond %0.1e, declaring infeasibility/unboundedness\n",
                    (double)(bady));
            }
        }
        if( *terminationtype>0&&ae_fp_greater_eq(besteprimal,ipm2solver_primalinfeasible1) )
        {
            *terminationtype = -2;
            if( state->dotrace )
            {
                ae_trace("> primal error at the best point is too high, declaring infeasibility/unboundedness\n");
            }
        }
        if( *terminationtype>0&&ae_fp_greater_eq(bestedual,ipm2solver_dualinfeasible1) )
        {
            *terminationtype = -2;
            if( state->dotrace )
            {
                ae_trace("> dual error at the best point is too high, declaring infeasibility/unboundedness\n");
            }
        }
    }
    
    /*
     * Output
     */
    for(i=0; i<=nuser-1; i++)
    {
        xs->ptr.p_double[i] = state->current.x.ptr.p_double[i];
        lagbc->ptr.p_double[i] = 0.0;
        if( state->hasgz.ptr.p_bool[i] )
        {
            lagbc->ptr.p_double[i] = lagbc->ptr.p_double[i]-state->current.z.ptr.p_double[i];
        }
        if( state->hasts.ptr.p_bool[i] )
        {
            lagbc->ptr.p_double[i] = lagbc->ptr.p_double[i]+state->current.s.ptr.p_double[i];
        }
    }
    for(i=0; i<=m-1; i++)
    {
        laglc->ptr.p_double[i] = -state->current.y.ptr.p_double[i];
    }
    
    /*
     * Unscale point and Lagrange multipliers
     */
    unscaleunshiftpointbc(&state->sclx, &state->xoriginx, &state->rawbndl, &state->rawbndu, &state->bndle, &state->bndue, &state->hasbndle, &state->hasbndue, xs, nuser, _state);
    for(i=0; i<=nuser-1; i++)
    {
        lagbc->ptr.p_double[i] = lagbc->ptr.p_double[i]*state->targetscale/state->sclx.ptr.p_double[i];
    }
    for(i=0; i<=m-1; i++)
    {
        laglc->ptr.p_double[i] = laglc->ptr.p_double[i]*state->targetscale/state->ascales.ptr.p_double[i];
    }
    
    /*
     * Timers and reports
     */
    if( state->dotimers )
    {
        stimerstop(&state->timertotal, _state);
        ae_trace("\n> printing internal timers (can be zero when run in OS-agnostic mode)\n");
        ae_trace("> total time:            %10.1f ms\n",
            (double)(stimergetms(&state->timertotal, _state)));
        ae_trace("> * in analyze:          %10.1f ms\n",
            (double)(stimergetms(&state->timeranalyze, _state)));
        ae_trace("> * in factorize:        %10.1f ms\n",
            (double)(stimergetms(&state->timerfactorize, _state)));
        ae_trace("> * in spsymmsolve():    %10.1f ms\n",
            (double)(stimergetms(&state->timerspsymmsolve, _state)));
        ae_trace("> * in other solve:      %10.1f ms\n",
            (double)(stimergetms(&state->timerothersolve, _state)-stimergetms(&state->timerspsymmsolve, _state)));
    }
}


/*************************************************************************
Allocates place for variables of IPM and fills by zeros.

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_varsinitbyzero(ipm2vars* vstate,
     ae_int_t ntotal,
     ae_int_t m,
     ae_state *_state)
{


    ae_assert(ntotal>=1, "VarsInitByZero: NTotal<1", _state);
    ae_assert(m>=0, "VarsInitByZero: M<0", _state);
    vstate->ntotal = ntotal;
    vstate->m = m;
    rsetallocv(ntotal, 0.0, &vstate->x, _state);
    rsetallocv(ntotal, 0.0, &vstate->g, _state);
    rsetallocv(ntotal, 0.0, &vstate->t, _state);
    rsetallocv(ntotal, 0.0, &vstate->z, _state);
    rsetallocv(ntotal, 0.0, &vstate->s, _state);
    rsetallocv(m, 0.0, &vstate->y, _state);
}


/*************************************************************************
Allocates place for variables of IPM and fills them by values of
the source

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_varsinitfrom(ipm2vars* vstate,
     const ipm2vars* vsrc,
     ae_state *_state)
{


    ae_assert(vsrc->ntotal>=1, "VarsInitFrom: NTotal<1", _state);
    ae_assert(vsrc->m>=0, "VarsInitFrom: M<0", _state);
    vstate->ntotal = vsrc->ntotal;
    vstate->m = vsrc->m;
    rcopyallocv(vsrc->ntotal, &vsrc->x, &vstate->x, _state);
    rcopyallocv(vsrc->ntotal, &vsrc->g, &vstate->g, _state);
    rcopyallocv(vsrc->ntotal, &vsrc->t, &vstate->t, _state);
    rcopyallocv(vsrc->ntotal, &vsrc->z, &vstate->z, _state);
    rcopyallocv(vsrc->ntotal, &vsrc->s, &vstate->s, _state);
    rcopyallocv(vsrc->m, &vsrc->y, &vstate->y, _state);
}


/*************************************************************************
Adds to variables direction vector times step length. Different
lengths are used for primal and dual steps.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_varsaddstep(ipm2vars* vstate,
     const ipm2vars* vdir,
     double stp,
     ae_state *_state)
{


    raddv(vstate->ntotal, stp, &vdir->x, &vstate->x, _state);
    raddv(vstate->ntotal, stp, &vdir->g, &vstate->g, _state);
    raddv(vstate->ntotal, stp, &vdir->z, &vstate->z, _state);
    raddv(vstate->ntotal, stp, &vdir->t, &vstate->t, _state);
    raddv(vstate->ntotal, stp, &vdir->s, &vstate->s, _state);
    raddv(vstate->m, stp, &vdir->y, &vstate->y, _state);
}


/*************************************************************************
Computes complementarity gap

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static double ipm2solver_varscomputecomplementaritygap(const ipm2vars* vstate,
     ae_state *_state)
{
    double result;


    result = rdotv(vstate->ntotal, &vstate->g, &vstate->z, _state)+rdotv(vstate->ntotal, &vstate->t, &vstate->s, _state);
    return result;
}


/*************************************************************************
Computes empirical value of the barrier parameter Mu

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static double ipm2solver_varscomputemu(const ipm2state* state,
     const ipm2vars* vstate,
     ae_state *_state)
{
    double result;


    result = (rdotv(state->ntotal, &vstate->z, &vstate->g, _state)+rdotv(state->ntotal, &vstate->s, &vstate->t, _state))/coalesce((double)(state->cntgz+state->cntts), (double)(1), _state);
    return result;
}


/*************************************************************************
Computes empirical value of the barrier parameter Mu at the trial point
Current+Alpha*Delta without computing the point itself.

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static double ipm2solver_varscomputemutrial(const ipm2state* state,
     const ipm2vars* current,
     const ipm2vars* delta,
     double alpha,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t nx;
    double result;


    nx = state->ntotal;
    result = (double)(0);
    for(i=0; i<=nx-1; i++)
    {
        result = result+(current->g.ptr.p_double[i]+alpha*delta->g.ptr.p_double[i])*(current->z.ptr.p_double[i]+alpha*delta->z.ptr.p_double[i]);
        result = result+(current->t.ptr.p_double[i]+alpha*delta->t.ptr.p_double[i])*(current->s.ptr.p_double[i]+alpha*delta->s.ptr.p_double[i]);
    }
    result = result/coalesce((double)(state->cntgz+state->cntts), (double)(1), _state);
    return result;
}


/*************************************************************************
Computes

    Y := alpha*A*x + beta*Y

where A is constraint matrix, X is user-specified source, Y is target.

Beta can be zero (in this case original contents of Y is ignored).
If Beta is nonzero, we expect that Y contains preallocated array.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_multiplygeax(const ipm2state* state,
     double alpha,
     /* Real    */ const ae_vector* x,
     ae_int_t offsx,
     double beta,
     /* Real    */ ae_vector* y,
     ae_int_t offsax,
     ae_state *_state)
{
    ae_int_t m;


    m = state->mraw;
    if( ae_fp_eq(beta,(double)(0)) )
    {
        rallocv(offsax+m, y, _state);
    }
    else
    {
        ae_assert(y->cnt>=offsax+m, "MultiplyGEAX: Y is too short", _state);
    }
    if( ae_fp_neq(alpha,(double)(0))&&m!=0 )
    {
        sparsegemv(&state->rawae, alpha, 0, x, offsx, beta, y, offsax, _state);
    }
}


/*************************************************************************
Computes

    Y := alpha*A'*x + beta*Y

where A is constraint matrix, X is user-specified source, Y is target.

Beta can be zero, in this case we automatically reallocate target if it is
too short (but do NOT reallocate it if its size is large enough).
If Beta is nonzero, we expect that Y contains preallocated array.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_multiplygeatx(const ipm2state* state,
     double alpha,
     /* Real    */ const ae_vector* x,
     ae_int_t offsx,
     double beta,
     /* Real    */ ae_vector* y,
     ae_int_t offsy,
     ae_state *_state)
{
    ae_int_t ntotal;
    ae_int_t m;


    ntotal = state->ntotal;
    m = state->mraw;
    if( ae_fp_eq(beta,(double)(0)) )
    {
        rallocv(offsy+ntotal, y, _state);
        rsetvx(ntotal, 0.0, y, offsy, _state);
    }
    else
    {
        ae_assert(y->cnt>=offsy+ntotal, "MultiplyGEATX: Y is too short", _state);
        rmulvx(ntotal, beta, y, offsy, _state);
    }
    if( ae_fp_neq(alpha,(double)(0))&&m!=0 )
    {
        sparsegemv(&state->rawae, alpha, 1, x, offsx, 1.0, y, offsy, _state);
    }
}


/*************************************************************************
Computes H*x, does not support advanced functionality of GEAX/GEATX

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_multiplyhx(const ipm2state* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* hx,
     ae_state *_state)
{
    ae_int_t ntotal;
    ae_int_t i;


    ntotal = state->ntotal;
    rallocv(ntotal, hx, _state);
    ae_assert(state->sparsehe.n==ntotal&&state->sparsehe.m==ntotal, "IPM2MultiplyHX: sparse H has incorrect size", _state);
    if( state->isdiagonalh )
    {
        
        /*
         * H is known to be diagonal, much faster code can be used
         */
        rcopyv(ntotal, &state->diagr, hx, _state);
        raddv(ntotal, 1.0, &state->sparsehe.vals, hx, _state);
        rmergemulv(ntotal, x, hx, _state);
    }
    else
    {
        
        /*
         * H is a general sparse matrix, use generic sparse matrix-vector multiply
         */
        sparsesmv(&state->sparsehe, ae_false, x, hx, _state);
        for(i=0; i<=ntotal-1; i++)
        {
            hx->ptr.p_double[i] = hx->ptr.p_double[i]+x->ptr.p_double[i]*state->diagr.ptr.p_double[i];
        }
    }
}


/*************************************************************************
Computes products H*x, A*x, A^T*y

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_ipm2multiply(const ipm2state* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     /* Real    */ ae_vector* hx,
     /* Real    */ ae_vector* ax,
     /* Real    */ ae_vector* aty,
     ae_state *_state)
{


    ipm2solver_multiplygeax(state, 1.0, x, 0, 0.0, ax, 0, _state);
    ipm2solver_multiplygeatx(state, 1.0, y, 0, 0.0, aty, 0, _state);
    ipm2solver_multiplyhx(state, x, hx, _state);
}


/*************************************************************************
This function "powers up" factorization, i.e. prepares some important
temporaries. It should be called once prior to the first call to
VIPMInitialPoint() or VIPMFactorize().

Parameters:
    RegFree     -   regularization for free variables;
                    good value sqrt(MachineEpsilon)

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_ipm2powerup(ipm2state* state,
     double regfree,
     ae_state *_state)
{
    ae_int_t ntotal;
    ae_int_t m;
    ae_int_t i;
    double initprimslack;
    double initdualslack;


    ntotal = state->ntotal;
    m = state->mraw;
    
    /*
     * Set up information about presence of slack variables.
     * Compute diagonal regularization matrix R.
     */
    bcopyallocv(ntotal, &state->hasbndle, &state->hasgz, _state);
    bcopyallocv(ntotal, &state->hasbndue, &state->hasts, _state);
    rsetallocv(ntotal, 0.0, &state->diagr, _state);
    ipm2solver_varsinitbyzero(&state->current, ntotal, m, _state);
    rsetallocv(ntotal, 0.0, &state->maskgz, _state);
    rsetallocv(ntotal, 0.0, &state->maskts, _state);
    state->cntgz = 0;
    state->cntts = 0;
    for(i=0; i<=ntotal-1; i++)
    {
        if( state->hasgz.ptr.p_bool[i] )
        {
            state->maskgz.ptr.p_double[i] = 1.0;
            state->cntgz = state->cntgz+1;
        }
        if( state->hasts.ptr.p_bool[i] )
        {
            state->maskts.ptr.p_double[i] = 1.0;
            state->cntts = state->cntts+1;
        }
        if( !state->hasgz.ptr.p_bool[i]&&!state->hasts.ptr.p_bool[i] )
        {
            state->diagr.ptr.p_double[i] = regfree;
        }
    }
    
    /*
     * Compute global regularizer for X and Y
     */
    if( ae_fp_neq(state->greg,(double)(0)) )
    {
        
        /*
         * Compute diagonal modifications to LHS and RHS
         */
        rsetallocv(ntotal+m, 0.0, &state->gregdiag, _state);
        rsetvx(state->nuser, -state->greg, &state->gregdiag, 0, _state);
        rsetvx(m, state->greg, &state->gregdiag, ntotal, _state);
        rallocv(ntotal, &state->gregrhsx, _state);
        rsetvx(state->nuser, state->greg, &state->gregrhsx, 0, _state);
        rsetvx(ntotal-state->nuser, 0.0, &state->gregrhsx, state->nuser, _state);
        rsetallocv(m, -state->greg, &state->gregrhsy, _state);
        
        /*
         * Combine X-origin and Y-origin
         */
        rsetallocv(ntotal, 0.0, &state->gregoriginx, _state);
        rcopyv(state->nuser, &state->gregoriginxuser, &state->gregoriginx, _state);
        rmergemulv(state->nuser, &state->invsclx, &state->gregoriginx, _state);
        rcopyallocv(m, &state->gregoriginyuser, &state->gregoriginy, _state);
        rmergemulv(m, &state->ascales, &state->gregoriginy, _state);
        rmulv(m, (double)1/state->targetscale, &state->gregoriginy, _state);
    }
    
    /*
     * Initialize sparse system
     * Set up current primal and dual slacks
     */
    ipm2solver_reducedsystempowerup(&state->reducedsystem, state, &state->current.x, &state->current.y, _state);
    initprimslack = ae_maxreal(ipm2solver_initslackval, rmaxabsv(ntotal, &state->current.x, _state), _state);
    initdualslack = ae_maxreal(ipm2solver_initslackval, rmaxabsv(m, &state->current.y, _state), _state);
    rsetv(ntotal, 0.0, &state->current.g, _state);
    rsetv(ntotal, 0.0, &state->current.z, _state);
    rsetv(ntotal, 0.0, &state->current.t, _state);
    rsetv(ntotal, 0.0, &state->current.s, _state);
    for(i=0; i<=ntotal-1; i++)
    {
        if( state->hasgz.ptr.p_bool[i] )
        {
            state->current.g.ptr.p_double[i] = ae_maxreal(initprimslack, state->current.x.ptr.p_double[i]-state->bndle.ptr.p_double[i], _state);
            state->current.z.ptr.p_double[i] = ae_minreal(initdualslack, initprimslack*initdualslack/state->current.g.ptr.p_double[i], _state);
        }
        if( state->hasts.ptr.p_bool[i] )
        {
            state->current.t.ptr.p_double[i] = ae_maxreal(initprimslack, state->bndue.ptr.p_double[i]-state->current.x.ptr.p_double[i], _state);
            state->current.s.ptr.p_double[i] = ae_minreal(initdualslack, initprimslack*initdualslack/state->current.t.ptr.p_double[i], _state);
        }
    }
    
    /*
     * Almost done
     */
    if( state->dotrace )
    {
        ae_trace("> initial point was generated\n");
    }
}


/*************************************************************************
This function "powers up" reduced (NX+M)*(NX+M) system, i.e. prepares data
structures to receive data necessary for the factorization and solution.

It should be called once at the beginning of the optimization.

This function also proposes initial X and Y by solving strongly damped
augmented system. It expects X and Y to be preallocated arrays long enough
to store the result.

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_reducedsystempowerup(ipm2reducedsystem* s,
     ipm2state* state,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* y,
     ae_state *_state)
{
    ae_int_t nuser;
    ae_int_t naug;
    ae_int_t ntotal;
    ae_int_t m;
    ae_int_t offs;
    double v;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t k;
    ae_int_t colthreshold;
    ae_int_t rowthreshold;
    ae_int_t sumcoldeg;
    ae_int_t sumrowdeg;
    ae_int_t n0;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t n3;
    ae_int_t n4;
    ae_int_t groupcols;
    ae_int_t grouprows;
    ae_int_t groupequality;
    ae_int_t groupfree;
    ae_int_t densegroup;
    ae_int_t denseupdatesgroup;
    ae_int_t orderingtouse;


    nuser = state->nuser;
    naug = state->naug;
    ntotal = state->ntotal;
    m = state->mraw;
    
    /*
     * Allocate sparse matrix
     */
    sumcoldeg = 0;
    sumrowdeg = 0;
    isetallocv(naug, 0, &s->coldegrees, _state);
    isetallocv(m, 0, &s->rowdegrees, _state);
    rallocv(ntotal+m, &s->extendedrawdiagonal, _state);
    s->compactkkt.matrixtype = 1;
    s->compactkkt.m = ntotal;
    s->compactkkt.n = ntotal;
    iallocv(s->compactkkt.m+1, &s->compactkkt.ridx, _state);
    s->compactkkt.ridx.ptr.p_int[0] = 0;
    for(i=0; i<=naug-1; i++)
    {
        offs = s->compactkkt.ridx.ptr.p_int[i];
        igrowv(offs+naug+1, &s->compactkkt.idx, _state);
        rgrowv(offs+naug+1, &s->compactkkt.vals, _state);
        
        /*
         * Offdiagonal elements
         */
        j0 = state->sparsehe.ridx.ptr.p_int[i];
        j1 = state->sparsehe.didx.ptr.p_int[i]-1;
        for(j=j0; j<=j1; j++)
        {
            k = state->sparsehe.idx.ptr.p_int[j];
            s->compactkkt.idx.ptr.p_int[offs] = k;
            s->compactkkt.vals.ptr.p_double[offs] = -state->sparsehe.vals.ptr.p_double[j];
            s->coldegrees.ptr.p_int[i] = s->coldegrees.ptr.p_int[i]+1;
            s->coldegrees.ptr.p_int[k] = s->coldegrees.ptr.p_int[k]+1;
            sumcoldeg = sumcoldeg+2;
            offs = offs+1;
        }
        
        /*
         * Diagonal element
         */
        j1 = state->sparsehe.didx.ptr.p_int[i];
        s->extendedrawdiagonal.ptr.p_double[i] = -(state->sparsehe.vals.ptr.p_double[j1]+state->diagr.ptr.p_double[i]);
        s->compactkkt.idx.ptr.p_int[offs] = state->sparsehe.idx.ptr.p_int[j1];
        s->compactkkt.vals.ptr.p_double[offs] = s->extendedrawdiagonal.ptr.p_double[i];
        offs = offs+1;
        
        /*
         * Done
         */
        s->compactkkt.ridx.ptr.p_int[i+1] = offs;
    }
    for(i=naug; i<=ntotal-1; i++)
    {
        ae_assert(state->sparsehe.ridx.ptr.p_int[i]==state->sparsehe.didx.ptr.p_int[i], "IPM2: integrity check 0546 failed", _state);
        ae_assert(state->sparsehe.didx.ptr.p_int[i]==state->sparsehe.uidx.ptr.p_int[i]-1, "IPM2: integrity check 0547 failed", _state);
        ae_assert(ae_fp_eq(state->sparsehe.vals.ptr.p_double[state->sparsehe.didx.ptr.p_int[i]],0.0), "IPM2: integrity check 0548 failed", _state);
        s->extendedrawdiagonal.ptr.p_double[i] = -(state->sparsehe.vals.ptr.p_double[state->sparsehe.didx.ptr.p_int[i]]+state->diagr.ptr.p_double[i]);
    }
    for(i=0; i<=m-1; i++)
    {
        ae_assert(state->rawae.ridx.ptr.p_int[i+1]>state->rawae.ridx.ptr.p_int[i], "IPM2: integrity check 1251 failed", _state);
        ae_assert(state->rawae.idx.ptr.p_int[state->rawae.ridx.ptr.p_int[i+1]-1]==naug+i, "IPM2: integrity check 1252 failed", _state);
        ae_assert(ae_fp_eq(state->rawae.vals.ptr.p_double[state->rawae.ridx.ptr.p_int[i+1]-1],-1.0), "IPM2: integrity check 1253 failed", _state);
        offs = s->compactkkt.ridx.ptr.p_int[naug+i];
        igrowv(offs+naug+1, &s->compactkkt.idx, _state);
        rgrowv(offs+naug+1, &s->compactkkt.vals, _state);
        j0 = state->rawae.ridx.ptr.p_int[i];
        j1 = state->rawae.ridx.ptr.p_int[i+1]-2;
        for(j=j0; j<=j1; j++)
        {
            k = state->rawae.idx.ptr.p_int[j];
            s->compactkkt.idx.ptr.p_int[offs] = k;
            s->compactkkt.vals.ptr.p_double[offs] = state->rawae.vals.ptr.p_double[j];
            s->rowdegrees.ptr.p_int[i] = s->rowdegrees.ptr.p_int[i]+1;
            s->coldegrees.ptr.p_int[k] = s->coldegrees.ptr.p_int[k]+1;
            sumrowdeg = sumrowdeg+1;
            sumcoldeg = sumcoldeg+1;
            offs = offs+1;
        }
        s->compactkkt.idx.ptr.p_int[offs] = naug+i;
        s->compactkkt.vals.ptr.p_double[offs] = 0.0;
        offs = offs+1;
        s->extendedrawdiagonal.ptr.p_double[ntotal+i] = 0.0;
        s->compactkkt.ridx.ptr.p_int[naug+i+1] = offs;
    }
    sparsecreatecrsinplace(&s->compactkkt, _state);
    
    /*
     * Determine elimination priorities
     */
    if( state->hasuserpermutation )
    {
        
        /*
         * Use permutation proposed by user
         */
        isetallocv(ntotal, -1, &s->compactpriorities, _state);
        for(i=0; i<=state->nuser-1; i++)
        {
            s->compactpriorities.ptr.p_int[i] = state->userpermutation.ptr.p_int[i];
        }
        for(i=state->nuser; i<=state->naug-1; i++)
        {
            s->compactpriorities.ptr.p_int[i] = state->nuser+state->mraw+(i-state->nuser);
        }
        for(i=state->naug; i<=state->ntotal-1; i++)
        {
            s->compactpriorities.ptr.p_int[i] = state->userpermutation.ptr.p_int[state->nuser+(i-state->naug)];
        }
        orderingtouse = -4;
        densegroup = 0;
    }
    else
    {
        
        /*
         * Use AMD to determine elimination priorities
         */
        colthreshold = ae_round(ipm2solver_muquasidense*(double)sumcoldeg/(double)naug, _state)+1;
        rowthreshold = ae_round(ipm2solver_muquasidense*(double)sumrowdeg/coalesce((double)(m), (double)(1), _state), _state)+1;
        if( ae_fp_less_eq((double)sumcoldeg/(double)naug,(double)sumrowdeg/coalesce((double)(m), (double)(1), _state)) )
        {
            groupcols = 0;
            grouprows = 1;
        }
        else
        {
            groupcols = 1;
            grouprows = 0;
        }
        groupequality = 2;
        groupfree = 3;
        densegroup = ae_maxint(groupfree, groupequality, _state);
        denseupdatesgroup = densegroup+1;
        isetallocv(ntotal, 0, &s->compactpriorities, _state);
        for(i=0; i<=nuser-1; i++)
        {
            s->compactpriorities.ptr.p_int[i] = groupcols;
            if( !state->hasbndle.ptr.p_bool[i]&&!state->hasbndue.ptr.p_bool[i] )
            {
                s->compactpriorities.ptr.p_int[i] = groupfree;
            }
            if( s->coldegrees.ptr.p_int[i]>colthreshold )
            {
                s->compactpriorities.ptr.p_int[i] = densegroup;
            }
        }
        for(i=nuser; i<=naug-1; i++)
        {
            s->compactpriorities.ptr.p_int[i] = denseupdatesgroup;
        }
        for(i=0; i<=m-1; i++)
        {
            s->compactpriorities.ptr.p_int[naug+i] = grouprows;
            if( (state->hasbndle.ptr.p_bool[naug+i]&&state->hasbndue.ptr.p_bool[naug+i])&&ae_fp_less(state->bndue.ptr.p_double[naug+i]-state->bndle.ptr.p_double[naug+i],(double)1000*ae_maxreal(state->epsp, ae_sqrt(ae_machineepsilon, _state), _state)*rmax3(ae_fabs(state->bndue.ptr.p_double[naug+i], _state), ae_fabs(state->bndle.ptr.p_double[naug+i], _state), 1.0, _state)) )
            {
                s->compactpriorities.ptr.p_int[naug+i] = groupequality;
            }
            if( s->rowdegrees.ptr.p_int[i]>rowthreshold )
            {
                s->compactpriorities.ptr.p_int[naug+i] = densegroup;
            }
        }
        if( state->dotrace )
        {
            n0 = 0;
            n1 = 0;
            n2 = 0;
            n3 = 0;
            n4 = 0;
            for(i=0; i<=ntotal-1; i++)
            {
                if( s->compactpriorities.ptr.p_int[i]==0 )
                {
                    inc(&n0, _state);
                }
                if( s->compactpriorities.ptr.p_int[i]==1 )
                {
                    inc(&n1, _state);
                }
                if( s->compactpriorities.ptr.p_int[i]==2 )
                {
                    inc(&n2, _state);
                }
                if( s->compactpriorities.ptr.p_int[i]==3 )
                {
                    inc(&n3, _state);
                }
                if( s->compactpriorities.ptr.p_int[i]==4 )
                {
                    inc(&n4, _state);
                }
            }
            ae_trace("> KKT matrix formed, elimination groups determined:\n");
            if( groupcols==0 )
            {
                ae_trace(">> group 0 (variables): %7d columns\n",
                    (int)(n0));
                ae_trace(">> group 1 (equations): %7d columns\n",
                    (int)(n1));
            }
            else
            {
                ae_trace(">> group 0 (equations): %7d columns\n",
                    (int)(n0));
                ae_trace(">> group 1 (variables): %7d columns\n",
                    (int)(n1));
            }
            ae_trace(">> group 2 (equality ): %7d columns\n",
                (int)(n2));
            ae_trace(">> group 3 (free     ): %7d columns\n",
                (int)(n3));
            ae_trace(">> group 4 (dense    ): %7d columns\n",
                (int)(n4));
        }
        orderingtouse = 3;
    }
    
    /*
     * Analyze
     */
    stimerstartcond(&state->timeranalyze, state->dotimers, _state);
    if( !spsymmanalyze(&s->compactkkt, &s->compactpriorities, ipm2solver_mupromote, densegroup, ipm2solver_spsymmfacttype, orderingtouse, 1, &s->analysis, _state) )
    {
        ae_assert(ae_false, "ReducedSystemPowerUp: critical integrity check failed, symbolically degenerate KKT system encountered", _state);
    }
    stimerstopcond(&state->timeranalyze, state->dotimers, _state);
    
    /*
     * Prepare initial X and Y by solving strongly factorized system
     */
    ae_assert(x->cnt>=ntotal, "IPM2: integrity check 7400 failed", _state);
    ae_assert(y->cnt>=m, "IPM2: integrity check 7401 failed", _state);
    rallocv(ntotal+m, &s->extendedeffdiagonal, _state);
    for(i=0; i<=nuser-1; i++)
    {
        s->extendedeffdiagonal.ptr.p_double[i] = ae_minreal(s->extendedrawdiagonal.ptr.p_double[i], 0.0, _state)-1.0;
    }
    for(i=nuser; i<=naug-1; i++)
    {
        s->extendedeffdiagonal.ptr.p_double[i] = s->extendedrawdiagonal.ptr.p_double[i]+possign(s->extendedrawdiagonal.ptr.p_double[i], _state);
    }
    for(i=naug; i<=ntotal-1; i++)
    {
        s->extendedeffdiagonal.ptr.p_double[i] = ae_minreal(s->extendedrawdiagonal.ptr.p_double[i], 0.0, _state)-1.0;
    }
    for(i=ntotal; i<=ntotal+m-1; i++)
    {
        s->extendedeffdiagonal.ptr.p_double[i] = ae_maxreal(s->extendedrawdiagonal.ptr.p_double[i], 0.0, _state)+1.0;
    }
    rallocv(naug+m, &s->compacteffdiagonal, _state);
    for(i=0; i<=naug-1; i++)
    {
        s->compacteffdiagonal.ptr.p_double[i] = s->extendedeffdiagonal.ptr.p_double[i];
    }
    for(i=0; i<=m-1; i++)
    {
        s->compacteffdiagonal.ptr.p_double[naug+i] = -(double)1/s->extendedeffdiagonal.ptr.p_double[naug+i]+s->extendedeffdiagonal.ptr.p_double[ntotal+i];
    }
    stimerstartcond(&state->timerfactorize, state->dotimers, _state);
    spsymmreloaddiagonal(&s->analysis, &s->compacteffdiagonal, _state);
    if( !spsymmfactorize(&s->analysis, _state) )
    {
        ae_assert(ae_false, "ReducedSystemPowerUp: unexpected failure during decomposition of a strongly damped system", _state);
    }
    stimerstopcond(&state->timerfactorize, state->dotimers, _state);
    rallocv(ntotal+m, &s->extendedrhs, _state);
    rcopyv(ntotal, &state->ce, &s->extendedrhs, _state);
    for(i=0; i<=m-1; i++)
    {
        v = (double)(0);
        if( state->hasbndle.ptr.p_bool[naug+i]&&ae_fp_greater(state->bndle.ptr.p_double[naug+i],v) )
        {
            v = state->bndle.ptr.p_double[naug+i];
        }
        if( state->hasbndue.ptr.p_bool[naug+i]&&ae_fp_less(state->bndue.ptr.p_double[naug+i],v) )
        {
            v = state->bndue.ptr.p_double[naug+i];
        }
        s->extendedrhs.ptr.p_double[ntotal+i] = v;
    }
    rallocv(ntotal, &s->compactrhs, _state);
    for(i=0; i<=naug-1; i++)
    {
        s->compactrhs.ptr.p_double[i] = s->extendedrhs.ptr.p_double[i];
    }
    for(i=0; i<=m-1; i++)
    {
        s->compactrhs.ptr.p_double[naug+i] = s->extendedrhs.ptr.p_double[ntotal+i]+s->extendedrhs.ptr.p_double[naug+i]/s->extendedeffdiagonal.ptr.p_double[naug+i];
    }
    stimerstartcond(&state->timerspsymmsolve, state->dotimers, _state);
    spsymmsolve(&s->analysis, &s->compactrhs, _state);
    stimerstopcond(&state->timerspsymmsolve, state->dotimers, _state);
    for(i=0; i<=naug-1; i++)
    {
        s->extendedrhs.ptr.p_double[i] = s->compactrhs.ptr.p_double[i];
    }
    for(i=0; i<=m-1; i++)
    {
        s->extendedrhs.ptr.p_double[ntotal+i] = s->compactrhs.ptr.p_double[naug+i];
        s->extendedrhs.ptr.p_double[naug+i] = (s->extendedrhs.ptr.p_double[naug+i]+s->compactrhs.ptr.p_double[naug+i])/s->extendedeffdiagonal.ptr.p_double[naug+i];
    }
    rcopyvx(ntotal, &s->extendedrhs, 0, x, 0, _state);
    rcopyvx(m, &s->extendedrhs, ntotal, y, 0, _state);
}


/*************************************************************************
Generates precomputed temporary  vectors  and  KKT  factorization  at  the
beginning of the current iteration.

This function uses representation of QP problem as a box-constrained one
with linear equality constraints A*x=0.

On successful factorization returns True; on failure returns False - it is
recommended to increase regularization parameter and try one more time.

--- DESCRIPTION ----------------------------------------------------------


  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static ae_bool ipm2solver_reducedsystemcomputefactorization(ipm2reducedsystem* redsys,
     ipm2state* sstate,
     const ipm2vars* current,
     double dampepsmu,
     double dampepszs,
     double dampepsp,
     double dampepsd,
     ae_state *_state)
{
    ae_int_t naug;
    ae_int_t ntotal;
    ae_int_t m;
    ae_int_t i;
    double gz;
    double st;
    double sumsq;
    double errsq;
    double badchol;
    ae_bool result;


    naug = sstate->naug;
    ntotal = sstate->ntotal;
    m = sstate->mraw;
    result = ae_true;
    badchol = 1.0E50;
    
    /*
     * Pivot out dT and dS, dG and dZ
     */
    rallocv(ntotal, &redsys->d0, _state);
    rallocv(ntotal, &redsys->d1, _state);
    rallocv(ntotal, &redsys->d2, _state);
    rallocv(ntotal, &redsys->d3, _state);
    rallocv(ntotal, &redsys->d4, _state);
    rallocv(ntotal, &redsys->d5, _state);
    rallocv(ntotal, &redsys->d6, _state);
    rallocv(ntotal, &redsys->d7, _state);
    rallocv(ntotal+m, &redsys->extendedeffdiagonal, _state);
    rsetvx(ntotal, -dampepsp, &redsys->extendedeffdiagonal, 0, _state);
    rsetvx(m, dampepsd, &redsys->extendedeffdiagonal, ntotal, _state);
    if( ae_fp_greater(sstate->greg,(double)(0)) )
    {
        raddv(ntotal+m, 1.0, &sstate->gregdiag, &redsys->extendedeffdiagonal, _state);
    }
    raddv(ntotal+m, 1.0, &redsys->extendedrawdiagonal, &redsys->extendedeffdiagonal, _state);
    for(i=0; i<=ntotal-1; i++)
    {
        st = current->s.ptr.p_double[i]*current->t.ptr.p_double[i];
        redsys->d0.ptr.p_double[i] = (double)1/(dampepsmu+st);
        redsys->d1.ptr.p_double[i] = redsys->d0.ptr.p_double[i]*current->t.ptr.p_double[i];
        redsys->d3.ptr.p_double[i] = sstate->maskts.ptr.p_double[i]/(dampepszs+redsys->d1.ptr.p_double[i]*current->t.ptr.p_double[i]);
        gz = current->g.ptr.p_double[i]*current->z.ptr.p_double[i];
        redsys->d4.ptr.p_double[i] = (double)1/(dampepsmu+gz);
        redsys->d5.ptr.p_double[i] = redsys->d4.ptr.p_double[i]*current->g.ptr.p_double[i];
        redsys->d7.ptr.p_double[i] = sstate->maskgz.ptr.p_double[i]/(dampepszs+redsys->d5.ptr.p_double[i]*current->g.ptr.p_double[i]);
    }
    raddv(ntotal, -1.0, &redsys->d3, &redsys->extendedeffdiagonal, _state);
    raddv(ntotal, -1.0, &redsys->d7, &redsys->extendedeffdiagonal, _state);
    rallocv(ntotal, &redsys->compacteffdiagonal, _state);
    for(i=0; i<=naug-1; i++)
    {
        redsys->compacteffdiagonal.ptr.p_double[i] = redsys->extendedeffdiagonal.ptr.p_double[i];
    }
    for(i=0; i<=m-1; i++)
    {
        redsys->compacteffdiagonal.ptr.p_double[naug+i] = -(double)1/redsys->extendedeffdiagonal.ptr.p_double[naug+i]+redsys->extendedeffdiagonal.ptr.p_double[ntotal+i];
    }
    spsymmreloaddiagonal(&redsys->analysis, &redsys->compacteffdiagonal, _state);
    spsymmsetmodificationstrategy(&redsys->analysis, 1, ae_machineepsilon, badchol, 0.0, 0.0, _state);
    stimerstartcond(&sstate->timerfactorize, sstate->dotimers, _state);
    if( !spsymmfactorize(&redsys->analysis, _state) )
    {
        stimerstopcond(&sstate->timerfactorize, sstate->dotimers, _state);
        result = ae_false;
        return result;
    }
    stimerstopcond(&sstate->timerfactorize, sstate->dotimers, _state);
    spsymmdiagerr(&redsys->analysis, &sumsq, &errsq, _state);
    if( sstate->dotrace )
    {
        ae_trace("LDLT-diag-err= %0.3e (diagonal reproduction error)\n",
            (double)(ae_sqrt(errsq/((double)1+sumsq), _state)));
    }
    if( ae_fp_greater(ae_sqrt(errsq/((double)1+sumsq), _state),ae_sqrt(ae_machineepsilon, _state)) )
    {
        result = ae_false;
        return result;
    }
    return result;
}


/*************************************************************************
This function estimates primal and dual step lengths (subject to step
decay parameter, which should be in [0,1] range).

Current version returns same step lengths for primal and dual steps.

INPUT PARAMETERS:
    State               -   solver state
    V0                  -   current point (we ignore one stored in State.Current)
    VS                  -   step direction
    StepDecay           -   decay parameter, the step is multiplied by this
                            coefficient. 1.0 corresponds to full step
                            length being returned. Values in (0,1] range.
    SeparateStep        -   separate step for primal and dual vars
    
OUTPUT PARAMETERS:
    AlphaP              -   primal step (after applying decay coefficient)
    AlphaD              -   dual   step (after applying decay coefficient)

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_ipm2computesteplength(const ipm2state* state,
     const ipm2vars* current,
     const ipm2vars* delta,
     double* alpha,
     ae_state *_state)
{
    ae_int_t ntotal;
    ae_int_t i;

    *alpha = 0.0;

    ntotal = state->ntotal;
    *alpha = (double)(1);
    for(i=0; i<=ntotal-1; i++)
    {
        
        /*
         * Primal
         */
        if( delta->g.ptr.p_double[i]<0.0 )
        {
            *alpha = safeminposrv(current->g.ptr.p_double[i], -delta->g.ptr.p_double[i], *alpha, _state);
        }
        if( delta->t.ptr.p_double[i]<0.0 )
        {
            *alpha = safeminposrv(current->t.ptr.p_double[i], -delta->t.ptr.p_double[i], *alpha, _state);
        }
        
        /*
         * Dual
         */
        if( delta->z.ptr.p_double[i]<0.0 )
        {
            *alpha = safeminposrv(current->z.ptr.p_double[i], -delta->z.ptr.p_double[i], *alpha, _state);
        }
        if( delta->s.ptr.p_double[i]<0.0 )
        {
            *alpha = safeminposrv(current->s.ptr.p_double[i], -delta->s.ptr.p_double[i], *alpha, _state);
        }
    }
    *alpha = ipm2solver_steplengthdecay*(*alpha);
}


/*************************************************************************
Compute primal/dual errors and complementarity gap

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_computeerrors(const ipm2state* state,
     const ipm2righthandside* rhs,
     /* Real    */ const ae_vector* currenthx,
     /* Real    */ const ae_vector* currentax,
     /* Real    */ const ae_vector* currentaty,
     double* errp2,
     double* errd2,
     double* errpinf,
     double* errdinf,
     double* egap,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t ntotal;
    ae_int_t cntp2;
    ae_int_t cntd2;
    double vtgt;

    *errp2 = 0.0;
    *errd2 = 0.0;
    *errpinf = 0.0;
    *errdinf = 0.0;
    *egap = 0.0;

    m = state->mraw;
    ntotal = state->ntotal;
    
    /*
     * Compute primal error
     */
    cntp2 = 0;
    *errp2 = (double)(0);
    *errpinf = (double)(0);
    *errp2 = *errp2+rdotv2(m, &rhs->ea, _state);
    *errpinf = ae_maxreal(*errpinf, rmaxabsv(m, &rhs->ea, _state), _state);
    cntp2 = cntp2+m;
    *errp2 = *errp2+rdotv2(ntotal, &rhs->el, _state);
    *errpinf = ae_maxreal(*errpinf, rmaxabsv(ntotal, &rhs->el, _state), _state);
    cntp2 = cntp2+state->cntgz;
    *errp2 = *errp2+rdotv2(ntotal, &rhs->eu, _state);
    *errpinf = ae_maxreal(*errpinf, rmaxabsv(ntotal, &rhs->eu, _state), _state);
    cntp2 = cntp2+state->cntts;
    *errp2 = ae_sqrt(*errp2/coalesce((double)(cntp2), (double)(1), _state), _state);
    
    /*
     * Compute dual error
     */
    cntd2 = 0;
    *errd2 = (double)(0);
    *errdinf = (double)(0);
    *errd2 = *errd2+rdotv2(ntotal, &rhs->ed, _state);
    *errdinf = ae_maxreal(*errdinf, rmaxabsv(ntotal, &rhs->ed, _state), _state);
    cntd2 = cntd2+m;
    *errd2 = ae_sqrt(*errd2/coalesce((double)(cntd2), (double)(1), _state), _state);
    
    /*
     * Compute complementarity gap
     */
    vtgt = 0.5*rdotv(ntotal, currenthx, &state->current.x, _state)+rdotv(ntotal, &state->ce, &state->current.x, _state);
    *egap = ipm2solver_varscomputecomplementaritygap(&state->current, _state)/(1.0+ae_fabs(vtgt, _state));
}


/*************************************************************************
Evaluate progress so far, outputs trace data, if requested to do so.

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_traceprogress(ipm2state* state,
     double muinit,
     double alphaaff,
     double alphafin,
     ae_state *_state)
{
    ae_int_t naug;
    ae_int_t mraw;
    ae_int_t ntotal;
    double errp2;
    double errd2;
    double errpinf;
    double errdinf;
    double errgap;


    naug = state->naug;
    mraw = state->mraw;
    ntotal = state->ntotal;
    if( !state->dotrace )
    {
        return;
    }
    
    /*
     * Compute temporary products, RHS and errors
     */
    ipm2solver_ipm2multiply(state, &state->current.x, &state->current.y, &state->tmphx, &state->tmpax, &state->tmpaty, _state);
    ipm2solver_rhscomputeprimdual(state, &state->current, &state->tmphx, &state->tmpax, &state->tmpaty, &state->rhsprogress, _state);
    ipm2solver_computeerrors(state, &state->rhsprogress, &state->tmphx, &state->tmpax, &state->tmpaty, &errp2, &errd2, &errpinf, &errdinf, &errgap, _state);
    
    /*
     * Print high-level information
     */
    ae_trace("--- step report ------------------------------------------------------------------------------------\n");
    ae_trace("> step information\n");
    ae_trace("mu_init = %0.3e    (at the beginning)\n",
        (double)(muinit));
    if( ae_fp_neq(alphafin,(double)(0)) )
    {
        ae_trace("alphaA  = %0.3e (initial affine scaling step)\n",
            (double)(alphaaff));
        ae_trace("alphaR  = %0.3e (refined step)\n",
            (double)(alphafin));
    }
    else
    {
        ae_trace("alpha   = %0.3e\n",
            (double)(alphaaff));
    }
    ae_trace("mu_cur  = %0.3e    (after the step)\n",
        (double)(ipm2solver_varscomputemu(state, &state->current, _state)));
    ae_trace("> errors\n");
    ae_trace("errP    = %0.3e    (primal infeasibility, inf-norm)\n",
        (double)(errpinf));
    ae_trace("errD    = %0.3e    (dual infeasibility,   inf-norm)\n",
        (double)(errdinf));
    ae_trace("errGap  = %0.3e    (complementarity gap)\n",
        (double)(errgap));
    ae_trace("> current point information (inf-norm, x-suffix stands for N raw vars, a-suffix stands for M slacks)\n");
    ae_trace("|Xx|=%8.1e,  |Xa|=%8.1e,  |Gx|=%8.1e,  |Tx|=%8.1e,  |Ga|=%8.1e,  |Ta|=%8.1e\n",
        (double)(ipm2solver_maxabsrange(&state->current.x, 0, naug, _state)),
        (double)(ipm2solver_maxabsrange(&state->current.x, naug, ntotal, _state)),
        (double)(ipm2solver_maxabsrange(&state->current.g, 0, naug, _state)),
        (double)(ipm2solver_maxabsrange(&state->current.t, 0, naug, _state)),
        (double)(ipm2solver_maxabsrange(&state->current.g, naug, ntotal, _state)),
        (double)(ipm2solver_maxabsrange(&state->current.t, naug, ntotal, _state)));
    ae_trace("                 |Y|=%8.1e,  |Zx|=%8.1e,  |Sx|=%8.1e,  |Za|=%8.1e,  |Sa|=%8.1e\n",
        (double)(ipm2solver_maxabsrange(&state->current.y, 0, mraw, _state)),
        (double)(ipm2solver_maxabsrange(&state->current.z, 0, naug, _state)),
        (double)(ipm2solver_maxabsrange(&state->current.s, 0, naug, _state)),
        (double)(ipm2solver_maxabsrange(&state->current.z, naug, ntotal, _state)),
        (double)(ipm2solver_maxabsrange(&state->current.s, naug, ntotal, _state)));
    
    /*
     * Print variable stats, if required
     */
    if( state->dotrace )
    {
        ae_trace("--- variable statistics ----------------------------------------------------------------------------\n");
        ae_trace("> smallest values for nonnegative vars\n");
        ae_trace("primal:                        minGx=%8.1e  minTx=%8.1e  minGa=%8.1e  minTa=%8.1e\n",
            (double)(ipm2solver_minnz(&state->current.g, 0, naug, _state)),
            (double)(ipm2solver_minnz(&state->current.t, 0, naug, _state)),
            (double)(ipm2solver_minnz(&state->current.g, naug, ntotal, _state)),
            (double)(ipm2solver_minnz(&state->current.t, naug, ntotal, _state)));
        ae_trace("dual:                          minZx=%8.1e  minSx=%8.1e  minGa=%8.1e  minSa=%8.1e\n",
            (double)(ipm2solver_minnz(&state->current.z, 0, naug, _state)),
            (double)(ipm2solver_minnz(&state->current.s, 0, naug, _state)),
            (double)(ipm2solver_minnz(&state->current.z, naug, ntotal, _state)),
            (double)(ipm2solver_minnz(&state->current.s, naug, ntotal, _state)));
        ae_trace("> min and max complementary slackness\n");
        ae_trace("min:                             GZx=%8.1e    TSx=%8.1e    GZa=%8.1e    TSa=%8.1e\n",
            (double)(ipm2solver_minprodnz(&state->current.g, &state->current.z, 0, naug, _state)),
            (double)(ipm2solver_minprodnz(&state->current.t, &state->current.s, 0, naug, _state)),
            (double)(ipm2solver_minprodnz(&state->current.g, &state->current.z, naug, ntotal, _state)),
            (double)(ipm2solver_minprodnz(&state->current.t, &state->current.s, naug, ntotal, _state)));
        ae_trace("max:                             GZx=%8.1e    TSx=%8.1e    GZa=%8.1e    TSa=%8.1e\n",
            (double)(ipm2solver_maxprodnz(&state->current.g, &state->current.z, 0, naug, _state)),
            (double)(ipm2solver_maxprodnz(&state->current.t, &state->current.s, 0, naug, _state)),
            (double)(ipm2solver_maxprodnz(&state->current.g, &state->current.z, naug, ntotal, _state)),
            (double)(ipm2solver_maxprodnz(&state->current.t, &state->current.s, naug, ntotal, _state)));
    }
    ae_trace("\n");
}


/*************************************************************************
Computes primal-dual right-hand side for a KKT system.

GammaGZ and GammaTS are not computed by this function.

INPUT PARAMETERS:
    State           -   IPM state
    Current         -   current point (used to compute RHS)
    CurrentHX       -   precomputed H*Current.X
    CurrentAX       -   precomputed A*Current.X
    CurrentATY      -   precomputed A'*Current.Y
    
OUTPUT PARAMETERS:
    Rhs             -   RHS

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_rhscomputeprimdual(const ipm2state* state,
     const ipm2vars* current,
     /* Real    */ const ae_vector* currenthx,
     /* Real    */ const ae_vector* currentax,
     /* Real    */ const ae_vector* currentaty,
     ipm2righthandside* rhs,
     ae_state *_state)
{
    ae_int_t ntotal;
    ae_int_t m;


    ntotal = state->ntotal;
    m = state->mraw;
    
    /*
     * ED
     */
    rallocv(ntotal, &rhs->ed, _state);
    if( ae_fp_greater(state->greg,(double)(0)) )
    {
        rcopyv(ntotal, &current->x, &rhs->ed, _state);
        raddv(ntotal, -1.0, &state->gregoriginx, &rhs->ed, _state);
        rmergemulv(ntotal, &state->gregrhsx, &rhs->ed, _state);
        raddv(ntotal, 1.0, &state->ce, &rhs->ed, _state);
    }
    else
    {
        rcopyv(ntotal, &state->ce, &rhs->ed, _state);
    }
    raddv(ntotal, -1.0, currentaty, &rhs->ed, _state);
    raddv(ntotal, 1.0, currenthx, &rhs->ed, _state);
    raddv(ntotal, -1.0, &current->z, &rhs->ed, _state);
    raddv(ntotal, 1.0, &current->s, &rhs->ed, _state);
    
    /*
     * EA
     */
    rallocv(m, &rhs->ea, _state);
    if( ae_fp_greater(state->greg,(double)(0)) )
    {
        rcopyv(m, &current->y, &rhs->ea, _state);
        raddv(m, -1.0, &state->gregoriginy, &rhs->ea, _state);
        rmergemulv(m, &state->gregrhsy, &rhs->ea, _state);
        raddv(m, -1.0, currentax, &rhs->ea, _state);
    }
    else
    {
        rcopymulv(m, -1.0, currentax, &rhs->ea, _state);
    }
    
    /*
     * EL
     */
    rcopyallocv(ntotal, &state->bndlef, &rhs->el, _state);
    raddv(ntotal, -1.0, &current->x, &rhs->el, _state);
    raddv(ntotal, 1.0, &current->g, &rhs->el, _state);
    rmergemulv(ntotal, &state->maskgz, &rhs->el, _state);
    
    /*
     * EU
     */
    rcopyallocv(ntotal, &current->x, &rhs->eu, _state);
    raddv(ntotal, 1.0, &current->t, &rhs->eu, _state);
    raddv(ntotal, -1.0, &state->bnduef, &rhs->eu, _state);
    rmergemulv(ntotal, &state->maskts, &rhs->eu, _state);
}


/*************************************************************************
Recomputes GammaGZ/TS according to a predictor (affine scaling) step

INPUT PARAMETERS:
    State           -   IPM state
    Current         -   current point (used to compute RHS)
    MuTarget        -   target Mu (can be zero),
                        used to compute GammaGZ and GammaST
    
OUTPUT PARAMETERS:
    Rhs             -   RHS

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_rhsrecomputegammapredictor(const ipm2state* state,
     const ipm2vars* current,
     double mutarget,
     ipm2righthandside* rhs,
     ae_state *_state)
{
    ae_int_t ntotal;


    ntotal = state->ntotal;
    
    /*
     * GammaGZ
     */
    rallocv(ntotal, &rhs->gammagz, _state);
    rcopymulv(ntotal, mutarget, &state->maskgz, &rhs->gammagz, _state);
    rnegmuladdv(ntotal, &current->g, &current->z, &rhs->gammagz, _state);
    
    /*
     * GammaTS
     */
    rallocv(ntotal, &rhs->gammats, _state);
    rcopymulv(ntotal, mutarget, &state->maskts, &rhs->gammats, _state);
    rnegmuladdv(ntotal, &current->t, &current->s, &rhs->gammats, _state);
}


/*************************************************************************
Recomputes GammaGZ/GammaTS part of RHS according to the Mehrotra corrector
heuristic. The rest of the right-hand side is unchanged.

INPUT PARAMETERS:
    State           -   IPM state
    Current         -   current point (used to compute RHS)
    MuTarget        -   target Mu, used to compute GammaGZ and GammaST
    Delta           -   affine-scaling step produced by predictor stage
                        of the Mehrotra algorithm
    
OUTPUT PARAMETERS:
    Rhs             -   RHS.GammaGZ, Rhs.GammaTS are recomputed

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static void ipm2solver_rhsrecomputegammamehrotra(const ipm2state* state,
     const ipm2vars* current,
     double mutarget,
     const ipm2vars* deltaaff,
     ipm2righthandside* rhs,
     ae_state *_state)
{
    ae_int_t ntotal;


    ntotal = state->ntotal;
    
    /*
     * GammaGZ
     */
    rallocv(ntotal, &rhs->gammagz, _state);
    rcopymulv(ntotal, mutarget, &state->maskgz, &rhs->gammagz, _state);
    rnegmuladdv(ntotal, &current->g, &current->z, &rhs->gammagz, _state);
    rnegmuladdv(ntotal, &deltaaff->g, &deltaaff->z, &rhs->gammagz, _state);
    
    /*
     * GammaTS
     */
    rallocv(ntotal, &rhs->gammats, _state);
    rcopymulv(ntotal, mutarget, &state->maskts, &rhs->gammats, _state);
    rnegmuladdv(ntotal, &current->t, &current->s, &rhs->gammats, _state);
    rnegmuladdv(ntotal, &deltaaff->t, &deltaaff->s, &rhs->gammats, _state);
}


/*************************************************************************
Solves KKT system with given right-hand side

INPUT PARAMETERS:
    State           -   IPM state
    Current         -   current point (beginning of the iteration)
    RHS             -   right-hand side
    
OUTPUT PARAMETERS:
    Delta           -   solution

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
static ae_bool ipm2solver_reducedsystemsolve(ipm2reducedsystem* redsys,
     ipm2state* sstate,
     const ipm2vars* current,
     const ipm2righthandside* rhs,
     ipm2vars* delta,
     ae_state *_state)
{
    ae_int_t naug;
    ae_int_t ntotal;
    ae_int_t m;
    ae_bool result;


    stimerstartcond(&sstate->timerothersolve, sstate->dotimers, _state);
    naug = sstate->naug;
    ntotal = sstate->ntotal;
    m = sstate->mraw;
    rallocv(ntotal+m, &redsys->extendedrhs, _state);
    rcopymuladdv(ntotal, &redsys->d1, &rhs->gammats, &rhs->eu, &redsys->d2, _state);
    rcopymuladdv(ntotal, &redsys->d5, &rhs->gammagz, &rhs->el, &redsys->d6, _state);
    rcopymuladdv(ntotal, &redsys->d3, &redsys->d2, &rhs->ed, &redsys->extendedrhs, _state);
    rnegmuladdv(ntotal, &redsys->d7, &redsys->d6, &redsys->extendedrhs, _state);
    rcopyvx(m, &rhs->ea, 0, &redsys->extendedrhs, ntotal, _state);
    
    /*
     * Reduce (NTotal+M)*(NTotal+M) extended system to a (N+M)*(N+M) compact one
     */
    rallocv(m, &redsys->vm0, _state);
    rsetallocv(m, 1.0, &redsys->um0, _state);
    rcopyvx(m, &redsys->extendedeffdiagonal, naug, &redsys->vm0, 0, _state);
    rmergedivv(m, &redsys->vm0, &redsys->um0, _state);
    rcopyvx(m, &redsys->extendedrhs, naug, &redsys->vm0, 0, _state);
    rallocv(m, &redsys->vm1, _state);
    rcopymuladdv(m, &redsys->um0, &redsys->vm0, &rhs->ea, &redsys->vm1, _state);
    rcopyvx(naug, &redsys->extendedrhs, 0, &redsys->compactrhs, 0, _state);
    rcopyvx(m, &redsys->vm1, 0, &redsys->compactrhs, naug, _state);
    stimerstartcond(&sstate->timerspsymmsolve, sstate->dotimers, _state);
    spsymmsolve(&redsys->analysis, &redsys->compactrhs, _state);
    stimerstopcond(&sstate->timerspsymmsolve, sstate->dotimers, _state);
    
    /*
     * Perform backsubstitution
     */
    raddvx(m, 1.0, &redsys->compactrhs, naug, &redsys->vm0, 0, _state);
    rmergemulv(m, &redsys->um0, &redsys->vm0, _state);
    rcopyvx(naug, &redsys->compactrhs, 0, &delta->x, 0, _state);
    rcopyvx(m, &redsys->vm0, 0, &delta->x, naug, _state);
    rcopyv(ntotal, &redsys->d2, &delta->s, _state);
    raddv(ntotal, 1.0, &delta->x, &delta->s, _state);
    rmergemulv(ntotal, &redsys->d3, &delta->s, _state);
    rcopyv(ntotal, &redsys->d6, &delta->z, _state);
    raddv(ntotal, -1.0, &delta->x, &delta->z, _state);
    rmergemulv(ntotal, &redsys->d7, &delta->z, _state);
    rcopynegmuladdv(ntotal, &current->t, &delta->s, &rhs->gammats, &delta->t, _state);
    rcopynegmuladdv(ntotal, &current->g, &delta->z, &rhs->gammagz, &delta->g, _state);
    rmergemulv(ntotal, &redsys->d1, &delta->t, _state);
    rmergemulv(ntotal, &redsys->d5, &delta->g, _state);
    rcopyvx(m, &redsys->compactrhs, naug, &delta->y, 0, _state);
    stimerstopcond(&sstate->timerothersolve, sstate->dotimers, _state);
    result = ae_true;
    return result;
}


/*************************************************************************
Computes minimum nonzero value of the vector. Returns 0 if all components
are nonpositive.

INPUT PARAMETERS:
    X               -   vector
    N               -   length

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double ipm2solver_minnz(/* Real    */ const ae_vector* x,
     ae_int_t n0,
     ae_int_t n1,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool nz;
    double result;


    result = (double)(0);
    nz = ae_false;
    for(i=n0; i<=n1-1; i++)
    {
        if( ae_fp_greater(x->ptr.p_double[i],(double)(0)) )
        {
            if( !nz )
            {
                result = x->ptr.p_double[i];
                nz = ae_true;
            }
            else
            {
                result = ae_minreal(result, x->ptr.p_double[i], _state);
            }
        }
    }
    return result;
}


/*************************************************************************
Computes minimum product of nonzero components.
Returns 0 if all components are nonpositive.

INPUT PARAMETERS:
    X               -   vector
    Y               -   vector
    N               -   length

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double ipm2solver_minprodnz(/* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     ae_int_t n0,
     ae_int_t n1,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool nz;
    double result;


    result = (double)(0);
    nz = ae_false;
    for(i=n0; i<=n1-1; i++)
    {
        if( ae_fp_greater(x->ptr.p_double[i],(double)(0))&&ae_fp_greater(y->ptr.p_double[i],(double)(0)) )
        {
            if( !nz )
            {
                result = x->ptr.p_double[i]*y->ptr.p_double[i];
                nz = ae_true;
            }
            else
            {
                result = ae_minreal(result, x->ptr.p_double[i]*y->ptr.p_double[i], _state);
            }
        }
    }
    return result;
}


/*************************************************************************
Computes maximum product of nonzero components.
Returns 0 if all components are nonpositive.

INPUT PARAMETERS:
    X               -   vector
    Y               -   vector
    N               -   length

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double ipm2solver_maxprodnz(/* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     ae_int_t n0,
     ae_int_t n1,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool nz;
    double result;


    result = (double)(0);
    nz = ae_false;
    for(i=n0; i<=n1-1; i++)
    {
        if( ae_fp_greater(x->ptr.p_double[i],(double)(0))&&ae_fp_greater(y->ptr.p_double[i],(double)(0)) )
        {
            if( !nz )
            {
                result = x->ptr.p_double[i]*y->ptr.p_double[i];
                nz = ae_true;
            }
            else
            {
                result = ae_maxreal(result, x->ptr.p_double[i]*y->ptr.p_double[i], _state);
            }
        }
    }
    return result;
}


/*************************************************************************
Computes maximum absolute value within a range of elements.
Debug function, not optimized.

INPUT PARAMETERS:
    X               -   vector
    R0,R1           -   range [R0,R1)

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double ipm2solver_maxabsrange(/* Real    */ const ae_vector* x,
     ae_int_t r0,
     ae_int_t r1,
     ae_state *_state)
{
    ae_int_t i;
    double result;


    result = (double)(0);
    for(i=r0; i<=r1-1; i++)
    {
        result = ae_maxreal(result, ae_fabs(x->ptr.p_double[i], _state), _state);
    }
    return result;
}


void _ipm2vars_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    ipm2vars *p = (ipm2vars*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->t, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->y, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->z, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
}


void _ipm2vars_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    ipm2vars       *dst = (ipm2vars*)_dst;
    const ipm2vars *src = (const ipm2vars*)_src;
    dst->ntotal = src->ntotal;
    dst->m = src->m;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    ae_vector_init_copy(&dst->t, &src->t, _state, make_automatic);
    ae_vector_init_copy(&dst->y, &src->y, _state, make_automatic);
    ae_vector_init_copy(&dst->z, &src->z, _state, make_automatic);
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
}


void _ipm2vars_clear(void* _p)
{
    ipm2vars *p = (ipm2vars*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->g);
    ae_vector_clear(&p->t);
    ae_vector_clear(&p->y);
    ae_vector_clear(&p->z);
    ae_vector_clear(&p->s);
}


void _ipm2vars_destroy(void* _p)
{
    ipm2vars *p = (ipm2vars*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->g);
    ae_vector_destroy(&p->t);
    ae_vector_destroy(&p->y);
    ae_vector_destroy(&p->z);
    ae_vector_destroy(&p->s);
}


void _ipm2reducedsystem_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    ipm2reducedsystem *p = (ipm2reducedsystem*)_p;
    ae_touch_ptr((void*)p);
    _sparsematrix_init(&p->compactkkt, _state, make_automatic);
    ae_vector_init(&p->compactpriorities, 0, DT_INT, _state, make_automatic);
    _spcholanalysis_init(&p->analysis, _state, make_automatic);
    ae_vector_init(&p->extendedrawdiagonal, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->extendedeffdiagonal, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->compacteffdiagonal, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->extendedrhs, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->compactrhs, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->u0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->u1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->v0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->v1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->vm0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->vm1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->um0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d3, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d4, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d5, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d6, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d7, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rowdegrees, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->coldegrees, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->tmpregrhs, 0, DT_REAL, _state, make_automatic);
}


void _ipm2reducedsystem_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    ipm2reducedsystem       *dst = (ipm2reducedsystem*)_dst;
    const ipm2reducedsystem *src = (const ipm2reducedsystem*)_src;
    _sparsematrix_init_copy(&dst->compactkkt, &src->compactkkt, _state, make_automatic);
    ae_vector_init_copy(&dst->compactpriorities, &src->compactpriorities, _state, make_automatic);
    _spcholanalysis_init_copy(&dst->analysis, &src->analysis, _state, make_automatic);
    ae_vector_init_copy(&dst->extendedrawdiagonal, &src->extendedrawdiagonal, _state, make_automatic);
    ae_vector_init_copy(&dst->extendedeffdiagonal, &src->extendedeffdiagonal, _state, make_automatic);
    ae_vector_init_copy(&dst->compacteffdiagonal, &src->compacteffdiagonal, _state, make_automatic);
    ae_vector_init_copy(&dst->extendedrhs, &src->extendedrhs, _state, make_automatic);
    ae_vector_init_copy(&dst->compactrhs, &src->compactrhs, _state, make_automatic);
    ae_vector_init_copy(&dst->u0, &src->u0, _state, make_automatic);
    ae_vector_init_copy(&dst->u1, &src->u1, _state, make_automatic);
    ae_vector_init_copy(&dst->v0, &src->v0, _state, make_automatic);
    ae_vector_init_copy(&dst->v1, &src->v1, _state, make_automatic);
    ae_vector_init_copy(&dst->vm0, &src->vm0, _state, make_automatic);
    ae_vector_init_copy(&dst->vm1, &src->vm1, _state, make_automatic);
    ae_vector_init_copy(&dst->um0, &src->um0, _state, make_automatic);
    ae_vector_init_copy(&dst->d0, &src->d0, _state, make_automatic);
    ae_vector_init_copy(&dst->d1, &src->d1, _state, make_automatic);
    ae_vector_init_copy(&dst->d2, &src->d2, _state, make_automatic);
    ae_vector_init_copy(&dst->d3, &src->d3, _state, make_automatic);
    ae_vector_init_copy(&dst->d4, &src->d4, _state, make_automatic);
    ae_vector_init_copy(&dst->d5, &src->d5, _state, make_automatic);
    ae_vector_init_copy(&dst->d6, &src->d6, _state, make_automatic);
    ae_vector_init_copy(&dst->d7, &src->d7, _state, make_automatic);
    ae_vector_init_copy(&dst->rowdegrees, &src->rowdegrees, _state, make_automatic);
    ae_vector_init_copy(&dst->coldegrees, &src->coldegrees, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpregrhs, &src->tmpregrhs, _state, make_automatic);
}


void _ipm2reducedsystem_clear(void* _p)
{
    ipm2reducedsystem *p = (ipm2reducedsystem*)_p;
    ae_touch_ptr((void*)p);
    _sparsematrix_clear(&p->compactkkt);
    ae_vector_clear(&p->compactpriorities);
    _spcholanalysis_clear(&p->analysis);
    ae_vector_clear(&p->extendedrawdiagonal);
    ae_vector_clear(&p->extendedeffdiagonal);
    ae_vector_clear(&p->compacteffdiagonal);
    ae_vector_clear(&p->extendedrhs);
    ae_vector_clear(&p->compactrhs);
    ae_vector_clear(&p->u0);
    ae_vector_clear(&p->u1);
    ae_vector_clear(&p->v0);
    ae_vector_clear(&p->v1);
    ae_vector_clear(&p->vm0);
    ae_vector_clear(&p->vm1);
    ae_vector_clear(&p->um0);
    ae_vector_clear(&p->d0);
    ae_vector_clear(&p->d1);
    ae_vector_clear(&p->d2);
    ae_vector_clear(&p->d3);
    ae_vector_clear(&p->d4);
    ae_vector_clear(&p->d5);
    ae_vector_clear(&p->d6);
    ae_vector_clear(&p->d7);
    ae_vector_clear(&p->rowdegrees);
    ae_vector_clear(&p->coldegrees);
    ae_vector_clear(&p->tmpregrhs);
}


void _ipm2reducedsystem_destroy(void* _p)
{
    ipm2reducedsystem *p = (ipm2reducedsystem*)_p;
    ae_touch_ptr((void*)p);
    _sparsematrix_destroy(&p->compactkkt);
    ae_vector_destroy(&p->compactpriorities);
    _spcholanalysis_destroy(&p->analysis);
    ae_vector_destroy(&p->extendedrawdiagonal);
    ae_vector_destroy(&p->extendedeffdiagonal);
    ae_vector_destroy(&p->compacteffdiagonal);
    ae_vector_destroy(&p->extendedrhs);
    ae_vector_destroy(&p->compactrhs);
    ae_vector_destroy(&p->u0);
    ae_vector_destroy(&p->u1);
    ae_vector_destroy(&p->v0);
    ae_vector_destroy(&p->v1);
    ae_vector_destroy(&p->vm0);
    ae_vector_destroy(&p->vm1);
    ae_vector_destroy(&p->um0);
    ae_vector_destroy(&p->d0);
    ae_vector_destroy(&p->d1);
    ae_vector_destroy(&p->d2);
    ae_vector_destroy(&p->d3);
    ae_vector_destroy(&p->d4);
    ae_vector_destroy(&p->d5);
    ae_vector_destroy(&p->d6);
    ae_vector_destroy(&p->d7);
    ae_vector_destroy(&p->rowdegrees);
    ae_vector_destroy(&p->coldegrees);
    ae_vector_destroy(&p->tmpregrhs);
}


void _ipm2righthandside_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    ipm2righthandside *p = (ipm2righthandside*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->gammagz, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gammats, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ed, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ea, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->el, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->eu, 0, DT_REAL, _state, make_automatic);
}


void _ipm2righthandside_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    ipm2righthandside       *dst = (ipm2righthandside*)_dst;
    const ipm2righthandside *src = (const ipm2righthandside*)_src;
    ae_vector_init_copy(&dst->gammagz, &src->gammagz, _state, make_automatic);
    ae_vector_init_copy(&dst->gammats, &src->gammats, _state, make_automatic);
    ae_vector_init_copy(&dst->ed, &src->ed, _state, make_automatic);
    ae_vector_init_copy(&dst->ea, &src->ea, _state, make_automatic);
    ae_vector_init_copy(&dst->el, &src->el, _state, make_automatic);
    ae_vector_init_copy(&dst->eu, &src->eu, _state, make_automatic);
}


void _ipm2righthandside_clear(void* _p)
{
    ipm2righthandside *p = (ipm2righthandside*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->gammagz);
    ae_vector_clear(&p->gammats);
    ae_vector_clear(&p->ed);
    ae_vector_clear(&p->ea);
    ae_vector_clear(&p->el);
    ae_vector_clear(&p->eu);
}


void _ipm2righthandside_destroy(void* _p)
{
    ipm2righthandside *p = (ipm2righthandside*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->gammagz);
    ae_vector_destroy(&p->gammats);
    ae_vector_destroy(&p->ed);
    ae_vector_destroy(&p->ea);
    ae_vector_destroy(&p->el);
    ae_vector_destroy(&p->eu);
}


void _ipm2state_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    ipm2state *p = (ipm2state*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->userpermutation, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->sclx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->invsclx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xoriginx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ce, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparsehe, _state, make_automatic);
    ae_vector_init(&p->diagr, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gregoriginxuser, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gregoriginyuser, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gregdiag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gregrhsx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gregrhsy, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gregoriginx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gregoriginy, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndle, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndue, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndlef, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bnduef, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndle, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndue, 0, DT_BOOL, _state, make_automatic);
    _sparsematrix_init(&p->rawae, _state, make_automatic);
    ae_vector_init(&p->ascales, 0, DT_REAL, _state, make_automatic);
    _ipm2vars_init(&p->current, _state, make_automatic);
    _ipm2vars_init(&p->best, _state, make_automatic);
    _ipm2vars_init(&p->delta, _state, make_automatic);
    _ipm2vars_init(&p->corr, _state, make_automatic);
    ae_vector_init(&p->hasgz, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasts, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->maskgz, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->maskts, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->currenthx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->currentax, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->currentaty, 0, DT_REAL, _state, make_automatic);
    _ipm2righthandside_init(&p->rhstarget, _state, make_automatic);
    _stimer_init(&p->timertotal, _state, make_automatic);
    _stimer_init(&p->timeranalyze, _state, make_automatic);
    _stimer_init(&p->timerfactorize, _state, make_automatic);
    _stimer_init(&p->timerspsymmsolve, _state, make_automatic);
    _stimer_init(&p->timerothersolve, _state, make_automatic);
    _ipm2reducedsystem_init(&p->reducedsystem, _state, make_automatic);
    _ipm2righthandside_init(&p->rhsprogress, _state, make_automatic);
    ae_vector_init(&p->tmphx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpax, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpaty, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dummyr, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpr2, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->tmpa, _state, make_automatic);
    ae_vector_init(&p->tmpal, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpau, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->tmpsparse0, _state, make_automatic);
    _sparsematrix_init(&p->tmplowerh, _state, make_automatic);
    ae_matrix_init(&p->tmpccorr, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpdcorr, 0, DT_REAL, _state, make_automatic);
}


void _ipm2state_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    ipm2state       *dst = (ipm2state*)_dst;
    const ipm2state *src = (const ipm2state*)_src;
    dst->nuser = src->nuser;
    dst->naug = src->naug;
    dst->ntotal = src->ntotal;
    dst->hasuserpermutation = src->hasuserpermutation;
    ae_vector_init_copy(&dst->userpermutation, &src->userpermutation, _state, make_automatic);
    dst->epsp = src->epsp;
    dst->epsd = src->epsd;
    dst->epsgap = src->epsgap;
    dst->maxipmits = src->maxipmits;
    dst->islinear = src->islinear;
    ae_vector_init_copy(&dst->sclx, &src->sclx, _state, make_automatic);
    ae_vector_init_copy(&dst->invsclx, &src->invsclx, _state, make_automatic);
    ae_vector_init_copy(&dst->xoriginx, &src->xoriginx, _state, make_automatic);
    dst->targetscale = src->targetscale;
    ae_vector_init_copy(&dst->ce, &src->ce, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsehe, &src->sparsehe, _state, make_automatic);
    ae_vector_init_copy(&dst->diagr, &src->diagr, _state, make_automatic);
    dst->isdiagonalh = src->isdiagonalh;
    dst->greg = src->greg;
    ae_vector_init_copy(&dst->gregoriginxuser, &src->gregoriginxuser, _state, make_automatic);
    ae_vector_init_copy(&dst->gregoriginyuser, &src->gregoriginyuser, _state, make_automatic);
    ae_vector_init_copy(&dst->gregdiag, &src->gregdiag, _state, make_automatic);
    ae_vector_init_copy(&dst->gregrhsx, &src->gregrhsx, _state, make_automatic);
    ae_vector_init_copy(&dst->gregrhsy, &src->gregrhsy, _state, make_automatic);
    ae_vector_init_copy(&dst->gregoriginx, &src->gregoriginx, _state, make_automatic);
    ae_vector_init_copy(&dst->gregoriginy, &src->gregoriginy, _state, make_automatic);
    ae_vector_init_copy(&dst->rawbndl, &src->rawbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->rawbndu, &src->rawbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->bndle, &src->bndle, _state, make_automatic);
    ae_vector_init_copy(&dst->bndue, &src->bndue, _state, make_automatic);
    ae_vector_init_copy(&dst->bndlef, &src->bndlef, _state, make_automatic);
    ae_vector_init_copy(&dst->bnduef, &src->bnduef, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndle, &src->hasbndle, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndue, &src->hasbndue, _state, make_automatic);
    _sparsematrix_init_copy(&dst->rawae, &src->rawae, _state, make_automatic);
    ae_vector_init_copy(&dst->ascales, &src->ascales, _state, make_automatic);
    dst->mraw = src->mraw;
    _ipm2vars_init_copy(&dst->current, &src->current, _state, make_automatic);
    _ipm2vars_init_copy(&dst->best, &src->best, _state, make_automatic);
    _ipm2vars_init_copy(&dst->delta, &src->delta, _state, make_automatic);
    _ipm2vars_init_copy(&dst->corr, &src->corr, _state, make_automatic);
    ae_vector_init_copy(&dst->hasgz, &src->hasgz, _state, make_automatic);
    ae_vector_init_copy(&dst->hasts, &src->hasts, _state, make_automatic);
    ae_vector_init_copy(&dst->maskgz, &src->maskgz, _state, make_automatic);
    ae_vector_init_copy(&dst->maskts, &src->maskts, _state, make_automatic);
    dst->cntgz = src->cntgz;
    dst->cntts = src->cntts;
    ae_vector_init_copy(&dst->currenthx, &src->currenthx, _state, make_automatic);
    ae_vector_init_copy(&dst->currentax, &src->currentax, _state, make_automatic);
    ae_vector_init_copy(&dst->currentaty, &src->currentaty, _state, make_automatic);
    _ipm2righthandside_init_copy(&dst->rhstarget, &src->rhstarget, _state, make_automatic);
    dst->repiterationscount = src->repiterationscount;
    dst->repncholesky = src->repncholesky;
    dst->dotrace = src->dotrace;
    dst->dotimers = src->dotimers;
    dst->dodetailedtrace = src->dodetailedtrace;
    _stimer_init_copy(&dst->timertotal, &src->timertotal, _state, make_automatic);
    _stimer_init_copy(&dst->timeranalyze, &src->timeranalyze, _state, make_automatic);
    _stimer_init_copy(&dst->timerfactorize, &src->timerfactorize, _state, make_automatic);
    _stimer_init_copy(&dst->timerspsymmsolve, &src->timerspsymmsolve, _state, make_automatic);
    _stimer_init_copy(&dst->timerothersolve, &src->timerothersolve, _state, make_automatic);
    _ipm2reducedsystem_init_copy(&dst->reducedsystem, &src->reducedsystem, _state, make_automatic);
    _ipm2righthandside_init_copy(&dst->rhsprogress, &src->rhsprogress, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphx, &src->tmphx, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpax, &src->tmpax, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpaty, &src->tmpaty, _state, make_automatic);
    ae_vector_init_copy(&dst->dummyr, &src->dummyr, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpr2, &src->tmpr2, _state, make_automatic);
    _sparsematrix_init_copy(&dst->tmpa, &src->tmpa, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpal, &src->tmpal, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpau, &src->tmpau, _state, make_automatic);
    _sparsematrix_init_copy(&dst->tmpsparse0, &src->tmpsparse0, _state, make_automatic);
    _sparsematrix_init_copy(&dst->tmplowerh, &src->tmplowerh, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpccorr, &src->tmpccorr, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpdcorr, &src->tmpdcorr, _state, make_automatic);
}


void _ipm2state_clear(void* _p)
{
    ipm2state *p = (ipm2state*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->userpermutation);
    ae_vector_clear(&p->sclx);
    ae_vector_clear(&p->invsclx);
    ae_vector_clear(&p->xoriginx);
    ae_vector_clear(&p->ce);
    _sparsematrix_clear(&p->sparsehe);
    ae_vector_clear(&p->diagr);
    ae_vector_clear(&p->gregoriginxuser);
    ae_vector_clear(&p->gregoriginyuser);
    ae_vector_clear(&p->gregdiag);
    ae_vector_clear(&p->gregrhsx);
    ae_vector_clear(&p->gregrhsy);
    ae_vector_clear(&p->gregoriginx);
    ae_vector_clear(&p->gregoriginy);
    ae_vector_clear(&p->rawbndl);
    ae_vector_clear(&p->rawbndu);
    ae_vector_clear(&p->bndle);
    ae_vector_clear(&p->bndue);
    ae_vector_clear(&p->bndlef);
    ae_vector_clear(&p->bnduef);
    ae_vector_clear(&p->hasbndle);
    ae_vector_clear(&p->hasbndue);
    _sparsematrix_clear(&p->rawae);
    ae_vector_clear(&p->ascales);
    _ipm2vars_clear(&p->current);
    _ipm2vars_clear(&p->best);
    _ipm2vars_clear(&p->delta);
    _ipm2vars_clear(&p->corr);
    ae_vector_clear(&p->hasgz);
    ae_vector_clear(&p->hasts);
    ae_vector_clear(&p->maskgz);
    ae_vector_clear(&p->maskts);
    ae_vector_clear(&p->currenthx);
    ae_vector_clear(&p->currentax);
    ae_vector_clear(&p->currentaty);
    _ipm2righthandside_clear(&p->rhstarget);
    _stimer_clear(&p->timertotal);
    _stimer_clear(&p->timeranalyze);
    _stimer_clear(&p->timerfactorize);
    _stimer_clear(&p->timerspsymmsolve);
    _stimer_clear(&p->timerothersolve);
    _ipm2reducedsystem_clear(&p->reducedsystem);
    _ipm2righthandside_clear(&p->rhsprogress);
    ae_vector_clear(&p->tmphx);
    ae_vector_clear(&p->tmpax);
    ae_vector_clear(&p->tmpaty);
    ae_vector_clear(&p->dummyr);
    ae_vector_clear(&p->tmp0);
    ae_matrix_clear(&p->tmpr2);
    _sparsematrix_clear(&p->tmpa);
    ae_vector_clear(&p->tmpal);
    ae_vector_clear(&p->tmpau);
    _sparsematrix_clear(&p->tmpsparse0);
    _sparsematrix_clear(&p->tmplowerh);
    ae_matrix_clear(&p->tmpccorr);
    ae_vector_clear(&p->tmpdcorr);
}


void _ipm2state_destroy(void* _p)
{
    ipm2state *p = (ipm2state*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->userpermutation);
    ae_vector_destroy(&p->sclx);
    ae_vector_destroy(&p->invsclx);
    ae_vector_destroy(&p->xoriginx);
    ae_vector_destroy(&p->ce);
    _sparsematrix_destroy(&p->sparsehe);
    ae_vector_destroy(&p->diagr);
    ae_vector_destroy(&p->gregoriginxuser);
    ae_vector_destroy(&p->gregoriginyuser);
    ae_vector_destroy(&p->gregdiag);
    ae_vector_destroy(&p->gregrhsx);
    ae_vector_destroy(&p->gregrhsy);
    ae_vector_destroy(&p->gregoriginx);
    ae_vector_destroy(&p->gregoriginy);
    ae_vector_destroy(&p->rawbndl);
    ae_vector_destroy(&p->rawbndu);
    ae_vector_destroy(&p->bndle);
    ae_vector_destroy(&p->bndue);
    ae_vector_destroy(&p->bndlef);
    ae_vector_destroy(&p->bnduef);
    ae_vector_destroy(&p->hasbndle);
    ae_vector_destroy(&p->hasbndue);
    _sparsematrix_destroy(&p->rawae);
    ae_vector_destroy(&p->ascales);
    _ipm2vars_destroy(&p->current);
    _ipm2vars_destroy(&p->best);
    _ipm2vars_destroy(&p->delta);
    _ipm2vars_destroy(&p->corr);
    ae_vector_destroy(&p->hasgz);
    ae_vector_destroy(&p->hasts);
    ae_vector_destroy(&p->maskgz);
    ae_vector_destroy(&p->maskts);
    ae_vector_destroy(&p->currenthx);
    ae_vector_destroy(&p->currentax);
    ae_vector_destroy(&p->currentaty);
    _ipm2righthandside_destroy(&p->rhstarget);
    _stimer_destroy(&p->timertotal);
    _stimer_destroy(&p->timeranalyze);
    _stimer_destroy(&p->timerfactorize);
    _stimer_destroy(&p->timerspsymmsolve);
    _stimer_destroy(&p->timerothersolve);
    _ipm2reducedsystem_destroy(&p->reducedsystem);
    _ipm2righthandside_destroy(&p->rhsprogress);
    ae_vector_destroy(&p->tmphx);
    ae_vector_destroy(&p->tmpax);
    ae_vector_destroy(&p->tmpaty);
    ae_vector_destroy(&p->dummyr);
    ae_vector_destroy(&p->tmp0);
    ae_matrix_destroy(&p->tmpr2);
    _sparsematrix_destroy(&p->tmpa);
    ae_vector_destroy(&p->tmpal);
    ae_vector_destroy(&p->tmpau);
    _sparsematrix_destroy(&p->tmpsparse0);
    _sparsematrix_destroy(&p->tmplowerh);
    ae_matrix_destroy(&p->tmpccorr);
    ae_vector_destroy(&p->tmpdcorr);
}


#endif
#if defined(AE_COMPILE_ECQPSOLVER) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
Initializes QP-IPM state and prepares it to receive quadratic/linear terms
and constraints.

INPUT PARAMETERS:
    State       -   solver  state  to  be configured; previously allocated
                    memory is reused as much as possible
    S           -   scale vector, array[N]:
                    * I-th element contains scale of I-th variable,
                    * S[I]>0
    XOrigin     -   origin term, array[N]. Can be zero. The solver solves
                    problem of the form
                    >
                    > min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin))
                    >
                    The terms A and b (as well as constraints) will be
                    specified later with separate calls.
    N           -   total number of variables, N>=1
    TargetScale -   target scale:
                    * >=0
                    * recommended to be max(nrm2(C),nrm2(H+corr))
                    * if zero, autodetected by performing several iterations
                      of a subspace eigensolver

  -- ALGLIB --
     Copyright 21.12.2022 by Bochkanov Sergey
*************************************************************************/
void ecqpsolve(ecqpstate* state,
     ae_int_t nuser,
     const sparsematrix* lowerh,
     /* Real    */ const ae_vector* c,
     const sparsematrix* sparsea,
     /* Real    */ const ae_vector* ae,
     ae_int_t m,
     /* Real    */ ae_vector* xs,
     /* Real    */ ae_vector* laglc,
     ae_int_t* terminationtype,
     ae_state *_state)
{
    ae_int_t nnza;
    ae_int_t ntotal;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t j0;
    ae_int_t j1;
    double v;
    double vv;
    ae_int_t offs;
    double damp;
    double reg;
    double badchol;
    double sumsq;
    double errsq;
    ae_int_t rfsidx;
    ae_int_t facttype;

    *terminationtype = 0;

    ae_assert(nuser>=1, "ECQPInit: N<1", _state);
    ae_assert(isfinitevector(c, nuser, _state), "ECQPInit: C contains infinite or NaN elements", _state);
    ae_assert(m>=0, "ECQPInit: M<0", _state);
    ae_assert(m==0||sparsea->matrixtype==1, "ECQPInit: non-CRS constraint matrix!", _state);
    ae_assert(m==0||(sparsea->m==m&&sparsea->n==nuser), "ECQPInit: constraint matrix has incorrect size", _state);
    ae_assert(ae->cnt>=m, "ECQPInit: AE is too short!", _state);
    
    /*
     * Problem metrics, settings and type
     */
    nnza = 0;
    if( m>0 )
    {
        nnza = sparsea->ridx.ptr.p_int[m];
    }
    ntotal = nuser+m;
    
    /*
     * Reports
     */
    state->repiterationscount = 0;
    state->repncholesky = 0;
    
    /*
     * Trace
     */
    state->dotrace = ae_is_trace_enabled("ECQP");
    state->dotimers = state->dotrace||ae_is_trace_enabled("TIMERS.ECQP");
    if( state->dotimers )
    {
        stimerinit(&state->timertotal, _state);
        stimerinit(&state->timeranalyze, _state);
        stimerinit(&state->timerfactorize, _state);
        stimerinit(&state->timerspsymmsolve, _state);
        stimerinit(&state->timerothersolve, _state);
        stimerstart(&state->timertotal, _state);
    }
    if( state->dotrace||state->dotimers )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("// SPARSE ECQP SOLVER STARTED                                                                     //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
    }
    
    /*
     * Generate extended (N+M)x(N+M) matrix
     */
    rallocv(ntotal, &state->effdiagonal, _state);
    state->haug.matrixtype = 1;
    state->haug.m = ntotal;
    state->haug.n = ntotal;
    iallocv(ntotal+1, &state->haug.ridx, _state);
    ae_assert(lowerh->matrixtype==1, "ECQP: unexpected sparse matrix format", _state);
    ae_assert(lowerh->m==nuser, "ECQP: unexpected sparse matrix size", _state);
    ae_assert(lowerh->n==nuser, "ECQP: unexpected sparse matrix size", _state);
    iallocv(lowerh->ridx.ptr.p_int[nuser]+nuser+nnza+nuser, &state->haug.idx, _state);
    rallocv(lowerh->ridx.ptr.p_int[nuser]+nuser+nnza+nuser, &state->haug.vals, _state);
    state->haug.ridx.ptr.p_int[0] = 0;
    offs = 0;
    vv = (double)(0);
    for(i=0; i<=nuser-1; i++)
    {
        
        /*
         * Copy subdiagonal elements (if needed)
         */
        j0 = lowerh->ridx.ptr.p_int[i];
        j1 = lowerh->didx.ptr.p_int[i]-1;
        for(k=j0; k<=j1; k++)
        {
            v = lowerh->vals.ptr.p_double[k];
            state->haug.idx.ptr.p_int[offs] = lowerh->idx.ptr.p_int[k];
            state->haug.vals.ptr.p_double[offs] = v;
            vv = vv+v;
            offs = offs+1;
        }
        
        /*
         * Diagonal element is always copied
         */
        v = (double)(0);
        if( lowerh->uidx.ptr.p_int[i]!=lowerh->didx.ptr.p_int[i] )
        {
            v = lowerh->vals.ptr.p_double[lowerh->didx.ptr.p_int[i]];
        }
        state->haug.idx.ptr.p_int[offs] = i;
        state->haug.vals.ptr.p_double[offs] = v;
        state->effdiagonal.ptr.p_double[i] = v;
        vv = vv+v;
        offs = offs+1;
        
        /*
         * Finalize row
         */
        state->haug.ridx.ptr.p_int[i+1] = offs;
    }
    for(i=0; i<=m-1; i++)
    {
        offs = state->haug.ridx.ptr.p_int[nuser+i];
        j0 = sparsea->ridx.ptr.p_int[i];
        j1 = sparsea->ridx.ptr.p_int[i+1]-1;
        for(j=j0; j<=j1; j++)
        {
            state->haug.idx.ptr.p_int[offs] = sparsea->idx.ptr.p_int[j];
            state->haug.vals.ptr.p_double[offs] = sparsea->vals.ptr.p_double[j];
            offs = offs+1;
        }
        state->haug.idx.ptr.p_int[offs] = nuser+i;
        state->haug.vals.ptr.p_double[offs] = 0.0;
        state->effdiagonal.ptr.p_double[nuser+i] = 0.0;
        offs = offs+1;
        state->haug.ridx.ptr.p_int[nuser+i+1] = offs;
    }
    ae_assert(ae_isfinite(vv, _state), "ECQPInit: LowerH contains infinite or NaN values!", _state);
    ae_assert(offs<=state->haug.vals.cnt&&offs<=state->haug.idx.cnt, "ECQPInit: integrity check failed", _state);
    sparsecreatecrsinplace(&state->haug, _state);
    
    /*
     * Prepare sparse Cholesky
     */
    facttype = 21;
    isetallocv(state->haug.n, 0, &state->priorities, _state);
    isetv(nuser, 1, &state->priorities, _state);
    stimerstartcond(&state->timeranalyze, state->dotimers, _state);
    if( !spsymmanalyze(&state->haug, &state->priorities, ecqpsolver_mupromote, 0, facttype, 3, 1, &state->analysis, _state) )
    {
        ae_assert(ae_false, "ReducedSystemPowerUp: critical integrity check failed, symbolically degenerate KKT system encountered", _state);
    }
    stimerstopcond(&state->timeranalyze, state->dotimers, _state);
    
    /*
     * Regularize until factorization succeeds
     */
    rallocv(ntotal, &state->dampdiagonal, _state);
    damp = ae_sqrt(ae_machineepsilon, _state);
    badchol = 1.0E50;
    for(;;)
    {
        for(i=0; i<=nuser-1; i++)
        {
            state->dampdiagonal.ptr.p_double[i] = state->effdiagonal.ptr.p_double[i]+damp;
        }
        for(i=nuser; i<=ntotal-1; i++)
        {
            state->dampdiagonal.ptr.p_double[i] = state->effdiagonal.ptr.p_double[i]-damp;
        }
        spsymmreloaddiagonal(&state->analysis, &state->dampdiagonal, _state);
        spsymmsetmodificationstrategy(&state->analysis, 1, ae_machineepsilon, badchol, 0.0, 0.0, _state);
        stimerstartcond(&state->timerfactorize, state->dotimers, _state);
        if( !spsymmfactorize(&state->analysis, _state) )
        {
            stimerstopcond(&state->timerfactorize, state->dotimers, _state);
            damp = (double)10*damp;
            if( state->dotrace )
            {
                ae_trace("> cholesky factorization failed, increasing damping to %0.2e\n",
                    (double)(damp));
            }
            continue;
        }
        stimerstopcond(&state->timerfactorize, state->dotimers, _state);
        spsymmdiagerr(&state->analysis, &sumsq, &errsq, _state);
        if( ae_fp_greater(ae_sqrt(errsq/((double)1+sumsq), _state),(double)100*ae_sqrt(ae_machineepsilon, _state)) )
        {
            damp = (double)10*damp;
            if( state->dotrace )
            {
                ae_trace("LDLT-diag-err= %0.3e (diagonal reproduction error), increasing damping to %0.2e\n",
                    (double)(ae_sqrt(errsq/((double)1+sumsq), _state)),
                    (double)(damp));
            }
            continue;
        }
        if( state->dotrace )
        {
            ae_trace("> factorization accepted\n");
        }
        break;
    }
    
    /*
     * Solve
     */
    stimerstartcond(&state->timerothersolve, state->dotimers, _state);
    reg = (double)1000*ae_machineepsilon;
    rallocv(ntotal, &state->bx, _state);
    rcopymulv(nuser, -1.0, c, &state->bx, _state);
    rcopyvx(m, ae, 0, &state->bx, nuser, _state);
    fblsgmrescreate(&state->bx, ntotal, ae_minint(25, ntotal, _state), &state->gmressolver, _state);
    state->gmressolver.epsres = (double)100*ae_machineepsilon;
    state->gmressolver.epsred = 0.999999;
    rfsidx = 0;
    while(fblsgmresiteration(&state->gmressolver, _state))
    {
        if( state->dotrace )
        {
            ae_trace(">> GMRES iteration %2d: %0.2e relative residual\n",
                (int)(rfsidx),
                (double)(state->gmressolver.reprelres));
        }
        for(i=0; i<=nuser-1; i++)
        {
            state->gmressolver.ax.ptr.p_double[i] = reg*state->gmressolver.x.ptr.p_double[i];
        }
        for(i=nuser; i<=ntotal-1; i++)
        {
            state->gmressolver.ax.ptr.p_double[i] = -reg*state->gmressolver.x.ptr.p_double[i];
        }
        rcopyallocv(ntotal, &state->gmressolver.x, &state->dx, _state);
        stimerstartcond(&state->timerspsymmsolve, state->dotimers, _state);
        spsymmsolve(&state->analysis, &state->dx, _state);
        stimerstopcond(&state->timerspsymmsolve, state->dotimers, _state);
        sparsesmv(lowerh, ae_false, &state->dx, &state->dh, _state);
        raddvx(nuser, 1.0, &state->dh, 0, &state->gmressolver.ax, 0, _state);
        if( m>0 )
        {
            sparsegemv(sparsea, 1.0, 1, &state->dx, nuser, 1.0, &state->gmressolver.ax, 0, _state);
            sparsegemv(sparsea, 1.0, 0, &state->dx, 0, 1.0, &state->gmressolver.ax, nuser, _state);
        }
        rfsidx = rfsidx+1;
    }
    stimerstartcond(&state->timerspsymmsolve, state->dotimers, _state);
    spsymmsolve(&state->analysis, &state->gmressolver.xs, _state);
    stimerstopcond(&state->timerspsymmsolve, state->dotimers, _state);
    stimerstopcond(&state->timerothersolve, state->dotimers, _state);
    rallocv(nuser, xs, _state);
    rallocv(m, laglc, _state);
    rcopyvx(nuser, &state->gmressolver.xs, 0, xs, 0, _state);
    rcopyvx(m, &state->gmressolver.xs, nuser, laglc, 0, _state);
    state->repiterationscount = 1;
    state->repncholesky = 1;
    *terminationtype = 1;
    
    /*
     * Timers and reports
     */
    if( state->dotimers )
    {
        stimerstop(&state->timertotal, _state);
        ae_trace("\n> printing internal timers (can be zero when run in OS-agnostic mode)\n");
        ae_trace("> total time:            %10.1f ms\n",
            (double)(stimergetms(&state->timertotal, _state)));
        ae_trace("> * in analyze:          %10.1f ms\n",
            (double)(stimergetms(&state->timeranalyze, _state)));
        ae_trace("> * in factorize:        %10.1f ms\n",
            (double)(stimergetms(&state->timerfactorize, _state)));
        ae_trace("> * in spsymmsolve():    %10.1f ms\n",
            (double)(stimergetms(&state->timerspsymmsolve, _state)));
        ae_trace("> * in other solve:      %10.1f ms\n",
            (double)(stimergetms(&state->timerothersolve, _state)-stimergetms(&state->timerspsymmsolve, _state)));
    }
}


void _ecqpstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    ecqpstate *p = (ecqpstate*)_p;
    ae_touch_ptr((void*)p);
    _sparsematrix_init(&p->haug, _state, make_automatic);
    ae_vector_init(&p->priorities, 0, DT_INT, _state, make_automatic);
    _spcholanalysis_init(&p->analysis, _state, make_automatic);
    ae_vector_init(&p->effdiagonal, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dampdiagonal, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dh, 0, DT_REAL, _state, make_automatic);
    _fblsgmresstate_init(&p->gmressolver, _state, make_automatic);
    _stimer_init(&p->timertotal, _state, make_automatic);
    _stimer_init(&p->timeranalyze, _state, make_automatic);
    _stimer_init(&p->timerfactorize, _state, make_automatic);
    _stimer_init(&p->timerspsymmsolve, _state, make_automatic);
    _stimer_init(&p->timerothersolve, _state, make_automatic);
}


void _ecqpstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    ecqpstate       *dst = (ecqpstate*)_dst;
    const ecqpstate *src = (const ecqpstate*)_src;
    _sparsematrix_init_copy(&dst->haug, &src->haug, _state, make_automatic);
    ae_vector_init_copy(&dst->priorities, &src->priorities, _state, make_automatic);
    _spcholanalysis_init_copy(&dst->analysis, &src->analysis, _state, make_automatic);
    ae_vector_init_copy(&dst->effdiagonal, &src->effdiagonal, _state, make_automatic);
    ae_vector_init_copy(&dst->dampdiagonal, &src->dampdiagonal, _state, make_automatic);
    ae_vector_init_copy(&dst->dx, &src->dx, _state, make_automatic);
    ae_vector_init_copy(&dst->bx, &src->bx, _state, make_automatic);
    ae_vector_init_copy(&dst->dh, &src->dh, _state, make_automatic);
    _fblsgmresstate_init_copy(&dst->gmressolver, &src->gmressolver, _state, make_automatic);
    dst->repiterationscount = src->repiterationscount;
    dst->repncholesky = src->repncholesky;
    dst->dotrace = src->dotrace;
    dst->dotimers = src->dotimers;
    _stimer_init_copy(&dst->timertotal, &src->timertotal, _state, make_automatic);
    _stimer_init_copy(&dst->timeranalyze, &src->timeranalyze, _state, make_automatic);
    _stimer_init_copy(&dst->timerfactorize, &src->timerfactorize, _state, make_automatic);
    _stimer_init_copy(&dst->timerspsymmsolve, &src->timerspsymmsolve, _state, make_automatic);
    _stimer_init_copy(&dst->timerothersolve, &src->timerothersolve, _state, make_automatic);
}


void _ecqpstate_clear(void* _p)
{
    ecqpstate *p = (ecqpstate*)_p;
    ae_touch_ptr((void*)p);
    _sparsematrix_clear(&p->haug);
    ae_vector_clear(&p->priorities);
    _spcholanalysis_clear(&p->analysis);
    ae_vector_clear(&p->effdiagonal);
    ae_vector_clear(&p->dampdiagonal);
    ae_vector_clear(&p->dx);
    ae_vector_clear(&p->bx);
    ae_vector_clear(&p->dh);
    _fblsgmresstate_clear(&p->gmressolver);
    _stimer_clear(&p->timertotal);
    _stimer_clear(&p->timeranalyze);
    _stimer_clear(&p->timerfactorize);
    _stimer_clear(&p->timerspsymmsolve);
    _stimer_clear(&p->timerothersolve);
}


void _ecqpstate_destroy(void* _p)
{
    ecqpstate *p = (ecqpstate*)_p;
    ae_touch_ptr((void*)p);
    _sparsematrix_destroy(&p->haug);
    ae_vector_destroy(&p->priorities);
    _spcholanalysis_destroy(&p->analysis);
    ae_vector_destroy(&p->effdiagonal);
    ae_vector_destroy(&p->dampdiagonal);
    ae_vector_destroy(&p->dx);
    ae_vector_destroy(&p->bx);
    ae_vector_destroy(&p->dh);
    _fblsgmresstate_destroy(&p->gmressolver);
    _stimer_destroy(&p->timertotal);
    _stimer_destroy(&p->timeranalyze);
    _stimer_destroy(&p->timerfactorize);
    _stimer_destroy(&p->timerspsymmsolve);
    _stimer_destroy(&p->timerothersolve);
}


#endif
#if defined(AE_COMPILE_GIPM) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
Get recommended initial value for mu.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
double gipmgetinitprimdual(ae_state *_state)
{
    double result;


    result = ae_sqrt(gipm_initmu, _state);
    return result;
}


/*************************************************************************
Initialize GIPM structure

NOTES: 

    FScales             -   objective/constraint scales, used merely for
                            reporting
    DoQNReport          -   array[MFlex+MHard+1], flags for quasi-Newton
                            reports.
                            
                            If DoQNReport[i] is true, then (I-1)-th constraint
                            is included into report sent with the code 1004.
                            The first element  corresponds to the objective.
                            Ignored when IsFirstOrder=False

IMPORTANT: equality box constraints on X are NOT supported! you have to
           reformulate your problem in order to avoid equality constraints
           on primal variables. Box constraints on them should be have
           width bounded away from zero.
           Nonlinear equality constraints are supported.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
void gipminitbuf(/* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* x0,
     ae_int_t nraw,
     /* Real    */ const ae_vector* nl,
     /* Real    */ const ae_vector* nu,
     ae_int_t mflex,
     ae_int_t mhard,
     /* Real    */ const ae_vector* fscales,
     /* Boolean */ const ae_vector* doqnreport,
     double eps,
     ae_int_t maxits,
     ae_bool isfirstorder,
     gipmstate* state,
     ae_state *_state)
{
    ae_int_t i;
    double eqeps;


    state->nraw = nraw;
    state->mflex = mflex;
    state->mhard = mhard;
    state->mudependent = ae_false;
    state->isfirstorder = isfirstorder;
    eqeps = ae_maxreal(1.0E-3*eps, 1.0E3*ae_machineepsilon, _state);
    if( isfirstorder )
    {
        rsetallocv(1+mflex+mhard, 0.0, &state->qnmask, _state);
        state->isqnmasknonzero = ae_false;
        for(i=0; i<=mflex+mhard; i++)
        {
            if( doqnreport->ptr.p_bool[i] )
            {
                state->qnmask.ptr.p_double[i] = 1.0;
                state->isqnmasknonzero = ae_true;
            }
        }
    }
    
    /*
     * Save initial point for further processing
     */
    rcopyallocv(nraw, x0, &state->x0, _state);
    
    /*
     * Function scales
     */
    rcopyallocv(1+mflex+mhard, fscales, &state->fscales, _state);
    
    /*
     * Prepare RCOMM-V2 protocol
     */
    ae_vector_set_length(&state->rstate.ia, 13+1, _state);
    ae_vector_set_length(&state->rstate.ba, 6+1, _state);
    ae_vector_set_length(&state->rstate.ra, 30+1, _state);
    state->rstate.stage = -1;
    
    /*
     * Box constraints: convert from the original form into a slack representation
     */
    bsetallocv(mflex, ae_false, &state->isequality, _state);
    rsetallocv(nraw+mflex, _state->v_neginf, &state->bndlx, _state);
    bsetallocv(nraw+mflex, ae_false, &state->hasbndlx, _state);
    rsetallocv(nraw+mflex+mhard, _state->v_posinf, &state->bndux, _state);
    bsetallocv(nraw+mflex+mhard, ae_false, &state->hasbndux, _state);
    for(i=0; i<=nraw-1; i++)
    {
        if( ae_isfinite(bndl->ptr.p_double[i], _state) )
        {
            state->bndlx.ptr.p_double[i] = bndl->ptr.p_double[i];
            state->hasbndlx.ptr.p_bool[i] = ae_true;
        }
        if( ae_isfinite(bndu->ptr.p_double[i], _state) )
        {
            state->bndux.ptr.p_double[i] = bndu->ptr.p_double[i];
            state->hasbndux.ptr.p_bool[i] = ae_true;
        }
        if( state->hasbndlx.ptr.p_bool[i]&&state->hasbndux.ptr.p_bool[i] )
        {
            ae_assert(ae_fp_less(state->bndlx.ptr.p_double[i],state->bndux.ptr.p_double[i]), "GIPM: equality box constraints on primal variables are NOT supported by design", _state);
        }
    }
    for(i=0; i<=mflex-1; i++)
    {
        ae_assert(ae_isfinite(nl->ptr.p_double[i], _state)||ae_isfinite(nu->ptr.p_double[i], _state), "GIPM: one of general constraints has both bounds absent", _state);
        if( ae_isfinite(nl->ptr.p_double[i], _state) )
        {
            state->bndlx.ptr.p_double[nraw+i] = nl->ptr.p_double[i];
            state->hasbndlx.ptr.p_bool[nraw+i] = ae_true;
        }
        if( ae_isfinite(nu->ptr.p_double[i], _state) )
        {
            state->bndux.ptr.p_double[nraw+i] = nu->ptr.p_double[i];
            state->hasbndux.ptr.p_bool[nraw+i] = ae_true;
        }
        if( state->hasbndlx.ptr.p_bool[nraw+i]&&state->hasbndux.ptr.p_bool[nraw+i] )
        {
            ae_assert(ae_fp_less_eq(state->bndlx.ptr.p_double[nraw+i],state->bndux.ptr.p_double[nraw+i]), "GIPM: one of nonlinear constraints has incompatible range", _state);
            if( ae_fp_less_eq(ae_fabs(state->bndlx.ptr.p_double[nraw+i]-state->bndux.ptr.p_double[nraw+i], _state),eqeps) )
            {
                state->isequality.ptr.p_bool[i] = ae_true;
            }
        }
    }
    for(i=mflex; i<=mflex+mhard-1; i++)
    {
        ae_assert(ae_isneginf(nl->ptr.p_double[i], _state)&&ae_isfinite(nu->ptr.p_double[i], _state), "GIPM: one of hard constraints has infinite upper bound or has finite upper bound", _state);
        state->bndux.ptr.p_double[nraw+i] = nu->ptr.p_double[i];
        state->hasbndux.ptr.p_bool[nraw+i] = ae_true;
    }
    
    /*
     * Stopping criteria and settings
     */
    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "GIPM: integrity check 9756 failed", _state);
    state->eps = coalesce(eps, (double)10*ae_sqrt(ae_machineepsilon, _state), _state);
    state->maxits = maxits;
    
    /*
     * Report fields
     */
    state->repterminationtype = 0;
    state->repiterationscount = 0;
    
    /*
     * Trace status
     */
    state->dolaconictrace = ae_is_trace_enabled("GENIPM.LACONIC");
    state->dotrace = ae_is_trace_enabled("GENIPM")&&!state->dolaconictrace;
    state->dodetailedtrace = state->dotrace&&ae_is_trace_enabled("GENIPM.DETAILED");
}


/*************************************************************************
This function performs actual processing for GIPM  algorithm.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
ae_bool gipmiteration(gipmstate* state,
     ae_bool userterminationneeded,
     ae_state *_state)
{
    ae_int_t nraw;
    ae_int_t mtotal;
    ae_int_t mflex;
    ae_int_t mhard;
    ae_int_t nxf;
    ae_int_t nxh;
    ae_int_t i;
    ae_int_t rfsidx;
    ae_int_t j;
    double v;
    double vv;
    double v2;
    double alpha;
    double f0;
    double f1;
    double p0;
    double p1;
    double cmpl0;
    double cmpl1;
    double gnrm;
    ae_int_t innerloopstart;
    double initprimdual;
    double eprim;
    double edual;
    double ecmpl;
    ae_bool firstouteriteration;
    ae_bool infinitiesdetected;
    ae_bool failedtorecoverfrominfinities;
    ae_bool unboundednesssuspected;
    ae_bool lambdafound;
    ae_bool accepted;
    ae_bool violated;
    double mumin;
    double curbigval;
    double curbiggrowth;
    ae_int_t smalltrials;
    double rhs0;
    double rhsk;
    double lastcheckpointp;
    double lastcheckpointd;
    double lastcheckpointc;
    ae_int_t lastcheckpointidx;
    ae_int_t btits;
    double lastprim;
    double lastdual;
    double lastcmpl;
    ae_int_t goodinnerits;
    double maxabsf;
    double minmeritv0;
    double curmeritv0;
    double merit0;
    double merit1;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        nraw = state->rstate.ia.ptr.p_int[0];
        mtotal = state->rstate.ia.ptr.p_int[1];
        mflex = state->rstate.ia.ptr.p_int[2];
        mhard = state->rstate.ia.ptr.p_int[3];
        nxf = state->rstate.ia.ptr.p_int[4];
        nxh = state->rstate.ia.ptr.p_int[5];
        i = state->rstate.ia.ptr.p_int[6];
        rfsidx = state->rstate.ia.ptr.p_int[7];
        j = state->rstate.ia.ptr.p_int[8];
        innerloopstart = state->rstate.ia.ptr.p_int[9];
        smalltrials = state->rstate.ia.ptr.p_int[10];
        lastcheckpointidx = state->rstate.ia.ptr.p_int[11];
        btits = state->rstate.ia.ptr.p_int[12];
        goodinnerits = state->rstate.ia.ptr.p_int[13];
        firstouteriteration = state->rstate.ba.ptr.p_bool[0];
        infinitiesdetected = state->rstate.ba.ptr.p_bool[1];
        failedtorecoverfrominfinities = state->rstate.ba.ptr.p_bool[2];
        unboundednesssuspected = state->rstate.ba.ptr.p_bool[3];
        lambdafound = state->rstate.ba.ptr.p_bool[4];
        accepted = state->rstate.ba.ptr.p_bool[5];
        violated = state->rstate.ba.ptr.p_bool[6];
        v = state->rstate.ra.ptr.p_double[0];
        vv = state->rstate.ra.ptr.p_double[1];
        v2 = state->rstate.ra.ptr.p_double[2];
        alpha = state->rstate.ra.ptr.p_double[3];
        f0 = state->rstate.ra.ptr.p_double[4];
        f1 = state->rstate.ra.ptr.p_double[5];
        p0 = state->rstate.ra.ptr.p_double[6];
        p1 = state->rstate.ra.ptr.p_double[7];
        cmpl0 = state->rstate.ra.ptr.p_double[8];
        cmpl1 = state->rstate.ra.ptr.p_double[9];
        gnrm = state->rstate.ra.ptr.p_double[10];
        initprimdual = state->rstate.ra.ptr.p_double[11];
        eprim = state->rstate.ra.ptr.p_double[12];
        edual = state->rstate.ra.ptr.p_double[13];
        ecmpl = state->rstate.ra.ptr.p_double[14];
        mumin = state->rstate.ra.ptr.p_double[15];
        curbigval = state->rstate.ra.ptr.p_double[16];
        curbiggrowth = state->rstate.ra.ptr.p_double[17];
        rhs0 = state->rstate.ra.ptr.p_double[18];
        rhsk = state->rstate.ra.ptr.p_double[19];
        lastcheckpointp = state->rstate.ra.ptr.p_double[20];
        lastcheckpointd = state->rstate.ra.ptr.p_double[21];
        lastcheckpointc = state->rstate.ra.ptr.p_double[22];
        lastprim = state->rstate.ra.ptr.p_double[23];
        lastdual = state->rstate.ra.ptr.p_double[24];
        lastcmpl = state->rstate.ra.ptr.p_double[25];
        maxabsf = state->rstate.ra.ptr.p_double[26];
        minmeritv0 = state->rstate.ra.ptr.p_double[27];
        curmeritv0 = state->rstate.ra.ptr.p_double[28];
        merit0 = state->rstate.ra.ptr.p_double[29];
        merit1 = state->rstate.ra.ptr.p_double[30];
    }
    else
    {
        nraw = 359;
        mtotal = -58;
        mflex = -919;
        mhard = -909;
        nxf = 81;
        nxh = 255;
        i = 74;
        rfsidx = -788;
        j = 809;
        innerloopstart = 205;
        smalltrials = -838;
        lastcheckpointidx = 939;
        btits = -526;
        goodinnerits = 763;
        firstouteriteration = ae_true;
        infinitiesdetected = ae_false;
        failedtorecoverfrominfinities = ae_false;
        unboundednesssuspected = ae_false;
        lambdafound = ae_false;
        accepted = ae_false;
        violated = ae_true;
        v = -536.0;
        vv = 487.0;
        v2 = -115.0;
        alpha = 886.0;
        f0 = 346.0;
        f1 = -722.0;
        p0 = -413.0;
        p1 = -461.0;
        cmpl0 = 927.0;
        cmpl1 = 201.0;
        gnrm = 922.0;
        initprimdual = -154.0;
        eprim = 306.0;
        edual = -1011.0;
        ecmpl = 951.0;
        mumin = -463.0;
        curbigval = 88.0;
        curbiggrowth = -861.0;
        rhs0 = -678.0;
        rhsk = -731.0;
        lastcheckpointp = -675.0;
        lastcheckpointd = -763.0;
        lastcheckpointc = -233.0;
        lastprim = -936.0;
        lastdual = -279.0;
        lastcmpl = 94.0;
        maxabsf = -812.0;
        minmeritv0 = 427.0;
        curmeritv0 = 178.0;
        merit0 = -819.0;
        merit1 = -826.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    if( state->rstate.stage==6 )
    {
        goto lbl_6;
    }
    if( state->rstate.stage==7 )
    {
        goto lbl_7;
    }
    if( state->rstate.stage==8 )
    {
        goto lbl_8;
    }
    if( state->rstate.stage==9 )
    {
        goto lbl_9;
    }
    if( state->rstate.stage==10 )
    {
        goto lbl_10;
    }
    
    /*
     * Routine body
     */
    nraw = state->nraw;
    mflex = state->mflex;
    mhard = state->mhard;
    mtotal = mflex+mhard;
    nxf = state->nraw+state->mflex;
    nxh = state->nraw+state->mflex+state->mhard;
    
    /*
     * Prepare debug timers and temporaries
     */
    stimerinit(&state->timertotal, _state);
    ae_nxpool_alloc(&state->mnx1pool, ae_maxint(nxh, mtotal, _state)+1, _state);
    stimerstart(&state->timertotal, _state);
    
    /*
     * Trace output (if needed)
     */
    if( state->dotrace )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("//  NONLINEAR IPM SOLVER STARTED                                                                  //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("N             = %6d\n",
            (int)(nraw));
        ae_trace("M             = %6d (incl %0d non-relaxable inequalities)\n",
            (int)(mflex+mhard),
            (int)(mhard));
    }
    
    /*
     * Prepare quantities used during iterations
     */
    rsetallocv(nraw, 0.0, &state->elp, _state);
    rsetallocv(nraw, 0.0, &state->eup, _state);
    rsetallocv(mflex, 0.0, &state->els, _state);
    rsetallocv(mflex, 0.0, &state->eus, _state);
    for(i=0; i<=nxf-1; i++)
    {
        if( state->hasbndlx.ptr.p_bool[i] )
        {
            if( i<nraw )
            {
                state->elp.ptr.p_double[i] = (double)(1);
            }
            if( i>=nraw&&!state->isequality.ptr.p_bool[i-nraw] )
            {
                state->els.ptr.p_double[i-nraw] = (double)(1);
            }
        }
        if( state->hasbndux.ptr.p_bool[i] )
        {
            if( i<nraw )
            {
                state->eup.ptr.p_double[i] = (double)(1);
            }
            if( i>=nraw&&!state->isequality.ptr.p_bool[i-nraw] )
            {
                state->eus.ptr.p_double[i-nraw] = (double)(1);
            }
        }
    }
    rsetallocv(nxf, -ae_sqrt(ae_maxrealnumber, _state), &state->finitebndlx, _state);
    rsetallocv(nxh, ae_sqrt(ae_maxrealnumber, _state), &state->finitebndux, _state);
    for(i=0; i<=nxf-1; i++)
    {
        if( state->hasbndlx.ptr.p_bool[i] )
        {
            state->finitebndlx.ptr.p_double[i] = state->bndlx.ptr.p_double[i];
        }
        if( state->hasbndux.ptr.p_bool[i] )
        {
            state->finitebndux.ptr.p_double[i] = state->bndux.ptr.p_double[i];
        }
    }
    for(i=nxf; i<=nxh-1; i++)
    {
        state->finitebndux.ptr.p_double[i] = state->bndux.ptr.p_double[i];
    }
    gipm_safeboundsfromfinitetightened(state, _state);
    
    /*
     * Choose initial mu
     */
    state->mu = gipm_initmu;
    mumin = ae_maxreal(0.0001*state->eps/((double)1+gipm_relaxfactor), (double)100*ae_machineepsilon, _state);
    
    /*
     * Initial point: perform RCOMM request, prepare the initial point
     */
    initprimdual = gipmgetinitprimdual(_state);
    gipm_varsinitbyzero(&state->current, nraw, mflex, mhard, _state);
    gipm_varsinitbyzero(&state->delta, nraw, mflex, mhard, _state);
    gipm_varsinitbyzero(&state->corr, nraw, mflex, mhard, _state);
    for(i=0; i<=nraw-1; i++)
    {
        if( state->hasbndlx.ptr.p_bool[i] )
        {
            ae_assert(ae_fp_greater(state->x0.ptr.p_double[i],state->bndlx.ptr.p_double[i]), "GIPM: the initial point is not strictly feasible wrt box constraints", _state);
        }
        if( state->hasbndux.ptr.p_bool[i] )
        {
            ae_assert(ae_fp_less(state->x0.ptr.p_double[i],state->bndux.ptr.p_double[i]), "GIPM: the initial point is not strictly feasible wrt box constraints", _state);
        }
    }
    rcopyv(nraw, &state->x0, &state->current.xp, _state);
    rmergemaxv(nraw, &state->safeguardbndlp, &state->current.xp, _state);
    rmergeminv(nraw, &state->safeguardbndup, &state->current.xp, _state);
    for(i=0; i<=mflex-1; i++)
    {
        if( state->hasbndlx.ptr.p_bool[nraw+i]&&!state->hasbndux.ptr.p_bool[nraw+i] )
        {
            state->current.yp.ptr.p_double[i] = -initprimdual;
        }
        if( state->hasbndux.ptr.p_bool[nraw+i]&&!state->hasbndlx.ptr.p_bool[nraw+i] )
        {
            state->current.yp.ptr.p_double[i] = initprimdual;
        }
    }
    rsetv(mhard, initprimdual, &state->current.yv, _state);
    state->requesttype = 1;
    state->querymu = state->mu;
    rallocv(nxh, &state->querydata, _state);
    rcopyvx(nraw, &state->current.xp, 0, &state->querydata, 0, _state);
    rcopyvx(mflex, &state->current.yp, 0, &state->querydata, nraw, _state);
    rcopyvx(mhard, &state->current.yv, 0, &state->querydata, nraw+mflex, _state);
    rallocv(1+mtotal, &state->replyfi, _state);
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    vfjinitfromsparse(&state->current.xp, nraw, &state->replyfi, 1+mtotal, &state->replysj, &state->currentfj, _state);
    for(i=0; i<=nraw-1; i++)
    {
        if( state->hasbndlx.ptr.p_bool[i] )
        {
            state->current.zlp.ptr.p_double[i] = boundval(gipm_initmu/(state->current.xp.ptr.p_double[i]-state->bndlx.ptr.p_double[i]), (double)100*ae_sqrt(ae_machineepsilon, _state), initprimdual, _state);
        }
        if( state->hasbndux.ptr.p_bool[i] )
        {
            state->current.zup.ptr.p_double[i] = boundval(gipm_initmu/(state->bndux.ptr.p_double[i]-state->current.xp.ptr.p_double[i]), (double)100*ae_sqrt(ae_machineepsilon, _state), initprimdual, _state);
        }
    }
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            state->current.xs.ptr.p_double[i] = state->replyfi.ptr.p_double[1+i];
            if( state->hasbndlx.ptr.p_bool[nraw+i] )
            {
                state->current.gl.ptr.p_double[i] = ae_maxreal(initprimdual, state->current.xs.ptr.p_double[i]-state->bndlx.ptr.p_double[nraw+i], _state);
                state->current.zls.ptr.p_double[i] = boundval(gipm_initmu/state->current.gl.ptr.p_double[i], (double)100*ae_sqrt(ae_machineepsilon, _state), initprimdual, _state);
            }
            if( state->hasbndux.ptr.p_bool[nraw+i] )
            {
                state->current.gu.ptr.p_double[i] = ae_maxreal(initprimdual, state->bndux.ptr.p_double[nraw+i]-state->current.xs.ptr.p_double[i], _state);
                state->current.zus.ptr.p_double[i] = boundval(gipm_initmu/state->current.gu.ptr.p_double[i], (double)100*ae_sqrt(ae_machineepsilon, _state), initprimdual, _state);
            }
        }
    }
    for(i=0; i<=mhard-1; i++)
    {
        ae_assert(ae_fp_less(state->replyfi.ptr.p_double[1+mflex+i],state->finitebndux.ptr.p_double[nxf+i]), "GIPM: the initial point is infeasible with respect to hard inequality constraints", _state);
        state->current.yv.ptr.p_double[i] = boundval(gipm_initmu/(state->finitebndux.ptr.p_double[nxf+i]-state->replyfi.ptr.p_double[1+mflex+i]), (double)100*ae_sqrt(ae_machineepsilon, _state), initprimdual, _state);
    }
    state->nrmx0 = rmaxabsv(nraw, &state->current.xp, _state);
    state->absf0 = ae_fabs(state->replyfi.ptr.p_double[0], _state);
    
    /*
     * Outer iterations
     */
    curbigval = gipm_initbigval;
    curbiggrowth = gipm_initbiggrowth;
    state->lambdav = 0.0;
    state->lambdadecay = 0.0;
    state->besterr = ae_maxrealnumber;
    gipm_varscopy(&state->current, &state->best, _state);
    infinitiesdetected = ae_false;
    failedtorecoverfrominfinities = ae_false;
    lambdafound = ae_false;
    state->subsequentfactfailures = 0;
    firstouteriteration = ae_true;
    gipm_barrierpenaltyandcmplerror(state, &state->current, &state->currentfj, state->mu, &f0, &p0, &cmpl0, _state);
    lastprim = ae_sqrt(ae_maxrealnumber, _state);
    lastdual = lastprim;
    lastcmpl = lastprim;
    gnrm = (double)(1);
    maxabsf = (double)(0);
lbl_11:
    if( ae_false )
    {
        goto lbl_12;
    }
    
    /*
     * Reset parameters and flags at the beginning of inner iterations
     */
    unboundednesssuspected = ae_false;
    lastcheckpointp = ae_maxrealnumber;
    lastcheckpointd = ae_maxrealnumber;
    lastcheckpointc = ae_maxrealnumber;
    lastcheckpointidx = state->repiterationscount;
    innerloopstart = state->repiterationscount;
    state->rhsstagnationcounter = 0;
    goodinnerits = 0;
    if( ae_fp_greater_eq(state->lambdav,gipm_lambdamax) )
    {
        if( state->dotrace )
        {
            ae_trace("> regularization is at maximum, resetting to 1.0\n");
        }
        state->lambdav = (double)(1);
        state->lambdadecay = (double)(1);
    }
    state->filterready = ae_false;
    state->nonmonotonicmemlen = -1;
    minmeritv0 = ae_maxrealnumber;
    curmeritv0 = minmeritv0;
    
    /*
     * If the problem formulation is mu-dependent, recompute target/objective using
     * current value of Mu.
     */
    if( !state->mudependent )
    {
        goto lbl_13;
    }
    state->requesttype = 1;
    state->querymu = state->mu;
    rallocv(nraw+mtotal, &state->querydata, _state);
    rcopyvx(nraw, &state->current.xp, 0, &state->querydata, 0, _state);
    rcopyvx(mflex, &state->current.yp, 0, &state->querydata, nraw, _state);
    rcopyvx(mhard, &state->current.yv, 0, &state->querydata, nraw+mflex, _state);
    rallocv(1+mtotal, &state->replyfi, _state);
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    vfjinitfromsparse(&state->current.xp, nraw, &state->replyfi, 1+mtotal, &state->replysj, &state->currentfj, _state);
lbl_13:
    
    /*
     * Inner iterations for fixed Mu.
     *
     * After this loop is done we have current point in State.Current/State.CurrentFJ and
     * current residual in State.RHS. Additionally, the current point is remembered as the
     * one where the Hessian should be calculated.
     */
lbl_15:
    if( ae_false )
    {
        goto lbl_16;
    }
    if( userterminationneeded )
    {
        goto lbl_16;
    }
    if( state->maxits>0&&state->repiterationscount>=state->maxits )
    {
        if( state->dotrace )
        {
            ae_trace("> maximum number of iterations performed, stopping\n");
        }
        state->repterminationtype = 5;
        goto lbl_16;
    }
    
    /*
     * Trace iteration start
     */
    if( state->dotrace )
    {
        ae_trace("\n=== ITERATION %5d STARTED ========================================================================\n",
            (int)(state->repiterationscount));
        ae_trace("mu            = %11.3e\n",
            (double)(state->mu));
        ae_trace("lambda        = %11.3e (damping term for the Hessian)\n",
            (double)(state->lambdav));
        ae_trace("lambda_decay  = %7.4f     (decay for the damping term)\n",
            (double)(state->lambdadecay));
        ae_trace("|x|inf        = %11.3e\n",
            (double)(rmaxabsv(nraw, &state->current.xp, _state)));
        ae_trace("|y|inf        = %11.3e\n",
            (double)(ae_maxreal(rmaxabsv(mflex, &state->current.yp, _state), rmaxabsv(mhard, &state->current.yv, _state), _state)));
        ae_trace("\n");
    }
    
    /*
     * Print variable stats if necessary
     */
    if( state->dotrace )
    {
        ae_trace("> var stats     min.abs     max.abs    \n");
        ae_trace("x (prim)        %11.3e %11.3e\n",
            (double)(gipm_minabsnz(&state->current.xp, nraw, _state)),
            (double)(rmaxabsv(nraw, &state->current.xp, _state)));
        ae_trace("x (slck)        %11.3e %11.3e\n",
            (double)(gipm_minabsnz(&state->current.xs, mflex, _state)),
            (double)(rmaxabsv(mflex, &state->current.xs, _state)));
        ae_trace("g (lo)          %11.3e %11.3e\n",
            (double)(gipm_minabsnz(&state->current.gl, mflex, _state)),
            (double)(rmaxabsv(mflex, &state->current.gl, _state)));
        ae_trace("g (hi)          %11.3e %11.3e\n",
            (double)(gipm_minabsnz(&state->current.gu, mflex, _state)),
            (double)(rmaxabsv(mflex, &state->current.gu, _state)));
        ae_trace("z (prim.lo)     %11.3e %11.3e\n",
            (double)(gipm_minabsnz(&state->current.zlp, nraw, _state)),
            (double)(rmaxabsv(nraw, &state->current.zlp, _state)));
        ae_trace("z (prim.hi)     %11.3e %11.3e\n",
            (double)(gipm_minabsnz(&state->current.zup, nraw, _state)),
            (double)(rmaxabsv(nraw, &state->current.zup, _state)));
        ae_trace("y (prim)        %11.3e %11.3e\n",
            (double)(gipm_minabsnz(&state->current.yp, mflex, _state)),
            (double)(rmaxabsv(mflex, &state->current.yp, _state)));
        ae_trace("y (lo)          %11.3e %11.3e\n",
            (double)(gipm_minabsnz(&state->current.yl, mflex, _state)),
            (double)(rmaxabsv(mflex, &state->current.yl, _state)));
        ae_trace("y (hi)          %11.3e %11.3e\n",
            (double)(gipm_minabsnz(&state->current.yu, mflex, _state)),
            (double)(rmaxabsv(mflex, &state->current.yu, _state)));
        ae_trace("y (hard)        %11.3e %11.3e\n",
            (double)(gipm_minabsnz(&state->current.yv, mhard, _state)),
            (double)(rmaxabsv(mhard, &state->current.yv, _state)));
        ae_trace("z (slck.lo)     %11.3e %11.3e\n",
            (double)(gipm_minabsnz(&state->current.zls, mflex, _state)),
            (double)(rmaxabsv(mflex, &state->current.zls, _state)));
        ae_trace("z (slck.hi)     %11.3e %11.3e\n",
            (double)(gipm_minabsnz(&state->current.zus, mflex, _state)),
            (double)(rmaxabsv(mflex, &state->current.zus, _state)));
        if( state->dodetailedtrace )
        {
            ae_trace("> printing detailed data\n");
            ae_trace("X (scaled)    = ");
            tracevectorautoprec(&state->current.xp, 0, nraw, _state);
            ae_trace("\n");
            ae_trace("XS (scaled)   = ");
            tracevectorautoprec(&state->current.xs, 0, mflex, _state);
            ae_trace("\n");
        }
    }
    
    /*
     * Compute residual for the non-condensed unsymmetric primal-dual system.
     * Compute erorrs, check stopping and stagnation criteria.
     */
    gipm_computerhs(state, &state->current, &state->currentfj, state->mu, &state->rhs, &eprim, &edual, &ecmpl, &v, &vv, &v2, _state);
    gnrm = ae_sqrt(rdotv2(nraw, &state->rhs.rp, _state), _state);
    rhs0 = rmax3(eprim, edual, ecmpl, _state);
    if( state->dotrace )
    {
        ae_trace("> computing absolute errors:\n");
        ae_trace("primal        = %0.3e\n",
            (double)(eprim));
        ae_trace("dual          = %0.3e\n",
            (double)(edual));
        ae_trace("compl         = %0.3e\n",
            (double)(ecmpl));
        ae_trace("rhs.max       = %0.3e\n",
            (double)(rhs0));
    }
    if( (ae_fp_less(eprim,gipm_checkpointacceptance*lastcheckpointp)||ae_fp_less(edual,gipm_checkpointacceptance*lastcheckpointd))||ae_fp_less(ecmpl,gipm_checkpointacceptance*lastcheckpointc) )
    {
        
        /*
         * Significant progress achieved for at least one parameter. Save checkpoint (used to detect stagnation).
         *
         * NOTE: it is important that the comparison is strict! Non-strict comparison is likely to cause inability to
         *       detect stagnation should any error become exactly zero.
         */
        ae_assert((ae_fp_neq(eprim,lastcheckpointp)||ae_fp_neq(edual,lastcheckpointd))||ae_fp_neq(ecmpl,lastcheckpointc), "GIPM: integrity check 1500 failed", _state);
        lastcheckpointp = ae_minreal(lastcheckpointp, eprim, _state);
        lastcheckpointd = ae_minreal(lastcheckpointd, edual, _state);
        lastcheckpointc = ae_minreal(lastcheckpointc, ecmpl, _state);
        lastcheckpointidx = state->repiterationscount;
    }
    if( ae_fp_less_eq(rmax3(eprim/v, edual/vv, ecmpl/v2, _state),state->mu) )
    {
        
        /*
         * NOTE: the additional 0.9 multiplier prevents the situation when internal iteration always finishes with error
         *       just below Eps, but outer iteration always sees the error just above Eps (due to mu being set to zero).
         */
        if( state->dotrace )
        {
            ae_trace("> stopping criteria for inner iterations met (relative error is small enough), exit to outer iterations\n");
        }
        goto lbl_16;
    }
    if( ae_fp_greater_eq(state->lambdav,gipm_lambdamax) )
    {
        if( state->dotrace )
        {
            ae_trace("> regularization is at maximum, stopping inner iterations\n");
        }
        state->lambdav = (double)(1);
        state->lambdadecay = (double)(1);
        goto lbl_16;
    }
    if( !firstouteriteration )
    {
        
        /*
         * Two stopping criteria below are checked only after the first outer iteration is done
         */
        if( ae_fp_greater_eq(state->mu,0.1*ae_sqrt(ae_machineepsilon, _state)) )
        {
            
            /*
             * Midgame stopping condition: RHS stagnation
             */
            if( firstouteriteration )
            {
                state->rhsstagnationcounter = 0;
            }
            if( ae_fp_greater(ae_fabs(eprim-lastprim, _state),rmaxabs2(gipm_rhsstagnationtol1*lastprim, gipm_rhsstagnationtol2*state->eps, _state)) )
            {
                state->rhsstagnationcounter = 0;
            }
            if( ae_fp_greater(ae_fabs(edual-lastdual, _state),rmaxabs2(gipm_rhsstagnationtol1*lastdual, gipm_rhsstagnationtol2*state->eps, _state)) )
            {
                state->rhsstagnationcounter = 0;
            }
            if( ae_fp_greater(ae_fabs(ecmpl-lastcmpl, _state),rmaxabs2(gipm_rhsstagnationtol1*lastcmpl, gipm_rhsstagnationtol2*state->eps, _state)) )
            {
                state->rhsstagnationcounter = 0;
            }
            if( state->rhsstagnationcounter>gipm_maxrhsstagnationits )
            {
                if( state->dotrace )
                {
                    ae_trace("> RHS stagnated (no significant changes for %0d its)\n",
                        (int)(gipm_maxrhsstagnationits));
                }
                goto lbl_16;
            }
        }
    }
    if( !firstouteriteration||ae_fp_less_eq(rmax3(eprim/v, edual/vv, ecmpl/v2, _state),gipm_firstiterationcheckpointstagnationeps) )
    {
        
        /*
         * This stopping condition is checked at the first outer iteration, in the midgame and at the late phase
         */
        if( state->repiterationscount>lastcheckpointidx+gipm_maxcheckpointstagnationits )
        {
            if( state->dotrace )
            {
                ae_trace("> iteration stagnated (no new checkpoints for %0d its)\n",
                    (int)(gipm_maxcheckpointstagnationits));
            }
            goto lbl_16;
        }
    }
    
    /*
     * Condense LHS (system matrix) and perform modified RCOMM request to factorize it
     */
    gipm_condenselhs(state, &state->current, &state->currentfj, state->mu, &state->condensed, _state);
    state->requesttype = 1001;
    state->factsuccess = ae_false;
    rallocv(nxh, &state->querydata, _state);
    rcopyvx(nraw, &state->condensed.d0, 0, &state->querydata, 0, _state);
    rcopyvx(mflex, &state->condensed.d1, 0, &state->querydata, nraw, _state);
    rcopyvx(mhard, &state->condensed.d1v, 0, &state->querydata, nraw+mflex, _state);
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    if( !state->factsuccess )
    {
        if( state->subsequentfactfailures<=gipm_maxsubsequentfailuresforweakgrowth )
        {
            
            /*
             * Slowly growing Lambda
             */
            if( state->dotrace )
            {
                ae_trace("> factorization failed, Hessian is not sufficiently positive definite, increasing damping factor\n");
            }
            state->lambdav = rcase2(lambdafound, gipm_lambdagrowth*coalesce(state->lambdav, state->mu, _state), coalesce((double)10*state->lambdav, (double)10*state->mu, _state), _state);
            state->lambdadecay = 1.0;
        }
        else
        {
            
            /*
             * Many subsequent failures, rapid growth
             */
            if( state->dotrace )
            {
                ae_trace("> factorization failed too many times in a row, rapid growth of LambdaV is activated\n");
            }
            state->lambdav = ae_maxreal(gipm_lambdarapidgrowth*state->lambdav, 0.001*state->mu, _state);
            state->lambdadecay = 1.0;
        }
        state->subsequentfactfailures = state->subsequentfactfailures+1;
        goto lbl_15;
    }
    state->subsequentfactfailures = 0;
    lambdafound = lambdafound||ae_fp_neq(state->lambdav,(double)(0));
    
    /*
     * Run iterative refinement
     */
    rhsk = ae_maxrealnumber;
    gipm_varsinitbyzero(&state->delta, nraw, mflex, mhard, _state);
    rfsidx = 0;
lbl_17:
    if( rfsidx>gipm_maxrfsits-1 )
    {
        goto lbl_19;
    }
    gipm_condenserhs(state, &state->current, &state->currentfj, state->mu, &state->rhs, &state->condensed, _state);
    state->requesttype = 1002;
    rallocv(nraw+mtotal, &state->querydata, _state);
    rcopyvx(nraw, &state->condensed.wx, 0, &state->querydata, 0, _state);
    rcopyvx(mflex, &state->condensed.wy, 0, &state->querydata, nraw, _state);
    rcopyvx(mhard, &state->condensed.wyv, 0, &state->querydata, nraw+mflex, _state);
    rallocv(nraw+mtotal, &state->replysol, _state);
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    gipm_unpacksolution(state, &state->current, &state->currentfj, state->mu, &state->rhs, &state->condensed, &state->replysol, &state->corr, _state);
    gipm_varsapplystep(&state->delta, &state->corr, 1.0, _state);
    state->requesttype = 1003;
    rallocv(nraw+mtotal, &state->querydata, _state);
    rallocv(nraw+mtotal+nraw, &state->replyprod, _state);
    rcopyvx(nraw, &state->corr.xp, 0, &state->querydata, 0, _state);
    rcopyvx(mflex, &state->corr.yp, 0, &state->querydata, nraw, _state);
    rcopyvx(mhard, &state->corr.yv, 0, &state->querydata, nraw+mflex, _state);
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    rallocv(nraw, &state->tmphdxp, _state);
    rallocv(mtotal, &state->tmpjdxp, _state);
    rallocv(nraw, &state->tmpjtdy, _state);
    rcopyvx(nraw, &state->replyprod, 0, &state->tmphdxp, 0, _state);
    rcopyvx(mtotal, &state->replyprod, nraw, &state->tmpjdxp, 0, _state);
    rcopyvx(nraw, &state->replyprod, nraw+mtotal, &state->tmpjtdy, 0, _state);
    rhsk = gipm_computeupdateresidual(state, &state->current, &state->currentfj, state->mu, state->lambdav, &state->corr, &state->tmphdxp, &state->tmpjdxp, &state->tmpjtdy, &state->rhs, _state);
    if( state->dotrace )
    {
        ae_trace("> analyzing solution to the linear system, relative residual norm is %0.2e\n",
            (double)(rhsk/coalesce(rhs0, (double)(1), _state)));
    }
    if( ae_fp_less(rhsk,gipm_rhstol*rhs0) )
    {
        if( state->dotrace )
        {
            ae_trace("> residual norm is small enough, stopping refinement\n");
        }
        goto lbl_19;
    }
    rfsidx = rfsidx+1;
    goto lbl_17;
lbl_19:
    if( ae_fp_greater(rhsk,ae_maxreal((double)2*gipm_rhstol, 0.001, _state)*rhs0) )
    {
        if( state->dotrace )
        {
            ae_trace("> iterative refinement failed to sufficiently decrease residual, increasing Lambda and retrying\n");
        }
        state->lambdav = coalesce((double)10*state->lambdav, (double)1000*ae_machineepsilon*((double)1/state->mu), _state);
        state->lambdadecay = 1.0;
        state->repiterationscount = state->repiterationscount+1;
        goto lbl_15;
    }
    
    /*
     * Perform line search
     */
    if( state->dodetailedtrace )
    {
        ae_trace("> printing detailed step information\n");
        ae_trace("dX            = ");
        tracevectorautoprec(&state->delta.xp, 0, nraw, _state);
        ae_trace("\n");
        ae_trace("dXS           = ");
        tracevectorautoprec(&state->delta.xs, 0, mflex, _state);
        ae_trace("\n");
    }
    alpha = gipm_computesteplength(state, &state->current, &state->delta, _state);
    if( state->dotrace )
    {
        ae_trace("> computed maximum step length: alpha=%0.6f\n",
            (double)(alpha));
    }
    gipm_barrierpenaltyandcmplerror(state, &state->current, &state->currentfj, state->mu, &f0, &p0, &cmpl0, _state);
    merit0 = gipm_meritfunctionpd(state, &state->current, &state->currentfj, state->mu, _state);
    if( !state->filterready )
    {
        nlpfinitx((double)10*(p0+cmpl0+(double)10), gipm_maxnd, &state->filter, _state);
        state->filterready = ae_true;
    }
    if( state->nonmonotonicmemlen<0 )
    {
        gipm_nonmonotonicinit(state, gipm_maxnonmonotonic, ae_sqrt(ae_maxrealnumber, _state), _state);
    }
    maxabsf = ae_maxreal(maxabsf, ae_fabs(f0, _state), _state);
    if( state->dotrace )
    {
        ae_trace("> line search starts: f_barr = %14.6e    l2_p = %14.6e    cmpl = %14.6e    mrtf = %14.6e\n",
            (double)(f0),
            (double)(p0),
            (double)(cmpl0),
            (double)(merit0));
    }
    smalltrials = 0;
    btits = 0;
lbl_20:
    if( ae_false )
    {
        goto lbl_21;
    }
    
    /*
     * Preliminary checks
     */
    if( ae_fp_less(alpha,gipm_smallstep) )
    {
        smalltrials = smalltrials+1;
    }
    if( smalltrials>=gipm_maxsmalltrials )
    {
        alpha = (double)(0);
        goto lbl_21;
    }
    
    /*
     * Compute merit function and complementarity error at the trial point
     */
    gipm_varscopy(&state->current, &state->trial, _state);
    gipm_varsapplystep(&state->trial, &state->delta, alpha, _state);
    rmergemaxv(nraw, &state->safeguardbndlp, &state->trial.xp, _state);
    rmergeminv(nraw, &state->safeguardbndup, &state->trial.xp, _state);
    state->requesttype = 1;
    state->querymu = state->mu;
    rallocv(nraw+mtotal, &state->querydata, _state);
    rcopyvx(nraw, &state->trial.xp, 0, &state->querydata, 0, _state);
    rcopyvx(mflex, &state->trial.yp, 0, &state->querydata, nraw, _state);
    rcopyvx(mhard, &state->trial.yv, 0, &state->querydata, nraw+mflex, _state);
    rallocv(1+mtotal, &state->replyfi, _state);
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    vfjinitfromsparse(&state->trial.xp, nraw, &state->replyfi, 1+mtotal, &state->replysj, &state->trialfj, _state);
    gipm_barrierpenaltyandcmplerror(state, &state->trial, &state->trialfj, state->mu, &f1, &p1, &cmpl1, _state);
    merit1 = gipm_meritfunctionpd(state, &state->trial, &state->trialfj, state->mu, _state);
    
    /*
     * Control violation of hard constraints
     */
    violated = ae_false;
    for(i=0; i<=mhard-1; i++)
    {
        if( ae_fp_greater_eq(state->trialfj.fi.ptr.p_double[1+mflex+i],state->finitebndux.ptr.p_double[nxf+i]) )
        {
            if( state->dotrace )
            {
                ae_trace("> stp=%11.3e, hard inequality %0d is violated, decreasing step\n",
                    (double)(alpha),
                    (int)(i));
            }
            btits = btits+1;
            alpha = 0.5*alpha;
            violated = ae_true;
            break;
        }
    }
    if( violated )
    {
        goto lbl_20;
    }
    
    /*
     * Report step
     */
    if( state->dotrace )
    {
        ae_trace("> stp=%11.3e, delta:      %14.6e           %14.6e           %14.6e           %14.6e\n",
            (double)(alpha),
            (double)(f1-f0),
            (double)(p1-p0),
            (double)(cmpl1-cmpl0),
            (double)(merit1-merit0));
    }
    
    /*
     * Markov filter or classic filter
     */
    if( (!state->isfirstorder||state->repiterationscount-innerloopstart<gipm_nonmonotonicdelay)||ae_fp_less(merit1,merit0+gipm_nonmonotonicgetcorr(state, merit0, _state)) )
    {
        if( (ae_fp_less(merit1,merit0)&&ae_fp_less(p1,(double)5*(p0+(double)1)))&&ae_fp_less(cmpl1,(double)5*(cmpl0+(double)1)) )
        {
            goto lbl_21;
        }
        if( (ae_fp_less(f1,f0)&&ae_fp_less(p1,(double)5*(p0+(double)1)))&&ae_fp_less(cmpl1,(double)5*(cmpl0+(double)1)) )
        {
            goto lbl_21;
        }
        if( (ae_fp_less(p1,p0)&&ae_fp_less(f1,(double)5*(ae_fabs(f0, _state)+(double)1)))&&ae_fp_less(cmpl1,(double)5*(cmpl0+(double)1)) )
        {
            goto lbl_21;
        }
        if( (ae_fp_less(cmpl1,cmpl0)&&ae_fp_less(f1,(double)2*(ae_fabs(f0, _state)+(double)1)))&&ae_fp_less(p1,(double)2*(p0+(double)1)) )
        {
            goto lbl_21;
        }
    }
    
    /*
     * The step is not accepted, decrease Alpha
     */
    btits = btits+1;
    alpha = 0.5*alpha;
    goto lbl_20;
lbl_21:
    if( btits>=3 )
    {
        state->lambdav = ae_maxreal((double)2*state->lambdav, gnrm, _state);
        state->lambdadecay = 1.0;
        if( state->dotrace )
        {
            ae_trace("> too many backtracking its performed, increasing regularization coeff LambdaV to %0.2e\n",
                (double)(state->lambdav));
        }
    }
    if( ae_fp_neq(alpha,(double)(0)) )
    {
        goto lbl_22;
    }
    
    /*
     * Line search failed, return back to the original point. We just need the caller to remember it,
     * so we actually do not use returned values. A suboptimal solution, but this line of code is
     * VERY rarely executed.
     */
    if( state->dotrace )
    {
        ae_trace("> line search failed to provide sufficient decrease, stopping inner iterations\n");
    }
    state->requesttype = 1;
    state->querymu = state->mu;
    rallocv(nraw+mtotal, &state->querydata, _state);
    rcopyvx(nraw, &state->current.xp, 0, &state->querydata, 0, _state);
    rcopyvx(mflex, &state->current.yp, 0, &state->querydata, nraw, _state);
    rcopyvx(mhard, &state->current.yv, 0, &state->querydata, nraw+mflex, _state);
    rallocv(1+mtotal, &state->replyfi, _state);
    state->rstate.stage = 6;
    goto lbl_rcomm;
lbl_6:
    state->repiterationscount = state->repiterationscount+1;
    goto lbl_16;
lbl_22:
    if( state->dotrace )
    {
        ae_trace("> line search finished with alpha=%0.2e\n",
            (double)(alpha));
        ae_trace("> f:         %0.9e -> %0.9e (delta=%0.3e)\n",
            (double)(state->currentfj.fi.ptr.p_double[0]),
            (double)(state->trialfj.fi.ptr.p_double[0]),
            (double)(state->trialfj.fi.ptr.p_double[0]-state->currentfj.fi.ptr.p_double[0]));
        ae_trace("> f_barrier: %0.9e -> %0.9e (delta=%0.3e)\n",
            (double)(f0),
            (double)(f1),
            (double)(f1-f0));
    }
    if( !state->isfirstorder )
    {
        goto lbl_24;
    }
    
    /*
     * Send quasi-Newton update
     */
    gipm_preparequasinewtonupdate(state, _state);
    state->requesttype = 1004;
    state->rstate.stage = 7;
    goto lbl_rcomm;
lbl_7:
lbl_24:
    gipm_varscopy(&state->trial, &state->current, _state);
    vfjcopy(&state->trialfj, &state->currentfj, _state);
    if( firstouteriteration )
    {
        nlpfappend(&state->filter, f0, p0+cmpl0, _state);
    }
    gipm_nonmonotonicpush(state, merit1, _state);
    curmeritv0 = gipm_meritv0(state, &state->current, &state->currentfj, state->mu, _state);
    minmeritv0 = ae_minreal(minmeritv0, curmeritv0, _state);
    if( state->dotrace )
    {
        ae_trace("> meritV0: min=%0.6e, cur=%0.6e\n",
            (double)(minmeritv0),
            (double)(curmeritv0));
    }
    
    /*
     * Check for unboundedness
     */
    v = rmaxabsv(nraw, &state->current.xp, _state);
    if( ae_fp_greater(v,curbigval)&&ae_fp_greater(v,curbiggrowth*state->nrmx0) )
    {
        if( state->dotrace )
        {
            ae_trace("> |x| tends to infinity, suspecting unboundedness\n");
        }
        unboundednesssuspected = ae_true;
    }
    vv = state->currentfj.fi.ptr.p_double[0];
    if( ae_fp_less(vv,-curbigval)&&ae_fp_less(vv,-curbiggrowth*state->absf0) )
    {
        if( state->dotrace )
        {
            ae_trace("> f tends to negative infinity, suspecting unboundedness\n");
        }
        unboundednesssuspected = ae_true;
    }
    if( unboundednesssuspected )
    {
        curbigval = rmaxabs3(curbigval, v, vv, _state);
        state->repiterationscount = state->repiterationscount+1;
        goto lbl_16;
    }
    
    /*
     * If we run the very first outer iteration, check whether we need to increase Mu.
     * Sometimes initial value of the Mu is too low, so we need a way to increase it.
     * Such increases are performed only during the first outer iteration; subsequent
     * iterations see only monotonic decrease.
     */
    if( !firstouteriteration )
    {
        goto lbl_26;
    }
    v = 1.0;
    v = ae_maxreal(v, rmaxabsv(nraw, &state->current.xp, _state), _state);
    v = ae_maxreal(v, rmaxabsv(mflex, &state->current.xs, _state), _state);
    v = ae_maxreal(v, rmaxabsv(mflex, &state->current.gl, _state), _state);
    v = ae_maxreal(v, rmaxabsv(mflex, &state->current.gu, _state), _state);
    v = ae_minreal(ae_pow(v, 0.75, _state)*gipm_initmu, gipm_maxmu, _state);
    if( ae_fp_less_eq(v,1.41*state->mu) )
    {
        goto lbl_28;
    }
    if( state->dotrace )
    {
        ae_trace("> increasing Mu to %0.2e according to the growth of the primal variables, reseting the filter\n",
            (double)(v));
    }
    state->mu = v;
    lastcheckpointp = ae_maxrealnumber;
    lastcheckpointd = ae_maxrealnumber;
    lastcheckpointc = ae_maxrealnumber;
    lastcheckpointidx = state->repiterationscount;
    if( !state->mudependent )
    {
        goto lbl_30;
    }
    
    /*
     * If the problem formulation is mu-dependent, recompute target/objective using
     * current value of Mu.
     */
    state->requesttype = 1;
    state->querymu = state->mu;
    rallocv(nraw+mtotal, &state->querydata, _state);
    rcopyvx(nraw, &state->current.xp, 0, &state->querydata, 0, _state);
    rcopyvx(mflex, &state->current.yp, 0, &state->querydata, nraw, _state);
    rcopyvx(mhard, &state->current.yv, 0, &state->querydata, nraw+mflex, _state);
    rallocv(1+mtotal, &state->replyfi, _state);
    state->rstate.stage = 8;
    goto lbl_rcomm;
lbl_8:
    vfjinitfromsparse(&state->current.xp, nraw, &state->replyfi, 1+mtotal, &state->replysj, &state->currentfj, _state);
    gipm_barrierpenaltyandcmplerror(state, &state->current, &state->currentfj, state->mu, &f1, &p1, &cmpl1, _state);
lbl_30:
    state->filterready = ae_false;
    state->nonmonotonicmemlen = -1;
    curmeritv0 = gipm_meritv0(state, &state->current, &state->currentfj, state->mu, _state);
    minmeritv0 = curmeritv0;
lbl_28:
lbl_26:
    
    /*
     * Next iteration
     */
    lastprim = eprim;
    lastdual = edual;
    lastcmpl = ecmpl;
    if( ae_fp_neq(state->lambdav,(double)(0)) )
    {
        state->lambdav = state->lambdav*state->lambdadecay;
        state->lambdadecay = ae_maxreal(state->lambdadecay*gipm_lambdadecayacc, ae_sqrt(ae_sqrt(ae_machineepsilon, _state), _state), _state);
        if( ae_fp_less(state->lambdav,state->mu*ae_machineepsilon) )
        {
            state->lambdav = (double)(0);
            state->lambdadecay = (double)(0);
        }
    }
    state->repiterationscount = state->repiterationscount+1;
    state->rhsstagnationcounter = state->rhsstagnationcounter+1;
    goodinnerits = goodinnerits+1;
    goto lbl_15;
lbl_16:
    firstouteriteration = ae_false;
    if( state->dotrace )
    {
        ae_trace("\n----- outer iteration ------------------------------------------------------------------------------\n");
    }
    if( userterminationneeded )
    {
        if( state->dotrace )
        {
            ae_trace("> user requested immediate termination, stopping\n");
        }
        state->repterminationtype = 8;
        goto lbl_12;
    }
    if( state->repterminationtype!=0 )
    {
        goto lbl_12;
    }
    if( unboundednesssuspected )
    {
        if( (ae_fp_less_eq(state->mu,1.001*mumin)&&ae_fp_greater_eq(curbigval,0.999*gipm_maxbigval))&&ae_fp_greater_eq(curbiggrowth,0.999*gipm_maxbiggrowth) )
        {
            if( state->dotrace )
            {
                ae_trace("> unboundedness detected (mu at minimum, thresholds at maximum, the problem is still unbounded)\n");
            }
            state->repterminationtype = -4;
            goto lbl_12;
        }
        if( state->dotrace )
        {
            ae_trace("> decreasing mu, increasing unboundedness detection thresholds, continuing iterations\n");
        }
        state->mu = ae_maxreal(mumin, 0.01*state->mu, _state);
        curbigval = ae_minreal(gipm_vggrowth*curbigval, gipm_maxbigval, _state);
        curbiggrowth = ae_minreal(gipm_vggrowth*curbiggrowth, gipm_maxbiggrowth, _state);
        goto lbl_11;
    }
    
    /*
     * Recompute RHS from scratch (less dependency on the internal loop).
     * Check primal/dual/complementarity errors for the original problem (mu=0).
     */
    if( !state->mudependent )
    {
        goto lbl_32;
    }
    
    /*
     * If the problem formulation is mu-dependent, recompute target/objective using zero Mu.
     */
    state->requesttype = 1;
    state->querymu = 0.0;
    rallocv(nraw+mtotal, &state->querydata, _state);
    rcopyvx(nraw, &state->current.xp, 0, &state->querydata, 0, _state);
    rcopyvx(mflex, &state->current.yp, 0, &state->querydata, nraw, _state);
    rcopyvx(mhard, &state->current.yv, 0, &state->querydata, nraw+mflex, _state);
    rallocv(1+mtotal, &state->replyfi, _state);
    state->rstate.stage = 9;
    goto lbl_rcomm;
lbl_9:
    vfjinitfromsparse(&state->current.xp, nraw, &state->replyfi, 1+mtotal, &state->replysj, &state->currentfj, _state);
lbl_32:
    gipm_computerhs(state, &state->current, &state->currentfj, 0.0, &state->rhs, &eprim, &edual, &ecmpl, &v, &vv, &v2, _state);
    if( state->dotrace )
    {
        ae_trace("> computing absolute errors for the original problem (mu=0):\n");
        ae_trace("primal        = %0.3e\n",
            (double)(eprim));
        ae_trace("dual          = %0.3e\n",
            (double)(edual));
        ae_trace("compl         = %0.3e\n",
            (double)(ecmpl));
    }
    if( ae_fp_less(rmax3(eprim, edual, ecmpl, _state),state->besterr) )
    {
        
        /*
         * Save the best point so far
         */
        gipm_varscopy(&state->current, &state->best, _state);
        vfjcopy(&state->currentfj, &state->bestfj, _state);
        state->besterr = rmax3(eprim, edual, ecmpl, _state);
        if( state->dotrace )
        {
            ae_trace("> the point provides the best prim/dual/cmpl errors so far, saving\n");
        }
    }
    else
    {
        if( state->dotrace )
        {
            ae_trace("> the point does not improve over the best prim/dual/cmpl errors so far, not saved\n");
        }
    }
    if( ae_fp_less_eq(rmax3(eprim/v, edual/vv, ecmpl/v2, _state),state->eps) )
    {
        if( state->dotrace )
        {
            ae_trace("> stopping criteria for outer iterations met (relative error is below Eps=%0.2e), solved\n",
                (double)(state->eps));
        }
        state->repterminationtype = 2;
        goto lbl_12;
    }
    if( ae_fp_less_eq(state->mu,1.001*mumin) )
    {
        if( state->dotrace )
        {
            ae_trace("> mu is at minimum, stagnation detected, stopping\n");
        }
        state->repterminationtype = 7;
        goto lbl_12;
    }
    
    /*
     * Inner iterations are over, tighten/relax bounds if necessary and decrease Mu
     */
    gipm_tightenrelaxbounds(state, _state);
    state->mu = ae_maxreal(mumin, ae_minreal(gipm_mudecaylinear*state->mu, ae_pow(state->mu, gipm_mudecaypower, _state), _state), _state);
    goto lbl_11;
lbl_12:
    
    /*
     * Almost done:
     * * compare error at State.Current with one at State.Best, restore from State.Best
     *   if optimizer went in wrong direction
     * * set completion flags
     */
    if( state->dotrace )
    {
        ae_trace("\n=== STOPPED ========================================================================================\n");
    }
    if( !state->mudependent )
    {
        goto lbl_34;
    }
    
    /*
     * If the problem formulation is mu-dependent, recompute target/objective using zero Mu.
     */
    state->requesttype = 1;
    state->querymu = 0.0;
    rallocv(nraw+mtotal, &state->querydata, _state);
    rcopyvx(nraw, &state->current.xp, 0, &state->querydata, 0, _state);
    rcopyvx(mflex, &state->current.yp, 0, &state->querydata, nraw, _state);
    rcopyvx(mhard, &state->current.yv, 0, &state->querydata, nraw+mflex, _state);
    rallocv(1+mtotal, &state->replyfi, _state);
    state->rstate.stage = 10;
    goto lbl_rcomm;
lbl_10:
    vfjinitfromsparse(&state->current.xp, nraw, &state->replyfi, 1+mtotal, &state->replysj, &state->currentfj, _state);
lbl_34:
    gipm_computerhs(state, &state->current, &state->currentfj, 0.0, &state->rhs, &eprim, &edual, &ecmpl, &v, &vv, &v2, _state);
    if( ae_fp_greater(rmax3(eprim, edual, ecmpl, _state),state->besterr) )
    {
        
        /*
         * Restore best point so far
         */
        gipm_varscopy(&state->best, &state->current, _state);
        vfjcopy(&state->bestfj, &state->currentfj, _state);
        if( state->dotrace )
        {
            ae_trace("> the last accepted point is worse than the best one seen so far (p/d/c err: %0.2e vs %0.2e), restoring the best one\n",
                (double)(rmax3(eprim, edual, ecmpl, _state)),
                (double)(state->besterr));
        }
    }
    if( infinitiesdetected )
    {
        state->repterminationtype = icase2(failedtorecoverfrominfinities, -8, state->repterminationtype+800, _state);
    }
    stimerstop(&state->timertotal, _state);
    if( state->dotrace )
    {
        if( infinitiesdetected&&failedtorecoverfrominfinities )
        {
            ae_trace("> infinities were detected, but we failed to recover by decreasing trust radius; declaring failure with the code -8\n");
        }
        ae_trace("raw target:     %20.12e\n",
            (double)(state->currentfj.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0]));
        ae_trace("total time:     %10.1f ms\n",
            (double)(stimergetms(&state->timertotal, _state)));
    }
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = nraw;
    state->rstate.ia.ptr.p_int[1] = mtotal;
    state->rstate.ia.ptr.p_int[2] = mflex;
    state->rstate.ia.ptr.p_int[3] = mhard;
    state->rstate.ia.ptr.p_int[4] = nxf;
    state->rstate.ia.ptr.p_int[5] = nxh;
    state->rstate.ia.ptr.p_int[6] = i;
    state->rstate.ia.ptr.p_int[7] = rfsidx;
    state->rstate.ia.ptr.p_int[8] = j;
    state->rstate.ia.ptr.p_int[9] = innerloopstart;
    state->rstate.ia.ptr.p_int[10] = smalltrials;
    state->rstate.ia.ptr.p_int[11] = lastcheckpointidx;
    state->rstate.ia.ptr.p_int[12] = btits;
    state->rstate.ia.ptr.p_int[13] = goodinnerits;
    state->rstate.ba.ptr.p_bool[0] = firstouteriteration;
    state->rstate.ba.ptr.p_bool[1] = infinitiesdetected;
    state->rstate.ba.ptr.p_bool[2] = failedtorecoverfrominfinities;
    state->rstate.ba.ptr.p_bool[3] = unboundednesssuspected;
    state->rstate.ba.ptr.p_bool[4] = lambdafound;
    state->rstate.ba.ptr.p_bool[5] = accepted;
    state->rstate.ba.ptr.p_bool[6] = violated;
    state->rstate.ra.ptr.p_double[0] = v;
    state->rstate.ra.ptr.p_double[1] = vv;
    state->rstate.ra.ptr.p_double[2] = v2;
    state->rstate.ra.ptr.p_double[3] = alpha;
    state->rstate.ra.ptr.p_double[4] = f0;
    state->rstate.ra.ptr.p_double[5] = f1;
    state->rstate.ra.ptr.p_double[6] = p0;
    state->rstate.ra.ptr.p_double[7] = p1;
    state->rstate.ra.ptr.p_double[8] = cmpl0;
    state->rstate.ra.ptr.p_double[9] = cmpl1;
    state->rstate.ra.ptr.p_double[10] = gnrm;
    state->rstate.ra.ptr.p_double[11] = initprimdual;
    state->rstate.ra.ptr.p_double[12] = eprim;
    state->rstate.ra.ptr.p_double[13] = edual;
    state->rstate.ra.ptr.p_double[14] = ecmpl;
    state->rstate.ra.ptr.p_double[15] = mumin;
    state->rstate.ra.ptr.p_double[16] = curbigval;
    state->rstate.ra.ptr.p_double[17] = curbiggrowth;
    state->rstate.ra.ptr.p_double[18] = rhs0;
    state->rstate.ra.ptr.p_double[19] = rhsk;
    state->rstate.ra.ptr.p_double[20] = lastcheckpointp;
    state->rstate.ra.ptr.p_double[21] = lastcheckpointd;
    state->rstate.ra.ptr.p_double[22] = lastcheckpointc;
    state->rstate.ra.ptr.p_double[23] = lastprim;
    state->rstate.ra.ptr.p_double[24] = lastdual;
    state->rstate.ra.ptr.p_double[25] = lastcmpl;
    state->rstate.ra.ptr.p_double[26] = maxabsf;
    state->rstate.ra.ptr.p_double[27] = minmeritv0;
    state->rstate.ra.ptr.p_double[28] = curmeritv0;
    state->rstate.ra.ptr.p_double[29] = merit0;
    state->rstate.ra.ptr.p_double[30] = merit1;
    return result;
}


/*************************************************************************
Extract solution

INPUT PARAMETERS:
    State               -   solver instance
    
OUTPUT PARAMETERS:
    XS                  -   array[N], solution
    LagBC               -   array[N], Lagrange multipliers for box constraints
    LagXC               -   array[M], Lagrange multipliers for linear, quadratic, conic constraints
    TerminationType     -   completion code, positive values for success,
                            negative for failures (XS constrains best point
                            found so far):
                            * -2    the task is either unbounded or infeasible;
                                    the IPM solver has difficulty distinguishing between these two.
                            * +1    stopping criteria are met
                            * +7    stopping criteria are too stringent

RESULT:

This function ALWAYS returns something  meaningful in XS, LagBC, LagLC - 
either solution or the best point so far, even for negative TerminationType.

  -- ALGLIB --
     Copyright 25.05.2024 by Bochkanov Sergey
*************************************************************************/
void gipmresults(gipmstate* state,
     /* Real    */ ae_vector* xs,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* lagxc,
     ae_int_t* terminationtype,
     ae_state *_state)
{

    *terminationtype = 0;

    *terminationtype = state->repterminationtype;
    rcopyallocv(state->nraw, &state->current.xp, xs, _state);
    rcopyallocv(state->nraw, &state->current.zup, lagbc, _state);
    raddv(state->nraw, -1.0, &state->current.zlp, lagbc, _state);
    rcopyallocv(state->mflex, &state->current.yp, lagxc, _state);
}


/*************************************************************************
Allocates place for variables of IPM and fills by zeros.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm_varsinitbyzero(gipmvars* vstate,
     ae_int_t nraw,
     ae_int_t mflex,
     ae_int_t mhard,
     ae_state *_state)
{


    ae_assert(nraw>=1, "VarsInitByZero: N<1", _state);
    ae_assert(mflex>=0, "VarsInitByZero: MFlex<0", _state);
    ae_assert(mhard>=0, "VarsInitByZero: MHard<0", _state);
    vstate->np = nraw;
    vstate->nf = nraw+mflex;
    vstate->mflex = mflex;
    vstate->mhard = mhard;
    rsetallocv(nraw, 0.0, &vstate->xp, _state);
    rsetallocv(nraw, 0.0, &vstate->zlp, _state);
    rsetallocv(nraw, 0.0, &vstate->zup, _state);
    rsetallocv(mflex, 0.0, &vstate->yp, _state);
    rsetallocv(mflex, 0.0, &vstate->yl, _state);
    rsetallocv(mflex, 0.0, &vstate->yu, _state);
    rsetallocv(mflex, 0.0, &vstate->xs, _state);
    rsetallocv(mflex, 0.0, &vstate->gl, _state);
    rsetallocv(mflex, 0.0, &vstate->gu, _state);
    rsetallocv(mflex, 0.0, &vstate->zls, _state);
    rsetallocv(mflex, 0.0, &vstate->zus, _state);
    rsetallocv(mhard, 0.0, &vstate->yv, _state);
}


/*************************************************************************
Apply step

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm_varsapplystep(gipmvars* vstate,
     const gipmvars* delta,
     double alpha,
     ae_state *_state)
{


    raddv(vstate->np, alpha, &delta->xp, &vstate->xp, _state);
    raddv(vstate->np, alpha, &delta->zlp, &vstate->zlp, _state);
    raddv(vstate->np, alpha, &delta->zup, &vstate->zup, _state);
    raddv(vstate->mflex, alpha, &delta->yp, &vstate->yp, _state);
    raddv(vstate->mflex, alpha, &delta->yl, &vstate->yl, _state);
    raddv(vstate->mflex, alpha, &delta->yu, &vstate->yu, _state);
    raddv(vstate->mflex, alpha, &delta->xs, &vstate->xs, _state);
    raddv(vstate->mflex, alpha, &delta->gl, &vstate->gl, _state);
    raddv(vstate->mflex, alpha, &delta->gu, &vstate->gu, _state);
    raddv(vstate->mflex, alpha, &delta->zls, &vstate->zls, _state);
    raddv(vstate->mflex, alpha, &delta->zus, &vstate->zus, _state);
    raddv(vstate->mhard, alpha, &delta->yv, &vstate->yv, _state);
}


/*************************************************************************
Copy vars

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm_varscopy(const gipmvars* src,
     gipmvars* dst,
     ae_state *_state)
{


    dst->np = src->np;
    dst->nf = src->nf;
    dst->mflex = src->mflex;
    dst->mhard = src->mhard;
    rcopyallocv(src->np, &src->xp, &dst->xp, _state);
    rcopyallocv(src->np, &src->zlp, &dst->zlp, _state);
    rcopyallocv(src->np, &src->zup, &dst->zup, _state);
    rcopyallocv(src->mflex, &src->yp, &dst->yp, _state);
    rcopyallocv(src->mflex, &src->yl, &dst->yl, _state);
    rcopyallocv(src->mflex, &src->yu, &dst->yu, _state);
    rcopyallocv(src->mflex, &src->xs, &dst->xs, _state);
    rcopyallocv(src->mflex, &src->gl, &dst->gl, _state);
    rcopyallocv(src->mflex, &src->gu, &dst->gu, _state);
    rcopyallocv(src->mflex, &src->zls, &dst->zls, _state);
    rcopyallocv(src->mflex, &src->zus, &dst->zus, _state);
    rcopyallocv(src->mhard, &src->yv, &dst->yv, _state);
}


/*************************************************************************
Computes step length

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static double gipm_computesteplength(const gipmstate* state,
     const gipmvars* current,
     const gipmvars* delta,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t mflex;
    ae_int_t mhard;
    ae_int_t i;
    double eps2;
    double result;


    np = state->nraw;
    mflex = state->mflex;
    mhard = state->mhard;
    eps2 = (double)1/ae_sqrt(ae_maxrealnumber, _state);
    result = ae_maxrealnumber;
    
    /*
     * Analyze NP-sized components
     */
    for(i=0; i<=np-1; i++)
    {
        if( delta->xp.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, (current->xp.ptr.p_double[i]-state->finitebndlx.ptr.p_double[i])/(eps2-delta->xp.ptr.p_double[i]), _state);
        }
        if( delta->xp.ptr.p_double[i]>0.0 )
        {
            result = ae_minreal(result, (state->finitebndux.ptr.p_double[i]-current->xp.ptr.p_double[i])/(eps2+delta->xp.ptr.p_double[i]), _state);
        }
        if( delta->zlp.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, current->zlp.ptr.p_double[i]/(eps2-delta->zlp.ptr.p_double[i]), _state);
        }
        if( delta->zup.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, current->zup.ptr.p_double[i]/(eps2-delta->zup.ptr.p_double[i]), _state);
        }
    }
    
    /*
     * analyze MFlex-sized components
     */
    for(i=0; i<=mflex-1; i++)
    {
        if( delta->gl.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, current->gl.ptr.p_double[i]/(eps2-delta->gl.ptr.p_double[i]), _state);
        }
        if( delta->gu.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, current->gu.ptr.p_double[i]/(eps2-delta->gu.ptr.p_double[i]), _state);
        }
        if( delta->zls.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, current->zls.ptr.p_double[i]/(eps2-delta->zls.ptr.p_double[i]), _state);
        }
        if( delta->zus.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, current->zus.ptr.p_double[i]/(eps2-delta->zus.ptr.p_double[i]), _state);
        }
    }
    
    /*
     * analyze MHard-sized components
     */
    for(i=0; i<=mhard-1; i++)
    {
        if( delta->yv.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, current->yv.ptr.p_double[i]/(eps2-delta->yv.ptr.p_double[i]), _state);
        }
    }
    
    /*
     * Apply fraction to boundary
     */
    result = ae_minreal(gipm_fractobnd*result, 1.0, _state);
    return result;
}


/*************************************************************************
Compute RHS for a Newton system.
Returns inf-norms of raw (unnormalized) primal, dual, complementarity errors.
Also returns normalizing coefficients for errors.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm_computerhs(gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     gipmrhs* rhs,
     double* eprim2,
     double* edual2,
     double* ecmpl2,
     double* sprim,
     double* sdual,
     double* scmpl,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t mflex;
    ae_int_t mhard;
    ae_int_t np;
    ae_int_t i;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    double v;
    ae_vector tmp;
    double nu;
    double relaxby;
    ae_int_t nprim2;
    ae_int_t ndual2;
    ae_int_t ncmpl2;

    ae_frame_make(_state, &_frame_block);
    memset(&tmp, 0, sizeof(tmp));
    *eprim2 = 0.0;
    *edual2 = 0.0;
    *ecmpl2 = 0.0;
    *sprim = 0.0;
    *sdual = 0.0;
    *scmpl = 0.0;
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);

    mflex = currentvars->mflex;
    mhard = currentvars->mhard;
    np = currentvars->np;
    ae_nxpool_retrieve(&state->mnx1pool, &tmp, _state);
    ae_assert(tmp.cnt>=mflex+mhard+1, "GIPM: integrity check 7328 failed", _state);
    nu = gipm_getnu(currentmu, _state);
    relaxby = ae_minreal(gipm_relaxfactor*currentmu, gipm_maxrelaxation, _state);
    *eprim2 = (double)(0);
    *edual2 = (double)(0);
    *ecmpl2 = (double)(0);
    *sprim = (double)(1);
    *sdual = (double)(1);
    *scmpl = (double)(1);
    nprim2 = 0;
    ndual2 = 0;
    ncmpl2 = 0;
    
    /*
     * Compute RP = -(gmod(xp) + J'*yp + Jv'*yv - ZLP + ZUP)
     */
    rsetallocv(np, 0.0, &rhs->rp, _state);
    ae_assert(!currentfj->isdense, "GIPM: integrity check 8517 failed", _state);
    j0 = currentfj->sj.ridx.ptr.p_int[0];
    j1 = currentfj->sj.ridx.ptr.p_int[1]-1;
    for(jj=j0; jj<=j1; jj++)
    {
        rhs->rp.ptr.p_double[currentfj->sj.idx.ptr.p_int[jj]] = currentfj->sj.vals.ptr.p_double[jj];
    }
    raddv(np, gipm_damptau*currentmu, &currentvars->xp, &rhs->rp, _state);
    tmp.ptr.p_double[0] = (double)(0);
    rcopyvx(mflex, &currentvars->yp, 0, &tmp, 1, _state);
    rcopyvx(mhard, &currentvars->yv, 0, &tmp, 1+mflex, _state);
    sparsegemv(&currentfj->sj, 1.0, 1, &tmp, 0, 1.0, &rhs->rp, 0, _state);
    raddv(np, -1.0, &currentvars->zlp, &rhs->rp, _state);
    raddv(np, 1.0, &currentvars->zup, &rhs->rp, _state);
    rmulv(np, -1.0, &rhs->rp, _state);
    *edual2 = *edual2+rdotv2(np, &rhs->rp, _state);
    ndual2 = ndual2+np;
    
    /*
     * Compute RS = yp - yl - yu
     */
    rsetallocv(mflex, 0.0, &rhs->rs, _state);
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            rhs->rs.ptr.p_double[i] = currentvars->yp.ptr.p_double[i]-currentvars->yl.ptr.p_double[i]-currentvars->yu.ptr.p_double[i];
        }
    }
    *edual2 = *edual2+rdotv2(mflex, &rhs->rs, _state);
    ndual2 = ndual2+mflex;
    
    /*
     * Compute RGL = yl + ZLS
     */
    rsetallocv(mflex, 0.0, &rhs->rgl, _state);
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            rhs->rgl.ptr.p_double[i] = currentvars->yl.ptr.p_double[i]+currentvars->zls.ptr.p_double[i];
        }
    }
    *edual2 = *edual2+rdotv2(mflex, &rhs->rgl, _state);
    ndual2 = ndual2+mflex;
    
    /*
     * Compute RGU = ZUS-yu
     */
    rsetallocv(mflex, 0.0, &rhs->rgu, _state);
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            rhs->rgu.ptr.p_double[i] = currentvars->zus.ptr.p_double[i]-currentvars->yu.ptr.p_double[i];
        }
    }
    *edual2 = *edual2+rdotv2(mflex, &rhs->rgu, _state);
    ndual2 = ndual2+mflex;
    
    /*
     * Compute RLP = mu*ELP-ZLP*(xp-bl)
     *         RUP = mu*EUP-ZUP*(bu-xp)
     */
    rallocv(np, &rhs->rlp, _state);
    rallocv(np, &rhs->rup, _state);
    for(i=0; i<=np-1; i++)
    {
        rhs->rlp.ptr.p_double[i] = currentmu*state->elp.ptr.p_double[i]-currentvars->zlp.ptr.p_double[i]*(currentvars->xp.ptr.p_double[i]-state->finitebndlx.ptr.p_double[i]);
        rhs->rup.ptr.p_double[i] = currentmu*state->eup.ptr.p_double[i]-currentvars->zup.ptr.p_double[i]*(state->finitebndux.ptr.p_double[i]-currentvars->xp.ptr.p_double[i]);
    }
    *ecmpl2 = *ecmpl2+rdotv2(np, &rhs->rlp, _state);
    ncmpl2 = ncmpl2+np;
    *ecmpl2 = *ecmpl2+rdotv2(np, &rhs->rup, _state);
    ncmpl2 = ncmpl2+np;
    
    /*
     *              / xs-c(xp)+nu*yp        for I in range/slack constraints
     * Compute RY = |
     *              \ nu*y-(c(xp)-bndu)     for I in equality constraints
     */
    rallocv(mflex, &rhs->ry, _state);
    for(i=0; i<=mflex-1; i++)
    {
        if( state->isequality.ptr.p_bool[i] )
        {
            rhs->ry.ptr.p_double[i] = nu*currentvars->yp.ptr.p_double[i]-(currentfj->fi.ptr.p_double[1+i]-state->finitebndux.ptr.p_double[np+i]);
        }
        else
        {
            rhs->ry.ptr.p_double[i] = currentvars->xs.ptr.p_double[i]-currentfj->fi.ptr.p_double[1+i]+nu*currentvars->yp.ptr.p_double[i];
        }
    }
    *eprim2 = *eprim2+rdotv2(mflex, &rhs->ry, _state);
    nprim2 = nprim2+mflex;
    
    /*
     * Compute RCL = ELS*(cl-rlx*mu+gl-xs+nu*yl)
     *         RCU = EUS*(cu+rlx*mu-gu-xs+nu*yu)
     */
    rsetallocv(mflex, 0.0, &rhs->rcl, _state);
    rsetallocv(mflex, 0.0, &rhs->rcu, _state);
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            rhs->rcl.ptr.p_double[i] = state->els.ptr.p_double[i]*(state->finitebndlx.ptr.p_double[np+i]-relaxby+currentvars->gl.ptr.p_double[i]-currentvars->xs.ptr.p_double[i]+nu*currentvars->yl.ptr.p_double[i]);
            rhs->rcu.ptr.p_double[i] = state->eus.ptr.p_double[i]*(state->finitebndux.ptr.p_double[np+i]+relaxby-currentvars->xs.ptr.p_double[i]-currentvars->gu.ptr.p_double[i]+nu*currentvars->yu.ptr.p_double[i]);
        }
    }
    *eprim2 = *eprim2+rdotv2(mflex, &rhs->rcl, _state);
    nprim2 = nprim2+mflex;
    *eprim2 = *eprim2+rdotv2(mflex, &rhs->rcu, _state);
    nprim2 = nprim2+mflex;
    
    /*
     * Compute RLS = mu*ELS-ZLS*gl
     *         RUS = mu*EUS-ZUS*gu
     */
    rsetallocv(mflex, 0.0, &rhs->rls, _state);
    rsetallocv(mflex, 0.0, &rhs->rus, _state);
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            rhs->rls.ptr.p_double[i] = currentmu*state->els.ptr.p_double[i]-currentvars->zls.ptr.p_double[i]*currentvars->gl.ptr.p_double[i];
            rhs->rus.ptr.p_double[i] = currentmu*state->eus.ptr.p_double[i]-currentvars->zus.ptr.p_double[i]*currentvars->gu.ptr.p_double[i];
        }
    }
    *ecmpl2 = *ecmpl2+rdotv2(mflex, &rhs->rls, _state);
    ncmpl2 = ncmpl2+mflex;
    *ecmpl2 = *ecmpl2+rdotv2(mflex, &rhs->rus, _state);
    ncmpl2 = ncmpl2+mflex;
    
    /*
     * Compute RV = mu-YV*(vu-v())
     */
    rallocv(mhard, &rhs->rv, _state);
    for(i=0; i<=mhard-1; i++)
    {
        rhs->rv.ptr.p_double[i] = currentvars->yv.ptr.p_double[i]*(state->finitebndux.ptr.p_double[np+mflex+i]-currentfj->fi.ptr.p_double[1+mflex+i])-currentmu;
    }
    *ecmpl2 = *ecmpl2+rdotv2(mhard, &rhs->rv, _state);
    ncmpl2 = ncmpl2+mhard;
    
    /*
     * Compute scales
     */
    *sdual = ae_maxreal(*sdual, rmaxabsv(mflex, &currentvars->yp, _state), _state);
    *sdual = ae_maxreal(*sdual, rmaxabsv(mflex, &currentvars->yl, _state), _state);
    *sdual = ae_maxreal(*sdual, rmaxabsv(mflex, &currentvars->yu, _state), _state);
    *sdual = ae_maxreal(*sdual, rmaxabsv(mhard, &currentvars->yv, _state), _state);
    v = rmaxabsv(np, &currentvars->zlp, _state);
    *sdual = ae_maxreal(*sdual, v, _state);
    *scmpl = ae_maxreal(*scmpl, v, _state);
    v = rmaxabsv(np, &currentvars->zup, _state);
    *sdual = ae_maxreal(*sdual, v, _state);
    *scmpl = ae_maxreal(*scmpl, v, _state);
    v = rmaxabsv(mflex, &currentvars->zls, _state);
    *sdual = ae_maxreal(*sdual, v, _state);
    *scmpl = ae_maxreal(*scmpl, v, _state);
    v = rmaxabsv(mflex, &currentvars->zus, _state);
    *sdual = ae_maxreal(*sdual, v, _state);
    *scmpl = ae_maxreal(*scmpl, v, _state);
    *sprim = ae_maxreal(*sprim/(double)1000, (double)(1), _state);
    *sdual = ae_maxreal(*sdual/(double)1000, (double)(1), _state);
    *scmpl = ae_maxreal(*scmpl/(double)1000, (double)(1), _state);
    
    /*
     * Done
     */
    *eprim2 = ae_sqrt(*eprim2/coalesce((double)(nprim2), (double)(1), _state), _state);
    *edual2 = ae_sqrt(*edual2/coalesce((double)(ndual2), (double)(1), _state), _state);
    *ecmpl2 = ae_sqrt(*ecmpl2/coalesce((double)(ncmpl2), (double)(1), _state), _state);
    ae_nxpool_recycle(&state->mnx1pool, &tmp, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Compute residual from the candidate solution for the already compute RHS:

    RHS := RHS - [SYSTEM]*delta
    
Inputs include current location, candidate solution Delta and precomputed
products H*Delta.XP, J*Delta.XP, J'*Delta.Y

Returns inf-norm of the residual.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static double gipm_computeupdateresidual(gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     double lambdatouse,
     const gipmvars* delta,
     /* Real    */ const ae_vector* hdxp,
     /* Real    */ const ae_vector* jdxp,
     /* Real    */ const ae_vector* jtdy,
     gipmrhs* rhs,
     ae_state *_state)
{
    ae_int_t mflex;
    ae_int_t mhard;
    ae_int_t np;
    ae_int_t i;
    double nu;
    double lambdatau;
    double result;


    mflex = currentvars->mflex;
    mhard = currentvars->mhard;
    np = currentvars->np;
    nu = gipm_getnu(currentmu, _state);
    lambdatau = lambdatouse+currentmu*gipm_damptau;
    result = (double)(0);
    
    /*
     * Update RP  := RP  - (Hmod+L+TAU)*d_xp - (J'*d_yp + Jv'*d_yv) + d_zlp - d_zup
     *        RS  := RS  + d_yp - d_yl - d_yu
     *        RGL := RGL + d_yl + d_zls
     *        RGU := RGU - d_yu + d_zus
     *
     */
    raddv(np, -1.0, hdxp, &rhs->rp, _state);
    raddv(np, -lambdatau, &delta->xp, &rhs->rp, _state);
    raddv(np, -1.0, jtdy, &rhs->rp, _state);
    raddv(np, 1.0, &delta->zlp, &rhs->rp, _state);
    raddv(np, -1.0, &delta->zup, &rhs->rp, _state);
    result = ae_maxreal(result, rmaxabsv(np, &rhs->rp, _state), _state);
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            rhs->rs.ptr.p_double[i] = rhs->rs.ptr.p_double[i]+delta->yp.ptr.p_double[i]-delta->yl.ptr.p_double[i]-delta->yu.ptr.p_double[i];
        }
    }
    result = ae_maxreal(result, rmaxabsv(mflex, &rhs->rs, _state), _state);
    raddv(mflex, 1.0, &delta->yl, &rhs->rgl, _state);
    raddv(mflex, 1.0, &delta->zls, &rhs->rgl, _state);
    result = ae_maxreal(result, rmaxabsv(mflex, &rhs->rgl, _state), _state);
    raddv(mflex, -1.0, &delta->yu, &rhs->rgu, _state);
    raddv(mflex, 1.0, &delta->zus, &rhs->rgu, _state);
    result = ae_maxreal(result, rmaxabsv(mflex, &rhs->rgu, _state), _state);
    
    /*
     * Update RLP := RLP - ZLP*dxp-(XP-BL)*dzlp
     *        RUP := RUP + ZUP*dxp-(BU-XP)*dzup
     */
    for(i=0; i<=np-1; i++)
    {
        rhs->rlp.ptr.p_double[i] = rhs->rlp.ptr.p_double[i]-currentvars->zlp.ptr.p_double[i]*delta->xp.ptr.p_double[i]-(currentvars->xp.ptr.p_double[i]-state->finitebndlx.ptr.p_double[i])*delta->zlp.ptr.p_double[i];
        rhs->rup.ptr.p_double[i] = rhs->rup.ptr.p_double[i]+currentvars->zup.ptr.p_double[i]*delta->xp.ptr.p_double[i]-(state->finitebndux.ptr.p_double[i]-currentvars->xp.ptr.p_double[i])*delta->zup.ptr.p_double[i];
    }
    result = ae_maxreal(result, rmaxabsv(np, &rhs->rlp, _state), _state);
    result = ae_maxreal(result, rmaxabsv(np, &rhs->rup, _state), _state);
    
    /*
     * Update RY := RY-J*d_xp + d_xs + nu*d_yp
     */
    for(i=0; i<=mflex-1; i++)
    {
        rhs->ry.ptr.p_double[i] = rhs->ry.ptr.p_double[i]-jdxp->ptr.p_double[i]+nu*delta->yp.ptr.p_double[i];
        if( !state->isequality.ptr.p_bool[i] )
        {
            rhs->ry.ptr.p_double[i] = rhs->ry.ptr.p_double[i]+delta->xs.ptr.p_double[i];
        }
    }
    result = ae_maxreal(result, rmaxabsv(mflex, &rhs->ry, _state), _state);
    
    /*
     * Update RCL := RCL - ELS*(dxs - dgl - nu*dyl)
     *        RCU := RCU - EUS*(dxs + dgu - nu*dyu)
     */
    for(i=0; i<=mflex-1; i++)
    {
        rhs->rcl.ptr.p_double[i] = rhs->rcl.ptr.p_double[i]-state->els.ptr.p_double[i]*(delta->xs.ptr.p_double[i]-delta->gl.ptr.p_double[i]-nu*delta->yl.ptr.p_double[i]);
        rhs->rcu.ptr.p_double[i] = rhs->rcu.ptr.p_double[i]-state->eus.ptr.p_double[i]*(delta->xs.ptr.p_double[i]+delta->gu.ptr.p_double[i]-nu*delta->yu.ptr.p_double[i]);
    }
    result = ae_maxreal(result, rmaxabsv(mflex, &rhs->rcl, _state), _state);
    result = ae_maxreal(result, rmaxabsv(mflex, &rhs->rcu, _state), _state);
    
    /*
     * Update RLS := RLS-(GL*d_zls + ZLS*d_gl)
     *        RUS := RUS-(GU*d_zus + ZUS*d_gu)
     */
    for(i=0; i<=mflex-1; i++)
    {
        rhs->rls.ptr.p_double[i] = rhs->rls.ptr.p_double[i]-(currentvars->gl.ptr.p_double[i]*delta->zls.ptr.p_double[i]+currentvars->zls.ptr.p_double[i]*delta->gl.ptr.p_double[i]);
        rhs->rus.ptr.p_double[i] = rhs->rus.ptr.p_double[i]-(currentvars->gu.ptr.p_double[i]*delta->zus.ptr.p_double[i]+currentvars->zus.ptr.p_double[i]*delta->gu.ptr.p_double[i]);
    }
    result = ae_maxreal(result, rmaxabsv(mflex, &rhs->rls, _state), _state);
    result = ae_maxreal(result, rmaxabsv(mflex, &rhs->rus, _state), _state);
    
    /*
     * Update RV := RV - (YV*Jv*d_xp - (vu-v(xp))*d_yv)
     */
    for(i=0; i<=mhard-1; i++)
    {
        rhs->rv.ptr.p_double[i] = rhs->rv.ptr.p_double[i]-(currentvars->yv.ptr.p_double[i]*jdxp->ptr.p_double[mflex+i]-(state->finitebndux.ptr.p_double[np+mflex+i]-currentfj->fi.ptr.p_double[1+mflex+i])*delta->yv.ptr.p_double[i]);
    }
    result = ae_maxreal(result, rmaxabsv(mhard, &rhs->rv, _state), _state);
    return result;
}


/*************************************************************************
Transforms nonsymmetric primal-dual Newtom system into a symmetric condensed form

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm_condenselhs(const gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     gipmcondensedsystem* c,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t mflex;
    ae_int_t mhard;
    ae_int_t i;
    double nu;
    double smallnumber;


    np = state->nraw;
    mflex = state->mflex;
    mhard = state->mhard;
    nu = gipm_getnu(currentmu, _state);
    smallnumber = ae_sqrt((double)1/ae_maxrealnumber, _state);
    rallocv(mhard, &c->d1v, _state);
    for(i=0; i<=mhard-1; i++)
    {
        c->d1v.ptr.p_double[i] = -(state->finitebndux.ptr.p_double[np+mflex+i]-currentfj->fi.ptr.p_double[1+mflex+i])/currentvars->yv.ptr.p_double[i];
    }
    rallocv(mflex, &c->t0, _state);
    rallocv(mflex, &c->t1, _state);
    rallocv(mflex, &c->t2, _state);
    rallocv(mflex, &c->t3, _state);
    rallocv(mflex, &c->ta, _state);
    rallocv(mflex, &c->d1, _state);
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            c->t0.ptr.p_double[i] = currentvars->zls.ptr.p_double[i]/(smallnumber+currentvars->gl.ptr.p_double[i]);
            c->t1.ptr.p_double[i] = currentvars->zus.ptr.p_double[i]/(smallnumber+currentvars->gu.ptr.p_double[i]);
            c->t2.ptr.p_double[i] = (double)1/((double)1+nu*c->t0.ptr.p_double[i]);
            c->t3.ptr.p_double[i] = (double)1/((double)1+nu*c->t1.ptr.p_double[i]);
            c->ta.ptr.p_double[i] = (double)1/(c->t2.ptr.p_double[i]*c->t0.ptr.p_double[i]+c->t3.ptr.p_double[i]*c->t1.ptr.p_double[i]);
            c->d1.ptr.p_double[i] = -c->ta.ptr.p_double[i]-nu;
        }
        else
        {
            c->d1.ptr.p_double[i] = -nu;
        }
    }
    rallocv(np, &c->t4, _state);
    rallocv(np, &c->t5, _state);
    rallocv(np, &c->d0, _state);
    for(i=0; i<=np-1; i++)
    {
        c->t4.ptr.p_double[i] = (double)1/(currentvars->xp.ptr.p_double[i]-state->finitebndlx.ptr.p_double[i]);
        c->t5.ptr.p_double[i] = (double)1/(state->finitebndux.ptr.p_double[i]-currentvars->xp.ptr.p_double[i]);
        c->d0.ptr.p_double[i] = state->lambdav+currentmu*gipm_damptau+c->t4.ptr.p_double[i]*currentvars->zlp.ptr.p_double[i]+c->t5.ptr.p_double[i]*currentvars->zup.ptr.p_double[i];
    }
}


/*************************************************************************
Condense only right-hand-side using already condensed left hand side.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm_condenserhs(const gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     const gipmrhs* rhs,
     gipmcondensedsystem* c,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t mflex;
    ae_int_t mhard;
    ae_int_t i;
    double invgli;
    double invgui;
    double smallnumber;


    np = state->nraw;
    mflex = state->mflex;
    mhard = state->mhard;
    smallnumber = ae_sqrt((double)1/ae_maxrealnumber, _state);
    rallocv(mhard, &c->wyv, _state);
    for(i=0; i<=mhard-1; i++)
    {
        c->wyv.ptr.p_double[i] = rhs->rv.ptr.p_double[i]/currentvars->yv.ptr.p_double[i];
    }
    rallocv(mflex, &c->tls, _state);
    rallocv(mflex, &c->tus, _state);
    rallocv(mflex, &c->tgl, _state);
    rallocv(mflex, &c->tgu, _state);
    rallocv(mflex, &c->ts, _state);
    rallocv(mflex, &c->wy, _state);
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            invgli = (double)1/(smallnumber+currentvars->gl.ptr.p_double[i]);
            invgui = (double)1/(smallnumber+currentvars->gu.ptr.p_double[i]);
            c->tls.ptr.p_double[i] = rhs->rls.ptr.p_double[i]+currentvars->zls.ptr.p_double[i]*rhs->rcl.ptr.p_double[i];
            c->tus.ptr.p_double[i] = rhs->rus.ptr.p_double[i]-currentvars->zus.ptr.p_double[i]*rhs->rcu.ptr.p_double[i];
            c->tgl.ptr.p_double[i] = rhs->rgl.ptr.p_double[i]+invgli*c->tls.ptr.p_double[i];
            c->tgu.ptr.p_double[i] = rhs->rgu.ptr.p_double[i]+invgui*c->tus.ptr.p_double[i];
            c->ts.ptr.p_double[i] = rhs->rs.ptr.p_double[i]-c->t3.ptr.p_double[i]*c->tgu.ptr.p_double[i]+c->t2.ptr.p_double[i]*c->tgl.ptr.p_double[i];
            c->wy.ptr.p_double[i] = rhs->ry.ptr.p_double[i]+c->ta.ptr.p_double[i]*c->ts.ptr.p_double[i];
        }
        else
        {
            c->wy.ptr.p_double[i] = rhs->ry.ptr.p_double[i];
        }
    }
    rallocv(np, &c->wx, _state);
    for(i=0; i<=np-1; i++)
    {
        c->wx.ptr.p_double[i] = rhs->rp.ptr.p_double[i]+c->t4.ptr.p_double[i]*rhs->rlp.ptr.p_double[i]-c->t5.ptr.p_double[i]*rhs->rup.ptr.p_double[i];
    }
}


/*************************************************************************
Unpack reply to Request=1002 into a solution to the original primal-dual system.
Returns maximum over step components;

Delta must be properly allocated GIPMVars; however, initial values of
its variables are ignored.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm_unpacksolution(const gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     const gipmrhs* rhs,
     gipmcondensedsystem* c,
     /* Real    */ const ae_vector* replysol,
     gipmvars* delta,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t mflex;
    ae_int_t mhard;
    ae_int_t i;
    double v0;
    double v1;
    double nu;
    double invgli;
    double invgui;
    double smallnumber;


    np = state->nraw;
    mflex = state->mflex;
    mhard = state->mhard;
    nu = gipm_getnu(currentmu, _state);
    smallnumber = ae_sqrt((double)1/ae_maxrealnumber, _state);
    rcopyvx(np, &state->replysol, 0, &delta->xp, 0, _state);
    rcopyvx(mflex, &state->replysol, np, &delta->yp, 0, _state);
    rcopyvx(mhard, &state->replysol, np+mflex, &delta->yv, 0, _state);
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            invgli = (double)1/(smallnumber+currentvars->gl.ptr.p_double[i]);
            invgui = (double)1/(smallnumber+currentvars->gu.ptr.p_double[i]);
            delta->xs.ptr.p_double[i] = c->ta.ptr.p_double[i]*(c->ts.ptr.p_double[i]+delta->yp.ptr.p_double[i]);
            delta->yl.ptr.p_double[i] = c->t2.ptr.p_double[i]*(c->t0.ptr.p_double[i]*delta->xs.ptr.p_double[i]-c->tgl.ptr.p_double[i]);
            delta->yu.ptr.p_double[i] = c->t3.ptr.p_double[i]*(c->t1.ptr.p_double[i]*delta->xs.ptr.p_double[i]+c->tgu.ptr.p_double[i]);
            v0 = delta->xs.ptr.p_double[i]-nu*delta->yl.ptr.p_double[i];
            v1 = delta->xs.ptr.p_double[i]-nu*delta->yu.ptr.p_double[i];
            delta->zls.ptr.p_double[i] = invgli*c->tls.ptr.p_double[i]-c->t0.ptr.p_double[i]*v0;
            delta->zus.ptr.p_double[i] = invgui*c->tus.ptr.p_double[i]+c->t1.ptr.p_double[i]*v1;
            delta->gl.ptr.p_double[i] = state->els.ptr.p_double[i]*(v0-rhs->rcl.ptr.p_double[i]);
            delta->gu.ptr.p_double[i] = state->eus.ptr.p_double[i]*(rhs->rcu.ptr.p_double[i]-v1);
        }
        else
        {
            delta->xs.ptr.p_double[i] = (double)(0);
            delta->yl.ptr.p_double[i] = (double)(0);
            delta->yu.ptr.p_double[i] = (double)(0);
            delta->zls.ptr.p_double[i] = (double)(0);
            delta->zus.ptr.p_double[i] = (double)(0);
            delta->gl.ptr.p_double[i] = (double)(0);
            delta->gu.ptr.p_double[i] = (double)(0);
        }
    }
    for(i=0; i<=np-1; i++)
    {
        delta->zlp.ptr.p_double[i] = c->t4.ptr.p_double[i]*(rhs->rlp.ptr.p_double[i]-currentvars->zlp.ptr.p_double[i]*delta->xp.ptr.p_double[i]);
        delta->zup.ptr.p_double[i] = c->t5.ptr.p_double[i]*(rhs->rup.ptr.p_double[i]+currentvars->zup.ptr.p_double[i]*delta->xp.ptr.p_double[i]);
    }
}


/*************************************************************************
Compute barrier function, L2 penalty and complementarity errors
    
  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm_barrierpenaltyandcmplerror(const gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     double* barrierf,
     double* penaltyf,
     double* cmplerr,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t mflex;
    ae_int_t mhard;
    ae_int_t i;
    double nu;
    double relaxby;

    *barrierf = 0.0;
    *penaltyf = 0.0;
    *cmplerr = 0.0;

    np = state->nraw;
    mflex = state->mflex;
    mhard = state->mhard;
    relaxby = ae_minreal(gipm_relaxfactor*currentmu, gipm_maxrelaxation, _state);
    nu = gipm_getnu(currentmu, _state);
    
    /*
     * Compute fmod(xp,mu) and barrier terms with respect to primal variables XP
     */
    *barrierf = currentfj->fi.ptr.p_double[0]+0.5*currentmu*gipm_damptau*rdotv2(np, &currentvars->xp, _state);
    for(i=0; i<=np-1; i++)
    {
        if( state->hasbndlx.ptr.p_bool[i] )
        {
            *barrierf = *barrierf-currentmu*ae_log(currentvars->xp.ptr.p_double[i]-state->finitebndlx.ptr.p_double[i], _state);
        }
        if( state->hasbndux.ptr.p_bool[i] )
        {
            *barrierf = *barrierf-currentmu*ae_log(state->finitebndux.ptr.p_double[i]-currentvars->xp.ptr.p_double[i], _state);
        }
    }
    
    /*
     * Handle barrier terms on primal variables GL and GU
     */
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            if( state->hasbndlx.ptr.p_bool[np+i] )
            {
                *barrierf = *barrierf-currentmu*ae_log(currentvars->gl.ptr.p_double[i], _state);
            }
            if( state->hasbndux.ptr.p_bool[np+i] )
            {
                *barrierf = *barrierf-currentmu*ae_log(currentvars->gu.ptr.p_double[i], _state);
            }
        }
    }
    
    /*
     * Handle barrier terms on 'hard' constraints V()
     */
    for(i=0; i<=mhard-1; i++)
    {
        *barrierf = *barrierf-currentmu*ae_log(state->finitebndux.ptr.p_double[np+mflex+i]-currentfj->fi.ptr.p_double[1+mflex+i], _state);
    }
    
    /*
     * Handle penalty for violation of equality constraints c(xp)-xs=0, xs-gl=cl-rlx*mu, xs+gu=cu+rlx*mu
     */
    *penaltyf = 0.0;
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            *penaltyf = *penaltyf+0.5*ae_sqr(currentfj->fi.ptr.p_double[1+i]-currentvars->xs.ptr.p_double[i]-nu*currentvars->yp.ptr.p_double[i], _state);
            if( state->hasbndlx.ptr.p_bool[np+i] )
            {
                *penaltyf = *penaltyf+0.5*ae_sqr(currentvars->xs.ptr.p_double[i]-currentvars->gl.ptr.p_double[i]-state->finitebndlx.ptr.p_double[np+i]-nu*currentvars->yl.ptr.p_double[i]+relaxby, _state);
            }
            if( state->hasbndux.ptr.p_bool[np+i] )
            {
                *penaltyf = *penaltyf+0.5*ae_sqr(currentvars->xs.ptr.p_double[i]+currentvars->gu.ptr.p_double[i]-state->finitebndux.ptr.p_double[np+i]-nu*currentvars->yu.ptr.p_double[i]-relaxby, _state);
            }
        }
        else
        {
            *penaltyf = *penaltyf+0.5*ae_sqr(currentfj->fi.ptr.p_double[1+i]-state->finitebndux.ptr.p_double[np+i]-nu*currentvars->yp.ptr.p_double[i], _state);
        }
    }
    
    /*
     * Compute complementarity error
     */
    *cmplerr = (double)(0);
    for(i=0; i<=np-1; i++)
    {
        if( state->hasbndlx.ptr.p_bool[i] )
        {
            *cmplerr = *cmplerr+ae_sqr((currentvars->xp.ptr.p_double[i]-state->finitebndlx.ptr.p_double[i])*currentvars->zlp.ptr.p_double[i]-currentmu, _state);
        }
        if( state->hasbndux.ptr.p_bool[i] )
        {
            *cmplerr = *cmplerr+ae_sqr((state->finitebndux.ptr.p_double[i]-currentvars->xp.ptr.p_double[i])*currentvars->zup.ptr.p_double[i]-currentmu, _state);
        }
    }
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            if( state->hasbndlx.ptr.p_bool[np+i] )
            {
                *cmplerr = *cmplerr+ae_sqr(currentvars->gl.ptr.p_double[i]*currentvars->zls.ptr.p_double[i]-currentmu, _state);
            }
            if( state->hasbndux.ptr.p_bool[np+i] )
            {
                *cmplerr = *cmplerr+ae_sqr(currentvars->gu.ptr.p_double[i]*currentvars->zus.ptr.p_double[i]-currentmu, _state);
            }
        }
    }
}


/*************************************************************************
Merit function V0
    
  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static double gipm_meritv0(const gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t mflex;
    ae_int_t mhard;
    ae_int_t i;
    double nu;
    double relaxby;
    double penaltyf;
    double result;


    np = state->nraw;
    mflex = state->mflex;
    mhard = state->mhard;
    relaxby = ae_minreal(gipm_relaxfactor*currentmu, gipm_maxrelaxation, _state);
    nu = gipm_getnu(currentmu, _state);
    
    /*
     * Compute fmod(xp,mu) and barrier terms with respect to primal variables XP
     */
    result = currentfj->fi.ptr.p_double[0]+0.5*currentmu*gipm_damptau*rdotv2(np, &currentvars->xp, _state);
    for(i=0; i<=np-1; i++)
    {
        if( state->hasbndlx.ptr.p_bool[i] )
        {
            result = result-currentmu*ae_log(currentvars->xp.ptr.p_double[i]-state->finitebndlx.ptr.p_double[i], _state);
        }
        if( state->hasbndux.ptr.p_bool[i] )
        {
            result = result-currentmu*ae_log(state->finitebndux.ptr.p_double[i]-currentvars->xp.ptr.p_double[i], _state);
        }
    }
    
    /*
     * Handle barrier terms on primal variables GL and GU
     */
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            if( state->hasbndlx.ptr.p_bool[np+i] )
            {
                result = result-currentmu*ae_log(currentvars->gl.ptr.p_double[i], _state);
            }
            if( state->hasbndux.ptr.p_bool[np+i] )
            {
                result = result-currentmu*ae_log(currentvars->gu.ptr.p_double[i], _state);
            }
        }
    }
    
    /*
     * Handle barrier terms on 'hard' constraints V()
     */
    for(i=0; i<=mhard-1; i++)
    {
        result = result-currentmu*ae_log(state->finitebndux.ptr.p_double[np+mflex+i]-currentfj->fi.ptr.p_double[1+mflex+i], _state);
    }
    
    /*
     * Handle penalty for violation of equality constraints c(xp)-xs=0, xs-gl=cl-rlx*mu, xs+gu=cu+rlx*mu
     */
    penaltyf = 0.0;
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            penaltyf = penaltyf+0.5*ae_sqr(currentfj->fi.ptr.p_double[1+i]-currentvars->xs.ptr.p_double[i], _state);
            if( state->hasbndlx.ptr.p_bool[np+i] )
            {
                penaltyf = penaltyf+0.5*ae_sqr(currentvars->xs.ptr.p_double[i]-currentvars->gl.ptr.p_double[i]-state->finitebndlx.ptr.p_double[np+i]+relaxby, _state);
            }
            if( state->hasbndux.ptr.p_bool[np+i] )
            {
                penaltyf = penaltyf+0.5*ae_sqr(currentvars->xs.ptr.p_double[i]+currentvars->gu.ptr.p_double[i]-state->finitebndux.ptr.p_double[np+i]-relaxby, _state);
            }
        }
        else
        {
            penaltyf = penaltyf+0.5*ae_sqr(currentfj->fi.ptr.p_double[1+i]-state->finitebndux.ptr.p_double[np+i], _state);
        }
    }
    result = result+penaltyf/((double)2*nu);
    return result;
}


/*************************************************************************
Merit function V0
    
  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static double gipm_meritfunctionpd(const gipmstate* state,
     const gipmvars* currentvars,
     const varsfuncjac* currentfj,
     double currentmu,
     ae_state *_state)
{
    double v;
    double vv;
    ae_int_t np;
    ae_int_t mflex;
    ae_int_t mhard;
    ae_int_t i;
    double nu;
    double relaxby;
    double penaltyf;
    double invmu;
    double result;


    np = state->nraw;
    mflex = state->mflex;
    mhard = state->mhard;
    relaxby = ae_minreal(gipm_relaxfactor*currentmu, gipm_maxrelaxation, _state);
    nu = gipm_getnu(currentmu, _state);
    invmu = (double)1/currentmu;
    
    /*
     * Compute fmod(xp,mu) and barrier terms with respect to primal variables XP
     */
    result = currentfj->fi.ptr.p_double[0]+0.5*currentmu*gipm_damptau*rdotv2(np, &currentvars->xp, _state);
    for(i=0; i<=np-1; i++)
    {
        if( state->hasbndlx.ptr.p_bool[i] )
        {
            v = currentvars->xp.ptr.p_double[i]-state->finitebndlx.ptr.p_double[i];
            vv = v*currentvars->zlp.ptr.p_double[i]*invmu;
            result = result-currentmu*(ae_log(v, _state)+gipm_dualmeritnu*(ae_log(vv, _state)+(double)1-vv));
        }
        if( state->hasbndux.ptr.p_bool[i] )
        {
            v = state->finitebndux.ptr.p_double[i]-currentvars->xp.ptr.p_double[i];
            vv = v*currentvars->zup.ptr.p_double[i]*invmu;
            result = result-currentmu*(ae_log(v, _state)+gipm_dualmeritnu*(ae_log(vv, _state)+(double)1-vv));
        }
    }
    
    /*
     * Handle barrier terms on primal variables GL and GU
     */
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            if( state->hasbndlx.ptr.p_bool[np+i] )
            {
                v = currentvars->gl.ptr.p_double[i];
                vv = v*currentvars->zls.ptr.p_double[i]*invmu;
                result = result-currentmu*(ae_log(v, _state)+gipm_dualmeritnu*(ae_log(vv, _state)+(double)1-vv));
            }
            if( state->hasbndux.ptr.p_bool[np+i] )
            {
                v = currentvars->gu.ptr.p_double[i];
                vv = v*currentvars->zus.ptr.p_double[i]*invmu;
                result = result-currentmu*(ae_log(v, _state)+gipm_dualmeritnu*(ae_log(vv, _state)+(double)1-vv));
            }
        }
    }
    
    /*
     * Handle barrier terms on 'hard' constraints V()
     */
    for(i=0; i<=mhard-1; i++)
    {
        v = state->finitebndux.ptr.p_double[np+mflex+i]-currentfj->fi.ptr.p_double[1+mflex+i];
        vv = v*currentvars->yv.ptr.p_double[i]*invmu;
        result = result-currentmu*(ae_log(v, _state)+gipm_dualmeritnu*(ae_log(vv, _state)+(double)1-vv));
    }
    
    /*
     * Handle penalty for violation of equality constraints c(xp)-xs=0, xs-gl=cl-rlx*mu, xs+gu=cu+rlx*mu
     */
    penaltyf = 0.0;
    for(i=0; i<=mflex-1; i++)
    {
        if( !state->isequality.ptr.p_bool[i] )
        {
            v = currentfj->fi.ptr.p_double[1+i]-currentvars->xs.ptr.p_double[i];
            vv = v+nu*currentvars->yp.ptr.p_double[i];
            penaltyf = penaltyf+0.5*v*v+gipm_dualmeritnu*vv*vv;
            if( state->hasbndlx.ptr.p_bool[np+i] )
            {
                v = currentvars->xs.ptr.p_double[i]-currentvars->gl.ptr.p_double[i]-state->finitebndlx.ptr.p_double[np+i]+relaxby;
                vv = v+nu*currentvars->yl.ptr.p_double[i];
                penaltyf = penaltyf+0.5*v*v+gipm_dualmeritnu*vv*vv;
            }
            if( state->hasbndux.ptr.p_bool[np+i] )
            {
                v = currentvars->xs.ptr.p_double[i]+currentvars->gu.ptr.p_double[i]-state->finitebndux.ptr.p_double[np+i]-relaxby;
                vv = v+nu*currentvars->yu.ptr.p_double[i];
                penaltyf = penaltyf+0.5*v*v+gipm_dualmeritnu*vv*vv;
            }
        }
        else
        {
            v = currentfj->fi.ptr.p_double[1+i]-state->finitebndux.ptr.p_double[np+i];
            vv = v+nu*currentvars->yp.ptr.p_double[i];
            penaltyf = penaltyf+0.5*v*v+gipm_dualmeritnu*vv*vv;
        }
    }
    result = result+penaltyf/((double)2*nu);
    return result;
}


/*************************************************************************
Prepare quasi-Newton update information
    
  -- ALGLIB --
     Copyright 11.04.2025 by Bochkanov Sergey
*************************************************************************/
static void gipm_preparequasinewtonupdate(gipmstate* state,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t mflex;
    ae_int_t mhard;
    ae_int_t np;
    ae_vector tmp0;

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

    mflex = state->mflex;
    mhard = state->mhard;
    np = state->nraw;
    ae_nxpool_retrieve(&state->mnx1pool, &tmp0, _state);
    ae_assert(tmp0.cnt>=mflex+mhard+1, "GIPM: integrity check 943226 failed", _state);
    tmp0.ptr.p_double[0] = (double)(1);
    rcopyvx(mflex, &state->trial.yp, 0, &tmp0, 1, _state);
    rcopyvx(mhard, &state->trial.yv, 0, &tmp0, 1+mflex, _state);
    rmergemulv(1+mflex+mhard, &state->qnmask, &tmp0, _state);
    rsetallocv(4*np, 0.0, &state->querydata, _state);
    rcopyvx(np, &state->current.xp, 0, &state->querydata, 0*np, _state);
    rcopyvx(np, &state->trial.xp, 0, &state->querydata, 1*np, _state);
    ae_assert(!state->currentfj.isdense&&!state->trialfj.isdense, "GIPM: integrity check 418223 failed", _state);
    sparsegemv(&state->currentfj.sj, 1.0, 1, &tmp0, 0, 0.0, &state->querydata, 2*np, _state);
    raddvx(np, gipm_damptau*state->mu, &state->current.xp, 0, &state->querydata, 2*np, _state);
    sparsegemv(&state->trialfj.sj, 1.0, 1, &tmp0, 0, 0.0, &state->querydata, 3*np, _state);
    raddvx(np, gipm_damptau*state->mu, &state->trial.xp, 0, &state->querydata, 3*np, _state);
    ae_nxpool_recycle(&state->mnx1pool, &tmp0, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Computes minimum nonzero value of the vector. Returns 0 if all components
are nonpositive.

INPUT PARAMETERS:
    X               -   vector
    N               -   length

  -- ALGLIB --
     Copyright 01.11.2020 by Bochkanov Sergey
*************************************************************************/
static double gipm_minabsnz(/* Real    */ const ae_vector* x,
     ae_int_t n1,
     ae_state *_state)
{
    ae_int_t n0;
    ae_int_t i;
    ae_bool nz;
    double result;


    result = (double)(0);
    n0 = 0;
    nz = ae_false;
    for(i=n0; i<=n1-1; i++)
    {
        if( ae_fp_neq(x->ptr.p_double[i],(double)(0)) )
        {
            if( !nz )
            {
                result = ae_fabs(x->ptr.p_double[i], _state);
                nz = ae_true;
            }
            else
            {
                result = ae_minreal(result, ae_fabs(x->ptr.p_double[i], _state), _state);
            }
        }
    }
    return result;
}


/*************************************************************************
Compute safeguard BndLP/UP from FiniteBndLX/UX.

  -- ALGLIB --
     Copyright 25.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm_safeboundsfromfinitetightened(gipmstate* state,
     ae_state *_state)
{
    ae_int_t nraw;
    ae_int_t i;
    double vmid;
    double v;


    nraw = state->nraw;
    rallocv(nraw, &state->safeguardbndlp, _state);
    rallocv(nraw, &state->safeguardbndup, _state);
    v = (double)10*ae_machineepsilon;
    for(i=0; i<=nraw-1; i++)
    {
        
        /*
         * Safeguard bounds on primal variables and check interval width
         */
        vmid = 0.5*(state->finitebndlx.ptr.p_double[i]+state->finitebndux.ptr.p_double[i]);
        state->safeguardbndlp.ptr.p_double[i] = ae_minreal(state->finitebndlx.ptr.p_double[i]+ae_maxreal(ae_fabs(state->finitebndlx.ptr.p_double[i], _state), 1.0, _state)*v, vmid, _state);
        state->safeguardbndup.ptr.p_double[i] = ae_maxreal(state->finitebndux.ptr.p_double[i]-ae_maxreal(ae_fabs(state->finitebndux.ptr.p_double[i], _state), 1.0, _state)*v, vmid, _state);
        ae_assert(ae_fp_greater_eq(state->safeguardbndup.ptr.p_double[i],state->safeguardbndlp.ptr.p_double[i]), "GIPM: bounds on primal variables are too tight, bad problem preprocessing", _state);
    }
}


/*************************************************************************
Tightens/relaxes bounds, recomputes safeguarded BndLP/UP if necessary.

  -- ALGLIB --
     Copyright 25.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm_tightenrelaxbounds(gipmstate* state, ae_state *_state)
{
    ae_int_t nraw;
    ae_int_t mflex;
    ae_int_t nxf;
    ae_int_t ntightened;
    ae_int_t nrelaxed;
    ae_int_t i;
    double xpnrm;
    double xsnrm;
    double vtgt;
    double vrlx;
    double vmid;
    double vx;
    double vnrm;
    double vprev;
    double targetdist;


    ae_assert(ae_fp_less(gipm_relaxdist,gipm_tightendist), "GIPM: integrity check 1821 failed", _state);
    targetdist = ae_sqrt(gipm_relaxdist*gipm_tightendist, _state);
    nraw = state->nraw;
    mflex = state->mflex;
    nxf = nraw+mflex;
    xpnrm = ae_maxreal(rmaxabsv(nraw, &state->current.xp, _state), (double)(1), _state);
    xsnrm = ae_maxreal(rmaxabsv(mflex, &state->current.xs, _state), (double)(1), _state);
    ntightened = 0;
    nrelaxed = 0;
    for(i=0; i<=nxf-1; i++)
    {
        if( i<nraw||!state->isequality.ptr.p_bool[i-nraw] )
        {
            
            /*
             * Variable value and distance scaling factor
             */
            if( i<nraw )
            {
                vx = state->current.xp.ptr.p_double[i];
                vnrm = xpnrm;
            }
            else
            {
                vx = state->current.xs.ptr.p_double[i-nraw];
                vnrm = xsnrm;
            }
            if( state->hasbndlx.ptr.p_bool[i] )
            {
                vtgt = ae_maxreal(vx-vnrm*gipm_tightendist, state->bndlx.ptr.p_double[i], _state);
                vrlx = ae_maxreal(vx-vnrm*gipm_relaxdist, state->bndlx.ptr.p_double[i], _state);
                vmid = ae_maxreal(vx-vnrm*targetdist, state->bndlx.ptr.p_double[i], _state);
                vprev = state->finitebndlx.ptr.p_double[i];
                if( ae_fp_less(state->finitebndlx.ptr.p_double[i],vtgt) )
                {
                    state->finitebndlx.ptr.p_double[i] = vmid;
                    ntightened = ntightened+1;
                }
                if( ae_fp_greater(state->finitebndlx.ptr.p_double[i],vrlx) )
                {
                    state->finitebndlx.ptr.p_double[i] = vmid;
                    nrelaxed = nrelaxed+1;
                }
                if( i>=nraw )
                {
                    state->current.gl.ptr.p_double[i-nraw] = ae_maxreal(state->current.gl.ptr.p_double[i-nraw]-(state->finitebndlx.ptr.p_double[i]-vprev), ae_machineepsilon, _state);
                }
            }
            if( state->hasbndux.ptr.p_bool[i] )
            {
                vtgt = ae_minreal(vx+vnrm*gipm_tightendist, state->bndux.ptr.p_double[i], _state);
                vrlx = ae_minreal(vx+vnrm*gipm_relaxdist, state->bndux.ptr.p_double[i], _state);
                vmid = ae_minreal(vx+vnrm*targetdist, state->bndux.ptr.p_double[i], _state);
                vprev = state->finitebndux.ptr.p_double[i];
                if( ae_fp_greater(state->finitebndux.ptr.p_double[i],vtgt) )
                {
                    state->finitebndux.ptr.p_double[i] = vmid;
                    ntightened = ntightened+1;
                }
                if( ae_fp_less(state->finitebndux.ptr.p_double[i],vrlx) )
                {
                    state->finitebndux.ptr.p_double[i] = vmid;
                    nrelaxed = nrelaxed+1;
                }
                if( i>=nraw )
                {
                    state->current.gu.ptr.p_double[i-nraw] = ae_maxreal(state->current.gu.ptr.p_double[i-nraw]-(vprev-state->finitebndux.ptr.p_double[i]), ae_machineepsilon, _state);
                }
            }
        }
    }
    gipm_safeboundsfromfinitetightened(state, _state);
    if( state->dotrace&&nrelaxed+ntightened>0 )
    {
        ae_trace("> %0d bounds tightened, %0d bounds relaxed\n",
            (int)(ntightened),
            (int)(nrelaxed));
    }
}


/*************************************************************************
Compute recommended penalty coefficient Nu

  -- ALGLIB --
     Copyright 25.05.2024 by Bochkanov Sergey
*************************************************************************/
static double gipm_getnu(double currentmu, ae_state *_state)
{
    double result;


    result = ae_minreal(currentmu/gipm_penaltygrowth, gipm_maxnu*ae_maxreal(currentmu, (double)(1), _state), _state);
    return result;
}


/*************************************************************************
Initialize memory for nonmonotonic merit function optimization

  -- ALGLIB --
     Copyright 25.02.2025 by Bochkanov Sergey
*************************************************************************/
static void gipm_nonmonotonicinit(gipmstate* state,
     ae_int_t memlen,
     double merit0,
     ae_state *_state)
{


    ae_assert(memlen>=0, "GIPM: integrity check 696157 failed", _state);
    state->nonmonotonicmemlen = memlen;
    state->nonmonotonicpos = 0;
    rsetallocv(memlen+1, merit0, &state->nonmonotonicmerit, _state);
    state->nonmonotonicmerit.ptr.p_double[memlen] = merit0;
}


/*************************************************************************
Push new value to the nonmonotonic memory

  -- ALGLIB --
     Copyright 25.02.2025 by Bochkanov Sergey
*************************************************************************/
static void gipm_nonmonotonicpush(gipmstate* state,
     double meritv,
     ae_state *_state)
{


    state->nonmonotonicmerit.ptr.p_double[state->nonmonotonicpos] = meritv;
    state->nonmonotonicpos = (state->nonmonotonicpos+1)%(state->nonmonotonicmemlen+1);
}


/*************************************************************************
Returns nonmonotonic correction that should be added to MeritV

  -- ALGLIB --
     Copyright 25.02.2025 by Bochkanov Sergey
*************************************************************************/
static double gipm_nonmonotonicgetcorr(gipmstate* state,
     double meritv,
     ae_state *_state)
{
    ae_int_t i;
    double mx;
    double result;


    mx = meritv;
    for(i=0; i<=state->nonmonotonicmemlen; i++)
    {
        mx = ae_maxreal(mx, state->nonmonotonicmerit.ptr.p_double[i], _state);
    }
    result = ae_maxreal(mx-meritv, 0.0, _state);
    return result;
}


void _gipmvars_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gipmvars *p = (gipmvars*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->xp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zlp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zup, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->yp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->yl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->yu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->yv, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xs, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zls, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zus, 0, DT_REAL, _state, make_automatic);
}


void _gipmvars_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gipmvars       *dst = (gipmvars*)_dst;
    const gipmvars *src = (const gipmvars*)_src;
    dst->np = src->np;
    dst->nf = src->nf;
    dst->mflex = src->mflex;
    dst->mhard = src->mhard;
    ae_vector_init_copy(&dst->xp, &src->xp, _state, make_automatic);
    ae_vector_init_copy(&dst->zlp, &src->zlp, _state, make_automatic);
    ae_vector_init_copy(&dst->zup, &src->zup, _state, make_automatic);
    ae_vector_init_copy(&dst->yp, &src->yp, _state, make_automatic);
    ae_vector_init_copy(&dst->yl, &src->yl, _state, make_automatic);
    ae_vector_init_copy(&dst->yu, &src->yu, _state, make_automatic);
    ae_vector_init_copy(&dst->yv, &src->yv, _state, make_automatic);
    ae_vector_init_copy(&dst->xs, &src->xs, _state, make_automatic);
    ae_vector_init_copy(&dst->gl, &src->gl, _state, make_automatic);
    ae_vector_init_copy(&dst->gu, &src->gu, _state, make_automatic);
    ae_vector_init_copy(&dst->zls, &src->zls, _state, make_automatic);
    ae_vector_init_copy(&dst->zus, &src->zus, _state, make_automatic);
}


void _gipmvars_clear(void* _p)
{
    gipmvars *p = (gipmvars*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->xp);
    ae_vector_clear(&p->zlp);
    ae_vector_clear(&p->zup);
    ae_vector_clear(&p->yp);
    ae_vector_clear(&p->yl);
    ae_vector_clear(&p->yu);
    ae_vector_clear(&p->yv);
    ae_vector_clear(&p->xs);
    ae_vector_clear(&p->gl);
    ae_vector_clear(&p->gu);
    ae_vector_clear(&p->zls);
    ae_vector_clear(&p->zus);
}


void _gipmvars_destroy(void* _p)
{
    gipmvars *p = (gipmvars*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->xp);
    ae_vector_destroy(&p->zlp);
    ae_vector_destroy(&p->zup);
    ae_vector_destroy(&p->yp);
    ae_vector_destroy(&p->yl);
    ae_vector_destroy(&p->yu);
    ae_vector_destroy(&p->yv);
    ae_vector_destroy(&p->xs);
    ae_vector_destroy(&p->gl);
    ae_vector_destroy(&p->gu);
    ae_vector_destroy(&p->zls);
    ae_vector_destroy(&p->zus);
}


void _gipmrhs_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gipmrhs *p = (gipmrhs*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->rp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rs, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rgl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rgu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ry, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rcl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rcu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rlp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rup, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rls, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rus, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rv, 0, DT_REAL, _state, make_automatic);
}


void _gipmrhs_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gipmrhs       *dst = (gipmrhs*)_dst;
    const gipmrhs *src = (const gipmrhs*)_src;
    ae_vector_init_copy(&dst->rp, &src->rp, _state, make_automatic);
    ae_vector_init_copy(&dst->rs, &src->rs, _state, make_automatic);
    ae_vector_init_copy(&dst->rgl, &src->rgl, _state, make_automatic);
    ae_vector_init_copy(&dst->rgu, &src->rgu, _state, make_automatic);
    ae_vector_init_copy(&dst->ry, &src->ry, _state, make_automatic);
    ae_vector_init_copy(&dst->rcl, &src->rcl, _state, make_automatic);
    ae_vector_init_copy(&dst->rcu, &src->rcu, _state, make_automatic);
    ae_vector_init_copy(&dst->rlp, &src->rlp, _state, make_automatic);
    ae_vector_init_copy(&dst->rup, &src->rup, _state, make_automatic);
    ae_vector_init_copy(&dst->rls, &src->rls, _state, make_automatic);
    ae_vector_init_copy(&dst->rus, &src->rus, _state, make_automatic);
    ae_vector_init_copy(&dst->rv, &src->rv, _state, make_automatic);
}


void _gipmrhs_clear(void* _p)
{
    gipmrhs *p = (gipmrhs*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->rp);
    ae_vector_clear(&p->rs);
    ae_vector_clear(&p->rgl);
    ae_vector_clear(&p->rgu);
    ae_vector_clear(&p->ry);
    ae_vector_clear(&p->rcl);
    ae_vector_clear(&p->rcu);
    ae_vector_clear(&p->rlp);
    ae_vector_clear(&p->rup);
    ae_vector_clear(&p->rls);
    ae_vector_clear(&p->rus);
    ae_vector_clear(&p->rv);
}


void _gipmrhs_destroy(void* _p)
{
    gipmrhs *p = (gipmrhs*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->rp);
    ae_vector_destroy(&p->rs);
    ae_vector_destroy(&p->rgl);
    ae_vector_destroy(&p->rgu);
    ae_vector_destroy(&p->ry);
    ae_vector_destroy(&p->rcl);
    ae_vector_destroy(&p->rcu);
    ae_vector_destroy(&p->rlp);
    ae_vector_destroy(&p->rup);
    ae_vector_destroy(&p->rls);
    ae_vector_destroy(&p->rus);
    ae_vector_destroy(&p->rv);
}


void _gipmcondensedsystem_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gipmcondensedsystem *p = (gipmcondensedsystem*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->tls, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tus, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tgl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tgu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ts, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ta, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->t0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->t1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->t2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->t3, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->t4, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->t5, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->wx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->wy, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->wyv, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d1v, 0, DT_REAL, _state, make_automatic);
}


void _gipmcondensedsystem_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gipmcondensedsystem       *dst = (gipmcondensedsystem*)_dst;
    const gipmcondensedsystem *src = (const gipmcondensedsystem*)_src;
    ae_vector_init_copy(&dst->tls, &src->tls, _state, make_automatic);
    ae_vector_init_copy(&dst->tus, &src->tus, _state, make_automatic);
    ae_vector_init_copy(&dst->tgl, &src->tgl, _state, make_automatic);
    ae_vector_init_copy(&dst->tgu, &src->tgu, _state, make_automatic);
    ae_vector_init_copy(&dst->ts, &src->ts, _state, make_automatic);
    ae_vector_init_copy(&dst->ta, &src->ta, _state, make_automatic);
    ae_vector_init_copy(&dst->t0, &src->t0, _state, make_automatic);
    ae_vector_init_copy(&dst->t1, &src->t1, _state, make_automatic);
    ae_vector_init_copy(&dst->t2, &src->t2, _state, make_automatic);
    ae_vector_init_copy(&dst->t3, &src->t3, _state, make_automatic);
    ae_vector_init_copy(&dst->t4, &src->t4, _state, make_automatic);
    ae_vector_init_copy(&dst->t5, &src->t5, _state, make_automatic);
    ae_vector_init_copy(&dst->wx, &src->wx, _state, make_automatic);
    ae_vector_init_copy(&dst->wy, &src->wy, _state, make_automatic);
    ae_vector_init_copy(&dst->wyv, &src->wyv, _state, make_automatic);
    ae_vector_init_copy(&dst->d0, &src->d0, _state, make_automatic);
    ae_vector_init_copy(&dst->d1, &src->d1, _state, make_automatic);
    ae_vector_init_copy(&dst->d1v, &src->d1v, _state, make_automatic);
}


void _gipmcondensedsystem_clear(void* _p)
{
    gipmcondensedsystem *p = (gipmcondensedsystem*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->tls);
    ae_vector_clear(&p->tus);
    ae_vector_clear(&p->tgl);
    ae_vector_clear(&p->tgu);
    ae_vector_clear(&p->ts);
    ae_vector_clear(&p->ta);
    ae_vector_clear(&p->t0);
    ae_vector_clear(&p->t1);
    ae_vector_clear(&p->t2);
    ae_vector_clear(&p->t3);
    ae_vector_clear(&p->t4);
    ae_vector_clear(&p->t5);
    ae_vector_clear(&p->wx);
    ae_vector_clear(&p->wy);
    ae_vector_clear(&p->wyv);
    ae_vector_clear(&p->d0);
    ae_vector_clear(&p->d1);
    ae_vector_clear(&p->d1v);
}


void _gipmcondensedsystem_destroy(void* _p)
{
    gipmcondensedsystem *p = (gipmcondensedsystem*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->tls);
    ae_vector_destroy(&p->tus);
    ae_vector_destroy(&p->tgl);
    ae_vector_destroy(&p->tgu);
    ae_vector_destroy(&p->ts);
    ae_vector_destroy(&p->ta);
    ae_vector_destroy(&p->t0);
    ae_vector_destroy(&p->t1);
    ae_vector_destroy(&p->t2);
    ae_vector_destroy(&p->t3);
    ae_vector_destroy(&p->t4);
    ae_vector_destroy(&p->t5);
    ae_vector_destroy(&p->wx);
    ae_vector_destroy(&p->wy);
    ae_vector_destroy(&p->wyv);
    ae_vector_destroy(&p->d0);
    ae_vector_destroy(&p->d1);
    ae_vector_destroy(&p->d1v);
}


void _gipmstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gipmstate *p = (gipmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndlx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndux, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndlx, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndux, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->isequality, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->fscales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->qnmask, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->reportx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->querydata, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyfi, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->replysj, _state, make_automatic);
    ae_vector_init(&p->replysol, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyprod, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->elp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->eup, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->els, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->eus, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitebndlx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitebndux, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->safeguardbndlp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->safeguardbndup, 0, DT_REAL, _state, make_automatic);
    _gipmvars_init(&p->current, _state, make_automatic);
    _gipmvars_init(&p->best, _state, make_automatic);
    _varsfuncjac_init(&p->currentfj, _state, make_automatic);
    _varsfuncjac_init(&p->bestfj, _state, make_automatic);
    _nlpfilter_init(&p->filter, _state, make_automatic);
    ae_vector_init(&p->nonmonotonicmerit, 0, DT_REAL, _state, make_automatic);
    _stimer_init(&p->timertotal, _state, make_automatic);
    ae_nxpool_init(&p->mnx1pool, DT_REAL, _state, make_automatic);
    _gipmrhs_init(&p->rhs, _state, make_automatic);
    _gipmcondensedsystem_init(&p->condensed, _state, make_automatic);
    _gipmvars_init(&p->delta, _state, make_automatic);
    _gipmvars_init(&p->corr, _state, make_automatic);
    ae_vector_init(&p->tmphdxp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpjdxp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpjtdy, 0, DT_REAL, _state, make_automatic);
    _gipmvars_init(&p->trial, _state, make_automatic);
    _varsfuncjac_init(&p->trialfj, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
}


void _gipmstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gipmstate       *dst = (gipmstate*)_dst;
    const gipmstate *src = (const gipmstate*)_src;
    dst->nraw = src->nraw;
    dst->mflex = src->mflex;
    dst->mhard = src->mhard;
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->bndlx, &src->bndlx, _state, make_automatic);
    ae_vector_init_copy(&dst->bndux, &src->bndux, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndlx, &src->hasbndlx, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndux, &src->hasbndux, _state, make_automatic);
    ae_vector_init_copy(&dst->isequality, &src->isequality, _state, make_automatic);
    ae_vector_init_copy(&dst->fscales, &src->fscales, _state, make_automatic);
    dst->isfirstorder = src->isfirstorder;
    ae_vector_init_copy(&dst->qnmask, &src->qnmask, _state, make_automatic);
    dst->isqnmasknonzero = src->isqnmasknonzero;
    dst->trustradxp = src->trustradxp;
    dst->mudependent = src->mudependent;
    dst->eps = src->eps;
    dst->maxits = src->maxits;
    dst->requesttype = src->requesttype;
    ae_vector_init_copy(&dst->reportx, &src->reportx, _state, make_automatic);
    dst->reportf = src->reportf;
    ae_vector_init_copy(&dst->querydata, &src->querydata, _state, make_automatic);
    dst->querymu = src->querymu;
    ae_vector_init_copy(&dst->replyfi, &src->replyfi, _state, make_automatic);
    _sparsematrix_init_copy(&dst->replysj, &src->replysj, _state, make_automatic);
    ae_vector_init_copy(&dst->replysol, &src->replysol, _state, make_automatic);
    ae_vector_init_copy(&dst->replyprod, &src->replyprod, _state, make_automatic);
    dst->hasjac = src->hasjac;
    dst->factsuccess = src->factsuccess;
    ae_vector_init_copy(&dst->elp, &src->elp, _state, make_automatic);
    ae_vector_init_copy(&dst->eup, &src->eup, _state, make_automatic);
    ae_vector_init_copy(&dst->els, &src->els, _state, make_automatic);
    ae_vector_init_copy(&dst->eus, &src->eus, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndlx, &src->finitebndlx, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndux, &src->finitebndux, _state, make_automatic);
    ae_vector_init_copy(&dst->safeguardbndlp, &src->safeguardbndlp, _state, make_automatic);
    ae_vector_init_copy(&dst->safeguardbndup, &src->safeguardbndup, _state, make_automatic);
    dst->nrmx0 = src->nrmx0;
    dst->absf0 = src->absf0;
    dst->mu = src->mu;
    dst->lambdav = src->lambdav;
    dst->lambdadecay = src->lambdadecay;
    dst->subsequentfactfailures = src->subsequentfactfailures;
    _gipmvars_init_copy(&dst->current, &src->current, _state, make_automatic);
    _gipmvars_init_copy(&dst->best, &src->best, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->currentfj, &src->currentfj, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->bestfj, &src->bestfj, _state, make_automatic);
    dst->besterr = src->besterr;
    dst->rhsstagnationcounter = src->rhsstagnationcounter;
    dst->filterready = src->filterready;
    _nlpfilter_init_copy(&dst->filter, &src->filter, _state, make_automatic);
    dst->dotrace = src->dotrace;
    dst->dodetailedtrace = src->dodetailedtrace;
    dst->dolaconictrace = src->dolaconictrace;
    dst->nonmonotonicmemlen = src->nonmonotonicmemlen;
    dst->nonmonotonicpos = src->nonmonotonicpos;
    ae_vector_init_copy(&dst->nonmonotonicmerit, &src->nonmonotonicmerit, _state, make_automatic);
    dst->repiterationscount = src->repiterationscount;
    dst->repterminationtype = src->repterminationtype;
    _stimer_init_copy(&dst->timertotal, &src->timertotal, _state, make_automatic);
    ae_nxpool_init_copy(&dst->mnx1pool, &src->mnx1pool, _state, make_automatic);
    _gipmrhs_init_copy(&dst->rhs, &src->rhs, _state, make_automatic);
    _gipmcondensedsystem_init_copy(&dst->condensed, &src->condensed, _state, make_automatic);
    _gipmvars_init_copy(&dst->delta, &src->delta, _state, make_automatic);
    _gipmvars_init_copy(&dst->corr, &src->corr, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphdxp, &src->tmphdxp, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpjdxp, &src->tmpjdxp, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpjtdy, &src->tmpjtdy, _state, make_automatic);
    _gipmvars_init_copy(&dst->trial, &src->trial, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->trialfj, &src->trialfj, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
}


void _gipmstate_clear(void* _p)
{
    gipmstate *p = (gipmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->bndlx);
    ae_vector_clear(&p->bndux);
    ae_vector_clear(&p->hasbndlx);
    ae_vector_clear(&p->hasbndux);
    ae_vector_clear(&p->isequality);
    ae_vector_clear(&p->fscales);
    ae_vector_clear(&p->qnmask);
    ae_vector_clear(&p->reportx);
    ae_vector_clear(&p->querydata);
    ae_vector_clear(&p->replyfi);
    _sparsematrix_clear(&p->replysj);
    ae_vector_clear(&p->replysol);
    ae_vector_clear(&p->replyprod);
    ae_vector_clear(&p->elp);
    ae_vector_clear(&p->eup);
    ae_vector_clear(&p->els);
    ae_vector_clear(&p->eus);
    ae_vector_clear(&p->finitebndlx);
    ae_vector_clear(&p->finitebndux);
    ae_vector_clear(&p->safeguardbndlp);
    ae_vector_clear(&p->safeguardbndup);
    _gipmvars_clear(&p->current);
    _gipmvars_clear(&p->best);
    _varsfuncjac_clear(&p->currentfj);
    _varsfuncjac_clear(&p->bestfj);
    _nlpfilter_clear(&p->filter);
    ae_vector_clear(&p->nonmonotonicmerit);
    _stimer_clear(&p->timertotal);
    ae_nxpool_clear(&p->mnx1pool);
    _gipmrhs_clear(&p->rhs);
    _gipmcondensedsystem_clear(&p->condensed);
    _gipmvars_clear(&p->delta);
    _gipmvars_clear(&p->corr);
    ae_vector_clear(&p->tmphdxp);
    ae_vector_clear(&p->tmpjdxp);
    ae_vector_clear(&p->tmpjtdy);
    _gipmvars_clear(&p->trial);
    _varsfuncjac_clear(&p->trialfj);
    _rcommstate_clear(&p->rstate);
}


void _gipmstate_destroy(void* _p)
{
    gipmstate *p = (gipmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->bndlx);
    ae_vector_destroy(&p->bndux);
    ae_vector_destroy(&p->hasbndlx);
    ae_vector_destroy(&p->hasbndux);
    ae_vector_destroy(&p->isequality);
    ae_vector_destroy(&p->fscales);
    ae_vector_destroy(&p->qnmask);
    ae_vector_destroy(&p->reportx);
    ae_vector_destroy(&p->querydata);
    ae_vector_destroy(&p->replyfi);
    _sparsematrix_destroy(&p->replysj);
    ae_vector_destroy(&p->replysol);
    ae_vector_destroy(&p->replyprod);
    ae_vector_destroy(&p->elp);
    ae_vector_destroy(&p->eup);
    ae_vector_destroy(&p->els);
    ae_vector_destroy(&p->eus);
    ae_vector_destroy(&p->finitebndlx);
    ae_vector_destroy(&p->finitebndux);
    ae_vector_destroy(&p->safeguardbndlp);
    ae_vector_destroy(&p->safeguardbndup);
    _gipmvars_destroy(&p->current);
    _gipmvars_destroy(&p->best);
    _varsfuncjac_destroy(&p->currentfj);
    _varsfuncjac_destroy(&p->bestfj);
    _nlpfilter_destroy(&p->filter);
    ae_vector_destroy(&p->nonmonotonicmerit);
    _stimer_destroy(&p->timertotal);
    ae_nxpool_destroy(&p->mnx1pool);
    _gipmrhs_destroy(&p->rhs);
    _gipmcondensedsystem_destroy(&p->condensed);
    _gipmvars_destroy(&p->delta);
    _gipmvars_destroy(&p->corr);
    ae_vector_destroy(&p->tmphdxp);
    ae_vector_destroy(&p->tmpjdxp);
    ae_vector_destroy(&p->tmpjtdy);
    _gipmvars_destroy(&p->trial);
    _varsfuncjac_destroy(&p->trialfj);
    _rcommstate_destroy(&p->rstate);
}


#endif
#if defined(AE_COMPILE_GQPIPM) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
This function canonicalizes conic constraints by  adding  slack  variables
and linear equality constraints in order to transform the problem  into  a
canonic form (all conic constraints are axis-orthogonal, ConeType>=0,  any
constraint can be made strictly feasible by shifting one/several variables
without disturbing other constraints.

The function can add new variables and modify scales/origin/constraints.

The function must be called (if necessary) prior to initializing the GQPIPM
solver, so that it receives modified constraints.

  -- ALGLIB --
     Copyright 11.09.2024 by Bochkanov Sergey
*************************************************************************/
void gqpipmcanonicalizeconicconstraints(/* Real    */ ae_vector* s,
     /* Real    */ ae_vector* xorigin,
     /* Real    */ ae_vector* b,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     /* Real    */ ae_vector* xs,
     sparsematrix* sparseh,
     ae_bool hupper,
     ae_bool hash,
     ae_int_t* n,
     sparsematrix* a,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t* m,
     xquadraticconstraints* xqc,
     xconicconstraints* xcc,
     /* Real    */ ae_vector* lagbcmin,
     /* Real    */ ae_vector* lagbcmax,
     ae_state *_state)
{
    ae_frame _frame_block;
    xconicconstraint *cci;
    ae_smart_ptr _cci;
    ae_int_t coneidx;
    ae_int_t nv;
    ae_int_t i;
    ae_int_t j;
    ae_int_t newnradial;
    ae_int_t newaxial;
    ae_vector impliedbndl;
    ae_vector impliedbndu;
    ae_vector isconicradial;
    ae_vector isconicaxial;
    ae_vector cntradial;
    ae_vector cntupwardaxial;
    ae_vector cntdownwardaxial;
    double initprimdual;
    double vbnd;
    double ccscale;
    ae_bool needslacks;
    ae_bool donecc;
    ae_int_t axisidx;
    ae_int_t kpow;
    double a0;
    double a1;
    double b0;
    double b1;
    double rhs;
    double tmpr;
    ae_int_t tmpi;

    ae_frame_make(_state, &_frame_block);
    memset(&_cci, 0, sizeof(_cci));
    memset(&impliedbndl, 0, sizeof(impliedbndl));
    memset(&impliedbndu, 0, sizeof(impliedbndu));
    memset(&isconicradial, 0, sizeof(isconicradial));
    memset(&isconicaxial, 0, sizeof(isconicaxial));
    memset(&cntradial, 0, sizeof(cntradial));
    memset(&cntupwardaxial, 0, sizeof(cntupwardaxial));
    memset(&cntdownwardaxial, 0, sizeof(cntdownwardaxial));
    ae_smart_ptr_init(&_cci, (void**)&cci, ae_false, _state, ae_true);
    ae_vector_init(&impliedbndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&impliedbndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&isconicradial, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&isconicaxial, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&cntradial, 0, DT_INT, _state, ae_true);
    ae_vector_init(&cntupwardaxial, 0, DT_INT, _state, ae_true);
    ae_vector_init(&cntdownwardaxial, 0, DT_INT, _state, ae_true);

    initprimdual = gipmgetinitprimdual(_state);
    rsetallocv(*n, _state->v_neginf, &impliedbndl, _state);
    rsetallocv(*n, _state->v_posinf, &impliedbndu, _state);
    rsetallocv(*n, -ae_maxrealnumber, lagbcmin, _state);
    rsetallocv(*n, ae_maxrealnumber, lagbcmax, _state);
    for(coneidx=0; coneidx<=xccgetcount(xcc, _state)-1; coneidx++)
    {
        ae_obj_array_get(&xcc->constraints, coneidx, &_cci, _state);
        nv = cci->nvars;
        if( nv==0 )
        {
            continue;
        }
        if( cci->conetype==xccgenericorthogonalconetype(_state) )
        {
            tagsortmiddleirr(&cci->varidx, &cci->diaga, &cci->shftc, 0, nv-1, _state);
            newnradial = 0;
            for(i=0; i<=nv-2; i++)
            {
                if( ae_fp_eq(cci->diaga.ptr.p_double[i],0.0) )
                {
                    cci->shftc.ptr.p_double[nv] = cci->shftc.ptr.p_double[nv]+cci->shftc.ptr.p_double[i]*cci->shftc.ptr.p_double[i];
                    continue;
                }
                if( newnradial>0&&cci->varidx.ptr.p_int[i]==cci->varidx.ptr.p_int[newnradial-1] )
                {
                    a0 = cci->diaga.ptr.p_double[newnradial-1];
                    b0 = cci->shftc.ptr.p_double[newnradial-1];
                    a1 = cci->diaga.ptr.p_double[i];
                    b1 = cci->shftc.ptr.p_double[i];
                    cci->diaga.ptr.p_double[newnradial-1] = ae_sqrt(a0*a0+a1*a1, _state);
                    ae_assert(ae_fp_greater(cci->diaga.ptr.p_double[newnradial-1],(double)(0)), "GQPIPM: integrity check 248622 failed", _state);
                    cci->shftc.ptr.p_double[newnradial-1] = (a0*b0+a1*b1)/cci->diaga.ptr.p_double[newnradial-1];
                    cci->shftc.ptr.p_double[nv] = cci->shftc.ptr.p_double[nv]+ae_maxreal(b0*b0+b1*b1-cci->shftc.ptr.p_double[newnradial-1]*cci->shftc.ptr.p_double[newnradial-1], 0.0, _state);
                    continue;
                }
                cci->varidx.ptr.p_int[newnradial] = cci->varidx.ptr.p_int[i];
                cci->diaga.ptr.p_double[newnradial] = cci->diaga.ptr.p_double[i];
                cci->shftc.ptr.p_double[newnradial] = cci->shftc.ptr.p_double[i];
                newnradial = newnradial+1;
            }
            cci->varidx.ptr.p_int[newnradial] = cci->varidx.ptr.p_int[nv-1];
            cci->diaga.ptr.p_double[newnradial] = cci->diaga.ptr.p_double[nv-1];
            cci->shftc.ptr.p_double[newnradial] = cci->shftc.ptr.p_double[nv-1];
            newnradial = newnradial+1;
            cci->shftc.ptr.p_double[newnradial] = cci->shftc.ptr.p_double[nv];
            nv = newnradial;
            cci->nvars = nv;
            cci->conetype = xccprimitiveconetype(_state);
            if( ibinarysearchispresent(&cci->varidx, 0, nv-1, cci->varidx.ptr.p_int[nv-1], _state) )
            {
                cci->conetype = xccdenormalizedprimitiveconetype(_state);
            }
            continue;
        }
        if( cci->conetype==xccgenericorthogonalpowerconetype(_state) )
        {
            kpow = cci->kpow;
            newnradial = 0;
            if( kpow<nv )
            {
                tagsortmiddleirr(&cci->varidx, &cci->diaga, &cci->shftc, 0, nv-kpow, _state);
                for(i=0; i<=nv-kpow-1; i++)
                {
                    if( ae_fp_eq(cci->diaga.ptr.p_double[i],0.0) )
                    {
                        cci->shftc.ptr.p_double[nv] = cci->shftc.ptr.p_double[nv]+cci->shftc.ptr.p_double[i]*cci->shftc.ptr.p_double[i];
                        continue;
                    }
                    if( newnradial>0&&cci->varidx.ptr.p_int[i]==cci->varidx.ptr.p_int[newnradial-1] )
                    {
                        a0 = cci->diaga.ptr.p_double[newnradial-1];
                        b0 = cci->shftc.ptr.p_double[newnradial-1];
                        a1 = cci->diaga.ptr.p_double[i];
                        b1 = cci->shftc.ptr.p_double[i];
                        cci->diaga.ptr.p_double[newnradial-1] = ae_sqrt(a0*a0+a1*a1, _state);
                        ae_assert(ae_fp_greater(cci->diaga.ptr.p_double[newnradial-1],(double)(0)), "GQPIPM: integrity check 248622 failed", _state);
                        cci->shftc.ptr.p_double[newnradial-1] = (a0*b0+a1*b1)/cci->diaga.ptr.p_double[newnradial-1];
                        cci->shftc.ptr.p_double[nv] = cci->shftc.ptr.p_double[nv]+ae_maxreal(b0*b0+b1*b1-cci->shftc.ptr.p_double[newnradial-1]*cci->shftc.ptr.p_double[newnradial-1], 0.0, _state);
                        continue;
                    }
                    cci->varidx.ptr.p_int[newnradial] = cci->varidx.ptr.p_int[i];
                    cci->diaga.ptr.p_double[newnradial] = cci->diaga.ptr.p_double[i];
                    cci->shftc.ptr.p_double[newnradial] = cci->shftc.ptr.p_double[i];
                    newnradial = newnradial+1;
                }
            }
            newaxial = 0;
            for(i=nv-kpow; i<=nv-1; i++)
            {
                cci->varidx.ptr.p_int[newnradial+newaxial] = cci->varidx.ptr.p_int[i];
                cci->diaga.ptr.p_double[newnradial+newaxial] = cci->diaga.ptr.p_double[i];
                cci->shftc.ptr.p_double[newnradial+newaxial] = cci->shftc.ptr.p_double[i];
                newaxial = newaxial+1;
            }
            cci->shftc.ptr.p_double[newnradial+newaxial] = cci->shftc.ptr.p_double[nv];
            nv = newnradial+newaxial;
            cci->nvars = nv;
            cci->conetype = xccdenormalizedprimitivepowerconetype(_state);
            continue;
        }
        if( cci->conetype==xccprimitiveconetype(_state) )
        {
            continue;
        }
        if( cci->conetype==xccprimitivepowerconetype(_state) )
        {
            continue;
        }
        ae_assert(ae_false, "GQPIPM: integrity check 227420 failed", _state);
    }
    bsetallocv(*n, ae_false, &isconicradial, _state);
    bsetallocv(*n, ae_false, &isconicaxial, _state);
    isetallocv(*n, 0, &cntradial, _state);
    isetallocv(*n, 0, &cntupwardaxial, _state);
    isetallocv(*n, 0, &cntdownwardaxial, _state);
    for(coneidx=0; coneidx<=xccgetcount(xcc, _state)-1; coneidx++)
    {
        ae_obj_array_get(&xcc->constraints, coneidx, &_cci, _state);
        nv = cci->nvars;
        if( nv==0 )
        {
            continue;
        }
        if( cci->conetype==xccdenormalizedprimitiveconetype(_state)||cci->conetype==xccprimitiveconetype(_state) )
        {
            for(i=0; i<=nv-2; i++)
            {
                cntradial.ptr.p_int[cci->varidx.ptr.p_int[i]] = cntradial.ptr.p_int[cci->varidx.ptr.p_int[i]]+1;
            }
            if( ae_fp_greater(cci->diaga.ptr.p_double[nv-1],(double)(0)) )
            {
                cntupwardaxial.ptr.p_int[cci->varidx.ptr.p_int[nv-1]] = cntupwardaxial.ptr.p_int[cci->varidx.ptr.p_int[nv-1]]+1;
            }
            if( ae_fp_less(cci->diaga.ptr.p_double[nv-1],(double)(0)) )
            {
                cntdownwardaxial.ptr.p_int[cci->varidx.ptr.p_int[nv-1]] = cntdownwardaxial.ptr.p_int[cci->varidx.ptr.p_int[nv-1]]+1;
            }
            continue;
        }
        if( cci->conetype==xccdenormalizedprimitivepowerconetype(_state)||cci->conetype==xccprimitivepowerconetype(_state) )
        {
            kpow = cci->kpow;
            for(i=0; i<=nv-kpow-1; i++)
            {
                cntradial.ptr.p_int[cci->varidx.ptr.p_int[i]] = cntradial.ptr.p_int[cci->varidx.ptr.p_int[i]]+1;
            }
            for(i=nv-kpow; i<=nv-1; i++)
            {
                if( ae_fp_greater(cci->diaga.ptr.p_double[i],(double)(0)) )
                {
                    cntupwardaxial.ptr.p_int[cci->varidx.ptr.p_int[i]] = cntupwardaxial.ptr.p_int[cci->varidx.ptr.p_int[i]]+1;
                }
                if( ae_fp_less(cci->diaga.ptr.p_double[i],(double)(0)) )
                {
                    cntdownwardaxial.ptr.p_int[cci->varidx.ptr.p_int[i]] = cntdownwardaxial.ptr.p_int[cci->varidx.ptr.p_int[i]]+1;
                }
            }
            continue;
        }
        ae_assert(ae_false, "GQPIPM: integrity check 397122 failed", _state);
    }
    for(coneidx=0; coneidx<=xccgetcount(xcc, _state)-1; coneidx++)
    {
        ae_obj_array_get(&xcc->constraints, coneidx, &_cci, _state);
        nv = cci->nvars;
        if( nv==0 )
        {
            continue;
        }
        donecc = ae_false;
        if( cci->conetype==xccprimitiveconetype(_state) )
        {
            ae_assert(!donecc, "GQIPM: integrity check 267115 failed", _state);
            needslacks = ae_false;
            ccscale = (double)(0);
            for(i=0; i<=nv-1; i++)
            {
                ccscale = ae_maxreal(ccscale, ae_fabs(cci->diaga.ptr.p_double[i]*s->ptr.p_double[cci->varidx.ptr.p_int[i]], _state), _state);
            }
            ccscale = ae_maxreal(ccscale, (double)(1), _state);
            axisidx = cci->varidx.ptr.p_int[nv-1];
            a0 = cci->diaga.ptr.p_double[nv-1];
            b0 = cci->shftc.ptr.p_double[nv-1];
            needslacks = (needslacks||ae_fp_eq(ccscale,(double)(0)))||ae_fp_less_eq(ae_fabs(a0*s->ptr.p_double[axisidx], _state),gqpipm_minconicdenominator*ccscale);
            needslacks = needslacks||ibinarysearchispresent(&cci->varidx, 0, nv-1, axisidx, _state);
            needslacks = needslacks||((ae_isfinite(bndl->ptr.p_double[axisidx], _state)&&ae_isfinite(bndu->ptr.p_double[axisidx], _state))&&ae_fp_less_eq(bndu->ptr.p_double[axisidx]-bndl->ptr.p_double[axisidx],gqpipm_tootightconstraints*s->ptr.p_double[axisidx]));
            if( !needslacks )
            {
                vbnd = -b0/a0;
                if( ae_fp_greater(a0,(double)(0)) )
                {
                    if( !ae_isfinite(bndl->ptr.p_double[axisidx], _state)||ae_fp_greater(vbnd,bndl->ptr.p_double[axisidx]) )
                    {
                        bndl->ptr.p_double[axisidx] = vbnd;
                        lagbcmin->ptr.p_double[axisidx] = 0.0;
                    }
                }
                else
                {
                    if( !ae_isfinite(bndu->ptr.p_double[axisidx], _state)||ae_fp_less(vbnd,bndu->ptr.p_double[axisidx]) )
                    {
                        bndu->ptr.p_double[axisidx] = vbnd;
                        lagbcmax->ptr.p_double[axisidx] = 0.0;
                    }
                }
                continue;
            }
        }
        if( cci->conetype==xccdenormalizedprimitiveconetype(_state)||cci->conetype==xccprimitiveconetype(_state) )
        {
            ae_assert(!donecc, "GQIPM: integrity check 314116 failed", _state);
            a0 = cci->diaga.ptr.p_double[nv-1];
            b0 = cci->shftc.ptr.p_double[nv-1];
            axisidx = cci->varidx.ptr.p_int[nv-1];
            rgrowappendv(*n+1, s, s->ptr.p_double[axisidx], _state);
            rgrowappendv(*n+1, xorigin, 0.0, _state);
            rgrowappendv(*n+1, b, 0.0, _state);
            rgrowappendv(*n+1, bndl, 0.0, _state);
            rgrowappendv(*n+1, bndu, _state->v_posinf, _state);
            rgrowappendv(*n+1, xs, initprimdual, _state);
            if( hash )
            {
                ae_assert(sparseh->n==(*n)&&sparseh->m==(*n), "GQPIPM: integrity check 243517 failed", _state);
                sparseh->n = sparseh->n+1;
                sparseappendemptyrow(sparseh, _state);
            }
            if( *m==0 )
            {
                sparsecreatecrsemptybuf(*n, a, _state);
            }
            ae_assert(a->n==(*n)&&a->m==(*m), "GQPIPM: integrity check 243517 failed", _state);
            a->n = *n+1;
            sparseappendemptyrow(a, _state);
            sparseappendelement(a, axisidx, -a0, _state);
            sparseappendelement(a, *n, 1.0, _state);
            rhs = cci->shftc.ptr.p_double[nv-1];
            if( cci->applyorigin )
            {
                rhs = rhs-a0*xorigin->ptr.p_double[axisidx];
            }
            rgrowappendv(*m+1, al, rhs, _state);
            rgrowappendv(*m+1, au, rhs, _state);
            rgrowappendv(*n+1, lagbcmin, -ae_maxrealnumber, _state);
            rgrowappendv(*n+1, lagbcmax, ae_maxrealnumber, _state);
            rgrowappendv(*n+1, &impliedbndl, 0.0, _state);
            rgrowappendv(*n+1, &impliedbndu, _state->v_posinf, _state);
            bgrowappendv(*n+1, &isconicradial, ae_false, _state);
            bgrowappendv(*n+1, &isconicaxial, ae_true, _state);
            cci->diaga.ptr.p_double[nv-1] = 1.0;
            cci->shftc.ptr.p_double[nv-1] = 0.0;
            cci->varidx.ptr.p_int[nv-1] = *n;
            if( ae_fp_neq(a0,(double)(0))&&ae_fp_less(ae_fabs(b0/a0, _state),gqpipm_bigboxconstraint*s->ptr.p_double[axisidx]) )
            {
                vbnd = -b0/a0;
                if( (ae_fp_greater(a0,(double)(0))&&(!ae_isfinite(bndl->ptr.p_double[axisidx], _state)||ae_fp_greater(vbnd,bndl->ptr.p_double[axisidx])))&&(!ae_isfinite(bndu->ptr.p_double[axisidx], _state)||ae_fp_less(vbnd,bndu->ptr.p_double[axisidx]-gqpipm_tootightconstraints*s->ptr.p_double[axisidx])) )
                {
                    bndl->ptr.p_double[axisidx] = vbnd;
                    lagbcmin->ptr.p_double[axisidx] = 0.0;
                }
                if( (ae_fp_less(a0,(double)(0))&&(!ae_isfinite(bndu->ptr.p_double[axisidx], _state)||ae_fp_less(vbnd,bndu->ptr.p_double[axisidx])))&&(!ae_isfinite(bndl->ptr.p_double[axisidx], _state)||ae_fp_greater(vbnd,bndl->ptr.p_double[axisidx]+gqpipm_tootightconstraints*s->ptr.p_double[axisidx])) )
                {
                    bndu->ptr.p_double[axisidx] = vbnd;
                    lagbcmax->ptr.p_double[axisidx] = 0.0;
                }
            }
            if( ae_fp_greater(a0,(double)(0)) )
            {
                ae_assert(cntupwardaxial.ptr.p_int[axisidx]>0, "GQPIPM: integrity check 395304 failed", _state);
                cntupwardaxial.ptr.p_int[axisidx] = cntupwardaxial.ptr.p_int[axisidx]-1;
                igrowappendv(*n+1, &cntupwardaxial, 1, _state);
            }
            if( ae_fp_less(a0,(double)(0)) )
            {
                ae_assert(cntdownwardaxial.ptr.p_int[axisidx]>0, "GQPIPM: integrity check 400305 failed", _state);
                cntdownwardaxial.ptr.p_int[axisidx] = cntdownwardaxial.ptr.p_int[axisidx]-1;
                igrowappendv(*n+1, &cntdownwardaxial, 1, _state);
            }
            cci->conetype = xccprimitiveconetype(_state);
            *n = *n+1;
            *m = *m+1;
            donecc = ae_true;
        }
        if( cci->conetype==xccdenormalizedprimitivepowerconetype(_state)||cci->conetype==xccprimitivepowerconetype(_state) )
        {
            ae_assert(!donecc, "GQIPM: integrity check 497219 failed", _state);
            kpow = cci->kpow;
            ccscale = (double)(0);
            for(i=0; i<=nv-kpow-1; i++)
            {
                ccscale = ae_maxreal(ccscale, ae_fabs(cci->diaga.ptr.p_double[i]*s->ptr.p_double[cci->varidx.ptr.p_int[i]], _state), _state);
            }
            ccscale = ae_maxreal(ccscale, (double)(1), _state);
            for(i=nv-kpow; i<=nv-1; i++)
            {
                axisidx = cci->varidx.ptr.p_int[i];
                a0 = cci->diaga.ptr.p_double[i];
                b0 = cci->shftc.ptr.p_double[i];
                needslacks = ae_false;
                needslacks = (needslacks||ae_fp_eq(ccscale,(double)(0)))||ae_fp_less_eq(ae_fabs(a0*s->ptr.p_double[axisidx], _state),gqpipm_minconicdenominator*ccscale);
                needslacks = (needslacks||ibinarysearchispresent(&cci->varidx, 0, nv-kpow, axisidx, _state))||ilinearsearchispresent(&cci->varidx, nv-kpow, i, axisidx, _state);
                needslacks = needslacks||((ae_isfinite(bndl->ptr.p_double[axisidx], _state)&&ae_isfinite(bndu->ptr.p_double[axisidx], _state))&&ae_fp_less_eq(bndu->ptr.p_double[axisidx]-bndl->ptr.p_double[axisidx],gqpipm_tootightconstraints*s->ptr.p_double[axisidx]));
                if( !needslacks )
                {
                    vbnd = -b0/a0;
                    if( ae_fp_greater(a0,(double)(0)) )
                    {
                        if( !ae_isfinite(bndl->ptr.p_double[axisidx], _state)||ae_fp_greater(vbnd,bndl->ptr.p_double[axisidx]) )
                        {
                            bndl->ptr.p_double[axisidx] = vbnd;
                            lagbcmin->ptr.p_double[axisidx] = 0.0;
                        }
                    }
                    else
                    {
                        if( !ae_isfinite(bndu->ptr.p_double[axisidx], _state)||ae_fp_less(vbnd,bndu->ptr.p_double[axisidx]) )
                        {
                            bndu->ptr.p_double[axisidx] = vbnd;
                            lagbcmax->ptr.p_double[axisidx] = 0.0;
                        }
                    }
                    continue;
                }
                rgrowappendv(*n+1, s, s->ptr.p_double[axisidx], _state);
                rgrowappendv(*n+1, xorigin, 0.0, _state);
                rgrowappendv(*n+1, b, 0.0, _state);
                rgrowappendv(*n+1, bndl, 0.0, _state);
                rgrowappendv(*n+1, bndu, _state->v_posinf, _state);
                rgrowappendv(*n+1, xs, initprimdual, _state);
                if( hash )
                {
                    ae_assert(sparseh->n==(*n)&&sparseh->m==(*n), "GQPIPM: integrity check 567225 failed", _state);
                    sparseh->n = sparseh->n+1;
                    sparseappendemptyrow(sparseh, _state);
                }
                if( *m==0 )
                {
                    sparsecreatecrsemptybuf(*n, a, _state);
                }
                ae_assert(a->n==(*n)&&a->m==(*m), "GQPIPM: integrity check 573226 failed", _state);
                a->n = *n+1;
                sparseappendemptyrow(a, _state);
                sparseappendelement(a, axisidx, -a0, _state);
                sparseappendelement(a, *n, 1.0, _state);
                rhs = b0;
                if( cci->applyorigin )
                {
                    rhs = rhs-a0*xorigin->ptr.p_double[axisidx];
                }
                rgrowappendv(*m+1, al, rhs, _state);
                rgrowappendv(*m+1, au, rhs, _state);
                rgrowappendv(*n+1, lagbcmin, -ae_maxrealnumber, _state);
                rgrowappendv(*n+1, lagbcmax, ae_maxrealnumber, _state);
                rgrowappendv(*n+1, &impliedbndl, 0.0, _state);
                rgrowappendv(*n+1, &impliedbndu, _state->v_posinf, _state);
                bgrowappendv(*n+1, &isconicradial, ae_false, _state);
                bgrowappendv(*n+1, &isconicaxial, ae_true, _state);
                cci->diaga.ptr.p_double[i] = 1.0;
                cci->shftc.ptr.p_double[i] = 0.0;
                cci->varidx.ptr.p_int[i] = *n;
                if( ae_fp_neq(a0,(double)(0))&&ae_fp_less(ae_fabs(b0/a0, _state),gqpipm_bigboxconstraint*s->ptr.p_double[axisidx]) )
                {
                    vbnd = -b0/a0;
                    if( (ae_fp_greater(a0,(double)(0))&&(!ae_isfinite(bndl->ptr.p_double[axisidx], _state)||ae_fp_greater(vbnd,bndl->ptr.p_double[axisidx])))&&(!ae_isfinite(bndu->ptr.p_double[axisidx], _state)||ae_fp_less(vbnd,bndu->ptr.p_double[axisidx]-gqpipm_tootightconstraints*s->ptr.p_double[axisidx])) )
                    {
                        bndl->ptr.p_double[axisidx] = vbnd;
                        lagbcmin->ptr.p_double[axisidx] = 0.0;
                    }
                    if( (ae_fp_less(a0,(double)(0))&&(!ae_isfinite(bndu->ptr.p_double[axisidx], _state)||ae_fp_less(vbnd,bndu->ptr.p_double[axisidx])))&&(!ae_isfinite(bndl->ptr.p_double[axisidx], _state)||ae_fp_greater(vbnd,bndl->ptr.p_double[axisidx]+gqpipm_tootightconstraints*s->ptr.p_double[axisidx])) )
                    {
                        bndu->ptr.p_double[axisidx] = vbnd;
                        lagbcmax->ptr.p_double[axisidx] = 0.0;
                    }
                }
                if( ae_fp_greater(a0,(double)(0)) )
                {
                    ae_assert(cntupwardaxial.ptr.p_int[axisidx]>0, "GQPIPM: integrity check 395304 failed", _state);
                    cntupwardaxial.ptr.p_int[axisidx] = cntupwardaxial.ptr.p_int[axisidx]-1;
                    igrowappendv(*n+1, &cntupwardaxial, 1, _state);
                }
                if( ae_fp_less(a0,(double)(0)) )
                {
                    ae_assert(cntdownwardaxial.ptr.p_int[axisidx]>0, "GQPIPM: integrity check 400305 failed", _state);
                    cntdownwardaxial.ptr.p_int[axisidx] = cntdownwardaxial.ptr.p_int[axisidx]-1;
                    igrowappendv(*n+1, &cntdownwardaxial, 1, _state);
                }
                *n = *n+1;
                *m = *m+1;
            }
            for(i=nv-kpow; i<=nv-2; i++)
            {
                for(j=i; j<=nv-2; j++)
                {
                    if( cci->varidx.ptr.p_int[j]>cci->varidx.ptr.p_int[j+1] )
                    {
                        tmpi = cci->varidx.ptr.p_int[j];
                        cci->varidx.ptr.p_int[j] = cci->varidx.ptr.p_int[j+1];
                        cci->varidx.ptr.p_int[j+1] = tmpi;
                        tmpr = cci->diaga.ptr.p_double[j];
                        cci->diaga.ptr.p_double[j] = cci->diaga.ptr.p_double[j+1];
                        cci->diaga.ptr.p_double[j+1] = tmpr;
                        tmpr = cci->shftc.ptr.p_double[j];
                        cci->shftc.ptr.p_double[j] = cci->shftc.ptr.p_double[j+1];
                        cci->shftc.ptr.p_double[j+1] = tmpr;
                        tmpr = cci->alphapow.ptr.p_double[j-(nv-kpow)];
                        cci->alphapow.ptr.p_double[j-(nv-kpow)] = cci->alphapow.ptr.p_double[j+1-(nv-kpow)];
                        cci->alphapow.ptr.p_double[j+1-(nv-kpow)] = tmpr;
                    }
                }
            }
            cci->conetype = xccprimitivepowerconetype(_state);
            donecc = ae_true;
        }
        ae_assert(donecc, "GQPIPM: integrity check 218502 failed (unexpected cone type and/or barrier type)", _state);
    }
    xqcupdaten(xqc, *n, _state);
    xccupdaten(xcc, *n, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Initialize GQPIPM structure.

X0      -   initial point; must be:
            * strictly feasible wrt non-equality box constraints
            * non-strictly feasible wrt equality box constraints
            * strictly feasible wrt conic constraints, if present
            Failure to satisfy these properties will lead to an  exception
            generated either during  initialization  or  during  the  very
            first iteration.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
void gqpipminitbuf(/* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* x0,
     ae_int_t n,
     ae_bool isdense,
     ae_bool isfirstorder,
     double eps,
     ae_int_t maxits,
     gqpipmstate* state,
     ae_state *_state)
{
    ae_int_t i;
    double fixtol;
    ae_bool bflg;


    ae_assert(!(isdense&&isfirstorder), "QPIPM: dense problems do not support first order iteration, only second order one", _state);
    fixtol = ae_maxreal(coalesce(eps, 0.01*ae_sqrt(ae_machineepsilon, _state), _state), ae_pow(ae_machineepsilon, 0.75, _state), _state);
    state->algomode = gqpipm_amdense;
    if( !isdense )
    {
        state->algomode = icase2(isfirstorder, gqpipm_amhybrid, gqpipm_amsparse, _state);
    }
    state->basehrank = 0;
    if( state->algomode==gqpipm_amhybrid )
    {
        rsetallocv(n, 0.0, &state->basehsr1diag, _state);
    }
    state->n = n;
    state->mlc = 0;
    state->mqc = 0;
    state->msocc = 0;
    state->hessmemlen = 0;
    state->hessmaxoffdiag = 0;
    state->cvxobjective = 0;
    state->cvxqc = 0;
    state->cvxcc = 0;
    xqcinit(n, &state->xqc, _state);
    xccinit(n, &state->xcc, _state);
    state->optionaldenseq1.hasdenseterm = ae_false;
    ae_obj_array_clear(&state->optionaldensexqc);
    state->fixedvarscnt = 0;
    rsetallocv(n, _state->v_neginf, &state->wrkbndl, _state);
    rsetallocv(n, _state->v_posinf, &state->wrkbndu, _state);
    rsetallocv(n, -ae_sqrt(ae_maxrealnumber, _state), &state->finiterawbndl, _state);
    rsetallocv(n, ae_sqrt(ae_maxrealnumber, _state), &state->finiterawbndu, _state);
    bsetallocv(n, ae_false, &state->isfixed, _state);
    iallocv(n, &state->fixedidx, _state);
    rallocv(n, &state->x0, _state);
    for(i=0; i<=n-1; i++)
    {
        if( (ae_isfinite(bndl->ptr.p_double[i], _state)&&ae_isfinite(bndu->ptr.p_double[i], _state))&&ae_fp_less(bndu->ptr.p_double[i]-bndl->ptr.p_double[i],fixtol) )
        {
            state->isfixed.ptr.p_bool[i] = ae_true;
            state->fixedidx.ptr.p_int[state->fixedvarscnt] = i;
            state->fixedvarscnt = state->fixedvarscnt+1;
        }
        state->x0.ptr.p_double[i] = x0->ptr.p_double[i];
        if( ae_isfinite(bndl->ptr.p_double[i], _state) )
        {
            state->wrkbndl.ptr.p_double[i] = bndl->ptr.p_double[i];
            state->finiterawbndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        }
        if( ae_isfinite(bndu->ptr.p_double[i], _state) )
        {
            state->wrkbndu.ptr.p_double[i] = bndu->ptr.p_double[i];
            state->finiterawbndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        }
    }
    rsetallocv(n, 0.0, &state->c, _state);
    iallocv(n, &state->iotac, _state);
    for(i=0; i<=n-1; i++)
    {
        state->iotac.ptr.p_int[i] = i;
    }
    state->nq = 0;
    bflg = ae_false;
    if( state->algomode==gqpipm_amdense )
    {
        rsetallocm(n, n, 0.0, &state->denseq, _state);
        bflg = ae_true;
    }
    if( state->algomode==gqpipm_amsparse||state->algomode==gqpipm_amhybrid )
    {
        state->sparseq1.m = n;
        state->sparseq1.n = n;
        iallocv(n+1, &state->sparseq1.ridx, _state);
        iallocv(n, &state->sparseq1.idx, _state);
        rallocv(n, &state->sparseq1.vals, _state);
        for(i=0; i<=n-1; i++)
        {
            state->sparseq1.ridx.ptr.p_int[i] = i;
            state->sparseq1.idx.ptr.p_int[i] = i;
            state->sparseq1.vals.ptr.p_double[i] = 0.0;
        }
        state->sparseq1.ridx.ptr.p_int[n] = n;
        sparsecreatecrsinplace(&state->sparseq1, _state);
        bflg = ae_true;
    }
    ae_assert(bflg, "GQPIPM: 896235 failed", _state);
    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "QPIPM: integrity check 9756 failed", _state);
    state->eps = coalesce(eps, (double)10*ae_sqrt(ae_machineepsilon, _state), _state);
    state->maxits = maxits;
    state->pctype = 0;
    state->repterminationtype = 0;
    state->repiterationscount = 0;
    state->dolaconictrace = ae_is_trace_enabled("GENIPM.LACONIC");
    state->dotrace = ae_is_trace_enabled("GENIPM")&&!state->dolaconictrace;
}


/*************************************************************************
Set linear and quadratic terms

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
void gqpipmsetquadraticlinear(gqpipmstate* state,
     /* Real    */ const ae_matrix* denseh,
     const sparsematrix* sparseh,
     ae_int_t hkind,
     /* Real    */ const ae_vector* c,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;


    if( state->algomode==gqpipm_amdense )
    {
        quadraticlinearconverttodenseltr(c, state->n, denseh, sparseh, hkind, ae_false, &state->c, &state->denseq, _state);
        for(i=0; i<=state->n-1; i++)
        {
            for(j=0; j<=i-1; j++)
            {
                state->denseq.ptr.pp_double[j][i] = state->denseq.ptr.pp_double[i][j];
            }
        }
        for(i=state->n-1; i>=0; i--)
        {
            if( ae_fp_greater(rmaxabsr(state->n, &state->denseq, i, _state),(double)(0)) )
            {
                state->nq = ae_maxint(state->nq, i+1, _state);
                break;
            }
        }
    }
    else
    {
        ae_assert(state->algomode==gqpipm_amsparse||state->algomode==gqpipm_amhybrid, "QPGIPM: integrity check 945236 failed", _state);
        ae_assert(hkind==0||hkind==1, "QPGIPM: integrity check 5022 failed", _state);
        rcopyv(state->n, c, &state->c, _state);
        if( hkind==0 )
        {
            sparsecreatecrsfromdensebuf(denseh, state->n, state->n, &state->sparseq1, _state);
        }
        if( hkind==1 )
        {
            sparsecopytocrsbuf(sparseh, &state->sparseq1, _state);
        }
        rsetallocv(state->n, 0.0, &state->tmp0, _state);
        for(i=0; i<=state->n-1; i++)
        {
            j0 = state->sparseq1.ridx.ptr.p_int[i];
            j1 = state->sparseq1.uidx.ptr.p_int[i]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                if( ae_fp_neq(state->sparseq1.vals.ptr.p_double[jj],(double)(0)) )
                {
                    state->tmp0.ptr.p_double[i] = (double)(1);
                    state->tmp0.ptr.p_double[state->sparseq1.idx.ptr.p_int[jj]] = (double)(1);
                }
            }
        }
        for(i=state->n-1; i>=0; i--)
        {
            if( ae_fp_neq(state->tmp0.ptr.p_double[i],(double)(0)) )
            {
                state->nq = ae_maxint(state->nq, i+1, _state);
                break;
            }
        }
    }
}


/*************************************************************************
Set linear constraints

INPUT PARAMETERS:
    State               -   instance initialized with one of the initialization
                            functions
    SparseA             -   sparse constraint matrix, CRS format
    CL, CU              -   lower and upper bounds for constraints
                            -INF <= CL[I] <= CU[I] <= +INF.
    XQC                 -   quadratic constraints
    XCC                 -   conic constraints

  -- ALGLIB --
     Copyright 25.05.2024 by Bochkanov Sergey
*************************************************************************/
void gqpipmsetconstraints(gqpipmstate* state,
     const sparsematrix* sparsea,
     /* Real    */ const ae_vector* cl,
     /* Real    */ const ae_vector* cu,
     ae_int_t m,
     xquadraticconstraints* xqc,
     xconicconstraints* xcc,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    xquadraticconstraint *qci;
    ae_smart_ptr _qci;

    ae_frame_make(_state, &_frame_block);
    memset(&_qci, 0, sizeof(_qci));
    ae_smart_ptr_init(&_qci, (void**)&qci, ae_false, _state, ae_true);

    n = state->n;
    ae_assert(m>=0, "GQPIPMSetConstraints: M<0", _state);
    ae_assert(m==0||((sparsea->matrixtype==1&&sparsea->m==m)&&sparsea->n==n), "GQPIPMSetConstraints: constraint matrix has incorrect size", _state);
    ae_assert(cl->cnt>=m, "GQPIPMSetConstraints: CL is too short!", _state);
    ae_assert(cu->cnt>=m, "GQPIPMSetConstraints: CU is too short!", _state);
    state->mlc = m;
    if( m>0 )
    {
        sparsecopytocrsbuf(sparsea, &state->sparselc, _state);
        rallocv(m, &state->lcbndl, _state);
        rallocv(m, &state->lcbndu, _state);
        for(i=0; i<=m-1; i++)
        {
            ae_assert(ae_isfinite(cl->ptr.p_double[i], _state)||ae_isneginf(cl->ptr.p_double[i], _state), "GQPIPMSetConstraints: CL is not finite number or -INF", _state);
            ae_assert(ae_isfinite(cu->ptr.p_double[i], _state)||ae_isposinf(cu->ptr.p_double[i], _state), "GQPIPMSetConstraints: CU is not finite number or +INF", _state);
            state->lcbndl.ptr.p_double[i] = cl->ptr.p_double[i];
            state->lcbndu.ptr.p_double[i] = cu->ptr.p_double[i];
        }
    }
    state->mqc = xqcgetcount(xqc, _state);
    xqccopy(xqc, &state->xqc, _state);
    for(i=0; i<=state->mqc-1; i++)
    {
        ae_obj_array_get(&state->xqc.constraints, i, &_qci, _state);
        for(j=0; j<=qci->nvars-1; j++)
        {
            j0 = qci->lowerq.ridx.ptr.p_int[j];
            j1 = qci->lowerq.didx.ptr.p_int[j]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                state->nq = ae_maxint(state->nq, qci->varidx.ptr.p_int[j], _state);
                state->nq = ae_maxint(state->nq, qci->varidx.ptr.p_int[qci->lowerq.idx.ptr.p_int[jj]], _state);
            }
        }
    }
    state->msocc = xccgetcount(xcc, _state);
    xcccopy(xcc, &state->xcc, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Set quasi-Newton parameters

INPUT PARAMETERS:
    State               -   instance initialized with one of the initialization
                            functions
    MemLen              -   memory length
    MaxOffdiag          -   threshold for moving quadratic terms to a
                            quasi-Newton approximation

  -- ALGLIB --
     Copyright 25.05.2024 by Bochkanov Sergey
*************************************************************************/
void gqpipmsetquasinewtonparams(gqpipmstate* state,
     ae_int_t memlen,
     ae_int_t maxoffdiag,
     ae_state *_state)
{


    ae_assert(memlen>=0, "GQPIPMSetQuasiNewtonParams: MemLen<0", _state);
    ae_assert(maxoffdiag>=0, "GQPIPMSetQuasiNewtonParams: MaxOffdiag<0", _state);
    state->hessmemlen = memlen;
    state->hessmaxoffdiag = maxoffdiag;
}


/*************************************************************************
Solve QP problem.

INPUT PARAMETERS:
    State               -   solver instance
    
OUTPUT PARAMETERS:
    XS                  -   array[N], solution
    LagBC               -   array[N], Lagrange multipliers for box constraints
    LagLC               -   array[MLC], Lagrange multipliers for linear constraints
    LagQC               -   array[MQC], Lagrange multipliers for quadratic constraints
    TerminationType     -   completion code, positive values for success,
                            negative for failures (XS constrains best point
                            found so far):
                            * -2    the task is either unbounded or infeasible;
                                    the IPM solver has difficulty distinguishing between these two.
                            * +1    stopping criteria are met
                            * +7    stopping criteria are too stringent

RESULT:

This function ALWAYS returns something  meaningful in XS, LagBC, LagLC - 
either solution or the best point so far, even for negative TerminationType.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
void gqpipmoptimize(gqpipmstate* state,
     /* Real    */ ae_vector* xs,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* laglc,
     /* Real    */ ae_vector* lagqc,
     ae_int_t* terminationtype,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t nnz;
    double initprimdual;
    ae_int_t mlc;
    ae_int_t mtotal;
    ae_int_t offslast;
    ae_int_t offslc;
    ae_int_t offsqc;
    ae_int_t offscc;
    ae_int_t offsec;
    ae_bool remembered;
    ae_bool factorized;
    ae_bool donecc;
    xquadraticconstraint *qci;
    ae_smart_ptr _qci;
    xconicconstraint *cci;
    ae_smart_ptr _cci;

    ae_frame_make(_state, &_frame_block);
    memset(&_qci, 0, sizeof(_qci));
    memset(&_cci, 0, sizeof(_cci));
    *terminationtype = 0;
    ae_smart_ptr_init(&_qci, (void**)&qci, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_cci, (void**)&cci, ae_false, _state, ae_true);

    n = state->n;
    mlc = state->mlc;
    offslast = 0;
    mtotal = 0;
    ae_nxpool_alloc(&state->nrealpool, n, _state);
    rsetallocv(1, 1.0, &state->fscales, _state);
    offslc = offslast;
    offslast = offslast+mlc;
    for(i=0; i<=mlc-1; i++)
    {
        rgrowv(mtotal+1, &state->totalnl, _state);
        rgrowv(mtotal+1, &state->totalnu, _state);
        state->totalnl.ptr.p_double[mtotal] = state->lcbndl.ptr.p_double[i];
        state->totalnu.ptr.p_double[mtotal] = state->lcbndu.ptr.p_double[i];
        rgrowv(1+mtotal+1, &state->fscales, _state);
        state->fscales.ptr.p_double[1+mtotal] = 1.0;
        mtotal = mtotal+1;
    }
    offsec = offslast;
    offslast = offsec+state->fixedvarscnt;
    for(i=0; i<=n-1; i++)
    {
        if( state->isfixed.ptr.p_bool[i] )
        {
            rgrowv(mtotal+1, &state->totalnl, _state);
            rgrowv(mtotal+1, &state->totalnu, _state);
            state->totalnl.ptr.p_double[mtotal] = state->wrkbndl.ptr.p_double[i];
            state->totalnu.ptr.p_double[mtotal] = state->wrkbndu.ptr.p_double[i];
            rgrowv(1+mtotal+1, &state->fscales, _state);
            state->fscales.ptr.p_double[1+mtotal] = 1.0;
            state->wrkbndl.ptr.p_double[i] = _state->v_neginf;
            state->wrkbndu.ptr.p_double[i] = _state->v_posinf;
            mtotal = mtotal+1;
        }
    }
    offsqc = offslast;
    offslast = offslast+state->mqc;
    for(i=0; i<=state->mqc-1; i++)
    {
        rgrowv(mtotal+1, &state->totalnl, _state);
        rgrowv(mtotal+1, &state->totalnu, _state);
        ae_obj_array_get(&state->xqc.constraints, i, &_qci, _state);
        state->totalnl.ptr.p_double[mtotal] = qci->cl;
        state->totalnu.ptr.p_double[mtotal] = qci->cu;
        rgrowv(1+mtotal+1, &state->fscales, _state);
        state->fscales.ptr.p_double[1+mtotal] = 1.0;
        mtotal = mtotal+1;
    }
    offscc = offslast;
    offslast = offslast+state->msocc;
    for(i=0; i<=state->msocc-1; i++)
    {
        rgrowv(mtotal+1, &state->totalnl, _state);
        rgrowv(mtotal+1, &state->totalnu, _state);
        ae_obj_array_get(&state->xcc.constraints, i, &_cci, _state);
        ae_assert(cci->conetype>=0, "GQPIPM: non-canonic cone constraints are provided", _state);
        donecc = ae_false;
        if( cci->conetype==xccprimitiveconetype(_state) )
        {
            state->totalnl.ptr.p_double[mtotal] = _state->v_neginf;
            state->totalnu.ptr.p_double[mtotal] = 0.0;
            donecc = ae_true;
        }
        if( cci->conetype==xccprimitivepowerconetype(_state) )
        {
            state->totalnl.ptr.p_double[mtotal] = _state->v_neginf;
            state->totalnu.ptr.p_double[mtotal] = 0.0;
            donecc = ae_true;
        }
        ae_assert(donecc, "GQPIPM: unexpected cone type", _state);
        rgrowv(1+mtotal+1, &state->fscales, _state);
        state->fscales.ptr.p_double[1+mtotal] = 1.0;
        mtotal = mtotal+1;
    }
    ae_assert(mtotal==offslast, "GQPIPM: integrity check 5535 failed", _state);
    bsetallocv(1+mtotal, ae_false, &state->doqnupdates, _state);
    if( state->algomode==gqpipm_amhybrid )
    {
        nnz = 0;
        for(i=0; i<=n-1; i++)
        {
            nnz = nnz+(state->sparseq1.didx.ptr.p_int[i]-state->sparseq1.ridx.ptr.p_int[i]);
        }
        state->doqnupdates.ptr.p_bool[0] = nnz>state->hessmaxoffdiag;
        for(i=0; i<=state->mqc-1; i++)
        {
            ae_obj_array_get(&state->xqc.constraints, i, &_qci, _state);
            nnz = 0;
            for(j=0; j<=qci->nvars-1; j++)
            {
                nnz = nnz+(qci->lowerq.didx.ptr.p_int[j]-qci->lowerq.ridx.ptr.p_int[j]);
            }
            state->doqnupdates.ptr.p_bool[1+offsqc+i] = nnz>state->hessmaxoffdiag;
        }
    }
    gqpipm_initializehessjac(state, offslc, offsqc, offsec, offscc, mtotal, _state);
    rsetallocv(n, 0.0, xs, _state);
    rsetallocv(n, 0.0, lagbc, _state);
    rsetallocv(state->mlc, 0.0, laglc, _state);
    rsetallocv(state->mqc, 0.0, lagqc, _state);
    initprimdual = gipmgetinitprimdual(_state);
    for(i=0; i<=n-1; i++)
    {
        if( !ae_isfinite(state->wrkbndl.ptr.p_double[i], _state)&&!ae_isfinite(state->wrkbndu.ptr.p_double[i], _state) )
        {
            continue;
        }
        if( ae_isfinite(state->wrkbndl.ptr.p_double[i], _state)&&!ae_isfinite(state->wrkbndu.ptr.p_double[i], _state) )
        {
            state->x0.ptr.p_double[i] = ae_maxreal(state->x0.ptr.p_double[i], state->wrkbndl.ptr.p_double[i]+initprimdual, _state);
            continue;
        }
        if( ae_isfinite(state->wrkbndu.ptr.p_double[i], _state)&&!ae_isfinite(state->wrkbndl.ptr.p_double[i], _state) )
        {
            state->x0.ptr.p_double[i] = ae_minreal(state->x0.ptr.p_double[i], state->wrkbndu.ptr.p_double[i]-initprimdual, _state);
            continue;
        }
        if( ae_fp_greater(state->wrkbndu.ptr.p_double[i]-state->wrkbndl.ptr.p_double[i],(double)2*initprimdual) )
        {
            state->x0.ptr.p_double[i] = ae_maxreal(state->x0.ptr.p_double[i], state->wrkbndl.ptr.p_double[i]+initprimdual, _state);
            state->x0.ptr.p_double[i] = ae_minreal(state->x0.ptr.p_double[i], state->wrkbndu.ptr.p_double[i]-initprimdual, _state);
            continue;
        }
        ae_assert(ae_fp_less(state->wrkbndl.ptr.p_double[i],state->wrkbndu.ptr.p_double[i]), "GQPIPM: integrity check 609424 failed", _state);
        state->x0.ptr.p_double[i] = 0.5*(state->wrkbndl.ptr.p_double[i]+state->wrkbndu.ptr.p_double[i]);
        ae_assert(ae_fp_greater(state->x0.ptr.p_double[i],state->wrkbndl.ptr.p_double[i]), "GQPIPM: integrity check 609425 failed", _state);
        ae_assert(ae_fp_less(state->x0.ptr.p_double[i],state->wrkbndu.ptr.p_double[i]), "GQPIPM: integrity check 609426 failed", _state);
    }
    for(i=0; i<=state->msocc-1; i++)
    {
        ae_obj_array_get(&state->xcc.constraints, i, &_cci, _state);
        if( cci->nvars==0 )
        {
            continue;
        }
        donecc = ae_false;
        if( cci->conetype==xccprimitiveconetype(_state) )
        {
            donecc = ae_true;
        }
        if( cci->conetype==xccprimitivepowerconetype(_state) )
        {
            donecc = ae_true;
        }
        ae_assert(donecc, "GQPIPM: integrity check 876620 failed", _state);
    }
    ae_assert(state->fscales.cnt>=1+mtotal, "GQPIPM: integrity check 8248 failed", _state);
    gipminitbuf(&state->wrkbndl, &state->wrkbndu, &state->x0, n, &state->totalnl, &state->totalnu, mtotal-0*state->msocc, 0*state->msocc, &state->fscales, &state->doqnupdates, state->eps, state->maxits, state->algomode==gqpipm_amhybrid, &state->solver, _state);
    if( state->dotrace )
    {
        ae_trace("\n----- printing additional problem info -------------------------------------------------------------\n");
        if( (state->cvxobjective!=0||state->cvxqc!=0)||state->cvxcc!=0 )
        {
            ae_trace("> analyzing problem convexity:\n");
            if( state->cvxobjective>0 )
            {
                ae_trace(">> objective = convex\n");
            }
            if( state->cvxobjective<0 )
            {
                ae_trace(">> objective = non-convex\n");
            }
            if( state->cvxobjective==0 )
            {
                ae_trace(">> objective = convexity is unknown\n");
            }
            if( xqcgetcount(&state->xqc, _state)>0 )
            {
                if( state->cvxqc>0 )
                {
                    ae_trace(">> quadratic constraints = all convex\n");
                }
                if( state->cvxqc<0 )
                {
                    ae_trace(">> quadratic constraints = non-convex (at least one)\n");
                }
                if( state->cvxqc==0 )
                {
                    ae_trace(">> quadratic constraints = convexity is unknown\n");
                }
            }
            else
            {
                ae_trace(">> no quadratic constraints\n");
            }
            if( xccgetcount(&state->xcc, _state)>0 )
            {
                if( state->cvxcc>0 )
                {
                    ae_trace(">> conic constraints = all convex\n");
                }
                if( state->cvxcc<0 )
                {
                    ae_trace(">> conic constraints = non-convex (at least one)\n");
                }
                if( state->cvxcc==0 )
                {
                    ae_trace(">> conic constraints = convexity is unknown\n");
                }
            }
            else
            {
                ae_trace(">> no conic constraints\n");
            }
        }
        ae_trace("\n");
    }
    remembered = ae_false;
    factorized = ae_false;
    while(gipmiteration(&state->solver, ae_false, _state))
    {
        if( state->solver.requesttype==1 )
        {
            gqpipm_handlecomputeandremember(state, offslc, offsqc, offsec, offscc, mtotal, &remembered, &factorized, _state);
            continue;
        }
        if( state->solver.requesttype==1001 )
        {
            gqpipm_handlefactorize(state, mtotal, &remembered, &factorized, _state);
            continue;
        }
        if( state->solver.requesttype==1002 )
        {
            gqpipm_handlesolve(state, mtotal, &remembered, &factorized, _state);
            continue;
        }
        if( state->solver.requesttype==1003 )
        {
            gqpipm_handlemv(state, offslc, offsqc, offsec, offscc, mtotal, &remembered, &factorized, _state);
            continue;
        }
        if( state->solver.requesttype==1004 )
        {
            if( state->algomode==gqpipm_amhybrid&&state->nq>0 )
            {
                rallocv(n, &state->tmp0, _state);
                rallocv(n, &state->tmp1, _state);
                rallocv(n, &state->tmp2, _state);
                rallocv(n, &state->tmp3, _state);
                rcopyvx(n, &state->solver.querydata, 0*n, &state->tmp0, 0, _state);
                rcopyvx(n, &state->solver.querydata, 1*n, &state->tmp1, 0, _state);
                rcopyvx(n, &state->solver.querydata, 2*n, &state->tmp2, 0, _state);
                rcopyvx(n, &state->solver.querydata, 3*n, &state->tmp3, 0, _state);
                hessianupdatev2(&state->hess, &state->tmp0, &state->tmp2, &state->tmp1, &state->tmp3, 2, ae_false, state->dotrace, 2, _state);
                hessianmultiplyby(&state->hess, 0.95, _state);
            }
            continue;
        }
        ae_assert(ae_false, "GQPIPM: request not implemented", _state);
    }
    gipmresults(&state->solver, xs, lagbc, &state->tmp0, terminationtype, _state);
    state->repiterationscount = state->solver.repiterationscount;
    for(i=0; i<=state->fixedvarscnt-1; i++)
    {
        lagbc->ptr.p_double[state->fixedidx.ptr.p_int[i]] = state->tmp0.ptr.p_double[offsec+i];
    }
    for(i=0; i<=mlc-1; i++)
    {
        laglc->ptr.p_double[i] = state->tmp0.ptr.p_double[offslc+i];
    }
    for(i=0; i<=state->mqc-1; i++)
    {
        lagqc->ptr.p_double[i] = state->tmp0.ptr.p_double[offsqc+i];
    }
    rmergemaxv(n, &state->finiterawbndl, xs, _state);
    rmergeminv(n, &state->finiterawbndu, xs, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Appends a lower triangular sparse matrix to the list of row/column/id/value
elements. Grows list as necessary, returns modified list length.

If VarIdx[] is unordered, i.e. VarIdx[i0]>VarIdx[i1] for some i0<i1, then
this function correctly reorders mapped indexes of Q so that they correspond
to the lower triangle of a matrix.

  -- ALGLIB --
     Copyright 25.10.2024 by Bochkanov Sergey
*************************************************************************/
static void gqpipm_appendtorckv(/* Integer */ const ae_vector* varidx,
     const sparsematrix* q,
     ae_int_t nv,
     ae_int_t id,
     /* Integer */ ae_vector* hr,
     /* Integer */ ae_vector* hc,
     /* Integer */ ae_vector* hk,
     /* Real    */ ae_vector* hv,
     ae_int_t* hcnt,
     ae_state *_state)
{
    ae_int_t tgt0;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t jj;
    ae_int_t w;
    double v;


    if( nv==0 )
    {
        return;
    }
    for(tgt0=0; tgt0<=nv-1; tgt0++)
    {
        j0 = q->ridx.ptr.p_int[tgt0];
        j1 = q->didx.ptr.p_int[tgt0]-1;
        w = j1-j0+1+1;
        igrowv(*hcnt+w, hr, _state);
        igrowv(*hcnt+w, hc, _state);
        igrowv(*hcnt+w, hk, _state);
        rgrowv(*hcnt+w, hv, _state);
        for(jj=j0; jj<=j1; jj++)
        {
            k0 = varidx->ptr.p_int[tgt0];
            k1 = varidx->ptr.p_int[q->idx.ptr.p_int[jj]];
            if( k0>=k1 )
            {
                hr->ptr.p_int[*hcnt] = k0;
                hc->ptr.p_int[*hcnt] = k1;
            }
            else
            {
                hr->ptr.p_int[*hcnt] = k1;
                hc->ptr.p_int[*hcnt] = k0;
            }
            hk->ptr.p_int[*hcnt] = id;
            hv->ptr.p_double[*hcnt] = q->vals.ptr.p_double[jj];
            *hcnt = *hcnt+1;
        }
        v = 0.0;
        if( j1+1<q->uidx.ptr.p_int[tgt0] )
        {
            v = q->vals.ptr.p_double[j1+1];
        }
        hr->ptr.p_int[*hcnt] = varidx->ptr.p_int[tgt0];
        hc->ptr.p_int[*hcnt] = varidx->ptr.p_int[tgt0];
        hk->ptr.p_int[*hcnt] = id;
        hv->ptr.p_double[*hcnt] = v;
        *hcnt = *hcnt+1;
    }
}


/*************************************************************************
Prepares sparse Hessian, dependencies of its elements on Lagrange multipliers,
SPSymmAnalyze(), etc.

  -- ALGLIB --
     Copyright 25.07.2024 by Bochkanov Sergey
*************************************************************************/
static void gqpipm_initializehessjac(gqpipmstate* state,
     ae_int_t offslc,
     ae_int_t offsqc,
     ae_int_t offsec,
     ae_int_t offscc,
     ae_int_t mtotal,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t w;
    ae_int_t n;
    ae_int_t mlc;
    ae_int_t offs;
    ae_int_t dstrow;
    ae_int_t cidx;
    ae_int_t kpow;
    double v;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t jj;
    ae_int_t jprev;
    ae_int_t nv;
    ae_int_t tgt0;
    ae_int_t tgt1;
    ae_int_t tgtx;
    xquadraticconstraint *qci;
    ae_smart_ptr _qci;
    xconicconstraint *cci;
    ae_smart_ptr _cci;
    gqpsparseconichessian *chi;
    ae_smart_ptr _chi;
    gqpoptionaldense *opti;
    ae_smart_ptr _opti;
    ae_int_t hcnt;
    ae_int_t dstidx;
    ae_int_t srcidx;
    ae_int_t srcrange1;
    ae_int_t facttype;

    ae_frame_make(_state, &_frame_block);
    memset(&_qci, 0, sizeof(_qci));
    memset(&_cci, 0, sizeof(_cci));
    memset(&_chi, 0, sizeof(_chi));
    memset(&_opti, 0, sizeof(_opti));
    ae_smart_ptr_init(&_qci, (void**)&qci, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_cci, (void**)&cci, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_chi, (void**)&chi, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_opti, (void**)&opti, ae_false, _state, ae_true);

    n = state->n;
    mlc = state->mlc;
    iallocv(n, &state->tmpi, _state);
    ae_obj_array_clear(&state->xchsparse);
    for(i=0; i<=state->msocc-1; i++)
    {
        ae_obj_array_get(&state->xcc.constraints, i, &_cci, _state);
        chi = (gqpsparseconichessian*)ae_malloc(sizeof(gqpsparseconichessian), _state); /* note: using chi as a temporary prior to assigning its value to _chi */
        memset(chi, 0, sizeof(gqpsparseconichessian));
        _gqpsparseconichessian_init(chi, _state, ae_false);
        ae_smart_ptr_assign(&_chi, chi, ae_true, ae_true, (ae_int_t)sizeof(gqpsparseconichessian), _gqpsparseconichessian_init_copy, _gqpsparseconichessian_destroy);
        ae_obj_array_append_transfer(&state->xchsparse, &_chi, _state);
        nv = cci->nvars;
        if( nv==0 )
        {
            continue;
        }
        if( cci->conetype==xccprimitiveconetype(_state) )
        {
            chi->lowerh.n = nv;
            chi->lowerh.m = nv;
            iallocv(nv+1, &chi->lowerh.ridx, _state);
            iallocv(2*(nv-1)+1, &chi->lowerh.idx, _state);
            rallocv(2*(nv-1)+1, &chi->lowerh.vals, _state);
            offs = 0;
            for(j=0; j<=nv-2; j++)
            {
                chi->lowerh.ridx.ptr.p_int[j] = offs;
                chi->lowerh.idx.ptr.p_int[offs] = j;
                chi->lowerh.vals.ptr.p_double[offs] = 0.0;
                offs = offs+1;
            }
            chi->lowerh.ridx.ptr.p_int[nv-1] = offs;
            for(j=0; j<=nv-1; j++)
            {
                chi->lowerh.idx.ptr.p_int[offs] = j;
                chi->lowerh.vals.ptr.p_double[offs] = 0.0;
                offs = offs+1;
            }
            chi->lowerh.ridx.ptr.p_int[nv] = offs;
            ae_assert(offs==2*nv-1, "GQPIPM: integrity check 136309 failed", _state);
            sparsecreatecrsinplace(&chi->lowerh, _state);
            icopyv(nv, &cci->varidx, &state->tmpi, _state);
            iallocv(nv, &chi->ordervaridx, _state);
            for(j=0; j<=nv-1; j++)
            {
                chi->ordervaridx.ptr.p_int[j] = j;
            }
            tagsortmiddleii(&state->tmpi, &chi->ordervaridx, 0, nv, _state);
            continue;
        }
        if( cci->conetype==xccprimitivepowerconetype(_state) )
        {
            ae_assert(state->pctype==0, "GQPIPM: integrity check 446815 failed", _state);
            kpow = cci->kpow;
            chi->lowerh.n = nv;
            chi->lowerh.m = nv;
            iallocv(nv+1, &chi->lowerh.ridx, _state);
            iallocv(nv+kpow*nv, &chi->lowerh.idx, _state);
            rallocv(nv+kpow*nv, &chi->lowerh.vals, _state);
            offs = 0;
            for(j=0; j<=nv-kpow-1; j++)
            {
                chi->lowerh.ridx.ptr.p_int[j] = offs;
                chi->lowerh.idx.ptr.p_int[offs] = j;
                chi->lowerh.vals.ptr.p_double[offs] = 0.0;
                offs = offs+1;
            }
            for(j=nv-kpow; j<=nv-1; j++)
            {
                chi->lowerh.ridx.ptr.p_int[j] = offs;
                for(j1=0; j1<=j; j1++)
                {
                    chi->lowerh.idx.ptr.p_int[offs] = j1;
                    chi->lowerh.vals.ptr.p_double[offs] = 0.0;
                    offs = offs+1;
                }
            }
            chi->lowerh.ridx.ptr.p_int[nv] = offs;
            sparsecreatecrsinplace(&chi->lowerh, _state);
            icopyv(nv, &cci->varidx, &state->tmpi, _state);
            iallocv(nv, &chi->ordervaridx, _state);
            for(j=0; j<=nv-1; j++)
            {
                chi->ordervaridx.ptr.p_int[j] = j;
            }
            tagsortmiddleii(&state->tmpi, &chi->ordervaridx, 0, nv, _state);
            continue;
        }
        ae_assert(ae_false, "GQPIPM: integrity check 140309 failed", _state);
    }
    ae_assert(ae_obj_array_get_length(&state->xcc.constraints)==ae_obj_array_get_length(&state->xchsparse), "GQPIPM: integrity check 142310 failed", _state);
    state->curjac.m = mtotal;
    state->curjac.n = n;
    iallocv(mtotal+1, &state->curjac.ridx, _state);
    state->curjac.ridx.ptr.p_int[0] = 0;
    dstrow = 0;
    ae_assert(dstrow==offslc, "GQPIPM: integrity check 2900 failed", _state);
    if( mlc>0 )
    {
        k = state->sparselc.ridx.ptr.p_int[mlc];
        igrowv(state->curjac.ridx.ptr.p_int[dstrow]+k, &state->curjac.idx, _state);
        rgrowv(state->curjac.ridx.ptr.p_int[dstrow]+k, &state->curjac.vals, _state);
        for(i=0; i<=mlc-1; i++)
        {
            k = state->sparselc.ridx.ptr.p_int[i+1]-state->sparselc.ridx.ptr.p_int[i];
            icopyvx(k, &state->sparselc.idx, state->sparselc.ridx.ptr.p_int[i], &state->curjac.idx, state->curjac.ridx.ptr.p_int[dstrow], _state);
            rsetvx(k, 0.0, &state->curjac.vals, state->curjac.ridx.ptr.p_int[dstrow], _state);
            state->curjac.ridx.ptr.p_int[dstrow+1] = state->curjac.ridx.ptr.p_int[dstrow]+k;
            dstrow = dstrow+1;
        }
    }
    ae_assert(dstrow==offsec, "GQPIPM: integrity check 945839 failed", _state);
    igrowv(state->curjac.ridx.ptr.p_int[dstrow]+state->fixedvarscnt, &state->curjac.idx, _state);
    rgrowv(state->curjac.ridx.ptr.p_int[dstrow]+state->fixedvarscnt, &state->curjac.vals, _state);
    for(i=0; i<=state->fixedvarscnt-1; i++)
    {
        k = state->curjac.ridx.ptr.p_int[dstrow];
        state->curjac.idx.ptr.p_int[k] = state->fixedidx.ptr.p_int[i];
        state->curjac.vals.ptr.p_double[k] = 0.0;
        state->curjac.ridx.ptr.p_int[dstrow+1] = k+1;
        dstrow = dstrow+1;
    }
    ae_assert(dstrow==offsqc, "GQPIPM: integrity check 949844 failed", _state);
    for(i=0; i<=state->mqc-1; i++)
    {
        ae_obj_array_get(&state->xqc.constraints, i, &_qci, _state);
        igrowv(state->curjac.ridx.ptr.p_int[dstrow]+qci->nvars, &state->curjac.idx, _state);
        rgrowv(state->curjac.ridx.ptr.p_int[dstrow]+qci->nvars, &state->curjac.vals, _state);
        icopyvx(qci->nvars, &qci->varidx, 0, &state->curjac.idx, state->curjac.ridx.ptr.p_int[dstrow], _state);
        rsetvx(qci->nvars, 0.0, &state->curjac.vals, state->curjac.ridx.ptr.p_int[dstrow], _state);
        state->curjac.ridx.ptr.p_int[dstrow+1] = state->curjac.ridx.ptr.p_int[dstrow]+qci->nvars;
        dstrow = dstrow+1;
    }
    for(i=0; i<=state->msocc-1; i++)
    {
        ae_obj_array_get(&state->xcc.constraints, i, &_cci, _state);
        igrowv(state->curjac.ridx.ptr.p_int[dstrow]+cci->nvars, &state->curjac.idx, _state);
        rgrowv(state->curjac.ridx.ptr.p_int[dstrow]+cci->nvars, &state->curjac.vals, _state);
        icopyvx(cci->nvars, &cci->varidx, 0, &state->curjac.idx, state->curjac.ridx.ptr.p_int[dstrow], _state);
        tagsortmiddlei(&state->curjac.idx, state->curjac.ridx.ptr.p_int[dstrow], cci->nvars, _state);
        rsetvx(cci->nvars, 0.0, &state->curjac.vals, state->curjac.ridx.ptr.p_int[dstrow], _state);
        state->curjac.ridx.ptr.p_int[dstrow+1] = state->curjac.ridx.ptr.p_int[dstrow]+cci->nvars;
        dstrow = dstrow+1;
    }
    ae_assert(dstrow==mtotal, "GQPIPM: integrity check 993315 failed", _state);
    if( mtotal>0 )
    {
        sparsecreatecrsinplace(&state->curjac, _state);
    }
    if( state->algomode==gqpipm_amdense )
    {
        rallocm(n, n, &state->currenth, _state);
        ae_frame_leave(_state);
        return;
    }
    ae_assert(state->algomode==gqpipm_amsparse||state->algomode==gqpipm_amhybrid, "QPGIPM: 654249 failed", _state);
    if( state->algomode==gqpipm_amhybrid&&state->nq>0 )
    {
        state->hessmemlen = ae_minint(state->nq, icoalesce(state->hessmemlen, gqpipm_xbfgsdefaultmemlen, _state), _state);
        hessianinitlowranksr1(&state->hess, state->nq, state->hessmemlen, ae_maxreal(state->eps, ae_sqrt(ae_machineepsilon, _state), _state), gqpipm_xbfgsmaxhess, _state);
    }
    hcnt = 0;
    if( state->algomode==gqpipm_amhybrid&&state->doqnupdates.ptr.p_bool[0] )
    {
        igrowv(hcnt+n, &state->tmphr, _state);
        igrowv(hcnt+n, &state->tmphc, _state);
        igrowv(hcnt+n, &state->tmphk, _state);
        rgrowv(hcnt+n, &state->tmphv, _state);
        for(i=0; i<=n-1; i++)
        {
            state->tmphr.ptr.p_int[hcnt] = i;
            state->tmphc.ptr.p_int[hcnt] = i;
            state->tmphk.ptr.p_int[hcnt] = mtotal;
            state->tmphv.ptr.p_double[hcnt] = 0.0;
            hcnt = hcnt+1;
        }
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            j0 = state->sparseq1.ridx.ptr.p_int[i];
            j1 = state->sparseq1.didx.ptr.p_int[i]-1;
            w = j1-j0+1+1;
            igrowv(hcnt+w, &state->tmphr, _state);
            igrowv(hcnt+w, &state->tmphc, _state);
            igrowv(hcnt+w, &state->tmphk, _state);
            rgrowv(hcnt+w, &state->tmphv, _state);
            for(jj=j0; jj<=j1; jj++)
            {
                state->tmphr.ptr.p_int[hcnt] = i;
                state->tmphc.ptr.p_int[hcnt] = state->sparseq1.idx.ptr.p_int[jj];
                state->tmphk.ptr.p_int[hcnt] = mtotal;
                state->tmphv.ptr.p_double[hcnt] = state->sparseq1.vals.ptr.p_double[jj];
                hcnt = hcnt+1;
            }
            v = 0.0;
            if( j1+1<state->sparseq1.uidx.ptr.p_int[i] )
            {
                v = state->sparseq1.vals.ptr.p_double[j1+1];
            }
            state->tmphr.ptr.p_int[hcnt] = i;
            state->tmphc.ptr.p_int[hcnt] = i;
            state->tmphk.ptr.p_int[hcnt] = mtotal;
            state->tmphv.ptr.p_double[hcnt] = v;
            hcnt = hcnt+1;
        }
    }
    for(i=0; i<=state->mqc-1; i++)
    {
        if( state->algomode!=gqpipm_amhybrid||!state->doqnupdates.ptr.p_bool[1+offsqc+i] )
        {
            ae_obj_array_get(&state->xqc.constraints, i, &_qci, _state);
            if( qci->nvars==0 )
            {
                continue;
            }
            gqpipm_appendtorckv(&qci->varidx, &qci->lowerq, qci->nvars, offsqc+i, &state->tmphr, &state->tmphc, &state->tmphk, &state->tmphv, &hcnt, _state);
        }
    }
    for(i=0; i<=state->msocc-1; i++)
    {
        ae_assert(state->algomode!=gqpipm_amhybrid||!state->doqnupdates.ptr.p_bool[1+offscc+i], "GQPIPM: integrity check 832348 failed", _state);
        ae_obj_array_get(&state->xcc.constraints, i, &_cci, _state);
        ae_obj_array_get(&state->xchsparse, i, &_chi, _state);
        nv = cci->nvars;
        if( nv==0 )
        {
            continue;
        }
        gqpipm_appendtorckv(&cci->varidx, &chi->lowerh, nv, offscc+i, &state->tmphr, &state->tmphc, &state->tmphk, &state->tmphv, &hcnt, _state);
    }
    ae_assert(ae_obj_array_get_length(&state->xcc.constraints)==ae_obj_array_get_length(&state->xchsparse), "GQPIPM: integrity check 142310 failed", _state);
    ae_assert(hcnt>=n, "GQPIPN: integrity check 937222 failed", _state);
    gqpipm_sortrck(&state->tmphr, &state->tmphc, &state->tmphk, &state->tmphv, hcnt, &state->tmphr2, &state->tmphc2, &state->tmphk2, &state->tmphv2, _state);
    state->sparseh.m = n;
    state->sparseh.n = n;
    iallocv(n+1, &state->sparseh.ridx, _state);
    state->sparseh.ridx.ptr.p_int[0] = 0;
    state->sparsehdeps.m = 0;
    state->sparsehdeps.n = mtotal+1;
    iallocv(1, &state->sparsehdeps.ridx, _state);
    state->sparsehdeps.ridx.ptr.p_int[0] = 0;
    srcidx = 0;
    dstidx = 0;
    for(dstrow=0; dstrow<=n-1; dstrow++)
    {
        ae_assert(state->tmphr.ptr.p_int[srcidx]==dstrow, "GQPIPM: integrity check 951223 failed", _state);
        dstidx = state->sparseh.ridx.ptr.p_int[dstrow];
        jprev = -1;
        while(srcidx<hcnt&&state->tmphr.ptr.p_int[srcidx]==dstrow)
        {
            j = state->tmphc.ptr.p_int[srcidx];
            if( j<=jprev||j>dstrow )
            {
                ae_assert(ae_false, "GQPIPM: integrity check 958230 failed", _state);
            }
            srcrange1 = srcidx;
            offs = state->sparsehdeps.ridx.ptr.p_int[state->sparsehdeps.m];
            while((srcrange1<hcnt&&state->tmphr.ptr.p_int[srcrange1]==dstrow)&&state->tmphc.ptr.p_int[srcrange1]==j)
            {
                igrowv(offs+1, &state->sparsehdeps.idx, _state);
                rgrowv(offs+1, &state->sparsehdeps.vals, _state);
                state->sparsehdeps.idx.ptr.p_int[offs] = state->tmphk.ptr.p_int[srcrange1];
                state->sparsehdeps.vals.ptr.p_double[offs] = state->tmphv.ptr.p_double[srcrange1];
                srcrange1 = srcrange1+1;
                offs = offs+1;
            }
            state->sparsehdeps.m = state->sparsehdeps.m+1;
            igrowv(state->sparsehdeps.m+1, &state->sparsehdeps.ridx, _state);
            state->sparsehdeps.ridx.ptr.p_int[state->sparsehdeps.m] = offs;
            igrowv(dstidx+1, &state->sparseh.idx, _state);
            rgrowv(dstidx+1, &state->sparseh.vals, _state);
            state->sparseh.idx.ptr.p_int[dstidx] = j;
            state->sparseh.vals.ptr.p_double[dstidx] = 0.0;
            dstidx = dstidx+1;
            srcidx = srcrange1;
            jprev = j;
        }
        state->sparseh.ridx.ptr.p_int[dstrow+1] = dstidx;
    }
    sparsecreatecrsinplace(&state->sparseh, _state);
    sparsecreatecrsinplace(&state->sparsehdeps, _state);
    for(cidx=0; cidx<=state->msocc-1; cidx++)
    {
        ae_assert(state->algomode!=gqpipm_amhybrid||!state->doqnupdates.ptr.p_bool[1+offscc+cidx], "GQPIPM: integrity check 912350 failed", _state);
        ae_obj_array_get(&state->xcc.constraints, cidx, &_cci, _state);
        ae_obj_array_get(&state->xchsparse, cidx, &_chi, _state);
        nv = cci->nvars;
        if( nv==0 )
        {
            continue;
        }
        iallocv(chi->lowerh.ridx.ptr.p_int[nv], &chi->injectionoffsets, _state);
        for(i=0; i<=nv-1; i++)
        {
            j0 = chi->lowerh.ridx.ptr.p_int[i];
            j1 = chi->lowerh.ridx.ptr.p_int[i+1]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                tgt0 = cci->varidx.ptr.p_int[i];
                tgt1 = cci->varidx.ptr.p_int[chi->lowerh.idx.ptr.p_int[jj]];
                if( tgt1>tgt0 )
                {
                    tgtx = tgt0;
                    tgt0 = tgt1;
                    tgt1 = tgtx;
                }
                offs = ibinarysearchexisting(&state->sparseh.idx, state->sparseh.ridx.ptr.p_int[tgt0], state->sparseh.uidx.ptr.p_int[tgt0], tgt1, _state);
                chi->injectionoffsets.ptr.p_int[jj] = ibinarysearchexisting(&state->sparsehdeps.idx, state->sparsehdeps.ridx.ptr.p_int[offs], state->sparsehdeps.ridx.ptr.p_int[offs+1], offscc+cidx, _state);
            }
        }
    }
    ae_assert(state->algomode==gqpipm_amhybrid||state->hessmemlen==0, "QPGIPM: 838258 failed", _state);
    state->sparsesys.m = n+mtotal+2*state->hessmemlen;
    state->sparsesys.n = state->sparsesys.m;
    iallocv(state->sparsesys.m+1, &state->sparsesys.ridx, _state);
    state->sparsesys.ridx.ptr.p_int[0] = 0;
    offs = 0;
    for(i=0; i<=n-1; i++)
    {
        igrowv(offs+n, &state->sparsesys.idx, _state);
        rgrowv(offs+n, &state->sparsesys.vals, _state);
        j0 = state->sparseh.ridx.ptr.p_int[i];
        j1 = state->sparseh.didx.ptr.p_int[i]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            state->sparsesys.idx.ptr.p_int[offs] = state->sparseh.idx.ptr.p_int[jj];
            state->sparsesys.vals.ptr.p_double[offs] = 1.0;
            offs = offs+1;
        }
        state->sparsesys.idx.ptr.p_int[offs] = i;
        state->sparsesys.vals.ptr.p_double[offs] = 1.0;
        offs = offs+1;
        state->sparsesys.ridx.ptr.p_int[i+1] = offs;
    }
    for(i=0; i<=mtotal-1; i++)
    {
        igrowv(offs+n+1, &state->sparsesys.idx, _state);
        rgrowv(offs+n+1, &state->sparsesys.vals, _state);
        j0 = state->curjac.ridx.ptr.p_int[i];
        j1 = state->curjac.ridx.ptr.p_int[i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            state->sparsesys.idx.ptr.p_int[offs] = state->curjac.idx.ptr.p_int[jj];
            state->sparsesys.vals.ptr.p_double[offs] = 1.0;
            offs = offs+1;
        }
        state->sparsesys.idx.ptr.p_int[offs] = n+i;
        state->sparsesys.vals.ptr.p_double[offs] = 1.0;
        offs = offs+1;
        state->sparsesys.ridx.ptr.p_int[n+i+1] = offs;
    }
    for(i=0; i<=2*state->hessmemlen-1; i++)
    {
        igrowv(offs+state->nq+1, &state->sparsesys.idx, _state);
        rgrowv(offs+state->nq+1, &state->sparsesys.vals, _state);
        for(jj=0; jj<=state->nq-1; jj++)
        {
            state->sparsesys.idx.ptr.p_int[offs] = jj;
            state->sparsesys.vals.ptr.p_double[offs] = 0.0;
            offs = offs+1;
        }
        state->sparsesys.idx.ptr.p_int[offs] = n+mtotal+i;
        state->sparsesys.vals.ptr.p_double[offs] = 1.0;
        offs = offs+1;
        state->sparsesys.ridx.ptr.p_int[n+mtotal+i+1] = offs;
    }
    sparsecreatecrsinplace(&state->sparsesys, _state);
    gqpipm_optionaldensefromsparse(&state->optionaldenseq1, &state->sparseq1, state->nq, _state);
    ae_obj_array_clear(&state->optionaldensexqc);
    for(i=0; i<=state->mqc-1; i++)
    {
        ae_obj_array_get(&state->xqc.constraints, i, &_qci, _state);
        opti = (gqpoptionaldense*)ae_malloc(sizeof(gqpoptionaldense), _state); /* note: using opti as a temporary prior to assigning its value to _opti */
        memset(opti, 0, sizeof(gqpoptionaldense));
        _gqpoptionaldense_init(opti, _state, ae_false);
        ae_smart_ptr_assign(&_opti, opti, ae_true, ae_true, (ae_int_t)sizeof(gqpoptionaldense), _gqpoptionaldense_init_copy, _gqpoptionaldense_destroy);
        gqpipm_optionaldensefromsparseqc(opti, qci, _state);
        ae_obj_array_append_transfer(&state->optionaldensexqc, &_opti, _state);
    }
    facttype = 1;
    isetallocv(n+mtotal+2*state->hessmemlen, 0, &state->priorities, _state);
    if( state->algomode!=gqpipm_amhybrid )
    {
        for(i=0; i<=n-1; i++)
        {
            state->priorities.ptr.p_int[i] = 1;
        }
        for(i=n; i<=n+mtotal-1; i++)
        {
            state->priorities.ptr.p_int[i] = 0;
        }
    }
    for(i=n+mtotal; i<=n+mtotal+2*state->hessmemlen-1; i++)
    {
        state->priorities.ptr.p_int[i] = 2;
    }
    if( !spsymmanalyze(&state->sparsesys, &state->priorities, gqpipm_mupromote, 0, facttype, 3, 1, &state->analysis, _state) )
    {
        ae_assert(ae_false, "QPGIPM: critical integrity check failed, symbolically degenerate KKT system encountered", _state);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Lexicographically sorts a list of row-column-index elements.

  -- ALGLIB --
     Copyright 25.07.2024 by Bochkanov Sergey
*************************************************************************/
static void gqpipm_sortrck(/* Integer */ ae_vector* vecr,
     /* Integer */ ae_vector* vecc,
     /* Integer */ ae_vector* veck,
     /* Real    */ ae_vector* vecv,
     ae_int_t n,
     /* Integer */ ae_vector* tmpr,
     /* Integer */ ae_vector* tmpc,
     /* Integer */ ae_vector* tmpk,
     /* Real    */ ae_vector* tmpv,
     ae_state *_state)
{


    if( n==0 )
    {
        return;
    }
    iallocv(n, tmpr, _state);
    iallocv(n, tmpc, _state);
    iallocv(n, tmpk, _state);
    rallocv(n, tmpv, _state);
    gqpipm_sortrckrec(vecr, vecc, veck, vecv, 0, n, tmpr, tmpc, tmpk, tmpv, _state);
}


/*************************************************************************
Recursive sorting routine.

  -- ALGLIB --
     Copyright 25.07.2024 by Bochkanov Sergey
*************************************************************************/
static void gqpipm_sortrckrec(/* Integer */ ae_vector* vecr,
     /* Integer */ ae_vector* vecc,
     /* Integer */ ae_vector* veck,
     /* Real    */ ae_vector* vecv,
     ae_int_t idx0,
     ae_int_t idx1,
     /* Integer */ ae_vector* tmpr,
     /* Integer */ ae_vector* tmpc,
     /* Integer */ ae_vector* tmpk,
     /* Real    */ ae_vector* tmpv,
     ae_state *_state)
{
    ae_int_t idxmid;
    ae_int_t src0;
    ae_int_t src1;
    ae_int_t end0;
    ae_int_t end1;
    ae_int_t dst;
    ae_int_t s;


    if( idx1-idx0<=1 )
    {
        return;
    }
    idxmid = idx0+(idx1-idx0)/2;
    gqpipm_sortrckrec(vecr, vecc, veck, vecv, idx0, idxmid, tmpr, tmpc, tmpk, tmpv, _state);
    gqpipm_sortrckrec(vecr, vecc, veck, vecv, idxmid, idx1, tmpr, tmpc, tmpk, tmpv, _state);
    src0 = idx0;
    end0 = idxmid;
    src1 = idxmid;
    end1 = idx1;
    dst = idx0;
    while(src0<end0&&src1<end1)
    {
        s = 0;
        if( vecr->ptr.p_int[src0]<vecr->ptr.p_int[src1] )
        {
            s = -1;
        }
        if( vecr->ptr.p_int[src0]>vecr->ptr.p_int[src1] )
        {
            s = 1;
        }
        if( s==0 )
        {
            if( vecc->ptr.p_int[src0]<vecc->ptr.p_int[src1] )
            {
                s = -1;
            }
            if( vecc->ptr.p_int[src0]>vecc->ptr.p_int[src1] )
            {
                s = 1;
            }
        }
        if( s==0 )
        {
            if( veck->ptr.p_int[src0]<veck->ptr.p_int[src1] )
            {
                s = -1;
            }
            if( veck->ptr.p_int[src0]>veck->ptr.p_int[src1] )
            {
                s = 1;
            }
        }
        if( s==0 )
        {
            ae_assert(ae_false, "SortRCKRec: integrity check failed, non-distinct entries", _state);
        }
        if( s<0 )
        {
            tmpr->ptr.p_int[dst] = vecr->ptr.p_int[src0];
            tmpc->ptr.p_int[dst] = vecc->ptr.p_int[src0];
            tmpk->ptr.p_int[dst] = veck->ptr.p_int[src0];
            tmpv->ptr.p_double[dst] = vecv->ptr.p_double[src0];
            src0 = src0+1;
            dst = dst+1;
        }
        else
        {
            tmpr->ptr.p_int[dst] = vecr->ptr.p_int[src1];
            tmpc->ptr.p_int[dst] = vecc->ptr.p_int[src1];
            tmpk->ptr.p_int[dst] = veck->ptr.p_int[src1];
            tmpv->ptr.p_double[dst] = vecv->ptr.p_double[src1];
            src1 = src1+1;
            dst = dst+1;
        }
    }
    while(src0<end0)
    {
        tmpr->ptr.p_int[dst] = vecr->ptr.p_int[src0];
        tmpc->ptr.p_int[dst] = vecc->ptr.p_int[src0];
        tmpk->ptr.p_int[dst] = veck->ptr.p_int[src0];
        tmpv->ptr.p_double[dst] = vecv->ptr.p_double[src0];
        src0 = src0+1;
        dst = dst+1;
    }
    while(src1<end1)
    {
        tmpr->ptr.p_int[dst] = vecr->ptr.p_int[src1];
        tmpc->ptr.p_int[dst] = vecc->ptr.p_int[src1];
        tmpk->ptr.p_int[dst] = veck->ptr.p_int[src1];
        tmpv->ptr.p_double[dst] = vecv->ptr.p_double[src1];
        src1 = src1+1;
        dst = dst+1;
    }
    ae_assert(dst==idx1, "SortRCKRec: integrity check 211811 failed", _state);
    icopyvx(idx1-idx0, tmpr, idx0, vecr, idx0, _state);
    icopyvx(idx1-idx0, tmpc, idx0, vecc, idx0, _state);
    icopyvx(idx1-idx0, tmpk, idx0, veck, idx0, _state);
    rcopyvx(idx1-idx0, tmpv, idx0, vecv, idx0, _state);
}


/*************************************************************************
Handle compute-and-remember request

  -- ALGLIB --
     Copyright 25.07.2024 by Bochkanov Sergey
*************************************************************************/
static void gqpipm_handlecomputeandremember(gqpipmstate* state,
     ae_int_t offslc,
     ae_int_t offsqc,
     ae_int_t offsec,
     ae_int_t offscc,
     ae_int_t mtotal,
     ae_bool* remembered,
     ae_bool* factorized,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t mlc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t tgt0;
    ae_int_t tgt1;
    ae_int_t jmid;
    ae_int_t offs;
    ae_int_t nv;
    double v;
    double vlag;
    double valpha;
    ae_int_t kpow;
    xquadraticconstraint *qci;
    ae_smart_ptr _qci;
    xconicconstraint *cci;
    ae_smart_ptr _cci;
    gqpsparseconichessian *chi;
    ae_smart_ptr _chi;
    gqpoptionaldense *opti;
    ae_smart_ptr _opti;
    ae_bool donecc;
    ae_bool donemv;
    ae_vector ccy;
    double vv;
    double vy;
    double invvy;
    double vz;
    double u2;

    ae_frame_make(_state, &_frame_block);
    memset(&_qci, 0, sizeof(_qci));
    memset(&_cci, 0, sizeof(_cci));
    memset(&_chi, 0, sizeof(_chi));
    memset(&_opti, 0, sizeof(_opti));
    memset(&ccy, 0, sizeof(ccy));
    ae_smart_ptr_init(&_qci, (void**)&qci, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_cci, (void**)&cci, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_chi, (void**)&chi, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_opti, (void**)&opti, ae_false, _state, ae_true);
    ae_vector_init(&ccy, 0, DT_REAL, _state, ae_true);

    ae_assert((state->algomode==gqpipm_amdense||state->algomode==gqpipm_amsparse)||state->algomode==gqpipm_amhybrid, "QPGIPM: 087304 failed", _state);
    n = state->n;
    mlc = state->mlc;
    ae_nxpool_retrieve(&state->nrealpool, &ccy, _state);
    sparsecreatecrsemptybuf(n, &state->solver.replysj, _state);
    rallocv(n, &state->tmp0, _state);
    iallocv(n, &state->tmpi, _state);
    rcopyallocv(n, &state->solver.querydata, &state->tmpx, _state);
    rallocv(n, &state->currentx, _state);
    rallocv(mtotal, &state->currentlag, _state);
    rcopyvx(n, &state->solver.querydata, 0, &state->currentx, 0, _state);
    rcopyvx(mtotal, &state->solver.querydata, n, &state->currentlag, 0, _state);
    for(i=0; i<=state->msocc-1; i++)
    {
        state->currentlag.ptr.p_double[offscc+i] = ae_maxreal(state->currentlag.ptr.p_double[offscc+i], 0.0, _state);
    }
    *remembered = ae_true;
    *factorized = ae_false;
    if( state->algomode==gqpipm_amdense )
    {
        rgemv(n, n, 1.0, &state->denseq, 0, &state->tmpx, 0.0, &state->tmp0, _state);
    }
    else
    {
        ae_assert(state->algomode==gqpipm_amsparse||state->algomode==gqpipm_amhybrid, "GQPIPM: integrity check 387218 failed", _state);
        if( state->optionaldenseq1.hasdenseterm&&state->optionaldenseq1.nvars>0 )
        {
            ae_assert((state->optionaldenseq1.nvars>0&&state->optionaldenseq1.varidx.ptr.p_int[0]==0)&&state->optionaldenseq1.varidx.ptr.p_int[state->optionaldenseq1.nvars-1]==state->optionaldenseq1.nvars-1, "GQPIPM: integrity check 390220 failed", _state);
            rmatrixsymv(state->optionaldenseq1.nvars, 1.0, &state->optionaldenseq1.q, 0, 0, ae_false, &state->tmpx, 0, 0.0, &state->tmp0, 0, _state);
            rsetvx(n-state->optionaldenseq1.nvars, 0.0, &state->tmp0, state->optionaldenseq1.nvars, _state);
        }
        else
        {
            sparsesmv(&state->sparseq1, ae_false, &state->tmpx, &state->tmp0, _state);
        }
    }
    state->solver.replyfi.ptr.p_double[0] = rdotv(n, &state->tmpx, &state->c, _state)+0.5*rdotv(n, &state->tmpx, &state->tmp0, _state);
    raddv(n, 1.0, &state->c, &state->tmp0, _state);
    sparseappendcompressedrow(&state->solver.replysj, &state->iotac, &state->tmp0, n, _state);
    if( state->algomode==gqpipm_amdense )
    {
        rcopyallocm(n, n, &state->denseq, &state->currenth, _state);
    }
    if( mlc>0 )
    {
        sparsemv(&state->sparselc, &state->tmpx, &state->tmp0, _state);
        rcopyvx(mlc, &state->tmp0, 0, &state->solver.replyfi, 1+offslc, _state);
        ae_assert(state->curjac.ridx.ptr.p_int[offslc+mlc]-state->curjac.ridx.ptr.p_int[offslc]==state->sparselc.ridx.ptr.p_int[mlc], "GQPIPM: integrity check 3638 failed", _state);
        rcopyvx(state->sparselc.ridx.ptr.p_int[mlc], &state->sparselc.vals, 0, &state->curjac.vals, state->curjac.ridx.ptr.p_int[offslc], _state);
    }
    for(i=0; i<=state->fixedvarscnt-1; i++)
    {
        j = state->fixedidx.ptr.p_int[i];
        v = state->tmpx.ptr.p_double[j];
        state->solver.replyfi.ptr.p_double[1+offsec+i] = v;
        ae_assert(state->curjac.ridx.ptr.p_int[offsec+i+1]-state->curjac.ridx.ptr.p_int[offsec+i]==1, "GQPIPM: integrity check 6240 failed", _state);
        state->curjac.vals.ptr.p_double[state->curjac.ridx.ptr.p_int[offsec+i]] = 1.0;
    }
    for(i=0; i<=state->mqc-1; i++)
    {
        rallocv(n, &state->tmp0, _state);
        rallocv(n, &state->tmp1, _state);
        ae_obj_array_get(&state->xqc.constraints, i, &_qci, _state);
        if( qci->nvars==0 )
        {
            state->solver.replyfi.ptr.p_double[1+offsqc+i] = 0.0;
            continue;
        }
        for(j=0; j<=qci->nvars-1; j++)
        {
            state->tmp0.ptr.p_double[j] = state->tmpx.ptr.p_double[qci->varidx.ptr.p_int[j]];
        }
        donemv = ae_false;
        if( ae_obj_array_get_length(&state->optionaldensexqc)==state->mqc )
        {
            ae_obj_array_get(&state->optionaldensexqc, i, &_opti, _state);
            if( opti->hasdenseterm )
            {
                rmatrixsymv(opti->nvars, 1.0, &opti->q, 0, 0, ae_false, &state->tmp0, 0, 0.0, &state->tmp1, 0, _state);
                donemv = ae_true;
            }
        }
        if( !donemv )
        {
            sparsesmv(&qci->lowerq, ae_false, &state->tmp0, &state->tmp1, _state);
        }
        state->solver.replyfi.ptr.p_double[1+offsqc+i] = rdotv(qci->nvars, &qci->b, &state->tmp0, _state)+0.5*rdotv(qci->nvars, &state->tmp0, &state->tmp1, _state);
        raddv(qci->nvars, 1.0, &qci->b, &state->tmp1, _state);
        if( state->algomode==gqpipm_amdense )
        {
            vlag = state->currentlag.ptr.p_double[offsqc+i];
            for(j=0; j<=qci->nvars-1; j++)
            {
                ae_assert(qci->lowerq.didx.ptr.p_int[j]==qci->lowerq.uidx.ptr.p_int[j]-1, "GQPIPM: integrity check 2148 failed", _state);
                j0 = qci->lowerq.ridx.ptr.p_int[j];
                j1 = qci->lowerq.didx.ptr.p_int[j]-1;
                tgt0 = qci->varidx.ptr.p_int[j];
                for(jj=j0; jj<=j1; jj++)
                {
                    tgt1 = qci->varidx.ptr.p_int[qci->lowerq.idx.ptr.p_int[jj]];
                    state->currenth.ptr.pp_double[tgt0][tgt1] = state->currenth.ptr.pp_double[tgt0][tgt1]+vlag*qci->lowerq.vals.ptr.p_double[jj];
                }
                state->currenth.ptr.pp_double[tgt0][tgt0] = state->currenth.ptr.pp_double[tgt0][tgt0]+vlag*qci->lowerq.vals.ptr.p_double[qci->lowerq.didx.ptr.p_int[j]];
            }
        }
        ae_assert(state->curjac.ridx.ptr.p_int[offsqc+i+1]-state->curjac.ridx.ptr.p_int[offsqc+i]==qci->nvars, "GQPIPM: integrity check 2645 failed", _state);
        rcopyvx(qci->nvars, &state->tmp1, 0, &state->curjac.vals, state->curjac.ridx.ptr.p_int[offsqc+i], _state);
    }
    for(i=0; i<=state->msocc-1; i++)
    {
        rallocv(n, &state->tmp0, _state);
        rallocv(n, &state->tmp1, _state);
        ae_obj_array_get(&state->xcc.constraints, i, &_cci, _state);
        ae_obj_array_get(&state->xchsparse, i, &_chi, _state);
        if( cci->nvars==0 )
        {
            state->solver.replyfi.ptr.p_double[1+offscc+i] = 0.0;
            continue;
        }
        nv = cci->nvars;
        kpow = cci->kpow;
        for(j=0; j<=cci->nvars-1; j++)
        {
            state->tmp0.ptr.p_double[j] = state->tmpx.ptr.p_double[cci->varidx.ptr.p_int[j]];
        }
        donecc = ae_false;
        v = (double)(0);
        u2 = (double)(0);
        if( cci->conetype==xccprimitiveconetype(_state) )
        {
            vy = state->tmp0.ptr.p_double[cci->nvars-1]*cci->diaga.ptr.p_double[cci->nvars-1]+cci->shftc.ptr.p_double[cci->nvars-1];
            ccy.ptr.p_double[cci->nvars-1] = vy;
            ae_assert(ae_fp_greater(vy,(double)(0)), "GQPIPM: integrity check 251633 failed", _state);
            u2 = cci->shftc.ptr.p_double[cci->nvars];
            for(j=0; j<=cci->nvars-2; j++)
            {
                vv = state->tmp0.ptr.p_double[j]*cci->diaga.ptr.p_double[j]+cci->shftc.ptr.p_double[j];
                ccy.ptr.p_double[j] = vv;
                u2 = u2+vv*vv;
                state->tmp1.ptr.p_double[j] = (double)2*vv*cci->diaga.ptr.p_double[j];
            }
            rmulv(cci->nvars-1, (double)1/vy, &state->tmp1, _state);
            state->tmp1.ptr.p_double[cci->nvars-1] = (-u2/(vy*vy)-(double)1)*cci->diaga.ptr.p_double[cci->nvars-1];
            v = u2/vy-vy;
            donecc = ae_true;
        }
        if( cci->conetype==xccprimitivepowerconetype(_state) )
        {
            u2 = cci->shftc.ptr.p_double[nv];
            for(j=0; j<=nv-kpow-1; j++)
            {
                vv = state->tmp0.ptr.p_double[j]*cci->diaga.ptr.p_double[j]+cci->shftc.ptr.p_double[j];
                ccy.ptr.p_double[j] = vv;
                u2 = u2+vv*vv;
            }
            vy = (double)(1);
            vz = (double)(1);
            for(j=nv-kpow; j<=nv-1; j++)
            {
                vv = state->tmp0.ptr.p_double[j]*cci->diaga.ptr.p_double[j]+cci->shftc.ptr.p_double[j];
                ccy.ptr.p_double[j] = vv;
                ae_assert(ae_fp_greater(vv,(double)(0)), "GQPIPM: integrity check 063327 failed", _state);
                vy = vy*ae_pow(vv, cci->alphapow.ptr.p_double[j-(nv-kpow)], _state);
                vz = vz*vv;
            }
            invvy = (double)1/vy;
            if( state->pctype==0 )
            {
                v = u2*invvy-vy;
                for(j=0; j<=nv-kpow-1; j++)
                {
                    state->tmp1.ptr.p_double[j] = (double)2*ccy.ptr.p_double[j]*invvy*cci->diaga.ptr.p_double[j];
                }
                for(j=nv-kpow; j<=nv-1; j++)
                {
                    state->tmp1.ptr.p_double[j] = cci->diaga.ptr.p_double[j]*(-u2*invvy-vy)*cci->alphapow.ptr.p_double[j-(nv-kpow)]/ccy.ptr.p_double[j];
                }
                donecc = ae_true;
            }
            if( state->pctype==1 )
            {
                v = u2*vz*invvy-vz*vy;
                for(j=0; j<=nv-kpow-1; j++)
                {
                    state->tmp1.ptr.p_double[j] = (double)2*ccy.ptr.p_double[j]*vz*invvy*cci->diaga.ptr.p_double[j];
                }
                for(j=nv-kpow; j<=nv-1; j++)
                {
                    valpha = cci->alphapow.ptr.p_double[j-(nv-kpow)];
                    state->tmp1.ptr.p_double[j] = (u2*((double)1-valpha)*invvy-((double)1+valpha)*vy)*vz/ccy.ptr.p_double[j]*cci->diaga.ptr.p_double[j];
                }
                donecc = ae_true;
            }
        }
        ae_assert(donecc, "GQPIPM: integrity check 189820 failed (unexpected cone type and/or barrier type)", _state);
        state->solver.replyfi.ptr.p_double[1+offscc+i] = v;
        ae_assert(state->curjac.ridx.ptr.p_int[offscc+i+1]-state->curjac.ridx.ptr.p_int[offscc+i]==cci->nvars, "GQPIPM: integrity check 811110 failed", _state);
        offs = state->curjac.ridx.ptr.p_int[offscc+i];
        for(j=0; j<=cci->nvars-1; j++)
        {
            state->curjac.vals.ptr.p_double[offs+j] = state->tmp1.ptr.p_double[chi->ordervaridx.ptr.p_int[j]];
        }
        if( state->algomode==gqpipm_amdense )
        {
            vlag = state->currentlag.ptr.p_double[offscc+i];
            donecc = ae_false;
            if( cci->conetype==xccprimitiveconetype(_state) )
            {
                vy = ccy.ptr.p_double[cci->nvars-1];
                ae_assert(ae_fp_greater(vy,(double)(0)), "GQPIPM: integrity check 323638 failed", _state);
                v = (double)2/vy;
                tgt1 = cci->varidx.ptr.p_int[cci->nvars-1];
                jmid = ibinarysearchlft(&cci->varidx, 0, cci->nvars-1, tgt1, _state);
                ae_assert(jmid==cci->nvars-1||cci->varidx.ptr.p_int[jmid]>tgt1, "GQPIPM: integrity check 547204 failed", _state);
                for(j=0; j<=jmid-1; j++)
                {
                    tgt0 = cci->varidx.ptr.p_int[j];
                    state->currenth.ptr.pp_double[tgt0][tgt0] = state->currenth.ptr.pp_double[tgt0][tgt0]+vlag*v*(cci->diaga.ptr.p_double[j]*cci->diaga.ptr.p_double[j]);
                    state->currenth.ptr.pp_double[tgt1][tgt0] = state->currenth.ptr.pp_double[tgt1][tgt0]-vlag*v*(cci->diaga.ptr.p_double[j]*cci->diaga.ptr.p_double[cci->nvars-1]*ccy.ptr.p_double[j]/vy);
                }
                state->currenth.ptr.pp_double[tgt1][tgt1] = state->currenth.ptr.pp_double[tgt1][tgt1]+vlag*v*(cci->diaga.ptr.p_double[cci->nvars-1]*cci->diaga.ptr.p_double[cci->nvars-1]*u2/(vy*vy));
                for(j=jmid; j<=cci->nvars-2; j++)
                {
                    tgt0 = cci->varidx.ptr.p_int[j];
                    state->currenth.ptr.pp_double[tgt0][tgt0] = state->currenth.ptr.pp_double[tgt0][tgt0]+vlag*v*(cci->diaga.ptr.p_double[j]*cci->diaga.ptr.p_double[j]);
                    state->currenth.ptr.pp_double[tgt0][tgt1] = state->currenth.ptr.pp_double[tgt0][tgt1]-vlag*v*(cci->diaga.ptr.p_double[j]*cci->diaga.ptr.p_double[cci->nvars-1]*ccy.ptr.p_double[j]/vy);
                }
                donecc = ae_true;
            }
            if( cci->conetype==xccprimitivepowerconetype(_state) )
            {
                ae_assert(state->pctype==0, "GQPIPM: integrity check 194815 failed", _state);
                u2 = cci->shftc.ptr.p_double[nv];
                for(j=0; j<=nv-kpow-1; j++)
                {
                    u2 = u2+ccy.ptr.p_double[j]*ccy.ptr.p_double[j];
                }
                vy = (double)(1);
                for(j=nv-kpow; j<=nv-1; j++)
                {
                    vy = vy*ae_pow(ccy.ptr.p_double[j], cci->alphapow.ptr.p_double[j-(nv-kpow)], _state);
                }
                invvy = (double)1/vy;
                v = (double)2/vy;
                for(j=0; j<=nv-kpow-1; j++)
                {
                    tgt0 = cci->varidx.ptr.p_int[j];
                    state->currenth.ptr.pp_double[tgt0][tgt0] = state->currenth.ptr.pp_double[tgt0][tgt0]+vlag*v*(cci->diaga.ptr.p_double[j]*cci->diaga.ptr.p_double[j]);
                }
                for(j0=nv-kpow; j0<=nv-1; j0++)
                {
                    for(j1=nv-kpow; j1<=j0-1; j1++)
                    {
                        tgt0 = cci->varidx.ptr.p_int[j0];
                        tgt1 = cci->varidx.ptr.p_int[j1];
                        if( tgt1>tgt0 )
                        {
                            jj = tgt0;
                            tgt0 = tgt1;
                            tgt1 = jj;
                        }
                        v = cci->alphapow.ptr.p_double[j0-(nv-kpow)]*cci->alphapow.ptr.p_double[j1-(nv-kpow)]/(ccy.ptr.p_double[j0]*ccy.ptr.p_double[j1]);
                        state->currenth.ptr.pp_double[tgt0][tgt1] = state->currenth.ptr.pp_double[tgt0][tgt1]+vlag*v*(u2*invvy-vy)*(cci->diaga.ptr.p_double[j0]*cci->diaga.ptr.p_double[j1]);
                    }
                    tgt0 = cci->varidx.ptr.p_int[j0];
                    valpha = cci->alphapow.ptr.p_double[j0-(nv-kpow)];
                    v = valpha/(ccy.ptr.p_double[j0]*ccy.ptr.p_double[j0]);
                    state->currenth.ptr.pp_double[tgt0][tgt0] = state->currenth.ptr.pp_double[tgt0][tgt0]+vlag*v*((valpha+(double)1)*u2*invvy-(valpha-(double)1)*vy)*(cci->diaga.ptr.p_double[j0]*cci->diaga.ptr.p_double[j0]);
                }
                v = (double)2/vy;
                for(j0=nv-kpow; j0<=nv-1; j0++)
                {
                    for(j1=0; j1<=nv-kpow-1; j1++)
                    {
                        tgt0 = cci->varidx.ptr.p_int[j0];
                        tgt1 = cci->varidx.ptr.p_int[j1];
                        if( tgt1>tgt0 )
                        {
                            jj = tgt0;
                            tgt0 = tgt1;
                            tgt1 = jj;
                        }
                        valpha = cci->alphapow.ptr.p_double[j0-(nv-kpow)];
                        state->currenth.ptr.pp_double[tgt0][tgt1] = state->currenth.ptr.pp_double[tgt0][tgt1]+vlag*v*(-valpha*ccy.ptr.p_double[j1]/ccy.ptr.p_double[j0])*(cci->diaga.ptr.p_double[j0]*cci->diaga.ptr.p_double[j1]);
                    }
                }
                donecc = ae_true;
            }
            ae_assert(donecc, "GQPIPM: integrity check 251026 failed", _state);
        }
        else
        {
            donecc = ae_false;
            if( cci->conetype==xccprimitiveconetype(_state) )
            {
                vy = ccy.ptr.p_double[cci->nvars-1];
                ae_assert(ae_fp_greater(vy,(double)(0)), "GQPIPM: integrity check 323638 failed", _state);
                v = (double)2/vy;
                offs = 0;
                for(j=0; j<=nv-2; j++)
                {
                    chi->lowerh.vals.ptr.p_double[offs] = v*(cci->diaga.ptr.p_double[j]*cci->diaga.ptr.p_double[j]);
                    offs = offs+1;
                }
                for(j=0; j<=nv-2; j++)
                {
                    chi->lowerh.vals.ptr.p_double[offs] = -v*(cci->diaga.ptr.p_double[j]*cci->diaga.ptr.p_double[cci->nvars-1]*ccy.ptr.p_double[j]/vy);
                    offs = offs+1;
                }
                chi->lowerh.vals.ptr.p_double[offs] = v*(cci->diaga.ptr.p_double[cci->nvars-1]*cci->diaga.ptr.p_double[cci->nvars-1]*u2/(vy*vy));
                offs = offs+1;
                ae_assert(offs==2*nv-1, "GQPIPM: integrity check 895149 failed", _state);
                k = chi->lowerh.ridx.ptr.p_int[nv];
                for(j=0; j<=k-1; j++)
                {
                    state->sparsehdeps.vals.ptr.p_double[chi->injectionoffsets.ptr.p_int[j]] = chi->lowerh.vals.ptr.p_double[j];
                }
                donecc = ae_true;
            }
            if( cci->conetype==xccprimitivepowerconetype(_state) )
            {
                ae_assert(state->pctype==0, "GQPIPM: integrity check 311816 failed", _state);
                u2 = cci->shftc.ptr.p_double[nv];
                for(j=0; j<=nv-kpow-1; j++)
                {
                    u2 = u2+ccy.ptr.p_double[j]*ccy.ptr.p_double[j];
                }
                vy = (double)(1);
                for(j=nv-kpow; j<=nv-1; j++)
                {
                    vy = vy*ae_pow(ccy.ptr.p_double[j], cci->alphapow.ptr.p_double[j-(nv-kpow)], _state);
                }
                invvy = (double)1/vy;
                offs = 0;
                v = (double)2/vy;
                for(j0=0; j0<=nv-kpow-1; j0++)
                {
                    chi->lowerh.vals.ptr.p_double[offs] = v*(cci->diaga.ptr.p_double[j0]*cci->diaga.ptr.p_double[j0]);
                    offs = offs+1;
                }
                for(j0=nv-kpow; j0<=nv-1; j0++)
                {
                    for(j1=0; j1<=nv-kpow-1; j1++)
                    {
                        chi->lowerh.vals.ptr.p_double[offs] = (double)2*invvy*(-cci->alphapow.ptr.p_double[j0-(nv-kpow)]*ccy.ptr.p_double[j1]/ccy.ptr.p_double[j0])*(cci->diaga.ptr.p_double[j0]*cci->diaga.ptr.p_double[j1]);
                        offs = offs+1;
                    }
                    for(j1=nv-kpow; j1<=j0-1; j1++)
                    {
                        v = cci->alphapow.ptr.p_double[j0-(nv-kpow)]*cci->alphapow.ptr.p_double[j1-(nv-kpow)]/(ccy.ptr.p_double[j0]*ccy.ptr.p_double[j1]);
                        chi->lowerh.vals.ptr.p_double[offs] = v*(u2*invvy-vy)*(cci->diaga.ptr.p_double[j0]*cci->diaga.ptr.p_double[j1]);
                        offs = offs+1;
                    }
                    valpha = cci->alphapow.ptr.p_double[j0-(nv-kpow)];
                    v = valpha/(ccy.ptr.p_double[j0]*ccy.ptr.p_double[j0]);
                    chi->lowerh.vals.ptr.p_double[offs] = v*((valpha+(double)1)*u2*invvy-(valpha-(double)1)*vy)*(cci->diaga.ptr.p_double[j0]*cci->diaga.ptr.p_double[j0]);
                    offs = offs+1;
                }
                ae_assert(offs==chi->lowerh.ridx.ptr.p_int[nv], "GQPIPM: integrity check 318256 failed", _state);
                k = chi->lowerh.ridx.ptr.p_int[nv];
                for(j=0; j<=k-1; j++)
                {
                    state->sparsehdeps.vals.ptr.p_double[chi->injectionoffsets.ptr.p_int[j]] = chi->lowerh.vals.ptr.p_double[j];
                }
                donecc = ae_true;
            }
            ae_assert(donecc, "GQPIPM: integrity check 895132 failed", _state);
        }
    }
    if( mtotal>0 )
    {
        sparseappendmatrix(&state->solver.replysj, &state->curjac, _state);
    }
    if( state->algomode!=gqpipm_amdense )
    {
        rallocv(mtotal+1, &state->tmp0, _state);
        rcopyvx(mtotal, &state->solver.querydata, n, &state->tmp0, 0, _state);
        for(i=0; i<=state->msocc-1; i++)
        {
            state->tmp0.ptr.p_double[offscc+i] = ae_maxreal(state->tmp0.ptr.p_double[offscc+i], 0.0, _state);
        }
        state->tmp0.ptr.p_double[mtotal] = 1.0;
        sparsemv(&state->sparsehdeps, &state->tmp0, &state->sparseh.vals, _state);
    }
    ae_nxpool_recycle(&state->nrealpool, &ccy, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Handle factorization request

  -- ALGLIB --
     Copyright 25.07.2024 by Bochkanov Sergey
*************************************************************************/
static void gqpipm_handlefactorize(gqpipmstate* state,
     ae_int_t mtotal,
     ae_bool* remembered,
     ae_bool* factorized,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t offs;
    double v;


    n = state->n;
    ae_assert(*remembered, "GQPIPM: integrity check 8128 failed", _state);
    if( state->algomode==gqpipm_amdense )
    {
        rallocv(n, &state->tmpd1, _state);
        rcopyvx(n, &state->solver.querydata, 0, &state->tmpd1, 0, _state);
        rallocv(mtotal, &state->tmpd2, _state);
        rcopyvx(mtotal, &state->solver.querydata, n, &state->tmpd2, 0, _state);
        rcopyallocm(n, n, &state->currenth, &state->currentch, _state);
        for(i=0; i<=n-1; i++)
        {
            state->currentch.ptr.pp_double[i][i] = state->currentch.ptr.pp_double[i][i]+state->tmpd1.ptr.p_double[i];
        }
        if( mtotal>0 )
        {
            rallocm(mtotal, n, &state->tmpwj, _state);
            for(i=0; i<=mtotal-1; i++)
            {
                rsetr(n, 0.0, &state->tmpwj, i, _state);
                v = ae_sqrt(-(double)1/state->tmpd2.ptr.p_double[i], _state);
                j0 = state->curjac.ridx.ptr.p_int[i];
                j1 = state->curjac.ridx.ptr.p_int[i+1]-1;
                for(jj=j0; jj<=j1; jj++)
                {
                    state->tmpwj.ptr.pp_double[i][state->curjac.idx.ptr.p_int[jj]] = v*state->curjac.vals.ptr.p_double[jj];
                }
            }
            rmatrixsyrk(n, mtotal, 1.0, &state->tmpwj, 0, 0, 2, 1.0, &state->currentch, 0, 0, ae_false, _state);
        }
        *factorized = spdmatrixcholeskyrec(&state->currentch, 0, n, ae_false, &state->tmp0, _state);
    }
    else
    {
        ae_assert(state->algomode==gqpipm_amsparse||state->algomode==gqpipm_amhybrid, "GQPIPM: integrity check 577311 failed", _state);
        ae_assert(state->algomode==gqpipm_amhybrid||state->hessmemlen==0, "GQPIPM: integrity check 578312 failed", _state);
        if( state->algomode==gqpipm_amhybrid&&state->nq>0 )
        {
            ae_assert(state->basehrank<=2*state->hessmemlen, "GQPIPM: integrity check 533316 failed", _state);
            hessiangetlowrankstabilized(&state->hess, &state->basehsr1diag, &state->basehsr1corrc, &state->basehsr1corrd, &state->basehrank, _state);
        }
        ballocv(n+mtotal+2*state->hessmemlen, &state->tmpb, _state);
        state->sparsesys.m = n+mtotal+2*state->hessmemlen;
        state->sparsesys.n = n+mtotal+2*state->hessmemlen;
        iallocv(n+mtotal+1, &state->sparsesys.ridx, _state);
        state->sparsesys.ridx.ptr.p_int[0] = 0;
        offs = 0;
        for(i=0; i<=n-1; i++)
        {
            igrowv(offs+n, &state->sparsesys.idx, _state);
            rgrowv(offs+n, &state->sparsesys.vals, _state);
            j0 = state->sparseh.ridx.ptr.p_int[i];
            j1 = state->sparseh.didx.ptr.p_int[i]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                state->sparsesys.idx.ptr.p_int[offs] = state->sparseh.idx.ptr.p_int[jj];
                state->sparsesys.vals.ptr.p_double[offs] = state->sparseh.vals.ptr.p_double[jj];
                offs = offs+1;
            }
            v = 0.0;
            if( state->sparseh.didx.ptr.p_int[i]<state->sparseh.uidx.ptr.p_int[i] )
            {
                v = state->sparseh.vals.ptr.p_double[state->sparseh.didx.ptr.p_int[i]];
            }
            if( state->algomode==gqpipm_amhybrid&&i<state->nq )
            {
                v = v+state->basehsr1diag.ptr.p_double[i];
            }
            v = v+state->solver.querydata.ptr.p_double[i];
            state->sparsesys.idx.ptr.p_int[offs] = i;
            state->sparsesys.vals.ptr.p_double[offs] = v;
            offs = offs+1;
            state->sparsesys.ridx.ptr.p_int[i+1] = offs;
            state->tmpb.ptr.p_bool[i] = ae_true;
        }
        for(i=0; i<=mtotal-1; i++)
        {
            igrowv(offs+n+1, &state->sparsesys.idx, _state);
            rgrowv(offs+n+1, &state->sparsesys.vals, _state);
            j0 = state->curjac.ridx.ptr.p_int[i];
            j1 = state->curjac.ridx.ptr.p_int[i+1]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                state->sparsesys.idx.ptr.p_int[offs] = state->curjac.idx.ptr.p_int[jj];
                state->sparsesys.vals.ptr.p_double[offs] = state->curjac.vals.ptr.p_double[jj];
                offs = offs+1;
            }
            v = state->solver.querydata.ptr.p_double[n+i];
            state->sparsesys.idx.ptr.p_int[offs] = n+i;
            state->sparsesys.vals.ptr.p_double[offs] = v;
            offs = offs+1;
            state->sparsesys.ridx.ptr.p_int[n+i+1] = offs;
            state->tmpb.ptr.p_bool[n+i] = ae_false;
        }
        for(i=0; i<=2*state->hessmemlen-1; i++)
        {
            if( state->nq>0&&i<state->basehrank )
            {
                for(j=0; j<=state->nq-1; j++)
                {
                    state->sparsesys.vals.ptr.p_double[offs+j] = state->basehsr1corrc.ptr.pp_double[i][j];
                }
                state->sparsesys.vals.ptr.p_double[offs+state->nq] = -state->basehsr1corrd.ptr.p_double[i];
            }
            else
            {
                for(j=0; j<=state->nq-1; j++)
                {
                    state->sparsesys.vals.ptr.p_double[offs+j] = (double)(0);
                }
                state->sparsesys.vals.ptr.p_double[offs+state->nq] = (double)(1);
            }
            state->tmpb.ptr.p_bool[n+mtotal+i] = ae_fp_greater(state->sparsesys.vals.ptr.p_double[offs+state->nq],(double)(0));
            offs = offs+state->nq+1;
            state->sparsesys.ridx.ptr.p_int[n+mtotal+i+1] = offs;
        }
        sparsecreatecrsinplace(&state->sparsesys, _state);
        spsymmreload(&state->analysis, &state->sparsesys, _state);
        spsymmcontrolsign(&state->analysis, &state->tmpb, 0.0, _state);
        *factorized = spsymmfactorize(&state->analysis, _state);
    }
    state->solver.factsuccess = *factorized;
}


/*************************************************************************
Handle solve request

  -- ALGLIB --
     Copyright 25.07.2024 by Bochkanov Sergey
*************************************************************************/
static void gqpipm_handlesolve(gqpipmstate* state,
     ae_int_t mtotal,
     ae_bool* remembered,
     ae_bool* factorized,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(*factorized, "GQPIPM: integrity check 2043 failed", _state);
    if( state->algomode==gqpipm_amdense )
    {
        rallocv(mtotal, &state->tmprhsy, _state);
        rcopyvx(mtotal, &state->solver.querydata, n, &state->tmprhsy, 0, _state);
        rcopyallocv(mtotal, &state->tmprhsy, &state->tmp1, _state);
        rmergedivv(mtotal, &state->tmpd2, &state->tmp1, _state);
        rcopyv(n, &state->solver.querydata, &state->solver.replysol, _state);
        if( mtotal>0 )
        {
            sparsegemv(&state->curjac, -1.0, 1, &state->tmp1, 0, 1.0, &state->solver.replysol, 0, _state);
        }
        rtrsvx(n, &state->currentch, 0, 0, ae_false, ae_false, 0, &state->solver.replysol, 0, _state);
        rtrsvx(n, &state->currentch, 0, 0, ae_false, ae_false, 1, &state->solver.replysol, 0, _state);
        if( mtotal>0 )
        {
            sparsegemv(&state->curjac, -1.0, 0, &state->solver.replysol, 0, 1.0, &state->tmprhsy, 0, _state);
        }
        rmergedivv(mtotal, &state->tmpd2, &state->tmprhsy, _state);
        rcopyvx(mtotal, &state->tmprhsy, 0, &state->solver.replysol, n, _state);
        return;
    }
    if( state->algomode==gqpipm_amsparse )
    {
        rcopyv(n+mtotal, &state->solver.querydata, &state->solver.replysol, _state);
        spsymmsolve(&state->analysis, &state->solver.replysol, _state);
        return;
    }
    if( state->algomode==gqpipm_amhybrid )
    {
        rallocv(n+mtotal+2*state->hessmemlen, &state->tmp0, _state);
        rcopyv(n+mtotal, &state->solver.querydata, &state->tmp0, _state);
        rsetvx(2*state->hessmemlen, 0.0, &state->tmp0, n+mtotal, _state);
        spsymmsolve(&state->analysis, &state->tmp0, _state);
        rcopyv(n+mtotal, &state->tmp0, &state->solver.replysol, _state);
        return;
    }
    ae_assert(ae_false, "GQPIPM: integrity check 721333 failed", _state);
}


/*************************************************************************
Handle matrix-vector product request

  -- ALGLIB --
     Copyright 25.07.2024 by Bochkanov Sergey
*************************************************************************/
static void gqpipm_handlemv(gqpipmstate* state,
     ae_int_t offslc,
     ae_int_t offsqc,
     ae_int_t offsec,
     ae_int_t offscc,
     ae_int_t mtotal,
     ae_bool* remembered,
     ae_bool* factorized,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    double vlag;
    xquadraticconstraint *qci;
    ae_smart_ptr _qci;
    xconicconstraint *cci;
    ae_smart_ptr _cci;
    gqpsparseconichessian *chi;
    ae_smart_ptr _chi;

    ae_frame_make(_state, &_frame_block);
    memset(&_qci, 0, sizeof(_qci));
    memset(&_cci, 0, sizeof(_cci));
    memset(&_chi, 0, sizeof(_chi));
    ae_smart_ptr_init(&_qci, (void**)&qci, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_cci, (void**)&cci, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_chi, (void**)&chi, ae_false, _state, ae_true);

    n = state->n;
    ae_assert(*remembered, "GQPIPM: integrity check 4322 failed", _state);
    if( state->algomode==gqpipm_amdense )
    {
        rallocv(ae_maxint(n, mtotal, _state), &state->tmp0, _state);
        rallocv(ae_maxint(n, mtotal, _state), &state->tmp1, _state);
        rcopyvx(n, &state->solver.querydata, 0, &state->tmp0, 0, _state);
        rmatrixsymv(n, 1.0, &state->currenth, 0, 0, ae_false, &state->tmp0, 0, 0.0, &state->tmp1, 0, _state);
        rcopyvx(n, &state->tmp1, 0, &state->solver.replyprod, 0, _state);
        if( mtotal>0 )
        {
            sparsegemv(&state->curjac, 1.0, 0, &state->tmp0, 0, 0.0, &state->tmp1, 0, _state);
        }
        else
        {
            rsetv(mtotal, 0.0, &state->tmp1, _state);
        }
        rcopyvx(mtotal, &state->tmp1, 0, &state->solver.replyprod, n, _state);
        rcopyvx(mtotal, &state->solver.querydata, n, &state->tmp0, 0, _state);
        if( mtotal>0 )
        {
            sparsegemv(&state->curjac, 1.0, 1, &state->tmp0, 0, 0.0, &state->tmp1, 0, _state);
        }
        else
        {
            rsetv(n, 0.0, &state->tmp1, _state);
        }
        rcopyvx(n, &state->tmp1, 0, &state->solver.replyprod, n+mtotal, _state);
    }
    else
    {
        ae_assert(state->algomode==gqpipm_amsparse||state->algomode==gqpipm_amhybrid, "GQPIPM: integrity check 777338 failed", _state);
        rallocv(ae_maxint(n, mtotal, _state), &state->tmp0, _state);
        rallocv(ae_maxint(n, mtotal, _state), &state->tmp1, _state);
        if( state->algomode==gqpipm_amhybrid )
        {
            if( state->nq>0 )
            {
                rcopyv(state->nq, &state->solver.querydata, &state->tmp0, _state);
                rmergemulv(state->nq, &state->basehsr1diag, &state->tmp0, _state);
                if( state->basehrank>0 )
                {
                    rgemv(state->basehrank, state->nq, 1.0, &state->basehsr1corrc, 0, &state->solver.querydata, 0.0, &state->tmp1, _state);
                    rmergemulv(state->basehrank, &state->basehsr1corrd, &state->tmp1, _state);
                    rgemv(state->nq, state->basehrank, 1.0, &state->basehsr1corrc, 1, &state->tmp1, 1.0, &state->tmp0, _state);
                }
                rcopyvx(state->nq, &state->tmp0, 0, &state->solver.replyprod, 0, _state);
            }
            rsetvx(n-state->nq, 0.0, &state->solver.replyprod, state->nq, _state);
        }
        else
        {
            rsetv(n, 0.0, &state->solver.replyprod, _state);
        }
        if( state->algomode!=gqpipm_amhybrid||!state->doqnupdates.ptr.p_bool[0] )
        {
            sparsesmv(&state->sparseq1, ae_false, &state->solver.querydata, &state->tmp0, _state);
            raddv(n, 1.0, &state->tmp0, &state->solver.replyprod, _state);
        }
        for(i=0; i<=state->mqc-1; i++)
        {
            if( state->algomode!=gqpipm_amhybrid||!state->doqnupdates.ptr.p_bool[1+offsqc+i] )
            {
                ae_obj_array_get(&state->xqc.constraints, i, &_qci, _state);
                if( qci->nvars==0 )
                {
                    continue;
                }
                for(j=0; j<=qci->nvars-1; j++)
                {
                    state->tmp0.ptr.p_double[j] = state->solver.querydata.ptr.p_double[qci->varidx.ptr.p_int[j]];
                }
                sparsesmv(&qci->lowerq, ae_false, &state->tmp0, &state->tmp1, _state);
                vlag = state->currentlag.ptr.p_double[offsqc+i];
                for(j=0; j<=qci->nvars-1; j++)
                {
                    state->solver.replyprod.ptr.p_double[qci->varidx.ptr.p_int[j]] = state->solver.replyprod.ptr.p_double[qci->varidx.ptr.p_int[j]]+vlag*state->tmp1.ptr.p_double[j];
                }
            }
        }
        for(i=0; i<=state->msocc-1; i++)
        {
            ae_assert(state->algomode!=gqpipm_amhybrid||!state->doqnupdates.ptr.p_bool[1+offscc+i], "GQPIPM: integrity check 907345 failed", _state);
            ae_obj_array_get(&state->xcc.constraints, i, &_cci, _state);
            ae_obj_array_get(&state->xchsparse, i, &_chi, _state);
            if( cci->nvars==0 )
            {
                continue;
            }
            for(j=0; j<=cci->nvars-1; j++)
            {
                state->tmp0.ptr.p_double[j] = state->solver.querydata.ptr.p_double[cci->varidx.ptr.p_int[j]];
            }
            sparsesmv(&chi->lowerh, ae_false, &state->tmp0, &state->tmp1, _state);
            vlag = state->currentlag.ptr.p_double[offscc+i];
            for(j=0; j<=cci->nvars-1; j++)
            {
                state->solver.replyprod.ptr.p_double[cci->varidx.ptr.p_int[j]] = state->solver.replyprod.ptr.p_double[cci->varidx.ptr.p_int[j]]+vlag*state->tmp1.ptr.p_double[j];
            }
        }
        if( mtotal>0 )
        {
            sparsemv(&state->curjac, &state->solver.querydata, &state->tmp1, _state);
            rcopyvx(mtotal, &state->tmp1, 0, &state->solver.replyprod, n, _state);
            rcopyvx(mtotal, &state->solver.querydata, n, &state->tmp0, 0, _state);
            sparsemtv(&state->curjac, &state->tmp0, &state->tmp1, _state);
            rcopyvx(n, &state->tmp1, 0, &state->solver.replyprod, n+mtotal, _state);
        }
        else
        {
            rsetvx(n, 0.0, &state->solver.replyprod, n+mtotal, _state);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Create optional-dense quadratic term from a sparse quadratic objective term.

  -- ALGLIB --
     Copyright 02.05.2025 by Bochkanov Sergey
*************************************************************************/
static void gqpipm_optionaldensefromsparse(gqpoptionaldense* opt,
     sparsematrix* s,
     ae_int_t nq,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t k;


    ae_assert((sparseiscrs(s, _state)&&s->n==s->m)&&s->n>=nq, "GQPIPM: integrity check 127140 failed", _state);
    opt->hasdenseterm = ae_false;
    k = 0;
    for(i=0; i<=nq-1; i++)
    {
        k = k+(s->didx.ptr.p_int[i]-s->ridx.ptr.p_int[i])+1;
    }
    if( nq<=2||ae_fp_less((double)(k),0.5*((double)(nq*(nq-1))/(double)2)) )
    {
        return;
    }
    opt->nvars = nq;
    iallocv(nq, &opt->varidx, _state);
    for(i=0; i<=nq-1; i++)
    {
        opt->varidx.ptr.p_int[i] = i;
    }
    rallocm(nq, nq, &opt->q, _state);
    for(i=0; i<=nq-1; i++)
    {
        rsetr(i+1, 0.0, &opt->q, i, _state);
        j0 = s->ridx.ptr.p_int[i];
        j1 = s->didx.ptr.p_int[i]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            opt->q.ptr.pp_double[i][s->idx.ptr.p_int[jj]] = s->vals.ptr.p_double[jj];
        }
        if( s->didx.ptr.p_int[i]<s->uidx.ptr.p_int[i] )
        {
            opt->q.ptr.pp_double[i][i] = s->vals.ptr.p_double[s->didx.ptr.p_int[i]];
        }
    }
    opt->hasdenseterm = ae_true;
}


/*************************************************************************
Create optional-dense quadratic term from sparse quadratic constraint term

  -- ALGLIB --
     Copyright 02.05.2025 by Bochkanov Sergey
*************************************************************************/
static void gqpipm_optionaldensefromsparseqc(gqpoptionaldense* opt,
     xquadraticconstraint* qc,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t k;
    ae_int_t nq;


    opt->hasdenseterm = ae_false;
    if( qc->nvars<=2 )
    {
        return;
    }
    nq = qc->nvars;
    k = 0;
    for(i=0; i<=nq-1; i++)
    {
        k = k+(qc->lowerq.didx.ptr.p_int[i]-qc->lowerq.ridx.ptr.p_int[i])+1;
    }
    if( ae_fp_less((double)(k),0.5*((double)(nq*(nq-1))/(double)2)) )
    {
        return;
    }
    opt->nvars = nq;
    iallocv(nq, &opt->varidx, _state);
    for(i=0; i<=nq-1; i++)
    {
        opt->varidx.ptr.p_int[i] = qc->varidx.ptr.p_int[i];
    }
    rallocm(nq, nq, &opt->q, _state);
    for(i=0; i<=nq-1; i++)
    {
        rsetr(i+1, 0.0, &opt->q, i, _state);
        j0 = qc->lowerq.ridx.ptr.p_int[i];
        j1 = qc->lowerq.didx.ptr.p_int[i]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            opt->q.ptr.pp_double[i][qc->lowerq.idx.ptr.p_int[jj]] = qc->lowerq.vals.ptr.p_double[jj];
        }
        if( qc->lowerq.didx.ptr.p_int[i]<qc->lowerq.uidx.ptr.p_int[i] )
        {
            opt->q.ptr.pp_double[i][i] = qc->lowerq.vals.ptr.p_double[qc->lowerq.didx.ptr.p_int[i]];
        }
    }
    opt->hasdenseterm = ae_true;
}


void _gqpsparseconichessian_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gqpsparseconichessian *p = (gqpsparseconichessian*)_p;
    ae_touch_ptr((void*)p);
    _sparsematrix_init(&p->lowerh, _state, make_automatic);
    ae_vector_init(&p->ordervaridx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->injectionoffsets, 0, DT_INT, _state, make_automatic);
}


void _gqpsparseconichessian_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gqpsparseconichessian       *dst = (gqpsparseconichessian*)_dst;
    const gqpsparseconichessian *src = (const gqpsparseconichessian*)_src;
    _sparsematrix_init_copy(&dst->lowerh, &src->lowerh, _state, make_automatic);
    ae_vector_init_copy(&dst->ordervaridx, &src->ordervaridx, _state, make_automatic);
    ae_vector_init_copy(&dst->injectionoffsets, &src->injectionoffsets, _state, make_automatic);
}


void _gqpsparseconichessian_clear(void* _p)
{
    gqpsparseconichessian *p = (gqpsparseconichessian*)_p;
    ae_touch_ptr((void*)p);
    _sparsematrix_clear(&p->lowerh);
    ae_vector_clear(&p->ordervaridx);
    ae_vector_clear(&p->injectionoffsets);
}


void _gqpsparseconichessian_destroy(void* _p)
{
    gqpsparseconichessian *p = (gqpsparseconichessian*)_p;
    ae_touch_ptr((void*)p);
    _sparsematrix_destroy(&p->lowerh);
    ae_vector_destroy(&p->ordervaridx);
    ae_vector_destroy(&p->injectionoffsets);
}


void _gqpoptionaldense_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gqpoptionaldense *p = (gqpoptionaldense*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->varidx, 0, DT_INT, _state, make_automatic);
    ae_matrix_init(&p->q, 0, 0, DT_REAL, _state, make_automatic);
}


void _gqpoptionaldense_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gqpoptionaldense       *dst = (gqpoptionaldense*)_dst;
    const gqpoptionaldense *src = (const gqpoptionaldense*)_src;
    dst->hasdenseterm = src->hasdenseterm;
    dst->nvars = src->nvars;
    ae_vector_init_copy(&dst->varidx, &src->varidx, _state, make_automatic);
    ae_matrix_init_copy(&dst->q, &src->q, _state, make_automatic);
}


void _gqpoptionaldense_clear(void* _p)
{
    gqpoptionaldense *p = (gqpoptionaldense*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->varidx);
    ae_matrix_clear(&p->q);
}


void _gqpoptionaldense_destroy(void* _p)
{
    gqpoptionaldense *p = (gqpoptionaldense*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->varidx);
    ae_matrix_destroy(&p->q);
}


void _gqpipmstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gqpipmstate *p = (gqpipmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->wrkbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->wrkbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finiterawbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finiterawbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->isfixed, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->fixedidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->c, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->iotac, 0, DT_INT, _state, make_automatic);
    ae_matrix_init(&p->denseq, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparseq1, _state, make_automatic);
    _gqpoptionaldense_init(&p->optionaldenseq1, _state, make_automatic);
    _sparsematrix_init(&p->sparselc, _state, make_automatic);
    ae_vector_init(&p->lcbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lcbndu, 0, DT_REAL, _state, make_automatic);
    _xquadraticconstraints_init(&p->xqc, _state, make_automatic);
    _xconicconstraints_init(&p->xcc, _state, make_automatic);
    ae_obj_array_init(&p->optionaldensexqc, _state, make_automatic);
    ae_obj_array_init(&p->xchsparse, _state, make_automatic);
    _stimer_init(&p->timertotal, _state, make_automatic);
    _gipmstate_init(&p->solver, _state, make_automatic);
    _sparsematrix_init(&p->curjac, _state, make_automatic);
    ae_matrix_init(&p->currenth, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpd1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpd2, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->currentch, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpwj, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->currentx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->currentlag, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparseh, _state, make_automatic);
    _sparsematrix_init(&p->sparsehdeps, _state, make_automatic);
    _sparsematrix_init(&p->sparsesys, _state, make_automatic);
    ae_vector_init(&p->priorities, 0, DT_INT, _state, make_automatic);
    _spcholanalysis_init(&p->analysis, _state, make_automatic);
    _xbfgshessian_init(&p->hess, _state, make_automatic);
    ae_vector_init(&p->basehsr1diag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->basehsr1corrd, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->basehsr1corrc, 0, 0, DT_REAL, _state, make_automatic);
    ae_nxpool_init(&p->nrealpool, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->totalnl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->totalnu, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->wrklc, _state, make_automatic);
    ae_vector_init(&p->fscales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp3, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmprhsy, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpi, 0, DT_INT, _state, make_automatic);
    ae_matrix_init(&p->dummy2, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmphr, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->tmphc, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->tmphk, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->tmphr2, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->tmphc2, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->tmphk2, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->tmphv, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmphv2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpb, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->doqnupdates, 0, DT_BOOL, _state, make_automatic);
}


void _gqpipmstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gqpipmstate       *dst = (gqpipmstate*)_dst;
    const gqpipmstate *src = (const gqpipmstate*)_src;
    dst->algomode = src->algomode;
    dst->hessmemlen = src->hessmemlen;
    dst->hessmaxoffdiag = src->hessmaxoffdiag;
    dst->n = src->n;
    dst->mlc = src->mlc;
    dst->mqc = src->mqc;
    dst->msocc = src->msocc;
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->wrkbndl, &src->wrkbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->wrkbndu, &src->wrkbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->finiterawbndl, &src->finiterawbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->finiterawbndu, &src->finiterawbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->isfixed, &src->isfixed, _state, make_automatic);
    ae_vector_init_copy(&dst->fixedidx, &src->fixedidx, _state, make_automatic);
    dst->fixedvarscnt = src->fixedvarscnt;
    ae_vector_init_copy(&dst->c, &src->c, _state, make_automatic);
    ae_vector_init_copy(&dst->iotac, &src->iotac, _state, make_automatic);
    ae_matrix_init_copy(&dst->denseq, &src->denseq, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparseq1, &src->sparseq1, _state, make_automatic);
    _gqpoptionaldense_init_copy(&dst->optionaldenseq1, &src->optionaldenseq1, _state, make_automatic);
    dst->nq = src->nq;
    dst->cvxobjective = src->cvxobjective;
    _sparsematrix_init_copy(&dst->sparselc, &src->sparselc, _state, make_automatic);
    ae_vector_init_copy(&dst->lcbndl, &src->lcbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->lcbndu, &src->lcbndu, _state, make_automatic);
    _xquadraticconstraints_init_copy(&dst->xqc, &src->xqc, _state, make_automatic);
    _xconicconstraints_init_copy(&dst->xcc, &src->xcc, _state, make_automatic);
    ae_obj_array_init_copy(&dst->optionaldensexqc, &src->optionaldensexqc, _state, make_automatic);
    ae_obj_array_init_copy(&dst->xchsparse, &src->xchsparse, _state, make_automatic);
    dst->pctype = src->pctype;
    dst->cvxqc = src->cvxqc;
    dst->cvxcc = src->cvxcc;
    dst->eps = src->eps;
    dst->maxits = src->maxits;
    dst->repiterationscount = src->repiterationscount;
    dst->repterminationtype = src->repterminationtype;
    dst->dotrace = src->dotrace;
    dst->dolaconictrace = src->dolaconictrace;
    _stimer_init_copy(&dst->timertotal, &src->timertotal, _state, make_automatic);
    _gipmstate_init_copy(&dst->solver, &src->solver, _state, make_automatic);
    _sparsematrix_init_copy(&dst->curjac, &src->curjac, _state, make_automatic);
    ae_matrix_init_copy(&dst->currenth, &src->currenth, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpd1, &src->tmpd1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpd2, &src->tmpd2, _state, make_automatic);
    ae_matrix_init_copy(&dst->currentch, &src->currentch, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpwj, &src->tmpwj, _state, make_automatic);
    ae_vector_init_copy(&dst->currentx, &src->currentx, _state, make_automatic);
    ae_vector_init_copy(&dst->currentlag, &src->currentlag, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparseh, &src->sparseh, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsehdeps, &src->sparsehdeps, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsesys, &src->sparsesys, _state, make_automatic);
    ae_vector_init_copy(&dst->priorities, &src->priorities, _state, make_automatic);
    _spcholanalysis_init_copy(&dst->analysis, &src->analysis, _state, make_automatic);
    _xbfgshessian_init_copy(&dst->hess, &src->hess, _state, make_automatic);
    ae_vector_init_copy(&dst->basehsr1diag, &src->basehsr1diag, _state, make_automatic);
    ae_vector_init_copy(&dst->basehsr1corrd, &src->basehsr1corrd, _state, make_automatic);
    ae_matrix_init_copy(&dst->basehsr1corrc, &src->basehsr1corrc, _state, make_automatic);
    dst->basehrank = src->basehrank;
    ae_nxpool_init_copy(&dst->nrealpool, &src->nrealpool, _state, make_automatic);
    ae_vector_init_copy(&dst->totalnl, &src->totalnl, _state, make_automatic);
    ae_vector_init_copy(&dst->totalnu, &src->totalnu, _state, make_automatic);
    _sparsematrix_init_copy(&dst->wrklc, &src->wrklc, _state, make_automatic);
    ae_vector_init_copy(&dst->fscales, &src->fscales, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp2, &src->tmp2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp3, &src->tmp3, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpx, &src->tmpx, _state, make_automatic);
    ae_vector_init_copy(&dst->tmprhsy, &src->tmprhsy, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpi, &src->tmpi, _state, make_automatic);
    ae_matrix_init_copy(&dst->dummy2, &src->dummy2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphr, &src->tmphr, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphc, &src->tmphc, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphk, &src->tmphk, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphr2, &src->tmphr2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphc2, &src->tmphc2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphk2, &src->tmphk2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphv, &src->tmphv, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphv2, &src->tmphv2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpb, &src->tmpb, _state, make_automatic);
    ae_vector_init_copy(&dst->doqnupdates, &src->doqnupdates, _state, make_automatic);
}


void _gqpipmstate_clear(void* _p)
{
    gqpipmstate *p = (gqpipmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->wrkbndl);
    ae_vector_clear(&p->wrkbndu);
    ae_vector_clear(&p->finiterawbndl);
    ae_vector_clear(&p->finiterawbndu);
    ae_vector_clear(&p->isfixed);
    ae_vector_clear(&p->fixedidx);
    ae_vector_clear(&p->c);
    ae_vector_clear(&p->iotac);
    ae_matrix_clear(&p->denseq);
    _sparsematrix_clear(&p->sparseq1);
    _gqpoptionaldense_clear(&p->optionaldenseq1);
    _sparsematrix_clear(&p->sparselc);
    ae_vector_clear(&p->lcbndl);
    ae_vector_clear(&p->lcbndu);
    _xquadraticconstraints_clear(&p->xqc);
    _xconicconstraints_clear(&p->xcc);
    ae_obj_array_clear(&p->optionaldensexqc);
    ae_obj_array_clear(&p->xchsparse);
    _stimer_clear(&p->timertotal);
    _gipmstate_clear(&p->solver);
    _sparsematrix_clear(&p->curjac);
    ae_matrix_clear(&p->currenth);
    ae_vector_clear(&p->tmpd1);
    ae_vector_clear(&p->tmpd2);
    ae_matrix_clear(&p->currentch);
    ae_matrix_clear(&p->tmpwj);
    ae_vector_clear(&p->currentx);
    ae_vector_clear(&p->currentlag);
    _sparsematrix_clear(&p->sparseh);
    _sparsematrix_clear(&p->sparsehdeps);
    _sparsematrix_clear(&p->sparsesys);
    ae_vector_clear(&p->priorities);
    _spcholanalysis_clear(&p->analysis);
    _xbfgshessian_clear(&p->hess);
    ae_vector_clear(&p->basehsr1diag);
    ae_vector_clear(&p->basehsr1corrd);
    ae_matrix_clear(&p->basehsr1corrc);
    ae_nxpool_clear(&p->nrealpool);
    ae_vector_clear(&p->totalnl);
    ae_vector_clear(&p->totalnu);
    _sparsematrix_clear(&p->wrklc);
    ae_vector_clear(&p->fscales);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmp1);
    ae_vector_clear(&p->tmp2);
    ae_vector_clear(&p->tmp3);
    ae_vector_clear(&p->tmpx);
    ae_vector_clear(&p->tmprhsy);
    ae_vector_clear(&p->tmpi);
    ae_matrix_clear(&p->dummy2);
    ae_vector_clear(&p->tmphr);
    ae_vector_clear(&p->tmphc);
    ae_vector_clear(&p->tmphk);
    ae_vector_clear(&p->tmphr2);
    ae_vector_clear(&p->tmphc2);
    ae_vector_clear(&p->tmphk2);
    ae_vector_clear(&p->tmphv);
    ae_vector_clear(&p->tmphv2);
    ae_vector_clear(&p->tmpb);
    ae_vector_clear(&p->doqnupdates);
}


void _gqpipmstate_destroy(void* _p)
{
    gqpipmstate *p = (gqpipmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->wrkbndl);
    ae_vector_destroy(&p->wrkbndu);
    ae_vector_destroy(&p->finiterawbndl);
    ae_vector_destroy(&p->finiterawbndu);
    ae_vector_destroy(&p->isfixed);
    ae_vector_destroy(&p->fixedidx);
    ae_vector_destroy(&p->c);
    ae_vector_destroy(&p->iotac);
    ae_matrix_destroy(&p->denseq);
    _sparsematrix_destroy(&p->sparseq1);
    _gqpoptionaldense_destroy(&p->optionaldenseq1);
    _sparsematrix_destroy(&p->sparselc);
    ae_vector_destroy(&p->lcbndl);
    ae_vector_destroy(&p->lcbndu);
    _xquadraticconstraints_destroy(&p->xqc);
    _xconicconstraints_destroy(&p->xcc);
    ae_obj_array_destroy(&p->optionaldensexqc);
    ae_obj_array_destroy(&p->xchsparse);
    _stimer_destroy(&p->timertotal);
    _gipmstate_destroy(&p->solver);
    _sparsematrix_destroy(&p->curjac);
    ae_matrix_destroy(&p->currenth);
    ae_vector_destroy(&p->tmpd1);
    ae_vector_destroy(&p->tmpd2);
    ae_matrix_destroy(&p->currentch);
    ae_matrix_destroy(&p->tmpwj);
    ae_vector_destroy(&p->currentx);
    ae_vector_destroy(&p->currentlag);
    _sparsematrix_destroy(&p->sparseh);
    _sparsematrix_destroy(&p->sparsehdeps);
    _sparsematrix_destroy(&p->sparsesys);
    ae_vector_destroy(&p->priorities);
    _spcholanalysis_destroy(&p->analysis);
    _xbfgshessian_destroy(&p->hess);
    ae_vector_destroy(&p->basehsr1diag);
    ae_vector_destroy(&p->basehsr1corrd);
    ae_matrix_destroy(&p->basehsr1corrc);
    ae_nxpool_destroy(&p->nrealpool);
    ae_vector_destroy(&p->totalnl);
    ae_vector_destroy(&p->totalnu);
    _sparsematrix_destroy(&p->wrklc);
    ae_vector_destroy(&p->fscales);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmp1);
    ae_vector_destroy(&p->tmp2);
    ae_vector_destroy(&p->tmp3);
    ae_vector_destroy(&p->tmpx);
    ae_vector_destroy(&p->tmprhsy);
    ae_vector_destroy(&p->tmpi);
    ae_matrix_destroy(&p->dummy2);
    ae_vector_destroy(&p->tmphr);
    ae_vector_destroy(&p->tmphc);
    ae_vector_destroy(&p->tmphk);
    ae_vector_destroy(&p->tmphr2);
    ae_vector_destroy(&p->tmphc2);
    ae_vector_destroy(&p->tmphk2);
    ae_vector_destroy(&p->tmphv);
    ae_vector_destroy(&p->tmphv2);
    ae_vector_destroy(&p->tmpb);
    ae_vector_destroy(&p->doqnupdates);
}


#endif
#if defined(AE_COMPILE_LPQPPRESOLVE) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
No presolve, just user-supplied scaling + constraint and cost vector
normalization.

INPUT PARAMETERS:
    S           -   array[N], user-supplied scale vector, S[I]>0
    C           -   array[N], costs
    BndL        -   array[N], lower bounds (may contain -INF)
    BndU        -   array[N], upper bounds (may contain +INF)
    N           -   variable count, N>0
    SparseA     -   matrix[K,N], sparse constraints
    AL          -   array[K], lower constraint bounds (may contain -INF)
    AU          -   array[K], upper constraint bounds (may contain +INF)
    K           -   constraint count, K>=0
    Info        -   presolve info structure; temporaries allocated during
                    previous calls may be reused by this function.
                    
OUTPUT PARAMETERS:
    Info        -   contains transformed C, BndL, bndU,  SparseA,  AL,  AU
                    and   information   necessary   to   perform  backward
                    transformation.
                    Following fields can be accessed:
                    * ProblemStatus    which is:
                                       *  0 for successful transformation
                                       * -3 for infeasible problem
                                       * -4 for unbounded problem
                    
                    If Info.ProblemStatus=0, then the following fields can
                    be accessed:
                    * Info.NewN>0  for transformed problem size
                    * Info.NewM>=0 for transformed linear constraint count
                    * Info.NewMQC>=0 for transformed quadratic constraint count
                    * always:          Info.C, Info.BndL, Info.BndU - array[NewN]
                    * for Info.NewM>0: Info.SparseA, Info.AL, Info.AU
                    * for Info.NewMQC>0: Info.XQC
    
NOTE: this routine does not reallocate arrays if NNew<=NOld and/or KNew<=KOld.

  -- ALGLIB --
     Copyright 01.07.2020 by Bochkanov Sergey
*************************************************************************/
void presolvenonescaleuser(/* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     /* Real    */ const ae_vector* c,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     const sparsematrix* sparseh,
     ae_bool isupper,
     ae_bool hash,
     ae_int_t n,
     const sparsematrix* sparsea,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t k,
     xquadraticconstraints* xqc,
     xconicconstraints* xcc,
     ae_bool dotrace,
     presolveinfo* info,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    xconicconstraint *cc;
    ae_smart_ptr _cc;

    ae_frame_make(_state, &_frame_block);
    memset(&_cc, 0, sizeof(_cc));
    ae_smart_ptr_init(&_cc, (void**)&cc, ae_false, _state, ae_true);

    
    /*
     * Integrity checks
     */
    ae_assert(bndl->cnt>=n, "PresolveNoneScaleUser: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=n, "PresolveNoneScaleUser: Length(BndU)<N", _state);
    ae_assert(s->cnt>=n, "PresolveNoneScaleUser: Length(S)<N", _state);
    ae_assert(isfinitevector(s, n, _state), "PresolveNoneScaleUser: S contains infinite or NaN elements", _state);
    ae_assert(c->cnt>=n, "PresolveNoneScaleUser: Length(C)<N", _state);
    ae_assert(isfinitevector(c, n, _state), "PresolveNoneScaleUser: C contains infinite or NaN elements", _state);
    ae_assert(k>=0, "PresolveNoneScaleUser: K<0", _state);
    ae_assert(k==0||sparseiscrs(sparsea, _state), "PresolveNoneScaleUser: A is not CRS", _state);
    ae_assert(k==0||sparsea->m==k, "PresolveNoneScaleUser: rows(A)<>K", _state);
    ae_assert(k==0||sparsea->n==n, "PresolveNoneScaleUser: cols(A)<>N", _state);
    ae_assert(!hash||sparseiscrs(sparseh, _state), "PresolveNoneScaleUser: A is not CRS", _state);
    
    /*
     * Initial check for constraint feasibility and canonicity
     */
    for(i=0; i<=n-1; i++)
    {
        if( (ae_isfinite(bndl->ptr.p_double[i], _state)&&ae_isfinite(bndu->ptr.p_double[i], _state))&&ae_fp_greater(bndl->ptr.p_double[i],bndu->ptr.p_double[i]) )
        {
            if( dotrace )
            {
                ae_trace("> variable %0d is found to have infeasible box constraints, terminating\n",
                    (int)(i));
            }
            info->problemstatus = -3;
            ae_frame_leave(_state);
            return;
        }
    }
    for(i=0; i<=k-1; i++)
    {
        if( (ae_isfinite(al->ptr.p_double[i], _state)&&ae_isfinite(au->ptr.p_double[i], _state))&&ae_fp_greater(al->ptr.p_double[i],au->ptr.p_double[i]) )
        {
            if( dotrace )
            {
                ae_trace("> linear constraint %0d is found to have infeasible bounds, terminating\n",
                    (int)(i));
            }
            info->problemstatus = -3;
            ae_frame_leave(_state);
            return;
        }
    }
    for(i=0; i<=xccgetcount(xcc, _state)-1; i++)
    {
        ae_obj_array_get(&xcc->constraints, i, &_cc, _state);
        ae_assert(cc->conetype>=0, "PresolveNoneScaleUser: one of the conic constraints has non-canonic form", _state);
    }
    
    /*
     * Reallocate storage
     */
    rvectorgrowto(&info->rawc, n, _state);
    rvectorgrowto(&info->rawbndl, n, _state);
    rvectorgrowto(&info->rawbndu, n, _state);
    
    /*
     * Save original problem formulation
     */
    lpqppresolve_presolverstackinit(n, k, xqcgetcount(xqc, _state), xccgetcount(xcc, _state), &info->trfstack, _state);
    info->eps = ae_machineepsilon;
    info->problemstatus = 0;
    info->newn = n;
    info->oldn = n;
    info->newm = k;
    info->oldm = k;
    info->oldmqc = xqcgetcount(xqc, _state);
    info->newmqc = info->oldmqc;
    info->oldmcc = xccgetcount(xcc, _state);
    info->newmcc = info->oldmcc;
    bsetallocv(n, ae_false, &info->lagrangefromresidual, _state);
    iallocv(n, &info->packxperm, _state);
    iallocv(n, &info->unpackxperm, _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(s->ptr.p_double[i]>(double)0, "PresolveNoneScaleUser: S<=0", _state);
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "PresolveNoneScaleUser: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "PresolveNoneScaleUser: BndU contains NAN or -INF", _state);
        info->rawc.ptr.p_double[i] = c->ptr.p_double[i];
        info->rawbndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        info->rawbndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        info->packxperm.ptr.p_int[i] = i;
        info->unpackxperm.ptr.p_int[i] = i;
    }
    info->hash = hash;
    if( hash )
    {
        lpqppresolve_copyexpandh(sparseh, isupper, &info->rawh, _state);
        sparsecopybuf(&info->rawh, &info->sparseh, _state);
    }
    iallocv(k, &info->packyperm, _state);
    iallocv(k, &info->unpackyperm, _state);
    for(i=0; i<=k-1; i++)
    {
        info->packyperm.ptr.p_int[i] = i;
        info->unpackyperm.ptr.p_int[i] = i;
    }
    iallocv(info->newmqc, &info->packqcperm, _state);
    iallocv(info->newmqc, &info->unpackqcperm, _state);
    for(i=0; i<=info->newmqc-1; i++)
    {
        info->packqcperm.ptr.p_int[i] = i;
        info->unpackqcperm.ptr.p_int[i] = i;
    }
    iallocv(n+k, &info->packstatperm, _state);
    iallocv(n+k, &info->unpackstatperm, _state);
    for(i=0; i<=n+k-1; i++)
    {
        info->packstatperm.ptr.p_int[i] = i;
        info->unpackstatperm.ptr.p_int[i] = i;
    }
    sparsecopytocrsbuf(sparsea, &info->rawa, _state);
    xqccopy(xqc, &info->rawxqc, _state);
    xcccopy(xcc, &info->rawxcc, _state);
    
    /*
     * Scale cost and box constraints
     */
    rcopyallocv(n, c, &info->c, _state);
    rcopyallocv(n, bndl, &info->bndl, _state);
    rcopyallocv(n, bndu, &info->bndu, _state);
    if( k>0 )
    {
        rcopyallocv(k, al, &info->al, _state);
        rcopyallocv(k, au, &info->au, _state);
        sparsecopybuf(sparsea, &info->sparsea, _state);
    }
    xqccopy(xqc, &info->xqc, _state);
    xcccopy(xcc, &info->xcc, _state);
    lpqppresolve_scaleshiftobjectiveandconstraints(s, xorigin, n, &info->c, &info->bndl, &info->bndu, &info->sparseh, hash, &info->sparsea, &info->al, &info->au, k, &info->xqc, &info->xcc, &info->trfstack, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Extensive presolving for an LP problem, all techniques are used

INPUT PARAMETERS:
    S           -   array[N], user-supplied scale vector, S[I]>0
    C           -   array[N], costs
    BndL        -   array[N], lower bounds (may contain -INF)
    BndU        -   array[N], upper bounds (may contain +INF)
    N           -   variable count, N>0
    SparseA     -   matrix[K,N], sparse constraints
    AL          -   array[K], lower constraint bounds (may contain -INF)
    AU          -   array[K], upper constraint bounds (may contain +INF)
    K           -   constraint count, K>=0
    XQC         -   quadratic constraints; can be empty, but must be correctly
                    initialized with xqcInit()
    XCC         -   conic constraints; can be empty, but must be correctly
                    initialized with xccInit()
    Info        -   presolve info structure; temporaries allocated during
                    previous calls may be reused by this function.
                    
OUTPUT PARAMETERS:
    Info        -   contains transformed C, BndL, bndU,  SparseA,  AL,  AU
                    and   information   necessary   to   perform  backward
                    transformation.
                    Following fields can be acessed:
                    * Info.NewN>0  for transformed problem size
                    * Info.NewM>=0 for transformed constraint count
                    * always:          Info.C, Info.BndL, Info.BndU - array[NewN]
                    * for Info.NewM>0: Info.SparseA, Info.AL, Info.AU
    
NOTE: this routine does not reallocate arrays if NNew<=NOld and/or KNew<=KOld.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
void presolvelp(/* Real    */ const ae_vector* raws,
     /* Real    */ const ae_vector* rawc,
     /* Real    */ const ae_vector* rawbndl,
     /* Real    */ const ae_vector* rawbndu,
     ae_int_t n,
     const sparsematrix* rawsparsea,
     /* Real    */ const ae_vector* rawal,
     /* Real    */ const ae_vector* rawau,
     ae_int_t m,
     xquadraticconstraints* xqc,
     xconicconstraints* xcc,
     ae_bool dotrace,
     presolveinfo* presolved,
     ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix dummyh;
    ae_vector dummyorigin;

    ae_frame_make(_state, &_frame_block);
    memset(&dummyh, 0, sizeof(dummyh));
    memset(&dummyorigin, 0, sizeof(dummyorigin));
    _sparsematrix_init(&dummyh, _state, ae_true);
    ae_vector_init(&dummyorigin, 0, DT_REAL, _state, ae_true);

    rsetallocv(n, 0.0, &dummyorigin, _state);
    presolveqp(raws, &dummyorigin, rawc, rawbndl, rawbndu, &dummyh, ae_false, ae_false, n, rawsparsea, rawal, rawau, m, xqc, xcc, dotrace, presolved, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Extensive presolving for an QP problem, all techniques are used

INPUT PARAMETERS:
    S           -   array[N], user-supplied scale vector, S[I]>0
    C           -   array[N], costs
    BndL        -   array[N], lower bounds (may contain -INF)
    BndU        -   array[N], upper bounds (may contain +INF)
    H           -   if HasH=True, lower/upper triangular quadratic term
    HasH        -   if false, H is ignored
    N           -   variable count, N>0
    SparseA     -   matrix[K,N], sparse constraints
    AL          -   array[K], lower constraint bounds (may contain -INF)
    AU          -   array[K], upper constraint bounds (may contain +INF)
    K           -   constraint count, K>=0
    XQC         -   quadratic constraints; can be empty, but must be correctly
                    initialized with xqcInit(). Not modified, however it
                    is passed as shared because we access it with
                    objArray...() family of functions.
    XCC         -   conic constraints; can be empty, but must be correctly
                    initialized with xccInit(). Not modified, however it
                    is passed as shared because we access it with
                    objArray...() family of functions.
    Info        -   presolve info structure; temporaries allocated during
                    previous calls may be reused by this function.
                    
OUTPUT PARAMETERS:
    Info        -   contains transformed C, BndL, bndU,  SparseA,  AL,  AU
                    and   information   necessary   to   perform  backward
                    transformation.
                    Following fields can be acessed:
                    * Info.NewN>0  for transformed problem size
                    * Info.NewM>=0 for transformed constraint count
                    * always:          Info.C, Info.BndL, Info.BndU - array[NewN]
                    * for Info.NewM>0: Info.SparseA, Info.AL, Info.AU
    
NOTE: this routine does not reallocate arrays if NNew<=NOld and/or KNew<=KOld.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
void presolveqp(/* Real    */ const ae_vector* raws,
     /* Real    */ const ae_vector* xorigin,
     /* Real    */ const ae_vector* rawc,
     /* Real    */ const ae_vector* rawbndl,
     /* Real    */ const ae_vector* rawbndu,
     const sparsematrix* rawsparseh,
     ae_bool isupper,
     ae_bool hash,
     ae_int_t n,
     const sparsematrix* rawsparsea,
     /* Real    */ const ae_vector* rawal,
     /* Real    */ const ae_vector* rawau,
     ae_int_t m,
     xquadraticconstraints* rawxqc,
     xconicconstraints* rawxcc,
     ae_bool dotrace,
     presolveinfo* presolved,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool somethingchanged;
    sparsematrix normsparsea;
    sparsematrix normsparseat;
    xquadraticconstraints normxqc;
    xconicconstraints normxcc;
    dynamiccrs sparseh;
    dynamiccrs a;
    dynamiccrs at;
    dynamiccrsqconstraints qc;
    ae_vector c;
    ae_vector bndl;
    ae_vector bndu;
    ae_vector al;
    ae_vector au;
    ae_int_t i;
    ae_int_t k;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t mqc;
    ae_int_t mcc;
    ae_int_t nv;
    ae_int_t offs;
    ae_int_t dbgemptycol;
    ae_int_t dbgemptyrow;
    ae_int_t dbgemptyqc;
    ae_int_t dbgnonbindingrows;
    ae_int_t dbgnonbindingqc;
    ae_int_t dbgfixed;
    ae_int_t dbgsingletonrow;
    ae_int_t dbgdoubletonrow;
    ae_int_t dbgslackvars;
    ae_int_t dbgimplicitslacks;
    ae_int_t dbgfreecolumnsingletons;
    double eps;
    ae_int_t presolverounds;
    sparsematrix sparsetmp;
    dynamiccrsqconstraint *qci;
    ae_smart_ptr _qci;
    xconicconstraint *cci;
    ae_smart_ptr _cci;
    xquadraticconstraint *xqci;
    ae_smart_ptr _xqci;
    presolvervcstats vcstats;
    ae_int_t kpow;
    ae_bool donecc;

    ae_frame_make(_state, &_frame_block);
    memset(&normsparsea, 0, sizeof(normsparsea));
    memset(&normsparseat, 0, sizeof(normsparseat));
    memset(&normxqc, 0, sizeof(normxqc));
    memset(&normxcc, 0, sizeof(normxcc));
    memset(&sparseh, 0, sizeof(sparseh));
    memset(&a, 0, sizeof(a));
    memset(&at, 0, sizeof(at));
    memset(&qc, 0, sizeof(qc));
    memset(&c, 0, sizeof(c));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&al, 0, sizeof(al));
    memset(&au, 0, sizeof(au));
    memset(&sparsetmp, 0, sizeof(sparsetmp));
    memset(&_qci, 0, sizeof(_qci));
    memset(&_cci, 0, sizeof(_cci));
    memset(&_xqci, 0, sizeof(_xqci));
    memset(&vcstats, 0, sizeof(vcstats));
    _sparsematrix_init(&normsparsea, _state, ae_true);
    _sparsematrix_init(&normsparseat, _state, ae_true);
    _xquadraticconstraints_init(&normxqc, _state, ae_true);
    _xconicconstraints_init(&normxcc, _state, ae_true);
    _dynamiccrs_init(&sparseh, _state, ae_true);
    _dynamiccrs_init(&a, _state, ae_true);
    _dynamiccrs_init(&at, _state, ae_true);
    _dynamiccrsqconstraints_init(&qc, _state, ae_true);
    ae_vector_init(&c, 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(&al, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&au, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sparsetmp, _state, ae_true);
    ae_smart_ptr_init(&_qci, (void**)&qci, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_cci, (void**)&cci, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_xqci, (void**)&xqci, ae_false, _state, ae_true);
    _presolvervcstats_init(&vcstats, _state, ae_true);

    ae_assert(m==0||sparseiscrs(rawsparsea, _state), "PRESOLVER: A is non-CRS sparse matrix", _state);
    ae_assert(!hash||sparseiscrs(rawsparseh, _state), "PRESOLVER: quadratic term must be stored in the CRS format", _state);
    mqc = xqcgetcount(rawxqc, _state);
    mcc = xccgetcount(rawxcc, _state);
    
    /*
     * Trace
     */
    dotrace = (dotrace||(ae_is_trace_enabled("PRESOLVER.LP")&&!hash))||ae_is_trace_enabled("PRESOLVER.QP");
    if( dotrace )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        if( hash )
        {
            ae_trace("// QP PRESOLVER STARTED                                                                           //\n");
        }
        else
        {
            ae_trace("// LP PRESOLVER STARTED                                                                           //\n");
        }
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("----- printing input problem metrics ---------------------------------------------------------------\n");
        ae_trace("N            = %10d (variables)\n",
            (int)(n));
        ae_trace("M            = %10d (linear constraints)\n",
            (int)(m));
        if( m!=0 )
        {
            ae_trace("nz(A)        = %10d (nonzeros in linear constraints)\n",
                (int)(rawsparsea->ridx.ptr.p_int[rawsparsea->m]));
        }
        ae_trace("MQC          = %10d (quadratic constraints)\n",
            (int)(mqc));
        ae_trace("MCC          = %10d (conic constraints)\n",
            (int)(mcc));
    }
    
    /*
     * Initial check for constraint feasibility
     */
    for(i=0; i<=n-1; i++)
    {
        if( (ae_isfinite(rawbndl->ptr.p_double[i], _state)&&ae_isfinite(rawbndu->ptr.p_double[i], _state))&&ae_fp_greater(rawbndl->ptr.p_double[i],rawbndu->ptr.p_double[i]) )
        {
            if( dotrace )
            {
                ae_trace("> variable %0d is found to have infeasible box constraints, terminating\n",
                    (int)(i));
            }
            presolved->problemstatus = -3;
            ae_frame_leave(_state);
            return;
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( (ae_isfinite(rawal->ptr.p_double[i], _state)&&ae_isfinite(rawau->ptr.p_double[i], _state))&&ae_fp_greater(rawal->ptr.p_double[i],rawau->ptr.p_double[i]) )
        {
            if( dotrace )
            {
                ae_trace("> linear constraint %0d is found to have infeasible bounds, terminating\n",
                    (int)(i));
            }
            presolved->problemstatus = -3;
            ae_frame_leave(_state);
            return;
        }
    }
    for(i=0; i<=mqc-1; i++)
    {
        ae_obj_array_get(&rawxqc->constraints, i, &_xqci, _state);
        if( (ae_isfinite(xqci->cl, _state)&&ae_isfinite(xqci->cu, _state))&&ae_fp_greater(xqci->cl,xqci->cu) )
        {
            if( dotrace )
            {
                ae_trace("> quadratic constraint %0d is found to have infeasible bounds, terminating\n",
                    (int)(i));
            }
            presolved->problemstatus = -3;
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * Starting output
     */
    rcopyallocv(n, rawc, &presolved->rawc, _state);
    rcopyallocv(n, rawbndl, &presolved->rawbndl, _state);
    rcopyallocv(n, rawbndu, &presolved->rawbndu, _state);
    if( m>0 )
    {
        sparsecopytocrsbuf(rawsparsea, &presolved->rawa, _state);
    }
    xqccopy(rawxqc, &presolved->rawxqc, _state);
    xcccopy(rawxcc, &presolved->rawxcc, _state);
    presolved->hash = hash;
    if( hash )
    {
        lpqppresolve_copyexpandh(rawsparseh, isupper, &presolved->rawh, _state);
    }
    
    /*
     * Trace counters
     */
    dbgemptycol = 0;
    dbgemptyrow = 0;
    dbgemptyqc = 0;
    dbgnonbindingrows = 0;
    dbgnonbindingqc = 0;
    dbgfixed = 0;
    dbgsingletonrow = 0;
    dbgdoubletonrow = 0;
    dbgslackvars = 0;
    dbgimplicitslacks = 0;
    dbgfreecolumnsingletons = 0;
    
    /*
     * Initial state of the presolver
     */
    eps = (double)(1000+n+m)*ae_machineepsilon+ae_pow(ae_machineepsilon, 0.75, _state);
    presolved->eps = eps;
    lpqppresolve_presolverstackinit(n, m, xqcgetcount(rawxqc, _state), xccgetcount(rawxcc, _state), &presolved->trfstack, _state);
    presolved->problemstatus = 0;
    rcopyallocv(n, rawc, &c, _state);
    rcopyallocv(n, rawbndl, &bndl, _state);
    rcopyallocv(n, rawbndu, &bndu, _state);
    if( m>0 )
    {
        sparsecopybuf(rawsparsea, &normsparsea, _state);
        rcopyallocv(m, rawal, &al, _state);
        rcopyallocv(m, rawau, &au, _state);
    }
    xqccopy(rawxqc, &normxqc, _state);
    xcccopy(rawxcc, &normxcc, _state);
    if( hash )
    {
        sparsecopybuf(&presolved->rawh, &sparsetmp, _state);
    }
    lpqppresolve_scaleshiftobjectiveandconstraints(raws, xorigin, n, &c, &bndl, &bndu, &sparsetmp, hash, &normsparsea, &al, &au, m, &normxqc, &normxcc, &presolved->trfstack, _state);
    if( hash )
    {
        lpqppresolve_dyncrsinitfromsparsecrs(&sparsetmp, &sparseh, _state);
        lpqppresolve_dyncrsdropoffdiagonalzeros(&sparseh, _state);
    }
    if( m>0 )
    {
        sparsecopytransposecrsbuf(&normsparsea, &normsparseat, _state);
        lpqppresolve_dyncrsinitfromsparsecrs(&normsparsea, &a, _state);
        lpqppresolve_dyncrsinitfromsparsecrs(&normsparseat, &at, _state);
        lpqppresolve_dyncrsdropzeros(&a, _state);
        lpqppresolve_dyncrsdropzeros(&at, _state);
    }
    else
    {
        lpqppresolve_dyncrsinitempty(&a, 0, n, _state);
        lpqppresolve_dyncrsinitempty(&at, n, 0, _state);
    }
    bsetallocv(n, ae_false, &presolved->lagrangefromresidual, _state);
    lpqppresolve_presolvebuffersinit(&presolved->buf, n, m, _state);
    
    /*
     * Initial state of QC and CC-related variables
     */
    bsetallocv(n, ae_false, &vcstats.isdroppedcol, _state);
    bsetallocv(m, ae_false, &vcstats.isdroppedlc, _state);
    isetallocv(n, 0, &vcstats.nlcpervar, _state);
    isetallocv(n, 0, &vcstats.cntdownwardaxial, _state);
    isetallocv(n, 0, &vcstats.cntupwardaxial, _state);
    isetallocv(n, 0, &vcstats.cntaxial, _state);
    isetallocv(n, 0, &vcstats.cntradial, _state);
    lpqppresolve_dynqcinitfromxqc(&normxqc, &qc, _state);
    bsetallocv(mqc, ae_false, &vcstats.isdroppedqc, _state);
    knisinitunsorted(n, mqc, 0, &vcstats.varbyqc, _state);
    for(i=0; i<=mqc-1; i++)
    {
        ae_obj_array_get(&qc.constraints, i, &_qci, _state);
        for(j=0; j<=qci->nvars-1; j++)
        {
            k = qci->varidx.ptr.p_int[j];
            vcstats.nlcpervar.ptr.p_int[k] = vcstats.nlcpervar.ptr.p_int[k]+1;
            knisaddnewelement(&vcstats.varbyqc, k, i, _state);
        }
    }
    bsetallocv(mcc, ae_false, &vcstats.isdroppedcc, _state);
    knisinitunsorted(n, mcc, 0, &vcstats.varbycc, _state);
    for(i=0; i<=mcc-1; i++)
    {
        ae_obj_array_get(&normxcc.constraints, i, &_cci, _state);
        nv = cci->nvars;
        for(j=0; j<=nv-1; j++)
        {
            k = cci->varidx.ptr.p_int[j];
            vcstats.nlcpervar.ptr.p_int[k] = vcstats.nlcpervar.ptr.p_int[k]+1;
            knisaddnewelement(&vcstats.varbycc, k, i, _state);
        }
        donecc = ae_false;
        if( cci->conetype==xccprimitiveconetype(_state) )
        {
            for(j=0; j<=nv-2; j++)
            {
                vcstats.cntradial.ptr.p_int[cci->varidx.ptr.p_int[j]] = vcstats.cntradial.ptr.p_int[cci->varidx.ptr.p_int[j]]+1;
            }
            k = cci->varidx.ptr.p_int[nv-1];
            vcstats.cntaxial.ptr.p_int[k] = vcstats.cntaxial.ptr.p_int[k]+1;
            if( ae_fp_greater(cci->diaga.ptr.p_double[nv-1],(double)(0)) )
            {
                vcstats.cntupwardaxial.ptr.p_int[k] = vcstats.cntupwardaxial.ptr.p_int[k]+1;
            }
            if( ae_fp_less(cci->diaga.ptr.p_double[nv-1],(double)(0)) )
            {
                vcstats.cntdownwardaxial.ptr.p_int[k] = vcstats.cntdownwardaxial.ptr.p_int[k]+1;
            }
            donecc = ae_true;
        }
        if( cci->conetype==xccprimitivepowerconetype(_state) )
        {
            kpow = cci->kpow;
            for(j=0; j<=nv-kpow-1; j++)
            {
                vcstats.cntradial.ptr.p_int[cci->varidx.ptr.p_int[j]] = vcstats.cntradial.ptr.p_int[cci->varidx.ptr.p_int[j]]+1;
            }
            for(j=nv-kpow; j<=nv-1; j++)
            {
                k = cci->varidx.ptr.p_int[j];
                vcstats.cntaxial.ptr.p_int[k] = vcstats.cntaxial.ptr.p_int[k]+1;
                if( ae_fp_greater(cci->diaga.ptr.p_double[j],(double)(0)) )
                {
                    vcstats.cntupwardaxial.ptr.p_int[k] = vcstats.cntupwardaxial.ptr.p_int[k]+1;
                }
                if( ae_fp_less(cci->diaga.ptr.p_double[j],(double)(0)) )
                {
                    vcstats.cntdownwardaxial.ptr.p_int[k] = vcstats.cntdownwardaxial.ptr.p_int[k]+1;
                }
            }
            donecc = ae_true;
        }
        ae_assert(donecc, "PRESOLVER: integrity check 485227 failed", _state);
    }
    
    /*
     * While something changes, keep iterating
     */
    presolverounds = 0;
    somethingchanged = ae_true;
    while(somethingchanged)
    {
        somethingchanged = ae_false;
        if( !lpqppresolve_dropemptycol(&c, &bndl, &bndu, &vcstats, &presolved->lagrangefromresidual, &sparseh, hash, n, &a, &at, &al, &au, m, eps, dotrace, &presolved->buf, &presolved->trfstack, &presolved->problemstatus, &somethingchanged, &dbgemptycol, _state) )
        {
            ae_frame_leave(_state);
            return;
        }
        if( !lpqppresolve_dropclearlynonbindingrows(n, &vcstats, &a, &at, &al, &au, m, &qc, &normxcc, eps, dotrace, &presolved->buf, &presolved->trfstack, &presolved->problemstatus, &somethingchanged, &dbgemptyrow, &dbgemptyqc, &dbgnonbindingrows, &dbgnonbindingqc, _state) )
        {
            ae_frame_leave(_state);
            return;
        }
        if( !lpqppresolve_singletonrowtobc(&bndl, &bndu, &vcstats, n, &a, &at, &al, &au, m, eps, dotrace, &presolved->buf, &presolved->trfstack, &presolved->problemstatus, &somethingchanged, &dbgsingletonrow, _state) )
        {
            ae_frame_leave(_state);
            return;
        }
        if( !lpqppresolve_doubletonrow(&c, &bndl, &bndu, &vcstats, &sparseh, hash, n, &a, &at, &al, &au, m, &qc, &normxcc, eps, dotrace, &presolved->buf, &presolved->trfstack, &presolved->problemstatus, &somethingchanged, &dbgdoubletonrow, _state) )
        {
            ae_frame_leave(_state);
            return;
        }
        if( !lpqppresolve_fixvariables(&c, &bndl, &bndu, &vcstats, &sparseh, hash, n, &a, &at, &al, &au, m, &qc, &normxcc, eps, dotrace, &presolved->buf, &presolved->trfstack, &presolved->problemstatus, &somethingchanged, &dbgfixed, _state) )
        {
            ae_frame_leave(_state);
            return;
        }
        if( !lpqppresolve_singletoncols(&c, &bndl, &bndu, &vcstats, &sparseh, hash, n, &a, &at, &al, &au, m, eps, dotrace, &presolved->buf, &presolved->trfstack, &presolved->problemstatus, &somethingchanged, &dbgslackvars, &dbgimplicitslacks, &dbgfreecolumnsingletons, _state) )
        {
            ae_frame_leave(_state);
            return;
        }
        presolverounds = presolverounds+1;
    }
    
    /*
     * Integrity checks
     * * A and AT have the same number of elements (we do not check actual elements, only their total count)
     * * VCStats.NLCPerVar is zero after we subtract all non-dropped constraints
     */
    if( m>0 )
    {
        j0 = 0;
        for(j=0; j<=m-1; j++)
        {
            j0 = j0+(a.rowend.ptr.p_int[j]-a.rowbegin.ptr.p_int[j]);
        }
        j1 = 0;
        for(j=0; j<=n-1; j++)
        {
            j1 = j1+(at.rowend.ptr.p_int[j]-at.rowbegin.ptr.p_int[j]);
        }
        ae_assert(j0==j1, "PRESOLVER: integrity check 4347 failed", _state);
    }
    for(i=0; i<=n-1; i++)
    {
        ae_assert(vcstats.nlcpervar.ptr.p_int[i]==kniscountkth(&vcstats.varbyqc, i, _state)+kniscountkth(&vcstats.varbycc, i, _state), "PRESOLVER: integrity check 566152 failed", _state);
    }
    for(i=0; i<=mqc-1; i++)
    {
        if( vcstats.isdroppedqc.ptr.p_bool[i] )
        {
            continue;
        }
        ae_obj_array_get(&qc.constraints, i, &_qci, _state);
        for(j=0; j<=qci->nvars-1; j++)
        {
            if( vcstats.isdroppedcol.ptr.p_bool[qci->varidx.ptr.p_int[j]] )
            {
                ae_assert(ae_false, "PRESOLVER: integrity check 2602 failed", _state);
            }
            vcstats.nlcpervar.ptr.p_int[qci->varidx.ptr.p_int[j]] = vcstats.nlcpervar.ptr.p_int[qci->varidx.ptr.p_int[j]]-1;
        }
    }
    for(i=0; i<=mcc-1; i++)
    {
        if( vcstats.isdroppedcc.ptr.p_bool[i] )
        {
            continue;
        }
        ae_obj_array_get(&normxcc.constraints, i, &_cci, _state);
        for(j=0; j<=cci->nvars-1; j++)
        {
            if( vcstats.isdroppedcol.ptr.p_bool[cci->varidx.ptr.p_int[j]] )
            {
                ae_assert(ae_false, "PRESOLVER: integrity check 529049 failed", _state);
            }
            vcstats.nlcpervar.ptr.p_int[cci->varidx.ptr.p_int[j]] = vcstats.nlcpervar.ptr.p_int[cci->varidx.ptr.p_int[j]]-1;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( vcstats.nlcpervar.ptr.p_int[i]!=0 )
        {
            ae_assert(ae_false, "PRESOLVER: integrity check 3303 failed", _state);
        }
    }
    
    /*
     * Output data that are permuted by XPerm[]
     */
    presolved->oldn = n;
    presolved->newn = 0;
    isetallocv(n, -1, &presolved->packxperm, _state);
    isetallocv(n, -1, &presolved->unpackxperm, _state);
    for(i=0; i<=n-1; i++)
    {
        if( !vcstats.isdroppedcol.ptr.p_bool[i] )
        {
            presolved->unpackxperm.ptr.p_int[presolved->newn] = i;
            presolved->packxperm.ptr.p_int[i] = presolved->newn;
            presolved->newn = presolved->newn+1;
        }
    }
    rallocv(presolved->newn, &presolved->bndl, _state);
    rallocv(presolved->newn, &presolved->bndu, _state);
    rallocv(presolved->newn, &presolved->c, _state);
    for(i=0; i<=presolved->newn-1; i++)
    {
        presolved->c.ptr.p_double[i] = c.ptr.p_double[presolved->unpackxperm.ptr.p_int[i]];
        presolved->bndl.ptr.p_double[i] = bndl.ptr.p_double[presolved->unpackxperm.ptr.p_int[i]];
        presolved->bndu.ptr.p_double[i] = bndu.ptr.p_double[presolved->unpackxperm.ptr.p_int[i]];
    }
    if( hash )
    {
        presolved->sparseh.n = presolved->newn;
        presolved->sparseh.m = presolved->newn;
        iallocv(presolved->newn+1, &presolved->sparseh.ridx, _state);
        presolved->sparseh.ridx.ptr.p_int[0] = 0;
        for(i=0; i<=presolved->newn-1; i++)
        {
            offs = presolved->sparseh.ridx.ptr.p_int[i];
            igrowv(offs+n, &presolved->sparseh.idx, _state);
            rgrowv(offs+n, &presolved->sparseh.vals, _state);
            k = presolved->unpackxperm.ptr.p_int[i];
            j0 = sparseh.rowbegin.ptr.p_int[k];
            j1 = sparseh.rowend.ptr.p_int[k]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                j = presolved->packxperm.ptr.p_int[sparseh.idx.ptr.p_int[jj]];
                if( j<0||j>i )
                {
                    continue;
                }
                presolved->sparseh.idx.ptr.p_int[offs] = j;
                presolved->sparseh.vals.ptr.p_double[offs] = sparseh.vals.ptr.p_double[jj];
                offs = offs+1;
            }
            presolved->sparseh.ridx.ptr.p_int[i+1] = offs;
        }
        sparsecreatecrsinplace(&presolved->sparseh, _state);
    }
    
    /*
     * Output data that are permuted by YPerm[]
     */
    presolved->oldm = m;
    presolved->newm = 0;
    isetallocv(m, -1, &presolved->packyperm, _state);
    isetallocv(m, -1, &presolved->unpackyperm, _state);
    for(i=0; i<=m-1; i++)
    {
        if( !vcstats.isdroppedlc.ptr.p_bool[i] )
        {
            presolved->unpackyperm.ptr.p_int[presolved->newm] = i;
            presolved->packyperm.ptr.p_int[i] = presolved->newm;
            presolved->newm = presolved->newm+1;
        }
    }
    rallocv(presolved->newm, &presolved->al, _state);
    rallocv(presolved->newm, &presolved->au, _state);
    for(i=0; i<=presolved->newm-1; i++)
    {
        presolved->al.ptr.p_double[i] = al.ptr.p_double[presolved->unpackyperm.ptr.p_int[i]];
        presolved->au.ptr.p_double[i] = au.ptr.p_double[presolved->unpackyperm.ptr.p_int[i]];
    }
    
    /*
     * Output A which is permuted by both XPerm[] and YPerm[]
     */
    presolved->sparsea.m = 0;
    presolved->sparsea.n = presolved->newn;
    iallocv(presolved->newm+1, &presolved->sparsea.ridx, _state);
    presolved->sparsea.ridx.ptr.p_int[0] = 0;
    for(i=0; i<=m-1; i++)
    {
        if( !vcstats.isdroppedlc.ptr.p_bool[i] )
        {
            offs = presolved->sparsea.ridx.ptr.p_int[presolved->sparsea.m];
            igrowv(offs+n, &presolved->sparsea.idx, _state);
            rgrowv(offs+n, &presolved->sparsea.vals, _state);
            j0 = a.rowbegin.ptr.p_int[i];
            j1 = a.rowend.ptr.p_int[i]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                j = presolved->packxperm.ptr.p_int[a.idx.ptr.p_int[jj]];
                ae_assert(j>=0, "PRESOLVE: integrity check 54fc failed", _state);
                presolved->sparsea.idx.ptr.p_int[offs] = j;
                presolved->sparsea.vals.ptr.p_double[offs] = a.vals.ptr.p_double[jj];
                offs = offs+1;
            }
            presolved->sparsea.m = presolved->sparsea.m+1;
            presolved->sparsea.ridx.ptr.p_int[presolved->sparsea.m] = offs;
        }
    }
    ae_assert(presolved->sparsea.m==presolved->newm, "PRESOLVE: integrity check ee3a failed", _state);
    sparsecreatecrsinplace(&presolved->sparsea, _state);
    
    /*
     * Output data permuted by QCPerm[] and XPerm[]
     */
    presolved->xqc.n = n;
    ae_obj_array_clear(&presolved->xqc.constraints);
    presolved->oldmqc = mqc;
    presolved->newmqc = 0;
    isetallocv(mqc, -1, &presolved->packqcperm, _state);
    isetallocv(mqc, -1, &presolved->unpackqcperm, _state);
    for(i=0; i<=mqc-1; i++)
    {
        if( !vcstats.isdroppedqc.ptr.p_bool[i] )
        {
            presolved->unpackqcperm.ptr.p_int[presolved->newmqc] = i;
            presolved->packqcperm.ptr.p_int[i] = presolved->newmqc;
            presolved->newmqc = presolved->newmqc+1;
            ae_obj_array_get(&qc.constraints, i, &_qci, _state);
            xqci = (xquadraticconstraint*)ae_malloc(sizeof(xquadraticconstraint), _state); /* note: using xqci as a temporary prior to assigning its value to _xqci */
            memset(xqci, 0, sizeof(xquadraticconstraint));
            _xquadraticconstraint_init(xqci, _state, ae_false);
            ae_smart_ptr_assign(&_xqci, xqci, ae_true, ae_true, (ae_int_t)sizeof(xquadraticconstraint), _xquadraticconstraint_init_copy, _xquadraticconstraint_destroy);
            lpqppresolve_dynqcpermtoxqc(qci, &presolved->packxperm, &presolved->unpackxperm, xqci, _state);
            ae_obj_array_append_transfer(&presolved->xqc.constraints, &_xqci, _state);
        }
    }
    
    /*
     * Output data permuted by CCPerm and XPerm
     */
    presolved->oldmcc = mcc;
    presolved->newmcc = 0;
    isetallocv(mcc, -1, &presolved->packccperm, _state);
    isetallocv(mcc, -1, &presolved->unpackccperm, _state);
    for(i=0; i<=mcc-1; i++)
    {
        if( !vcstats.isdroppedcc.ptr.p_bool[i] )
        {
            presolved->unpackccperm.ptr.p_int[presolved->newmcc] = i;
            presolved->packccperm.ptr.p_int[i] = presolved->newmcc;
            presolved->newmcc = presolved->newmcc+1;
        }
    }
    xcccopywithskipandpack(&normxcc, &vcstats.isdroppedcc, &presolved->packxperm, &presolved->xcc, _state);
    ae_assert(presolved->newmcc==xccgetcount(&presolved->xcc, _state), "PRESOLVER: integrity check 682108 failed", _state);
    
    /*
     * Prepare permutation for constraint Stats[]
     */
    isetallocv(n+m, -1, &presolved->packstatperm, _state);
    isetallocv(n+m, -1, &presolved->unpackstatperm, _state);
    offs = 0;
    for(i=0; i<=n-1; i++)
    {
        if( !vcstats.isdroppedcol.ptr.p_bool[i] )
        {
            presolved->packstatperm.ptr.p_int[i] = offs;
            offs = offs+1;
        }
    }
    for(i=0; i<=m-1; i++)
    {
        if( !vcstats.isdroppedlc.ptr.p_bool[i] )
        {
            presolved->packstatperm.ptr.p_int[n+i] = offs;
            offs = offs+1;
        }
    }
    ae_assert(offs==presolved->newn+presolved->newm, "PRESOLVE: integrity check 3632 failed", _state);
    for(i=0; i<=n+m-1; i++)
    {
        if( presolved->packstatperm.ptr.p_int[i]>=0 )
        {
            presolved->unpackstatperm.ptr.p_int[presolved->packstatperm.ptr.p_int[i]] = i;
        }
    }
    
    /*
     * Trace output
     */
    if( dotrace )
    {
        ae_trace("done in %0d rounds\n\n",
            (int)(presolverounds));
        ae_trace("----- printing information about reductions --------------------------------------------------------\n");
        ae_trace("fixed vars           = %10d (fixed variables)\n",
            (int)(dbgfixed));
        ae_trace("empty cols           = %10d (empty constraint columns)\n",
            (int)(dbgemptycol));
        ae_trace("empty rows           = %10d (empty constraint rows)\n",
            (int)(dbgemptyrow));
        ae_trace("empty QC             = %10d (empty quadratic constraints)\n",
            (int)(dbgemptyqc));
        ae_trace("nonbinding rows      = %10d (irrelevant LC due to bounds)\n",
            (int)(dbgnonbindingrows));
        ae_trace("nonbinding QC        = %10d (irrelevant QC due to bounds)\n",
            (int)(dbgnonbindingqc));
        ae_trace("singleton rows       = %10d (singleton rows converted to box constraints)\n",
            (int)(dbgsingletonrow));
        ae_trace("doubleton rows       = %10d (doubleton equality constraints used to factor out variables)\n",
            (int)(dbgdoubletonrow));
        ae_trace("slack variables      = %10d (singleton cols with zero cost, recognized as explicit slacks)\n",
            (int)(dbgslackvars));
        ae_trace("implicit slacks      = %10d (singleton cols at equality rows, recognized as implicit slacks)\n",
            (int)(dbgimplicitslacks));
        ae_trace("free col singletons  = %10d (free singleton columns)\n",
            (int)(dbgfreecolumnsingletons));
        ae_trace("----- printing output problem metrics --------------------------------------------------------------\n");
        ae_trace("N            = %10d (variables)\n",
            (int)(presolved->newn));
        ae_trace("M            = %10d (constraints)\n",
            (int)(presolved->newm));
        ae_trace("MQC          = %10d (quadratic constraints)\n",
            (int)(presolved->newmqc));
        if( m!=0 )
        {
            ae_trace("nz(A)        = %10d (nonzeros in A)\n",
                (int)(presolved->sparsea.ridx.ptr.p_int[presolved->sparsea.m]));
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Forward transformation which transforms a set of original variables (e.g.
an initial point) to the converted problem format.

INPUT PARAMETERS:
    Info        -   presolve info structure
    X           -   array[NOld], original point
                    
OUTPUT PARAMETERS:
    X           -   array[NNew], transformed point
    
NOTE: this routine does not reallocate arrays if NOld<=NNew and/or KOld<=KNew.

  -- ALGLIB --
     Copyright 01.07.2020 by Bochkanov Sergey
*************************************************************************/
void presolvefwd(presolveinfo* s,
     /* Real    */ ae_vector* xx,
     ae_state *_state)
{
    ae_int_t tidx;
    ae_int_t tt;
    ae_int_t i;
    ae_int_t k;
    double v;


    rcopyallocv(s->oldn, xx, &s->x1, _state);
    for(tidx=0; tidx<=s->trfstack.ntrf-1; tidx++)
    {
        tt = s->trfstack.trftype.ptr.p_int[tidx];
        if( tt==lpqppresolve_pstobjectivescaling )
        {
            continue;
        }
        if( tt==lpqppresolve_pstcolscaling )
        {
            
            /*
             * Column scaling
             */
            lpqppresolve_presolverselectstreamsource(&s->trfstack, tidx, _state);
            lpqppresolve_presolverunstreamir(&s->trfstack, &k, &v, _state);
            lpqppresolve_presolverasserteos(&s->trfstack, _state);
            s->x1.ptr.p_double[k] = s->x1.ptr.p_double[k]*v;
            continue;
        }
        if( tt==lpqppresolve_pstcolshifting )
        {
            
            /*
             * Variable shifting
             */
            lpqppresolve_presolverselectstreamsource(&s->trfstack, tidx, _state);
            lpqppresolve_presolverunstreamir(&s->trfstack, &k, &v, _state);
            lpqppresolve_presolverasserteos(&s->trfstack, _state);
            s->x1.ptr.p_double[k] = s->x1.ptr.p_double[k]+v;
            continue;
        }
        if( tt==lpqppresolve_pstrowscaling )
        {
            continue;
        }
        if( tt==lpqppresolve_pstqcscaling )
        {
            continue;
        }
        if( tt==lpqppresolve_pstdropemptycol )
        {
            continue;
        }
        if( tt==lpqppresolve_pstdropemptyrow )
        {
            continue;
        }
        if( tt==lpqppresolve_pstdropemptyqc )
        {
            continue;
        }
        if( tt==lpqppresolve_pstsingletonrow )
        {
            continue;
        }
        if( tt==lpqppresolve_pstfixedvar )
        {
            continue;
        }
        if( tt==lpqppresolve_pstexplicitslack )
        {
            continue;
        }
        if( tt==lpqppresolve_pstimplicitslack )
        {
            continue;
        }
        if( tt==lpqppresolve_pstdoubletoneq )
        {
            continue;
        }
        ae_assert(ae_false, "PresolverFwd: unexpected transform type", _state);
    }
    rallocv(s->newn, xx, _state);
    for(i=0; i<=s->newn-1; i++)
    {
        xx->ptr.p_double[i] = s->x1.ptr.p_double[s->unpackxperm.ptr.p_int[i]];
    }
}


/*************************************************************************
Backward transformation which extracts original solution from that of  the
converted problem.

Below NNew/KNew correspond to transformed problem size (as returned by the
presolve routine) and NOld/KOld correspond to original  problem  size  (as
specified by caller). We expect that caller knows  these  sizes,  so  this
routine does not report them.

INPUT PARAMETERS:
    Info        -   presolve info structure
    X           -   array[NNew], transformed solution (primal variables)
    Stats       -   array[NNew+MNew], transformed constraint status (negative -
                    at lower bound, positive -  at  upper  bound,  zero  -
                    inactive).
    LagBC       -   array[NNew], transformed Lagrange multipliers
    LagLC       -   array[KNew], transformed Lagrange multipliers
    LagQC       -   array[MQCNew], transformed Lagrange multipliers
                    
OUTPUT PARAMETERS:
    X           -   array[NOld], original solution (primal variables)
    Stats       -   array[NOld+MOld], original constraint status
    LagBC       -   array[NOld], Lagrange multipliers
    LagLC       -   array[KOld], Lagrange multipliers
    LagQC       -   array[MQCOld], transformed Lagrange multipliers
    
NOTE: this routine does not reallocate arrays if NOld<=NNew and/or KOld<=KNew.

  -- ALGLIB --
     Copyright 01.07.2020 by Bochkanov Sergey
*************************************************************************/
void presolvebwd(presolveinfo* info,
     /* Real    */ ae_vector* x,
     /* Integer */ ae_vector* stats,
     ae_bool needstats,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* laglc,
     /* Real    */ ae_vector* lagqc,
     ae_state *_state)
{
    ae_int_t i;


    
    /*
     * Read state of the transformed problem into storage allocated for the original problem
     */
    if( needstats )
    {
        isetallocv(info->oldn+info->oldm, 0, &info->s1, _state);
        for(i=0; i<=info->newn+info->newm-1; i++)
        {
            info->s1.ptr.p_int[info->unpackstatperm.ptr.p_int[i]] = stats->ptr.p_int[i];
        }
    }
    rsetallocv(info->oldn, 0.0, &info->x1, _state);
    for(i=0; i<=info->newn-1; i++)
    {
        info->x1.ptr.p_double[info->unpackxperm.ptr.p_int[i]] = x->ptr.p_double[i];
    }
    rsetallocv(info->oldn, 0.0, &info->bc1, _state);
    for(i=0; i<=info->newn-1; i++)
    {
        info->bc1.ptr.p_double[info->unpackxperm.ptr.p_int[i]] = lagbc->ptr.p_double[i];
    }
    rsetallocv(info->oldm, 0.0, &info->y1, _state);
    for(i=0; i<=info->newm-1; i++)
    {
        info->y1.ptr.p_double[info->unpackyperm.ptr.p_int[i]] = laglc->ptr.p_double[i];
    }
    rsetallocv(info->oldmqc, 0.0, &info->yqc, _state);
    for(i=0; i<=info->newmqc-1; i++)
    {
        info->yqc.ptr.p_double[info->unpackqcperm.ptr.p_int[i]] = lagqc->ptr.p_double[i];
    }
    
    /*
     * Apply reverse transformation
     */
    lpqppresolve_presolverrestoresolution(&info->trfstack, &info->x1, &info->bc1, &info->y1, &info->yqc, &info->s1, needstats, info->eps, _state);
    
    /*
     * Polish X according to box constraints and info from newly recovered Stats[]
     * Recompute coefficients corresponding to variables fixed during presolve using residual costs.
     */
    for(i=0; i<=info->oldn-1; i++)
    {
        if( ae_isfinite(info->rawbndl.ptr.p_double[i], _state) )
        {
            info->x1.ptr.p_double[i] = ae_maxreal(info->x1.ptr.p_double[i], info->rawbndl.ptr.p_double[i], _state);
        }
        if( ae_isfinite(info->rawbndu.ptr.p_double[i], _state) )
        {
            info->x1.ptr.p_double[i] = ae_minreal(info->x1.ptr.p_double[i], info->rawbndu.ptr.p_double[i], _state);
        }
        if( needstats )
        {
            if( info->s1.ptr.p_int[i]<0 )
            {
                info->x1.ptr.p_double[i] = info->rawbndl.ptr.p_double[i];
            }
            if( info->s1.ptr.p_int[i]>0 )
            {
                info->x1.ptr.p_double[i] = info->rawbndu.ptr.p_double[i];
            }
        }
    }
    rcopyallocv(info->oldn, &info->rawc, &info->d, _state);
    if( info->oldm>0 )
    {
        sparsegemv(&info->rawa, 1.0, 1, &info->y1, 0, 1.0, &info->d, 0, _state);
    }
    for(i=0; i<=info->oldn-1; i++)
    {
        if( info->lagrangefromresidual.ptr.p_bool[i] )
        {
            info->bc1.ptr.p_double[i] = -info->d.ptr.p_double[i];
        }
    }
    
    /*
     * Output
     */
    rcopyallocv(info->oldn, &info->x1, x, _state);
    rcopyallocv(info->oldmqc, &info->yqc, lagqc, _state);
    rcopyallocv(info->oldm, &info->y1, laglc, _state);
    if( needstats )
    {
        icopyallocv(info->oldn+info->oldm, &info->s1, stats, _state);
    }
    rcopyallocv(info->oldn, &info->bc1, lagbc, _state);
}


/*************************************************************************
Returns true if both bounds are finite and the difference is less than Eps.

Used to provide consistent way of checking for fixed vars across the unit.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static ae_bool lpqppresolve_isfixed(double bndl,
     double bndu,
     double eps,
     ae_state *_state)
{
    ae_bool result;


    result = (ae_isfinite(bndl, _state)&&ae_isfinite(bndu, _state))&&ae_fp_greater_eq(bndl,bndu-eps);
    return result;
}


/*************************************************************************
Initializes empty dynamic CRS matrix with either M or N equal to zero.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrsinitempty(dynamiccrs* r,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state)
{


    ae_assert(n*m==0, "DynCRSInitEmpty: M*N<>0", _state);
    r->m = m;
    r->n = n;
    if( m>0 )
    {
        isetallocv(m, 0, &r->rowbegin, _state);
        isetallocv(m, 0, &r->rowend, _state);
    }
    r->maxallocated = 0;
}


/*************************************************************************
Copies dynamic CRS matrix

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrscopy(const dynamiccrs* src,
     dynamiccrs* dst,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t m;
    ae_int_t n;
    ae_int_t rowsize;


    m = src->m;
    n = src->n;
    dst->m = m;
    dst->n = n;
    dst->maxallocated = 0;
    iallocv(m, &dst->rowbegin, _state);
    iallocv(m, &dst->rowend, _state);
    for(i=0; i<=m-1; i++)
    {
        rowsize = src->rowend.ptr.p_int[i]-src->rowbegin.ptr.p_int[i];
        dst->rowbegin.ptr.p_int[i] = dst->maxallocated;
        dst->rowend.ptr.p_int[i] = dst->maxallocated+rowsize;
        dst->maxallocated = dst->rowend.ptr.p_int[i];
        igrowv(dst->rowend.ptr.p_int[i], &dst->idx, _state);
        rgrowv(dst->rowend.ptr.p_int[i], &dst->vals, _state);
        icopyvx(rowsize, &src->idx, src->rowbegin.ptr.p_int[i], &dst->idx, dst->rowbegin.ptr.p_int[i], _state);
        rcopyvx(rowsize, &src->vals, src->rowbegin.ptr.p_int[i], &dst->vals, dst->rowbegin.ptr.p_int[i], _state);
    }
}


/*************************************************************************
Copies DynCRS matrix with deletion of row/column at position DeletePos
(the matrix must be square), with 0<=DeletePos<N/M. Cols/rows at the
position DeletePos and past it are shifted to the left. If N=M=1, then an
empty matrix is correctly created.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrscopydeleterowcol(const dynamiccrs* src,
     ae_int_t deletepos,
     dynamiccrs* dst,
     ae_state *_state)
{
    ae_int_t isrc;
    ae_int_t idst;
    ae_int_t m;
    ae_int_t n;
    ae_int_t maxrowsize;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t j;


    m = src->m;
    n = src->n;
    ae_assert(m==n, "DynCRSCopyDeleteRowCol: non-square input matrix", _state);
    ae_assert(0<=deletepos&&deletepos<n, "DynCRSCopyDeleteRowCol: incorrect DeletePos", _state);
    dst->m = m-1;
    dst->n = n-1;
    dst->maxallocated = 0;
    iallocv(m-1, &dst->rowbegin, _state);
    iallocv(m-1, &dst->rowend, _state);
    idst = 0;
    for(isrc=0; isrc<=m-1; isrc++)
    {
        if( isrc!=deletepos )
        {
            maxrowsize = src->rowend.ptr.p_int[isrc]-src->rowbegin.ptr.p_int[isrc];
            dst->rowbegin.ptr.p_int[idst] = dst->maxallocated;
            igrowv(dst->maxallocated+maxrowsize, &dst->idx, _state);
            rgrowv(dst->maxallocated+maxrowsize, &dst->vals, _state);
            j0 = src->rowbegin.ptr.p_int[isrc];
            j1 = src->rowend.ptr.p_int[isrc]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                if( src->idx.ptr.p_int[jj]!=deletepos )
                {
                    j = src->idx.ptr.p_int[jj];
                    if( j>deletepos )
                    {
                        j = j-1;
                    }
                    dst->idx.ptr.p_int[dst->maxallocated] = j;
                    dst->vals.ptr.p_double[dst->maxallocated] = src->vals.ptr.p_double[jj];
                    dst->maxallocated = dst->maxallocated+1;
                }
            }
            dst->rowend.ptr.p_int[idst] = dst->maxallocated;
            idst = idst+1;
        }
    }
}


/*************************************************************************
Copies DynCRS matrix with deletion of rows/columns at positions in DeleteSet
(the matrix must be square). Cols/rows are shifted to the left. The matrix
must be a square one.

Correctly creates an empty matrix if all rows/cols are removed.

Buf must be a preallocated array, with at least Src.N elements.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrscopydeleterowscols(const dynamiccrs* src,
     const niset* deleteset,
     dynamiccrs* dst,
     /* Integer */ ae_vector* buf,
     ae_state *_state)
{
    ae_int_t isrc;
    ae_int_t idst;
    ae_int_t m;
    ae_int_t n;
    ae_int_t maxrowsize;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t j;


    m = src->m;
    n = src->n;
    
    /*
     * Integrity checks
     */
    ae_assert(m==n, "DynCRSCopyDeleteRowsCols: non-square input matrix", _state);
    ae_assert(buf->cnt>=n, "DynCRSCopyDeleteRowsCols: buf[] array is too short", _state);
    ae_assert(0<=deleteset->nstored&&deleteset->nstored<=n, "DynCRSCopyDeleteRowsCols: incorrect DeleteSet size", _state);
    for(j=0; j<=deleteset->nstored-1; j++)
    {
        ae_assert(deleteset->items.ptr.p_int[j]>=0&&deleteset->items.ptr.p_int[j]<n, "DynCRSCopyDeleteRowsCols: items in DeleteSet are outside of [0,N) range", _state);
    }
    
    /*
     * Prepare mapping for column indexes
     */
    idst = 0;
    for(j=0; j<=n-1; j++)
    {
        if( deleteset->locationof.ptr.p_int[j]<0 )
        {
            buf->ptr.p_int[j] = idst;
            idst = idst+1;
        }
        else
        {
            buf->ptr.p_int[j] = -999999;
        }
    }
    
    /*
     * Copy/convert matrix
     */
    dst->m = m-deleteset->nstored;
    dst->n = n-deleteset->nstored;
    dst->maxallocated = 0;
    iallocv(dst->m, &dst->rowbegin, _state);
    iallocv(dst->m, &dst->rowend, _state);
    idst = 0;
    for(isrc=0; isrc<=m-1; isrc++)
    {
        if( deleteset->locationof.ptr.p_int[isrc]<0 )
        {
            maxrowsize = src->rowend.ptr.p_int[isrc]-src->rowbegin.ptr.p_int[isrc];
            dst->rowbegin.ptr.p_int[idst] = dst->maxallocated;
            igrowv(dst->maxallocated+maxrowsize, &dst->idx, _state);
            rgrowv(dst->maxallocated+maxrowsize, &dst->vals, _state);
            j0 = src->rowbegin.ptr.p_int[isrc];
            j1 = src->rowend.ptr.p_int[isrc]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                if( deleteset->locationof.ptr.p_int[src->idx.ptr.p_int[jj]]<0 )
                {
                    j = src->idx.ptr.p_int[jj];
                    j = buf->ptr.p_int[j];
                    dst->idx.ptr.p_int[dst->maxallocated] = j;
                    dst->vals.ptr.p_double[dst->maxallocated] = src->vals.ptr.p_double[jj];
                    dst->maxallocated = dst->maxallocated+1;
                }
            }
            dst->rowend.ptr.p_int[idst] = dst->maxallocated;
            idst = idst+1;
        }
    }
}


/*************************************************************************
Copies DynCRS matrix with insertion of zero row/column at position InsertPos
(the matrix must be square), with 0<=InsertPos<=N/M. Cols/rows at the
position InsertPos and past it are shifted to the right. If N=M=1, then an
empty matrix is correctly created.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrscopyinsertrowcol(const dynamiccrs* src,
     ae_int_t insertpos,
     dynamiccrs* dst,
     ae_state *_state)
{
    ae_int_t isrc;
    ae_int_t idst;
    ae_int_t m;
    ae_int_t n;
    ae_int_t maxrowsize;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t j;


    m = src->m;
    n = src->n;
    ae_assert(m==n, "DynCRSCopyInsertRowCol: non-square input matrix", _state);
    ae_assert(0<=insertpos&&insertpos<=n, "DynCRSCopyInsertRowCol: incorrect InsertPos", _state);
    dst->m = m+1;
    dst->n = n+1;
    dst->maxallocated = 0;
    iallocv(m+1, &dst->rowbegin, _state);
    iallocv(m+1, &dst->rowend, _state);
    idst = 0;
    for(isrc=0; isrc<=m; isrc++)
    {
        if( isrc==insertpos )
        {
            dst->rowbegin.ptr.p_int[idst] = dst->maxallocated;
            igrowv(dst->maxallocated+1, &dst->idx, _state);
            rgrowv(dst->maxallocated+1, &dst->vals, _state);
            dst->idx.ptr.p_int[dst->maxallocated] = idst;
            dst->vals.ptr.p_double[dst->maxallocated] = 0.0;
            dst->maxallocated = dst->maxallocated+1;
            dst->rowend.ptr.p_int[idst] = dst->maxallocated;
            idst = idst+1;
        }
        if( isrc<m )
        {
            maxrowsize = src->rowend.ptr.p_int[isrc]-src->rowbegin.ptr.p_int[isrc];
            dst->rowbegin.ptr.p_int[idst] = dst->maxallocated;
            igrowv(dst->maxallocated+maxrowsize, &dst->idx, _state);
            rgrowv(dst->maxallocated+maxrowsize, &dst->vals, _state);
            j0 = src->rowbegin.ptr.p_int[isrc];
            j1 = src->rowend.ptr.p_int[isrc]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                j = src->idx.ptr.p_int[jj];
                if( j>=insertpos )
                {
                    j = j+1;
                }
                dst->idx.ptr.p_int[dst->maxallocated] = j;
                dst->vals.ptr.p_double[dst->maxallocated] = src->vals.ptr.p_double[jj];
                dst->maxallocated = dst->maxallocated+1;
            }
            dst->rowend.ptr.p_int[idst] = dst->maxallocated;
            idst = idst+1;
        }
    }
}


/*************************************************************************
Initialize dynamic CRS matrix using SparseMatrix structure

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrsinitfromsparsecrs(const sparsematrix* s,
     dynamiccrs* r,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t n;


    m = s->m;
    n = s->n;
    ae_assert(s->matrixtype==1, "DynCRSInitFromSparseCRS: S is not CRS matrix", _state);
    r->m = m;
    r->n = n;
    icopyallocv(s->ridx.ptr.p_int[m], &s->idx, &r->idx, _state);
    rcopyallocv(s->ridx.ptr.p_int[m], &s->vals, &r->vals, _state);
    iallocv(m, &r->rowbegin, _state);
    iallocv(m, &r->rowend, _state);
    icopyvx(m, &s->ridx, 0, &r->rowbegin, 0, _state);
    icopyvx(m, &s->ridx, 1, &r->rowend, 0, _state);
    r->maxallocated = r->rowend.ptr.p_int[m-1];
}


/*************************************************************************
Drops numerical zeros from the matrix

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrsdropzeros(dynamiccrs* a, ae_state *_state)
{
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t offs;
    double v;


    m = a->m;
    for(i=0; i<=m-1; i++)
    {
        offs = a->rowbegin.ptr.p_int[i];
        for(jj=a->rowbegin.ptr.p_int[i]; jj<=a->rowend.ptr.p_int[i]-1; jj++)
        {
            j = a->idx.ptr.p_int[jj];
            v = a->vals.ptr.p_double[jj];
            if( v==(double)0 )
            {
                continue;
            }
            a->idx.ptr.p_int[offs] = j;
            a->vals.ptr.p_double[offs] = v;
            offs = offs+1;
        }
        a->rowend.ptr.p_int[i] = offs;
    }
}


/*************************************************************************
Drops offdiagonal numerical zeros from the matrix

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrsdropoffdiagonalzeros(dynamiccrs* a,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t offs;
    double v;


    m = a->m;
    for(i=0; i<=m-1; i++)
    {
        offs = a->rowbegin.ptr.p_int[i];
        for(jj=a->rowbegin.ptr.p_int[i]; jj<=a->rowend.ptr.p_int[i]-1; jj++)
        {
            j = a->idx.ptr.p_int[jj];
            v = a->vals.ptr.p_double[jj];
            if( j!=i&&v==(double)0 )
            {
                continue;
            }
            a->idx.ptr.p_int[offs] = j;
            a->vals.ptr.p_double[offs] = v;
            offs = offs+1;
        }
        a->rowend.ptr.p_int[i] = offs;
    }
}


/*************************************************************************
Removes rows

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrsclearrow(dynamiccrs* a,
     ae_int_t rowidx,
     ae_state *_state)
{


    a->rowend.ptr.p_int[rowidx] = a->rowbegin.ptr.p_int[rowidx];
}


/*************************************************************************
Removes element from the row

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrsremovefromrow(dynamiccrs* a,
     ae_int_t rowidx,
     ae_int_t j,
     ae_state *_state)
{
    ae_int_t ii;
    ae_int_t offs;


    offs = a->rowbegin.ptr.p_int[rowidx];
    for(ii=a->rowbegin.ptr.p_int[rowidx]; ii<=a->rowend.ptr.p_int[rowidx]-1; ii++)
    {
        if( a->idx.ptr.p_int[ii]!=j )
        {
            a->idx.ptr.p_int[offs] = a->idx.ptr.p_int[ii];
            a->vals.ptr.p_double[offs] = a->vals.ptr.p_double[ii];
            offs = offs+1;
        }
    }
    a->rowend.ptr.p_int[rowidx] = offs;
}


/*************************************************************************
Factors out element J from the row RowIdx by means of transformation

    X[J] = C0+C1*X[K].
    
Depending on context, can be used to factor out elements from individual
rows of a quadratic matrix H (in this case C0 can be zero because it does
not actually used), or to factor out elements from the linear constraints
matrix.

The function returns the value of new element A[RowIdx,K]. A zero can be
returned, e.g. if cancellation or nearly-cancellation has occurred.

When used to process linear constraints, it also returns a shift that should be applied to row bounds,
which is equal to C0*A[I,J]. Correctly handles the situation when factoring
an element out results in a cancellation: if CheckCancellation is True,
it removes canceled element from the row.

When no element J is present in the row RowIdx, does nothing and returns zero.


  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrsfactoroutfromrow(dynamiccrs* a,
     ae_int_t rowidx,
     ae_int_t j,
     double c0,
     double c1,
     ae_int_t k,
     ae_bool checkcancellation,
     double* newval,
     double* shift,
     ae_state *_state)
{
    ae_int_t ii;
    ae_int_t pos0;
    ae_int_t pos1;
    ae_int_t i;
    ae_int_t i0;
    ae_int_t i1;
    double eps;
    double mx;
    double v;

    *newval = 0.0;
    *shift = 0.0;

    eps = (double)10*ae_machineepsilon;
    *newval = 0.0;
    *shift = 0.0;
    
    /*
     * Find elements J and K in the array.
     * Return if there is no J.
     * Compute result.
     */
    pos0 = a->rowend.ptr.p_int[rowidx];
    pos1 = a->rowend.ptr.p_int[rowidx];
    for(ii=a->rowbegin.ptr.p_int[rowidx]; ii<=a->rowend.ptr.p_int[rowidx]-1; ii++)
    {
        if( a->idx.ptr.p_int[ii]==j )
        {
            pos0 = ii;
        }
        if( a->idx.ptr.p_int[ii]==k )
        {
            pos1 = ii;
        }
    }
    if( pos0==a->rowend.ptr.p_int[rowidx] )
    {
        return;
    }
    *shift = a->vals.ptr.p_double[pos0]*c0;
    
    /*
     * Element J is present, what about K?
     */
    if( pos1!=a->rowend.ptr.p_int[rowidx] )
    {
        
        /*
         * If both elements are present, add J to K, remove J, check for cancellation
         */
        mx = ae_maxreal(ae_fabs(a->vals.ptr.p_double[pos0], _state), ae_fabs(a->vals.ptr.p_double[pos1], _state), _state);
        v = a->vals.ptr.p_double[pos1]+a->vals.ptr.p_double[pos0]*c1;
        a->vals.ptr.p_double[pos1] = v;
        *newval = v;
        lpqppresolve_dyncrsremovefromrow(a, rowidx, j, _state);
        if( checkcancellation&&ae_fp_less_eq(ae_fabs(v, _state),eps*mx) )
        {
            lpqppresolve_dyncrsremovefromrow(a, rowidx, k, _state);
            *newval = 0.0;
        }
    }
    else
    {
        
        /*
         * If only J is present, then perform inplace modification then run a bubble sort
         */
        v = a->vals.ptr.p_double[pos0]*c1;
        a->idx.ptr.p_int[pos0] = k;
        a->vals.ptr.p_double[pos0] = v;
        *newval = v;
        if( k>j )
        {
            i0 = pos0;
            i1 = a->rowend.ptr.p_int[rowidx]-1;
            while(i0<i1)
            {
                i = a->idx.ptr.p_int[i0+1];
                if( i>k )
                {
                    break;
                }
                a->idx.ptr.p_int[i0] = i;
                a->vals.ptr.p_double[i0] = a->vals.ptr.p_double[i0+1];
                a->idx.ptr.p_int[i0+1] = k;
                a->vals.ptr.p_double[i0+1] = v;
                i0 = i0+1;
            }
        }
        else
        {
            i0 = pos0;
            i1 = a->rowbegin.ptr.p_int[rowidx];
            while(i0>i1)
            {
                i = a->idx.ptr.p_int[i0-1];
                if( i<k )
                {
                    break;
                }
                a->idx.ptr.p_int[i0] = i;
                a->vals.ptr.p_double[i0] = a->vals.ptr.p_double[i0-1];
                a->idx.ptr.p_int[i0-1] = k;
                a->vals.ptr.p_double[i0-1] = v;
                i0 = i0-1;
            }
        }
    }
}


/*************************************************************************
Adds Row0 times C to Row1. If RetainDiagonal is True, never removes diagonal
elements from Row1 even if cancellation is present


  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrsaddrowto(dynamiccrs* a,
     ae_int_t row0,
     double c,
     ae_int_t row1,
     ae_bool retaindiagonal,
     ae_bool checkcancellation,
     ae_state *_state)
{
    ae_int_t begin0;
    ae_int_t begin1;
    ae_int_t end0;
    ae_int_t end1;
    ae_int_t i0;
    ae_int_t i1;
    double v0;
    double v1;
    double mx;
    ae_int_t beginr;
    ae_int_t endr;
    double eps;


    eps = (double)10*ae_machineepsilon;
    
    /*
     * Analyze inputs, allocate place for output
     */
    if( ae_fp_eq(c,0.0) )
    {
        return;
    }
    begin0 = a->rowbegin.ptr.p_int[row0];
    begin1 = a->rowbegin.ptr.p_int[row1];
    end0 = a->rowend.ptr.p_int[row0];
    end1 = a->rowend.ptr.p_int[row1];
    if( begin0==end0 )
    {
        return;
    }
    igrowv(a->maxallocated+(end0-begin0)+(end1-begin1), &a->idx, _state);
    rgrowv(a->maxallocated+(end0-begin0)+(end1-begin1), &a->vals, _state);
    beginr = a->maxallocated;
    endr = beginr;
    while(begin0<end0&&begin1<end1)
    {
        i0 = a->idx.ptr.p_int[begin0];
        i1 = a->idx.ptr.p_int[begin1];
        if( i0<i1 )
        {
            a->idx.ptr.p_int[endr] = i0;
            a->vals.ptr.p_double[endr] = c*a->vals.ptr.p_double[begin0];
            endr = endr+1;
            begin0 = begin0+1;
            continue;
        }
        if( i1<i0 )
        {
            a->idx.ptr.p_int[endr] = i1;
            a->vals.ptr.p_double[endr] = a->vals.ptr.p_double[begin1];
            endr = endr+1;
            begin1 = begin1+1;
            continue;
        }
        v0 = a->vals.ptr.p_double[begin0];
        v1 = a->vals.ptr.p_double[begin1];
        if( checkcancellation )
        {
            mx = ae_maxreal(ae_fabs(v0, _state), ae_fabs(v1, _state), _state);
            if( (!retaindiagonal||i1!=row1)&&(ae_fp_eq(mx,(double)(0))||ae_fabs(c*v0+v1, _state)<=eps*mx) )
            {
                
                /*
                 * Do not output zero elements resulting from cancellation.
                 */
                begin0 = begin0+1;
                begin1 = begin1+1;
                continue;
            }
        }
        a->idx.ptr.p_int[endr] = i1;
        a->vals.ptr.p_double[endr] = c*v0+v1;
        endr = endr+1;
        begin0 = begin0+1;
        begin1 = begin1+1;
    }
    while(begin0<end0)
    {
        a->idx.ptr.p_int[endr] = a->idx.ptr.p_int[begin0];
        a->vals.ptr.p_double[endr] = c*a->vals.ptr.p_double[begin0];
        endr = endr+1;
        begin0 = begin0+1;
    }
    while(begin1<end1)
    {
        a->idx.ptr.p_int[endr] = a->idx.ptr.p_int[begin1];
        a->vals.ptr.p_double[endr] = a->vals.ptr.p_double[begin1];
        endr = endr+1;
        begin1 = begin1+1;
    }
    a->maxallocated = endr;
    a->rowbegin.ptr.p_int[row1] = beginr;
    a->rowend.ptr.p_int[row1] = endr;
}


/*************************************************************************
Inserts sparse vector into the row, rewriting existing elements by new values.
If a new value is zero, the element (if exists) is removed from the target
row.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrsinsertwithrewrite(dynamiccrs* a,
     ae_int_t tgtrow,
     /* Integer */ const ae_vector* idx,
     /* Real    */ const ae_vector* vals,
     ae_int_t cnt,
     ae_state *_state)
{
    ae_int_t begins;
    ae_int_t ends;
    ae_int_t begini;
    ae_int_t i0;
    ae_int_t i1;
    ae_int_t beginr;
    ae_int_t endr;


    
    /*
     * Analyze inputs, allocate place for output
     */
    if( cnt==0 )
    {
        return;
    }
    begins = a->rowbegin.ptr.p_int[tgtrow];
    ends = a->rowend.ptr.p_int[tgtrow];
    igrowv(a->maxallocated+(ends-begins)+cnt, &a->idx, _state);
    rgrowv(a->maxallocated+(ends-begins)+cnt, &a->vals, _state);
    beginr = a->maxallocated;
    endr = beginr;
    begini = 0;
    while(begins<ends&&begini<cnt)
    {
        i0 = a->idx.ptr.p_int[begins];
        i1 = idx->ptr.p_int[begini];
        if( i0<i1 )
        {
            a->idx.ptr.p_int[endr] = i0;
            a->vals.ptr.p_double[endr] = a->vals.ptr.p_double[begins];
            endr = endr+1;
            begins = begins+1;
            continue;
        }
        if( i1<i0 )
        {
            if( vals->ptr.p_double[begini]!=0.0 )
            {
                a->idx.ptr.p_int[endr] = i1;
                a->vals.ptr.p_double[endr] = vals->ptr.p_double[begini];
                endr = endr+1;
            }
            begini = begini+1;
            continue;
        }
        if( vals->ptr.p_double[begini]!=0.0 )
        {
            a->idx.ptr.p_int[endr] = i1;
            a->vals.ptr.p_double[endr] = vals->ptr.p_double[begini];
            endr = endr+1;
        }
        begins = begins+1;
        begini = begini+1;
    }
    while(begins<ends)
    {
        a->idx.ptr.p_int[endr] = a->idx.ptr.p_int[begins];
        a->vals.ptr.p_double[endr] = a->vals.ptr.p_double[begins];
        endr = endr+1;
        begins = begins+1;
    }
    while(begini<cnt)
    {
        if( vals->ptr.p_double[begini]!=0.0 )
        {
            a->idx.ptr.p_int[endr] = idx->ptr.p_int[begini];
            a->vals.ptr.p_double[endr] = vals->ptr.p_double[begini];
            endr = endr+1;
        }
        begini = begini+1;
    }
    a->maxallocated = endr;
    a->rowbegin.ptr.p_int[tgtrow] = beginr;
    a->rowend.ptr.p_int[tgtrow] = endr;
}


/*************************************************************************
Removes elements with indexes in the set from the row

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrsremovesetfromrow(dynamiccrs* a,
     ae_int_t rowidx,
     const niset* s,
     ae_state *_state)
{
    ae_int_t ii;
    ae_int_t offs;


    offs = a->rowbegin.ptr.p_int[rowidx];
    for(ii=a->rowbegin.ptr.p_int[rowidx]; ii<=a->rowend.ptr.p_int[rowidx]-1; ii++)
    {
        if( s->locationof.ptr.p_int[a->idx.ptr.p_int[ii]]<0 )
        {
            a->idx.ptr.p_int[offs] = a->idx.ptr.p_int[ii];
            a->vals.ptr.p_double[offs] = a->vals.ptr.p_double[ii];
            offs = offs+1;
        }
    }
    a->rowend.ptr.p_int[rowidx] = offs;
}


/*************************************************************************
Get element that it known to exist in the matrix. If it does not, the
function fails with an exception. O(log(row_size)) running time.


  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static double lpqppresolve_dyncrsgetexistingelement(dynamiccrs* a,
     ae_int_t i,
     ae_int_t j,
     ae_state *_state)
{
    ae_int_t k;
    double result;


    k = ibinarysearchlft(&a->idx, a->rowbegin.ptr.p_int[i], a->rowend.ptr.p_int[i], j, _state);
    ae_assert(k<a->rowend.ptr.p_int[i]&&a->idx.ptr.p_int[k]==j, "DynCRSGetExistingElement: element not found", _state);
    result = a->vals.ptr.p_double[k];
    return result;
}


/*************************************************************************
Get element, return zero for non-existent element.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static double lpqppresolve_dyncrsgetelementifexists(dynamiccrs* a,
     ae_int_t i,
     ae_int_t j,
     ae_state *_state)
{
    ae_int_t k;
    double result;


    k = ibinarysearchlft(&a->idx, a->rowbegin.ptr.p_int[i], a->rowend.ptr.p_int[i], j, _state);
    result = (double)(0);
    if( k<a->rowend.ptr.p_int[i]&&a->idx.ptr.p_int[k]==j )
    {
        result = a->vals.ptr.p_double[k];
    }
    return result;
}


/*************************************************************************
Sets an element that it known to exist in the matrix. If it does not, the
function fails with an exception. O(log(row_size)) running time.


  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dyncrssetexistingelement(dynamiccrs* a,
     ae_int_t i,
     ae_int_t j,
     double v,
     ae_state *_state)
{
    ae_int_t k;


    k = ibinarysearchlft(&a->idx, a->rowbegin.ptr.p_int[i], a->rowend.ptr.p_int[i], j, _state);
    ae_assert(k<a->rowend.ptr.p_int[i]&&a->idx.ptr.p_int[k]==j, "DynCRSSetExistingElement: element not found", _state);
    a->vals.ptr.p_double[k] = v;
}


/*************************************************************************
Initialize dynamic-CRS-based quadratic constraints from the standard ones

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dynqcinitfromxqc(xquadraticconstraints* src,
     dynamiccrsqconstraints* dst,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t cnt;
    ae_int_t nv;
    xquadraticconstraint *qcsrc;
    ae_smart_ptr _qcsrc;
    dynamiccrsqconstraint *qcdst;
    ae_smart_ptr _qcdst;
    sparsematrix fullq;

    ae_frame_make(_state, &_frame_block);
    memset(&_qcsrc, 0, sizeof(_qcsrc));
    memset(&_qcdst, 0, sizeof(_qcdst));
    memset(&fullq, 0, sizeof(fullq));
    ae_smart_ptr_init(&_qcsrc, (void**)&qcsrc, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_qcdst, (void**)&qcdst, ae_false, _state, ae_true);
    _sparsematrix_init(&fullq, _state, ae_true);

    cnt = xqcgetcount(src, _state);
    ae_obj_array_clear(&dst->constraints);
    for(i=0; i<=cnt-1; i++)
    {
        ae_obj_array_get(&src->constraints, i, &_qcsrc, _state);
        qcdst = (dynamiccrsqconstraint*)ae_malloc(sizeof(dynamiccrsqconstraint), _state); /* note: using qcdst as a temporary prior to assigning its value to _qcdst */
        memset(qcdst, 0, sizeof(dynamiccrsqconstraint));
        _dynamiccrsqconstraint_init(qcdst, _state, ae_false);
        ae_smart_ptr_assign(&_qcdst, qcdst, ae_true, ae_true, (ae_int_t)sizeof(dynamiccrsqconstraint), _dynamiccrsqconstraint_init_copy, _dynamiccrsqconstraint_destroy);
        nv = qcsrc->nvars;
        qcdst->nvars = nv;
        qcdst->cl = qcsrc->cl;
        qcdst->cu = qcsrc->cu;
        qcdst->applyorigin = qcsrc->applyorigin;
        if( nv>0 )
        {
            icopyallocv(nv, &qcsrc->varidx, &qcdst->varidx, _state);
            rcopyallocv(nv, &qcsrc->b, &qcdst->b, _state);
            lpqppresolve_copyexpandh(&qcsrc->lowerq, ae_false, &fullq, _state);
            lpqppresolve_dyncrsinitfromsparsecrs(&fullq, &qcdst->fullq, _state);
        }
        ae_obj_array_append_transfer(&dst->constraints, &_qcdst, _state);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Copy structure

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dynqccopy(const dynamiccrsqconstraint* src,
     dynamiccrsqconstraint* dst,
     ae_state *_state)
{


    dst->nvars = src->nvars;
    icopyallocv(src->nvars, &src->varidx, &dst->varidx, _state);
    rcopyallocv(src->nvars, &src->b, &dst->b, _state);
    lpqppresolve_dyncrscopy(&src->fullq, &dst->fullq, _state);
    dst->cl = src->cl;
    dst->cu = src->cu;
    dst->applyorigin = src->applyorigin;
}


/*************************************************************************
Copies quadratic constraint with insertion of variable VIdx into  position
InsertPos, with 0<=InsertPos<=NVars. Variables at position  InsertPos  and
past it are shifted to the right.

The function checks that Src.VarIdx[InsertPos-1]<VIdx<Src.VarIdx[InsertPos].

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dynqccopyinsertrowcol(dynamiccrsqconstraint* src,
     ae_int_t vidx,
     ae_int_t insertpos,
     dynamiccrsqconstraint* dst,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t offs;


    ae_assert(insertpos>=0&&insertpos<=src->nvars, "dynqcCopyInsertRowCol: incorrect InsertPos", _state);
    ae_assert(insertpos==0||src->varidx.ptr.p_int[insertpos-1]<vidx, "dynqcCopyInsertRowCol: VIdx is out of order (left)", _state);
    ae_assert(insertpos==src->nvars||src->varidx.ptr.p_int[insertpos]>vidx, "dynqcCopyInsertRowCol: VIdx is out of order (right)", _state);
    dst->nvars = src->nvars+1;
    dst->cl = src->cl;
    dst->cu = src->cu;
    dst->applyorigin = src->applyorigin;
    iallocv(dst->nvars, &dst->varidx, _state);
    rallocv(dst->nvars, &dst->b, _state);
    offs = 0;
    for(i=0; i<=src->nvars; i++)
    {
        if( i==insertpos )
        {
            dst->varidx.ptr.p_int[offs] = vidx;
            dst->b.ptr.p_double[offs] = 0.0;
            offs = offs+1;
        }
        if( i<src->nvars )
        {
            dst->varidx.ptr.p_int[offs] = src->varidx.ptr.p_int[i];
            dst->b.ptr.p_double[offs] = src->b.ptr.p_double[i];
            offs = offs+1;
        }
    }
    ae_assert(offs==dst->nvars, "PRESOLVER: integrity check 4228 failed", _state);
    lpqppresolve_dyncrscopyinsertrowcol(&src->fullq, insertpos, &dst->fullq, _state);
}


/*************************************************************************
Copies quadratic constraint with deletion of variable at position DeletePos,
with 0<=DeletePos<NVars. Variables at the position DeletePos and  past  it
are shifted to the left. If NVars=1, then an empty quadratic constraint is
correctly created.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dynqccopydeleterowcol(const dynamiccrsqconstraint* src,
     ae_int_t deletepos,
     dynamiccrsqconstraint* dst,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t offs;


    ae_assert(deletepos>=0&&deletepos<src->nvars, "dynqcCopyDeleteRowCol: incorrect DeletePos", _state);
    dst->nvars = src->nvars-1;
    dst->cl = src->cl;
    dst->cu = src->cu;
    dst->applyorigin = src->applyorigin;
    if( dst->nvars==0 )
    {
        dst->fullq.n = 0;
        dst->fullq.m = 0;
        return;
    }
    iallocv(dst->nvars, &dst->varidx, _state);
    rallocv(dst->nvars, &dst->b, _state);
    offs = 0;
    for(i=0; i<=src->nvars-1; i++)
    {
        if( i!=deletepos )
        {
            dst->varidx.ptr.p_int[offs] = src->varidx.ptr.p_int[i];
            dst->b.ptr.p_double[offs] = src->b.ptr.p_double[i];
            offs = offs+1;
        }
    }
    lpqppresolve_dyncrscopydeleterowcol(&src->fullq, deletepos, &dst->fullq, _state);
}


/*************************************************************************
Copies quadratic constraint with deletion of  variables  DeleteSet  and  a
corresponding shifts to the left in variables indexes.

If NVars=|DeleteSet|, then an empty quadratic constraint is correctly created.

Buf must be a preallocated array, with at least Src.NVars elements.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dynqccopydeleterowscols(const dynamiccrsqconstraint* src,
     const niset* deleteset,
     dynamiccrsqconstraint* dst,
     /* Integer */ ae_vector* buf,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t offs;


    ae_assert(buf->cnt>=src->nvars, "dynqcCopyDeleteRowsCols: buf is too short", _state);
    ae_assert(deleteset->nstored>=0&&deleteset->nstored<=src->nvars, "dynqcCopyDeleteRowsCols: incorrect DeletePos", _state);
    for(i=0; i<=deleteset->nstored-1; i++)
    {
        ae_assert(deleteset->items.ptr.p_int[i]>=0&&deleteset->items.ptr.p_int[i]<src->nvars, "dynqcCopyDeleteRowsCols: items in DeleteSet are outside of [0,NVars) range", _state);
    }
    dst->nvars = src->nvars-deleteset->nstored;
    dst->cl = src->cl;
    dst->cu = src->cu;
    dst->applyorigin = src->applyorigin;
    if( dst->nvars==0 )
    {
        dst->fullq.n = 0;
        dst->fullq.m = 0;
        return;
    }
    iallocv(dst->nvars, &dst->varidx, _state);
    rallocv(dst->nvars, &dst->b, _state);
    offs = 0;
    for(i=0; i<=src->nvars-1; i++)
    {
        if( deleteset->locationof.ptr.p_int[i]<0 )
        {
            dst->varidx.ptr.p_int[offs] = src->varidx.ptr.p_int[i];
            dst->b.ptr.p_double[offs] = src->b.ptr.p_double[i];
            offs = offs+1;
        }
    }
    lpqppresolve_dyncrscopydeleterowscols(&src->fullq, deleteset, &dst->fullq, buf, _state);
}


/*************************************************************************
Factors out variable K0 from a  quadratic  form  c'x + 0.5x'Hx by making a
substitution X[k0]:=alpha0 + alpha1*X[k1]  and  correspondingly  rewriting
c and H.

Returns constant term that is produced as a result of such a transformation.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static double lpqppresolve_factoroutfromquadratic(dynamiccrs* h,
     /* Real    */ ae_vector* c,
     ae_int_t k0,
     double alpha0,
     double alpha1,
     ae_int_t k1,
     ae_state *_state)
{
    double h00;
    double h11;
    double h01;
    double v;
    double v0;
    double v1;
    ae_int_t ii;
    ae_int_t j;
    double result;


    result = (double)(0);
    
    /*
     * Modify linear term with an update to C[K1] from C[K0]
     */
    c->ptr.p_double[k1] = c->ptr.p_double[k1]+c->ptr.p_double[k0]*alpha1;
    result = result+c->ptr.p_double[k0]*alpha0;
    c->ptr.p_double[k0] = (double)(0);
    
    /*
     * Handle updates coming from the quadratic term
     *
     * Analyze how 2x2 quadratic [h00 h01 ; h10 h11] changes when we
     * substitute x0 := alpha0 + alpha1*x1.
     *
     * We have
     *
     *     h00*(alpha0+alpha1*x1)^2 + 2*h01*(alpha0+alpha1*x1)*x1 + h11*x1^2 =
     *     h00*alpha0^2 + 2*h00*alpha0*alpha1*x1 + h00*alpha1^2*x1^2   +   2*h01*alpha0*x1+2*h01*alpha1*x1^2 + h11*x1^2 =
     *     h00*alpha0^2 + 2*(h00*alpha0*alpha1+h01*alpha0)*x1 + (h00*alpha1^2+2*h01*alpha1+h11)*x1^2
     */
    h00 = lpqppresolve_dyncrsgetexistingelement(h, k0, k0, _state);
    h11 = lpqppresolve_dyncrsgetexistingelement(h, k1, k1, _state);
    h01 = lpqppresolve_dyncrsgetelementifexists(h, k0, k1, _state);
    
    /*
     * Handle updates coming from modification of elements of H that are outside of the
     * [k0,k1]x[k0,k1] diagonal block. The constant term in X[K0]:=Alpha0+Alpha1*X[K1]
     * updates the linear term C[], the linear term Alpha1 updates the matrix H.
     *
     * The matrix H is updates applied to its column K1, then to its row K1. The row update
     * modifies the [k0,k1]x[k0,k1] diagonal block because there is no easy way to do it
     * without touching elements that we want to process later. We handle it by saving
     * H00, H01, H11 prior to this block and by discarding and rewriting diagonal completely
     * after calling DynCRSAddRowTo().
     */
    for(ii=h->rowbegin.ptr.p_int[k0]; ii<=h->rowend.ptr.p_int[k0]-1; ii++)
    {
        j = h->idx.ptr.p_int[ii];
        if( j!=k0&&j!=k1 )
        {
            
            /*
             * Update C[] and column K1 of H.
             */
            v = h->vals.ptr.p_double[ii];
            c->ptr.p_double[j] = c->ptr.p_double[j]+v*alpha0;
            lpqppresolve_dyncrsfactoroutfromrow(h, j, k0, 0.0, alpha1, k1, ae_false, &v0, &v1, _state);
        }
    }
    lpqppresolve_dyncrsaddrowto(h, k0, alpha1, k1, ae_true, ae_false, _state);
    lpqppresolve_dyncrsclearrow(h, k0, _state);
    lpqppresolve_dyncrsremovefromrow(h, k1, k0, _state);
    
    /*
     * Handle updates coming from substituting X[K0]:=Alpha0+Alpha1*X[K1] to the
     * [k0,k1]x[k0,k1] diagonal block.
     */
    result = result+0.5*h00*alpha0*alpha0;
    c->ptr.p_double[k1] = c->ptr.p_double[k1]+(h00*alpha0*alpha1+h01*alpha0);
    lpqppresolve_dyncrssetexistingelement(h, k1, k1, h00*alpha1*alpha1+(double)2*h01*alpha1+h11, _state);
    return result;
}


/*************************************************************************
Fixed single variable at the quadratic form c'x + 0.5x'Hx and correspondingly
rewrites c and H, clearing entries (but not removing them).

Returns constant term that is produced as a result of such a transformation.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static double lpqppresolve_fixquadraticvariable(dynamiccrs* h,
     /* Real    */ ae_vector* c,
     ae_int_t fixidx,
     double fixval,
     ae_state *_state)
{
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t j;
    ae_int_t n;
    double result;


    ae_assert(h->n==h->m, "PRESOLVER: integrity check 5812 failed", _state);
    ae_assert(fixidx>=0&&fixidx<h->n, "PRESOLVER: integrity check 5813 failed", _state);
    result = (double)(0);
    n = h->n;
    if( n==0 )
    {
        return result;
    }
    
    /*
     * Modify linear term
     */
    result = result+c->ptr.p_double[fixidx]*fixval;
    c->ptr.p_double[fixidx] = 0.0;
    
    /*
     * Modify quadratic term: scan FixIdx-th row, for each off-diagonal element found
     * clear (set to zero) corresponding element from FixIdx-th column
     */
    j0 = h->rowbegin.ptr.p_int[fixidx];
    j1 = h->rowend.ptr.p_int[fixidx]-1;
    for(jj=j0; jj<=j1; jj++)
    {
        j = h->idx.ptr.p_int[jj];
        if( j!=fixidx )
        {
            
            /*
             * Offdiagonal element
             */
            c->ptr.p_double[j] = c->ptr.p_double[j]+h->vals.ptr.p_double[jj]*fixval;
            h->vals.ptr.p_double[jj] = 0.0;
            h->vals.ptr.p_double[ibinarysearchexisting(&h->idx, h->rowbegin.ptr.p_int[j], h->rowend.ptr.p_int[j], fixidx, _state)] = 0.0;
        }
        else
        {
            
            /*
             * Diagonal element
             */
            result = result+0.5*h->vals.ptr.p_double[jj]*fixval*fixval;
            h->vals.ptr.p_double[jj] = 0.0;
        }
    }
    return result;
}


/*************************************************************************
convert single quadratic constraint between formats


  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_dynqcpermtoxqc(dynamiccrsqconstraint* src,
     /* Integer */ const ae_vector* packxperm,
     /* Integer */ const ae_vector* unpackxperm,
     xquadraticconstraint* dst,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t offs;
    ae_int_t nv;
    ae_int_t jprev;


    nv = src->nvars;
    dst->nvars = nv;
    dst->cl = src->cl;
    dst->cu = src->cu;
    dst->applyorigin = src->applyorigin;
    if( nv>0 )
    {
        iallocv(nv, &dst->varidx, _state);
        for(i=0; i<=nv-1; i++)
        {
            dst->varidx.ptr.p_int[i] = packxperm->ptr.p_int[src->varidx.ptr.p_int[i]];
        }
        for(i=0; i<=nv-2; i++)
        {
            ae_assert(dst->varidx.ptr.p_int[i]<dst->varidx.ptr.p_int[i+1], "PRESOLVER: integrity check 1658 failed", _state);
        }
        rcopyallocv(nv, &src->b, &dst->b, _state);
        dst->lowerq.matrixtype = 1;
        dst->lowerq.m = nv;
        dst->lowerq.n = nv;
        iallocv(nv, &dst->lowerq.didx, _state);
        iallocv(nv, &dst->lowerq.uidx, _state);
        iallocv(nv+1, &dst->lowerq.ridx, _state);
        dst->lowerq.ridx.ptr.p_int[0] = 0;
        for(i=0; i<=nv-1; i++)
        {
            offs = dst->lowerq.ridx.ptr.p_int[i];
            igrowv(offs+nv, &dst->lowerq.idx, _state);
            rgrowv(offs+nv, &dst->lowerq.vals, _state);
            jprev = -1;
            j0 = src->fullq.rowbegin.ptr.p_int[i];
            j1 = src->fullq.rowend.ptr.p_int[i]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                j = src->fullq.idx.ptr.p_int[jj];
                if( j<=jprev )
                {
                    ae_assert(ae_false, "PRESOLVER: integrity check 381649 failed", _state);
                }
                jprev = j;
                if( j>i )
                {
                    continue;
                }
                dst->lowerq.idx.ptr.p_int[offs] = j;
                dst->lowerq.vals.ptr.p_double[offs] = src->fullq.vals.ptr.p_double[jj];
                offs = offs+1;
            }
            ae_assert(offs>dst->lowerq.ridx.ptr.p_int[i]&&dst->lowerq.idx.ptr.p_int[offs-1]==i, "PRESOLVER: integrity check 389651 failed", _state);
            dst->lowerq.didx.ptr.p_int[i] = offs-1;
            dst->lowerq.uidx.ptr.p_int[i] = offs;
            dst->lowerq.ridx.ptr.p_int[i+1] = offs;
        }
        dst->lowerq.ninitialized = dst->lowerq.ridx.ptr.p_int[nv];
    }
}


/*************************************************************************
Initialize presolver stack

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverstackinit(ae_int_t n,
     ae_int_t m,
     ae_int_t mqc,
     ae_int_t mcc,
     presolverstack* s,
     ae_state *_state)
{


    s->n = n;
    s->m = m;
    s->mqc = mqc;
    s->mcc = mcc;
    s->ntrf = 0;
    isetallocv(1, 0, &s->idataridx, _state);
    isetallocv(1, 0, &s->rdataridx, _state);
}


/*************************************************************************
Streams boolean value to the presolver stack data storage (the data
are appended to the last transform on top of the stack).

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverstreamb(presolverstack* s,
     ae_bool b,
     ae_state *_state)
{
    ae_int_t ilast;


    ilast = s->idataridx.ptr.p_int[s->ntrf];
    igrowv(ilast+1, &s->idata, _state);
    s->idata.ptr.p_int[ilast] = icase2(b, 1, 0, _state);
    s->idataridx.ptr.p_int[s->ntrf] = ilast+1;
}


/*************************************************************************
Streams integer value to the presolver stack data storage (the data
are appended to the last transform on top of the stack).

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverstreami(presolverstack* s,
     ae_int_t i,
     ae_state *_state)
{
    ae_int_t ilast;


    ilast = s->idataridx.ptr.p_int[s->ntrf];
    igrowv(ilast+1, &s->idata, _state);
    s->idata.ptr.p_int[ilast] = i;
    s->idataridx.ptr.p_int[s->ntrf] = ilast+1;
}


/*************************************************************************
Streams real value to the presolver stack data storage (the data
are appended to the last transform on top of the stack).

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverstreamr(presolverstack* s,
     double v,
     ae_state *_state)
{
    ae_int_t rlast;


    rlast = s->rdataridx.ptr.p_int[s->ntrf];
    rgrowv(rlast+1, &s->rdata, _state);
    s->rdata.ptr.p_double[rlast] = v;
    s->rdataridx.ptr.p_int[s->ntrf] = rlast+1;
}


/*************************************************************************
Streams a integer/real pair to the presolver stack data storage (the data
are appended to the last transform on top of the stack).

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverstreamir(presolverstack* s,
     ae_int_t i,
     double v,
     ae_state *_state)
{
    ae_int_t ilast;
    ae_int_t rlast;


    ilast = s->idataridx.ptr.p_int[s->ntrf];
    rlast = s->rdataridx.ptr.p_int[s->ntrf];
    igrowv(ilast+1, &s->idata, _state);
    rgrowv(rlast+1, &s->rdata, _state);
    s->idata.ptr.p_int[ilast] = i;
    s->rdata.ptr.p_double[rlast] = v;
    s->idataridx.ptr.p_int[s->ntrf] = ilast+1;
    s->rdataridx.ptr.p_int[s->ntrf] = rlast+1;
}


/*************************************************************************
Streams DynamicCRS row.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverstreamcrsrow(presolverstack* s,
     const dynamiccrs* a,
     ae_int_t i,
     ae_state *_state)
{
    ae_int_t k;


    lpqppresolve_presolverstreami(s, a->rowend.ptr.p_int[i]-a->rowbegin.ptr.p_int[i], _state);
    for(k=a->rowbegin.ptr.p_int[i]; k<=a->rowend.ptr.p_int[i]-1; k++)
    {
        lpqppresolve_presolverstreamir(s, a->idx.ptr.p_int[k], a->vals.ptr.p_double[k], _state);
    }
}


/*************************************************************************
Streams DynamicCRS row, except for element in the column NotIdx (if such an
element is present)

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverstreamcrsrownot1(presolverstack* s,
     const dynamiccrs* a,
     ae_int_t i,
     ae_int_t notidx,
     ae_state *_state)
{
    ae_int_t k;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t cnt;


    j0 = a->rowbegin.ptr.p_int[i];
    j1 = a->rowend.ptr.p_int[i]-1;
    cnt = 0;
    for(k=j0; k<=j1; k++)
    {
        if( a->idx.ptr.p_int[k]!=notidx )
        {
            cnt = cnt+1;
        }
    }
    lpqppresolve_presolverstreami(s, cnt, _state);
    for(k=j0; k<=j1; k++)
    {
        if( a->idx.ptr.p_int[k]!=notidx )
        {
            lpqppresolve_presolverstreamir(s, a->idx.ptr.p_int[k], a->vals.ptr.p_double[k], _state);
        }
    }
}


/*************************************************************************
Streams entries of a DynamicCRS row that have corresponding flags set to false.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverstreamcrsrownonflagged(presolverstack* s,
     const dynamiccrs* a,
     ae_int_t i,
     /* Boolean */ const ae_vector* flags,
     ae_state *_state)
{
    ae_int_t k;
    ae_int_t cnt;
    ae_int_t j0;
    ae_int_t j1;


    j0 = a->rowbegin.ptr.p_int[i];
    j1 = a->rowend.ptr.p_int[i]-1;
    cnt = 0;
    for(k=j0; k<=j1; k++)
    {
        if( !flags->ptr.p_bool[a->idx.ptr.p_int[k]] )
        {
            cnt = cnt+1;
        }
    }
    lpqppresolve_presolverstreami(s, cnt, _state);
    for(k=j0; k<=j1; k++)
    {
        if( !flags->ptr.p_bool[a->idx.ptr.p_int[k]] )
        {
            lpqppresolve_presolverstreamir(s, a->idx.ptr.p_int[k], a->vals.ptr.p_double[k], _state);
        }
    }
}


/*************************************************************************
Select transformation TIdx as the source for stream reads

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverselectstreamsource(presolverstack* s,
     ae_int_t tidx,
     ae_state *_state)
{


    s->sourceidx = tidx;
    s->isrc = s->idataridx.ptr.p_int[tidx];
    s->rsrc = s->rdataridx.ptr.p_int[tidx];
}


/*************************************************************************
Reads from presolver stack boolean value, advances stream pointer

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverunstreamb(presolverstack* s,
     ae_bool* v,
     ae_state *_state)
{

    *v = ae_false;

    *v = s->idata.ptr.p_int[s->isrc]!=0;
    s->isrc = s->isrc+1;
}


/*************************************************************************
Reads from presolver stack boolean value, advances stream pointer.
Function instead of a proceduring

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static ae_bool lpqppresolve_presolverunstreambf(presolverstack* s,
     ae_state *_state)
{
    ae_bool result;


    result = s->idata.ptr.p_int[s->isrc]!=0;
    s->isrc = s->isrc+1;
    return result;
}


/*************************************************************************
Reads from presolver stack integer value, advances stream pointer

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverunstreami(presolverstack* s,
     ae_int_t* v,
     ae_state *_state)
{

    *v = 0;

    *v = s->idata.ptr.p_int[s->isrc];
    s->isrc = s->isrc+1;
}


/*************************************************************************
Reads from presolver stack real value, advances stream pointer

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverunstreamr(presolverstack* s,
     double* v,
     ae_state *_state)
{

    *v = 0.0;

    *v = s->rdata.ptr.p_double[s->rsrc];
    s->rsrc = s->rsrc+1;
}


/*************************************************************************
Reads from presolver stack int/real pair, advances stream pointers

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverunstreamir(presolverstack* s,
     ae_int_t* vi,
     double* vr,
     ae_state *_state)
{

    *vi = 0;
    *vr = 0.0;

    *vi = s->idata.ptr.p_int[s->isrc];
    s->isrc = s->isrc+1;
    *vr = s->rdata.ptr.p_double[s->rsrc];
    s->rsrc = s->rsrc+1;
}


/*************************************************************************
Reads from presolver stack compressed sparse vector, advances stream pointers

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverunstreamsparsevec(presolverstack* s,
     ae_int_t* cnt,
     /* Integer */ ae_vector* idx,
     /* Real    */ ae_vector* vals,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;


    lpqppresolve_presolverunstreami(s, cnt, _state);
    iallocv(*cnt, idx, _state);
    rallocv(*cnt, vals, _state);
    for(i=0; i<=*cnt-1; i++)
    {
        lpqppresolve_presolverunstreamir(s, &j, &v, _state);
        idx->ptr.p_int[i] = j;
        vals->ptr.p_double[i] = v;
    }
}


/*************************************************************************
Checks that we are at the end of the stream corresponding to transfrom #TIdx

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverasserteos(presolverstack* s,
     ae_state *_state)
{


    ae_assert(s->isrc==s->idataridx.ptr.p_int[s->sourceidx+1], "PresolverAssertEOS: unread integers in the stream", _state);
    ae_assert(s->rsrc==s->rdataridx.ptr.p_int[s->sourceidx+1], "PresolverAssertEOS: unread reals in the stream", _state);
}


/*************************************************************************
Appends transform placeholder

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappendtrf(presolverstack* s,
     ae_int_t tt,
     ae_state *_state)
{


    igrowv(s->ntrf+1, &s->trftype, _state);
    igrowv(s->ntrf+2, &s->idataridx, _state);
    igrowv(s->ntrf+2, &s->rdataridx, _state);
    s->trftype.ptr.p_int[s->ntrf] = tt;
    s->idataridx.ptr.p_int[s->ntrf+1] = s->idataridx.ptr.p_int[s->ntrf];
    s->rdataridx.ptr.p_int[s->ntrf+1] = s->rdataridx.ptr.p_int[s->ntrf];
    s->ntrf = s->ntrf+1;
}


/*************************************************************************
Appends cost scaling to the presolver stack.

The cost vector is MULTIPLIED by the scale coefficient.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappendcostscaling(presolverstack* s,
     double vmul,
     ae_state *_state)
{


    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstobjectivescaling, _state);
    lpqppresolve_presolverstreamr(s, vmul, _state);
}


/*************************************************************************
Appends column scaling to the presolver stack.

The variable is MULTIPLIED by the scale coefficient.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappendcolscaling(presolverstack* s,
     ae_int_t colidx,
     double vmul,
     ae_state *_state)
{


    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstcolscaling, _state);
    lpqppresolve_presolverstreamir(s, colidx, vmul, _state);
}


/*************************************************************************
Appends column shifting to the presolver stack.

The variable is INCREASED by the shift offset.

  -- ALGLIB --
     Copyright 01.06.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappendcolshifting(presolverstack* s,
     ae_int_t colidx,
     double vadd,
     ae_state *_state)
{


    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstcolshifting, _state);
    lpqppresolve_presolverstreamir(s, colidx, vadd, _state);
}


/*************************************************************************
Appends row scaling to the presolver stack.

The row is MULTIPLIED by the scale coefficient.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappendrowscaling(presolverstack* s,
     ae_int_t rowidx,
     double vmul,
     ae_state *_state)
{


    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstrowscaling, _state);
    lpqppresolve_presolverstreamir(s, rowidx, vmul, _state);
}


/*************************************************************************
Appends quadratic constraint scaling to the presolver stack.

The row is MULTIPLIED by the scale coefficient.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappendqcscaling(presolverstack* s,
     ae_int_t qcidx,
     double vmul,
     ae_state *_state)
{


    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstqcscaling, _state);
    lpqppresolve_presolverstreamir(s, qcidx, vmul, _state);
}


/*************************************************************************
Appends conic constraint scaling to the presolver stack.

The row is MULTIPLIED by the scale coefficient.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappendccscaling(presolverstack* s,
     ae_int_t ccidx,
     double vmul,
     ae_state *_state)
{


}


/*************************************************************************
Appends command to drop empty col and set variable and Lagrange multiplier
to prescribed values

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappenddropemptycol(presolverstack* s,
     ae_int_t colidx,
     double varval,
     double lagval,
     ae_int_t statval,
     ae_state *_state)
{


    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstdropemptycol, _state);
    lpqppresolve_presolverstreami(s, colidx, _state);
    lpqppresolve_presolverstreamr(s, varval, _state);
    lpqppresolve_presolverstreamr(s, lagval, _state);
    lpqppresolve_presolverstreami(s, statval, _state);
}


/*************************************************************************
Appends command to drop empty row

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappenddropemptyrow(presolverstack* s,
     ae_int_t rowidx,
     ae_state *_state)
{


    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstdropemptyrow, _state);
    lpqppresolve_presolverstreami(s, rowidx, _state);
}


/*************************************************************************
Appends command to drop empty/irrelevant quadratic constraints

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappenddropemptyqc(presolverstack* s,
     ae_int_t qcidx,
     ae_state *_state)
{


    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstdropemptyqc, _state);
    lpqppresolve_presolverstreami(s, qcidx, _state);
}


/*************************************************************************
Appends command to convert singleton row to box constraint

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappendsingletonrow(presolverstack* s,
     ae_int_t i,
     ae_int_t j,
     double v,
     double swapsign,
     double bndl,
     ae_bool bndlisbc,
     double bndu,
     ae_bool bnduisbc,
     ae_state *_state)
{


    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstsingletonrow, _state);
    lpqppresolve_presolverstreami(s, i, _state);
    lpqppresolve_presolverstreami(s, j, _state);
    lpqppresolve_presolverstreamr(s, v, _state);
    lpqppresolve_presolverstreamr(s, swapsign, _state);
    lpqppresolve_presolverstreamr(s, bndl, _state);
    lpqppresolve_presolverstreamb(s, bndlisbc, _state);
    lpqppresolve_presolverstreamr(s, bndu, _state);
    lpqppresolve_presolverstreamb(s, bnduisbc, _state);
}


/*************************************************************************
Appends command to fix variable.

Parameters:
    ColIdx, FixVal  -   column index and value for fixing
    Ci              -   C[i], linear term
    SparseH         -   if HasH=True, quadratic term; may contain already
                        dropped elements. Both triangles are included.
    IsDroppedCol    -   flags for variables that were already dropped.
                        The ColIdx-th one must NOT be included.
                        Used to correctly output elements of H and QC when
                        multiple variables are fixed sequentially.
    HasH            -   if False, SparseH/IsDroppedCol are ignored.
    AT              -   if HasAT=True, linear constraints matrix (transposed)
    HasAT           -   if False, AT is ignored
    QC              -   quadratic constraints, if any

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappendfixedvar(presolverstack* s,
     ae_int_t colidx,
     double fixval,
     double ci,
     const dynamiccrs* sparseh,
     const presolvervcstats* vcstats,
     ae_bool hash,
     const dynamiccrs* at,
     ae_bool hasat,
     dynamiccrsqconstraints* qc,
     ae_state *_state)
{
    ae_frame _frame_block;
    dynamiccrsqconstraint *qci;
    ae_smart_ptr _qci;
    ae_int_t qidx;
    ae_int_t qccnt;
    ae_int_t k;
    ae_int_t varpos;
    ae_int_t cntout;

    ae_frame_make(_state, &_frame_block);
    memset(&_qci, 0, sizeof(_qci));
    ae_smart_ptr_init(&_qci, (void**)&qci, ae_false, _state, ae_true);

    ae_assert(!vcstats->isdroppedcol.ptr.p_bool[colidx], "PRESOLVER: integrity check 6632 failed", _state);
    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstfixedvar, _state);
    lpqppresolve_presolverstreami(s, colidx, _state);
    lpqppresolve_presolverstreamr(s, fixval, _state);
    lpqppresolve_presolverstreamr(s, ci, _state);
    lpqppresolve_presolverstreamb(s, hash, _state);
    if( hash )
    {
        lpqppresolve_presolverstreamcrsrownonflagged(s, sparseh, colidx, &vcstats->isdroppedcol, _state);
    }
    
    /*
     * Stream linear constraints column, or zero (denoting zero-length array) if no linear constraint
     */
    if( hasat )
    {
        lpqppresolve_presolverstreamcrsrow(s, at, colidx, _state);
    }
    else
    {
        lpqppresolve_presolverstreami(s, 0, _state);
    }
    
    /*
     * Stream quadratic constraints
     */
    qccnt = ae_obj_array_get_length(&qc->constraints);
    for(qidx=0; qidx<=qccnt-1; qidx++)
    {
        
        /*
         * Skip constraints that are dropped at this moment
         */
        if( vcstats->isdroppedqc.ptr.p_bool[qidx] )
        {
            continue;
        }
        
        /*
         * Skip constraints that do not include the fixed variable
         */
        ae_obj_array_get(&qc->constraints, qidx, &_qci, _state);
        varpos = ibinarysearchlft(&qci->varidx, 0, qci->nvars, colidx, _state);
        if( varpos==qci->nvars||qci->varidx.ptr.p_int[varpos]!=colidx )
        {
            continue;
        }
        
        /*
         * The constraint is accepted and streamed
         */
        lpqppresolve_presolverstreamb(s, ae_true, _state);
        lpqppresolve_presolverstreami(s, qidx, _state);
        lpqppresolve_presolverstreamr(s, qci->b.ptr.p_double[varpos], _state);
        cntout = 0;
        for(k=qci->fullq.rowbegin.ptr.p_int[varpos]; k<=qci->fullq.rowend.ptr.p_int[varpos]-1; k++)
        {
            if( !vcstats->isdroppedcol.ptr.p_bool[qci->varidx.ptr.p_int[qci->fullq.idx.ptr.p_int[k]]] )
            {
                cntout = cntout+1;
            }
        }
        lpqppresolve_presolverstreami(s, cntout, _state);
        for(k=qci->fullq.rowbegin.ptr.p_int[varpos]; k<=qci->fullq.rowend.ptr.p_int[varpos]-1; k++)
        {
            if( !vcstats->isdroppedcol.ptr.p_bool[qci->varidx.ptr.p_int[qci->fullq.idx.ptr.p_int[k]]] )
            {
                lpqppresolve_presolverstreamir(s, qci->varidx.ptr.p_int[qci->fullq.idx.ptr.p_int[k]], qci->fullq.vals.ptr.p_double[k], _state);
            }
        }
    }
    lpqppresolve_presolverstreamb(s, ae_false, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Appends explicit slack transformation

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappendexplicitslack(presolverstack* s,
     ae_int_t i,
     ae_int_t j,
     double aij,
     double slackbndl,
     double slackbndu,
     double al,
     double au,
     const dynamiccrs* a,
     ae_state *_state)
{


    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstexplicitslack, _state);
    lpqppresolve_presolverstreami(s, i, _state);
    lpqppresolve_presolverstreami(s, j, _state);
    lpqppresolve_presolverstreamr(s, aij, _state);
    lpqppresolve_presolverstreamr(s, slackbndl, _state);
    lpqppresolve_presolverstreamr(s, slackbndu, _state);
    lpqppresolve_presolverstreamr(s, al, _state);
    lpqppresolve_presolverstreamr(s, au, _state);
    lpqppresolve_presolverstreamcrsrow(s, a, i, _state);
}


/*************************************************************************
Appends implicit slack transformation

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappendimplicitslack(presolverstack* s,
     ae_int_t i,
     ae_int_t j,
     double aij,
     double cj,
     double equalitybnd,
     const dynamiccrs* a,
     ae_state *_state)
{


    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstimplicitslack, _state);
    lpqppresolve_presolverstreami(s, i, _state);
    lpqppresolve_presolverstreami(s, j, _state);
    lpqppresolve_presolverstreamr(s, aij, _state);
    lpqppresolve_presolverstreamr(s, cj, _state);
    lpqppresolve_presolverstreamr(s, equalitybnd, _state);
    lpqppresolve_presolverstreamcrsrow(s, a, i, _state);
}


/*************************************************************************
Appends doubleton row which is used to factor out variable Col0 using
substitution X[COL0] := (ABnd-Ai1*X[COL1])/Ai0.

AT and SparseH (if HasH=True) must contain original linear constraints prior to substitution.

  -- ALGLIB --
     Copyright 11.06.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverappenddoubletonfactorout(presolverstack* s,
     const presolvervcstats* vcstats,
     ae_int_t rowidx,
     ae_int_t col0,
     double bndl0,
     double bndu0,
     ae_int_t col1,
     double bndl1,
     double bndu1,
     double c0,
     double c1,
     const dynamiccrs* sparseh,
     ae_bool hash,
     const dynamiccrs* at,
     dynamiccrsqconstraints* qc,
     double ai0,
     double ai1,
     double abnd,
     double newbndl1,
     double newbndu1,
     ae_int_t bndlactivates,
     ae_int_t bnduactivates,
     ae_state *_state)
{
    ae_frame _frame_block;
    dynamiccrsqconstraint *qci;
    ae_smart_ptr _qci;
    ae_int_t qccnt;
    ae_int_t k;
    ae_int_t qidx;
    ae_int_t pos0;
    ae_int_t pos1;

    ae_frame_make(_state, &_frame_block);
    memset(&_qci, 0, sizeof(_qci));
    ae_smart_ptr_init(&_qci, (void**)&qci, ae_false, _state, ae_true);

    qccnt = ae_obj_array_get_length(&qc->constraints);
    lpqppresolve_presolverappendtrf(s, lpqppresolve_pstdoubletoneq, _state);
    lpqppresolve_presolverstreami(s, rowidx, _state);
    lpqppresolve_presolverstreami(s, col0, _state);
    lpqppresolve_presolverstreamr(s, bndl0, _state);
    lpqppresolve_presolverstreamr(s, bndu0, _state);
    lpqppresolve_presolverstreami(s, col1, _state);
    lpqppresolve_presolverstreamr(s, bndl1, _state);
    lpqppresolve_presolverstreamr(s, bndu1, _state);
    lpqppresolve_presolverstreamr(s, ai0, _state);
    lpqppresolve_presolverstreamr(s, ai1, _state);
    lpqppresolve_presolverstreamr(s, abnd, _state);
    lpqppresolve_presolverstreamr(s, newbndl1, _state);
    lpqppresolve_presolverstreamr(s, newbndu1, _state);
    lpqppresolve_presolverstreami(s, bndlactivates, _state);
    lpqppresolve_presolverstreami(s, bnduactivates, _state);
    lpqppresolve_presolverstreamr(s, c0, _state);
    lpqppresolve_presolverstreamr(s, c1, _state);
    lpqppresolve_presolverstreamb(s, hash, _state);
    if( hash )
    {
        lpqppresolve_presolverstreamcrsrow(s, sparseh, col0, _state);
        lpqppresolve_presolverstreamcrsrow(s, sparseh, col1, _state);
    }
    lpqppresolve_presolverstreamcrsrownot1(s, at, col0, rowidx, _state);
    lpqppresolve_presolverstreamcrsrownot1(s, at, col1, rowidx, _state);
    for(qidx=0; qidx<=qccnt-1; qidx++)
    {
        
        /*
         * Skip constraints that are dropped at this moment
         */
        if( vcstats->isdroppedqc.ptr.p_bool[qidx] )
        {
            continue;
        }
        
        /*
         * Skip constraints that do not include at least one of these variables
         */
        ae_obj_array_get(&qc->constraints, qidx, &_qci, _state);
        pos0 = ibinarysearchlft(&qci->varidx, 0, qci->nvars, col0, _state);
        pos1 = ibinarysearchlft(&qci->varidx, 0, qci->nvars, col1, _state);
        if( (pos0==qci->nvars||qci->varidx.ptr.p_int[pos0]!=col0)&&(pos1==qci->nvars||qci->varidx.ptr.p_int[pos1]!=col1) )
        {
            continue;
        }
        
        /*
         * The constraint is accepted and streamed
         */
        lpqppresolve_presolverstreamb(s, ae_true, _state);
        lpqppresolve_presolverstreami(s, qidx, _state);
        if( pos0<qci->nvars&&qci->varidx.ptr.p_int[pos0]==col0 )
        {
            lpqppresolve_presolverstreamr(s, qci->b.ptr.p_double[pos0], _state);
            lpqppresolve_presolverstreami(s, qci->fullq.rowend.ptr.p_int[pos0]-qci->fullq.rowbegin.ptr.p_int[pos0], _state);
            for(k=qci->fullq.rowbegin.ptr.p_int[pos0]; k<=qci->fullq.rowend.ptr.p_int[pos0]-1; k++)
            {
                lpqppresolve_presolverstreamir(s, qci->varidx.ptr.p_int[qci->fullq.idx.ptr.p_int[k]], qci->fullq.vals.ptr.p_double[k], _state);
            }
        }
        else
        {
            lpqppresolve_presolverstreamr(s, 0.0, _state);
            lpqppresolve_presolverstreami(s, 0, _state);
        }
        if( pos1<qci->nvars&&qci->varidx.ptr.p_int[pos1]==col1 )
        {
            lpqppresolve_presolverstreamr(s, qci->b.ptr.p_double[pos1], _state);
            lpqppresolve_presolverstreami(s, qci->fullq.rowend.ptr.p_int[pos1]-qci->fullq.rowbegin.ptr.p_int[pos1], _state);
            for(k=qci->fullq.rowbegin.ptr.p_int[pos1]; k<=qci->fullq.rowend.ptr.p_int[pos1]-1; k++)
            {
                lpqppresolve_presolverstreamir(s, qci->varidx.ptr.p_int[qci->fullq.idx.ptr.p_int[k]], qci->fullq.vals.ptr.p_double[k], _state);
            }
        }
        else
        {
            lpqppresolve_presolverstreamr(s, 0.0, _state);
            lpqppresolve_presolverstreami(s, 0, _state);
        }
    }
    lpqppresolve_presolverstreamb(s, ae_false, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
This function restores original solution, given the solution of the
transformed problem.

Below N and M denote column/row count for both the original problem and
transformed one PRIOR to removal of dropped columns and rows.

INPUT PARAMETERS:
    S   -           a sequence of presolve transformations
    X   -           array[N], transformed solution after permutation
                    that restores original variable order (moves all
                    fixed variables to their positions)
    LagBC-          array[N], Lagrange coeffs for box constraints
    LagLC-          array[M], Lagrange coeffs for linear constraints
                    after permutation that restores original row order
                    (moves all dropped rows to their positions)
    LagQC-          array[MQC], Lagrange coeffs for quadratic constraints
                    after permutation that restores original row order
                    (moves all dropped rows to their positions)
    Stats-          array[N+M], constraint stats for box and linear constraints;
                    quadratic constraints are not included.
                    Ignored if NeedStats=False
    EPS -           stopping criteria, >=0, used to regularize some computations
                    (the idea is that the more regularization, the better, as
                    long as it is below EPS).
    
OUTPUT PARAMETERS:
    X, LagBC, LagLC, Stats-restored solution
    

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolverrestoresolution(presolverstack* s,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* laglc,
     /* Real    */ ae_vector* lagqc,
     /* Integer */ ae_vector* stats,
     ae_bool needstats,
     double eps,
     ae_state *_state)
{
    ae_int_t tidx;
    ae_int_t tt;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t cnt;
    ae_int_t vstat;
    double aij;
    double v;
    double vv;
    double vvar;
    double vlag;
    double vswap;
    double fixval;
    double ci;
    double cj;
    double c0;
    double c1;
    double equalitybnd;
    double bndl;
    double bndu;
    double al;
    double au;
    ae_bool bndlisbc;
    ae_bool bnduisbc;
    ae_bool transferlagtobc;
    double ratingl;
    double ratingu;
    ae_bool hash;
    ae_int_t rowidx;
    ae_int_t col0;
    double bndl0;
    double bndu0;
    ae_int_t col1;
    double bndl1;
    double bndu1;
    double ai0;
    double ai1;
    double abnd;
    double newbndl1;
    double newbndu1;
    ae_int_t bndlactivates;
    ae_int_t bnduactivates;
    ae_int_t cnt0;
    ae_int_t cnt1;
    ae_int_t cnt2;
    ae_int_t cnt3;
    ae_int_t cntq;
    double g0;
    double g1;
    ae_int_t relevantboundactivatedby;
    ae_int_t qcidx;
    double d;
    double cs;
    double sn;
    double s00;
    double s01;
    double s10;
    double s11;
    double s20;
    double s21;
    double r0;
    double r1;
    double r2;
    double v0;
    double v1;


    for(tidx=s->ntrf-1; tidx>=0; tidx--)
    {
        tt = s->trftype.ptr.p_int[tidx];
        if( tt==lpqppresolve_pstobjectivescaling )
        {
            
            /*
             * Reverse cost scaling
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreamr(s, &v, _state);
            lpqppresolve_presolverasserteos(s, _state);
            rmulv(s->n, (double)1/v, lagbc, _state);
            rmulv(s->m, (double)1/v, laglc, _state);
            rmulv(s->mqc, (double)1/v, lagqc, _state);
            continue;
        }
        if( tt==lpqppresolve_pstcolscaling )
        {
            
            /*
             * Reverse column scaling
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreamir(s, &k, &v, _state);
            lpqppresolve_presolverasserteos(s, _state);
            x->ptr.p_double[k] = x->ptr.p_double[k]/v;
            lagbc->ptr.p_double[k] = lagbc->ptr.p_double[k]*v;
            continue;
        }
        if( tt==lpqppresolve_pstcolshifting )
        {
            
            /*
             * Reverse variable shifting
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreamir(s, &k, &v, _state);
            lpqppresolve_presolverasserteos(s, _state);
            x->ptr.p_double[k] = x->ptr.p_double[k]-v;
            continue;
        }
        if( tt==lpqppresolve_pstrowscaling )
        {
            
            /*
             * Reverse row scaling
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreamir(s, &k, &v, _state);
            lpqppresolve_presolverasserteos(s, _state);
            laglc->ptr.p_double[k] = laglc->ptr.p_double[k]*v;
            continue;
        }
        if( tt==lpqppresolve_pstqcscaling )
        {
            
            /*
             * Reverse QC scaling
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreamir(s, &k, &v, _state);
            lpqppresolve_presolverasserteos(s, _state);
            lagqc->ptr.p_double[k] = lagqc->ptr.p_double[k]*v;
            continue;
        }
        if( tt==lpqppresolve_pstdropemptycol )
        {
            
            /*
             * Reverse dropping empty col
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreami(s, &k, _state);
            lpqppresolve_presolverunstreamr(s, &vvar, _state);
            lpqppresolve_presolverunstreamr(s, &vlag, _state);
            lpqppresolve_presolverunstreami(s, &vstat, _state);
            lpqppresolve_presolverasserteos(s, _state);
            x->ptr.p_double[k] = vvar;
            lagbc->ptr.p_double[k] = vlag;
            if( needstats )
            {
                stats->ptr.p_int[k] = vstat;
            }
            continue;
        }
        if( tt==lpqppresolve_pstdropemptyrow )
        {
            
            /*
             * Reverse dropping empty row
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreami(s, &k, _state);
            lpqppresolve_presolverasserteos(s, _state);
            laglc->ptr.p_double[k] = (double)(0);
            if( needstats )
            {
                stats->ptr.p_int[k] = 0;
            }
            continue;
        }
        if( tt==lpqppresolve_pstdropemptyqc )
        {
            
            /*
             * Reverse dropping QC
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreami(s, &k, _state);
            lpqppresolve_presolverasserteos(s, _state);
            lagqc->ptr.p_double[k] = (double)(0);
            continue;
        }
        if( tt==lpqppresolve_pstsingletonrow )
        {
            
            /*
             * Handle singleton row.
             * Read data from stream.
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreami(s, &i, _state);
            lpqppresolve_presolverunstreami(s, &j, _state);
            lpqppresolve_presolverunstreamr(s, &v, _state);
            lpqppresolve_presolverunstreamr(s, &vswap, _state);
            lpqppresolve_presolverunstreamr(s, &bndl, _state);
            lpqppresolve_presolverunstreamb(s, &bndlisbc, _state);
            lpqppresolve_presolverunstreamr(s, &bndu, _state);
            lpqppresolve_presolverunstreamb(s, &bnduisbc, _state);
            lpqppresolve_presolverasserteos(s, _state);
            
            /*
             * Determine bound that is "most active" using rating that combines Lagrangian magnitude
             */
            ae_assert(ae_isfinite(bndl, _state)||ae_isfinite(bndu, _state), "PRESOLVE: singleton row with both bounds absent", _state);
            ae_assert(ae_fp_eq(ae_fabs(vswap, _state),(double)(1)), "PRESOLVE: unexpected VSwap", _state);
            ratingl = x->ptr.p_double[j]-bndl+ae_maxreal(lagbc->ptr.p_double[j], 0.0, _state);
            ratingu = bndu-x->ptr.p_double[j]+ae_maxreal(-lagbc->ptr.p_double[j], 0.0, _state);
            if( ae_fp_less(ratingl,ratingu) )
            {
                
                /*
                 * Lower bound is more likely
                 */
                transferlagtobc = bndlisbc;
            }
            else
            {
                
                /*
                 * Upper bound is more likely
                 */
                transferlagtobc = bnduisbc;
            }
            
            /*
             * Transfer constraint activity from the transformed problem to the original one
             */
            if( transferlagtobc )
            {
                
                /*
                 * Box constraint of the original problem is active, linear singleton constraint is inactive
                 */
                laglc->ptr.p_double[i] = 0.0;
                if( needstats )
                {
                    stats->ptr.p_int[s->n+i] = 0;
                }
            }
            else
            {
                
                /*
                 * Linear constraint of the original problem is active, box constraint is inactive.
                 */
                laglc->ptr.p_double[i] = lagbc->ptr.p_double[j]/v*vswap;
                lagbc->ptr.p_double[j] = (double)(0);
                if( needstats )
                {
                    stats->ptr.p_int[s->n+i] = ae_round((double)stats->ptr.p_int[j]*vswap, _state);
                    stats->ptr.p_int[j] = 0;
                }
            }
            continue;
        }
        if( tt==lpqppresolve_pstfixedvar )
        {
            
            /*
             * Fixed variable
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreami(s, &j, _state);
            lpqppresolve_presolverunstreamr(s, &fixval, _state);
            
            /*
             * Restore variable
             */
            x->ptr.p_double[j] = fixval;
            
            /*
             * Unstream linear term and start computing gradient for the J-th variable
             */
            lpqppresolve_presolverunstreamr(s, &ci, _state);
            g0 = ci;
            
            /*
             * Unstream quadratic term and update gradient
             */
            lpqppresolve_presolverunstreamb(s, &hash, _state);
            if( hash )
            {
                lpqppresolve_presolverunstreamsparsevec(s, &cnt, &s->sparseidx0, &s->sparseval0, _state);
                for(i=0; i<=cnt-1; i++)
                {
                    g0 = g0+s->sparseval0.ptr.p_double[i]*x->ptr.p_double[s->sparseidx0.ptr.p_int[i]];
                }
            }
            
            /*
             * Unstream linear constraints and update gradient
             */
            lpqppresolve_presolverunstreamsparsevec(s, &cnt, &s->sparseidx0, &s->sparseval0, _state);
            for(i=0; i<=cnt-1; i++)
            {
                g0 = g0+s->sparseval0.ptr.p_double[i]*laglc->ptr.p_double[s->sparseidx0.ptr.p_int[i]];
            }
            
            /*
             * Unstream quadratic constraints and update gradient
             */
            while(lpqppresolve_presolverunstreambf(s, _state))
            {
                lpqppresolve_presolverunstreami(s, &qcidx, _state);
                lpqppresolve_presolverunstreamr(s, &v, _state);
                lpqppresolve_presolverunstreami(s, &cntq, _state);
                for(i=0; i<=cntq-1; i++)
                {
                    lpqppresolve_presolverunstreamir(s, &k, &vv, _state);
                    v = v+vv*x->ptr.p_double[k];
                }
                g0 = g0+lagqc->ptr.p_double[qcidx]*v;
            }
            
            /*
             * Check EOS
             */
            lpqppresolve_presolverasserteos(s, _state);
            
            /*
             * Recompute Lagrange multiplier for the constraint
             */
            lagbc->ptr.p_double[j] = -g0;
            if( needstats )
            {
                stats->ptr.p_int[j] = ae_sign(lagbc->ptr.p_double[j], _state);
            }
            continue;
        }
        if( tt==lpqppresolve_pstexplicitslack )
        {
            
            /*
             * Explicit slack variable:
             * * deduce its bounds from row activity
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreami(s, &i, _state);
            lpqppresolve_presolverunstreami(s, &j, _state);
            lpqppresolve_presolverunstreamr(s, &aij, _state);
            lpqppresolve_presolverunstreamr(s, &bndl, _state);
            lpqppresolve_presolverunstreamr(s, &bndu, _state);
            lpqppresolve_presolverunstreamr(s, &al, _state);
            lpqppresolve_presolverunstreamr(s, &au, _state);
            lpqppresolve_presolverunstreamsparsevec(s, &cnt, &s->sparseidx0, &s->sparseval0, _state);
            lpqppresolve_presolverasserteos(s, _state);
            x->ptr.p_double[j] = (double)(0);
            v = (double)(0);
            for(k=0; k<=cnt-1; k++)
            {
                v = v+s->sparseval0.ptr.p_double[k]*x->ptr.p_double[s->sparseidx0.ptr.p_int[k]];
            }
            if( ae_isfinite(al, _state) )
            {
                al = (al-v)/aij;
            }
            if( ae_isfinite(au, _state) )
            {
                au = (au-v)/aij;
            }
            if( ae_fp_less(aij,(double)(0)) )
            {
                swapr(&al, &au, _state);
                if( !ae_isfinite(al, _state) )
                {
                    al = _state->v_neginf;
                }
                if( !ae_isfinite(au, _state) )
                {
                    au = _state->v_posinf;
                }
            }
            if( ae_isfinite(al, _state)&&ae_fp_greater(al,bndl) )
            {
                bndl = al;
            }
            if( ae_isfinite(au, _state)&&ae_fp_less(au,bndu) )
            {
                bndu = au;
            }
            if( ae_isfinite(bndl, _state) )
            {
                x->ptr.p_double[j] = bndl;
            }
            else
            {
                if( ae_isfinite(bndu, _state) )
                {
                    x->ptr.p_double[j] = bndu;
                }
                else
                {
                    x->ptr.p_double[j] = (double)(0);
                }
            }
            lagbc->ptr.p_double[j] = -aij*laglc->ptr.p_double[i];
            if( needstats )
            {
                stats->ptr.p_int[j] = -ae_sign(aij, _state)*stats->ptr.p_int[s->n+i];
            }
            continue;
        }
        if( tt==lpqppresolve_pstimplicitslack )
        {
            
            /*
             * Implicit slack variable:
             * * deduce its bounds from row activity
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreami(s, &i, _state);
            lpqppresolve_presolverunstreami(s, &j, _state);
            lpqppresolve_presolverunstreamr(s, &aij, _state);
            lpqppresolve_presolverunstreamr(s, &cj, _state);
            lpqppresolve_presolverunstreamr(s, &equalitybnd, _state);
            lpqppresolve_presolverunstreamsparsevec(s, &cnt, &s->sparseidx0, &s->sparseval0, _state);
            lpqppresolve_presolverasserteos(s, _state);
            x->ptr.p_double[j] = (double)(0);
            v = (double)(0);
            for(k=0; k<=cnt-1; k++)
            {
                v = v+s->sparseval0.ptr.p_double[k]*x->ptr.p_double[s->sparseidx0.ptr.p_int[k]];
            }
            x->ptr.p_double[j] = (equalitybnd-v)/aij;
            lagbc->ptr.p_double[j] = -aij*laglc->ptr.p_double[i];
            laglc->ptr.p_double[i] = laglc->ptr.p_double[i]-cj/aij;
            if( needstats )
            {
                stats->ptr.p_int[j] = -ae_sign(aij, _state)*stats->ptr.p_int[s->n+i];
            }
            continue;
        }
        if( tt==lpqppresolve_pstdoubletoneq )
        {
            
            /*
             * Doubleton equality transformation: unstream data and compute residual gradient wrt variables col0 and col1
             */
            lpqppresolve_presolverselectstreamsource(s, tidx, _state);
            lpqppresolve_presolverunstreami(s, &rowidx, _state);
            lpqppresolve_presolverunstreami(s, &col0, _state);
            lpqppresolve_presolverunstreamr(s, &bndl0, _state);
            lpqppresolve_presolverunstreamr(s, &bndu0, _state);
            lpqppresolve_presolverunstreami(s, &col1, _state);
            lpqppresolve_presolverunstreamr(s, &bndl1, _state);
            lpqppresolve_presolverunstreamr(s, &bndu1, _state);
            lpqppresolve_presolverunstreamr(s, &ai0, _state);
            lpqppresolve_presolverunstreamr(s, &ai1, _state);
            lpqppresolve_presolverunstreamr(s, &abnd, _state);
            lpqppresolve_presolverunstreamr(s, &newbndl1, _state);
            lpqppresolve_presolverunstreamr(s, &newbndu1, _state);
            lpqppresolve_presolverunstreami(s, &bndlactivates, _state);
            lpqppresolve_presolverunstreami(s, &bnduactivates, _state);
            
            /*
             * Restore variable Col0
             */
            x->ptr.p_double[col0] = boundval((abnd-ai1*x->ptr.p_double[col1])/ai0, bndl0, bndu0, _state);
            
            /*
             * Unstream linear term and start computing gradient
             */
            lpqppresolve_presolverunstreamr(s, &c0, _state);
            lpqppresolve_presolverunstreamr(s, &c1, _state);
            g0 = c0;
            g1 = c1;
            
            /*
             * Unstream quadratic term, add to the residual gradient
             */
            lpqppresolve_presolverunstreamb(s, &hash, _state);
            if( hash )
            {
                lpqppresolve_presolverunstreamsparsevec(s, &cnt0, &s->sparseidx0, &s->sparseval0, _state);
                lpqppresolve_presolverunstreamsparsevec(s, &cnt1, &s->sparseidx1, &s->sparseval1, _state);
            }
            else
            {
                cnt0 = 0;
                cnt1 = 0;
            }
            for(i=0; i<=cnt0-1; i++)
            {
                g0 = g0+s->sparseval0.ptr.p_double[i]*x->ptr.p_double[s->sparseidx0.ptr.p_int[i]];
            }
            for(i=0; i<=cnt1-1; i++)
            {
                g1 = g1+s->sparseval1.ptr.p_double[i]*x->ptr.p_double[s->sparseidx1.ptr.p_int[i]];
            }
            
            /*
             * Unstream linear term, add to the residual gradient
             */
            lpqppresolve_presolverunstreamsparsevec(s, &cnt2, &s->sparseidx2, &s->sparseval2, _state);
            lpqppresolve_presolverunstreamsparsevec(s, &cnt3, &s->sparseidx3, &s->sparseval3, _state);
            for(i=0; i<=cnt2-1; i++)
            {
                g0 = g0+s->sparseval2.ptr.p_double[i]*laglc->ptr.p_double[s->sparseidx2.ptr.p_int[i]];
            }
            for(i=0; i<=cnt3-1; i++)
            {
                g1 = g1+s->sparseval3.ptr.p_double[i]*laglc->ptr.p_double[s->sparseidx3.ptr.p_int[i]];
            }
            
            /*
             * Unstream quadratic constraints, add to the residual gradient
             */
            while(lpqppresolve_presolverunstreambf(s, _state))
            {
                lpqppresolve_presolverunstreami(s, &qcidx, _state);
                lpqppresolve_presolverunstreamr(s, &v, _state);
                lpqppresolve_presolverunstreami(s, &cntq, _state);
                for(i=0; i<=cntq-1; i++)
                {
                    lpqppresolve_presolverunstreamir(s, &j, &vv, _state);
                    v = v+vv*x->ptr.p_double[j];
                }
                g0 = g0+lagqc->ptr.p_double[qcidx]*v;
                lpqppresolve_presolverunstreamr(s, &v, _state);
                lpqppresolve_presolverunstreami(s, &cntq, _state);
                for(i=0; i<=cntq-1; i++)
                {
                    lpqppresolve_presolverunstreamir(s, &j, &vv, _state);
                    v = v+vv*x->ptr.p_double[j];
                }
                g1 = g1+lagqc->ptr.p_double[qcidx]*v;
            }
            
            /*
             * The stream was fully processed
             */
            lpqppresolve_presolverasserteos(s, _state);
            
            /*
             * Compute Lagrange multipliers for box and linear constraints. In order to do
             * it we solve the following system:
             *
             *     [ E0  Ai0 ] [ lagBC ]   [ -(C0 + SUM(lagLC[j]*Aj0)) ]
             *     [         ] [       ] = [                           ]
             *     [ E1  Ai1 ] [ lagLC ]   [ -(C1 + SUM(lagLC[j]*Aj1)) ]
             *
             * where [E0,E1] is either [0,1], [1,0] or [0,0], depending on which box constraint
             * is active, if active at all.
             *
             * Here LagLC corresponds to the doubleton equality row being handled, and LagBC
             * corresponds to one of the variable bounds (the specific one is determined
             * during analysis of the solution recovered).
             *
             * We try all three options: no bounds active, bound on col0 is active, bound
             * on col1 is active, and compare dual and complementarity errors. The option
             * which gives the smallest error is chosen.
             *
             * The current box constraint on variable COL1 is an amalgamation of original
             * box constraints coming from variables COL0 and COL1. First, we have to determine
             * whether the constraint is at the upper or at the lower bound. Lagrange
             * multiplier sign is not a good measure because nearly-zero multipliers can have
             * their sign flipped due to rounding errors. In order to do so we compute
             * rating for lower and upper bounds. After comparing them we determine
             * which bound is active.
             */
            v = (-g0*ai0-g1*ai1)/(ai0*ai0+ai1*ai1);
            if( ae_isfinite(newbndl1, _state)||ae_isfinite(newbndu1, _state) )
            {
                ratingl = ae_maxrealnumber;
                ratingu = ae_maxrealnumber;
                if( ae_isfinite(newbndl1, _state) )
                {
                    ratingl = ae_fabs(x->ptr.p_double[col1]-newbndl1, _state)*ae_maxreal(-lagbc->ptr.p_double[col1], 0.0, _state)+ae_maxreal(x->ptr.p_double[col1]-newbndl1, 0.0, _state)+ae_maxreal(lagbc->ptr.p_double[col1], 0.0, _state);
                }
                if( ae_isfinite(newbndu1, _state) )
                {
                    ratingu = ae_fabs(newbndu1-x->ptr.p_double[col1], _state)*ae_maxreal(lagbc->ptr.p_double[col1], 0.0, _state)+ae_maxreal(newbndu1-x->ptr.p_double[col1], 0.0, _state)+ae_maxreal(-lagbc->ptr.p_double[col1], 0.0, _state);
                }
                relevantboundactivatedby = icase2(ae_fp_less(ratingl,ratingu), bndlactivates, bnduactivates, _state);
            }
            else
            {
                relevantboundactivatedby = lpqppresolve_actnone;
            }
            if( relevantboundactivatedby==lpqppresolve_actlowerboundonk0||relevantboundactivatedby==lpqppresolve_actupperboundonk0 )
            {
                
                /*
                 * The system to be solved in the least-squares sense is
                 *
                 *     [ 1  Ai0 ]             [ -(G0 + SUM(lagLC[j]*Aj0)) ]
                 *     [        ] [ lagBC ]   [                           ]
                 *     [    Ai1 ]*[       ] = [ -(G1 + SUM(lagLC[j]*Aj1)) ]
                 *     [        ] [ lagLC ]   [                           ]
                 *     [ D      ]             [             0             ]
                 *
                 * where D=|X0-BNDL0|+eps or D=|X0-BNDU0|+eps, depending on RelevantBoundActivatedBy.
                 * The first two rows come from requirement for Lagrangian gradient to be zero,
                 * the last row comes from complementarity conditions for lagBC, with EPS being added
                 * to help regularize the problem.
                 *
                 * The system is rewritten as
                 *
                 *     [ S00 S01 ]             [ R0 ]
                 *     [         ] [ lagBC ]   [    ]
                 *     [     S11 ]*[       ] = [ R1 ]
                 *     [         ] [ lagLC ]   [    ]
                 *     [ S20     ]             [ R2 ]
                 *
                 * and is solved by applying a sequence of rotations that converts it to an upper
                 * triangular form.
                 */
                d = ae_fabs(x->ptr.p_double[col0]-rcase2(relevantboundactivatedby==lpqppresolve_actlowerboundonk0, bndl0, bndu0, _state), _state)+eps;
                s00 = (double)(1);
                s01 = ai0;
                s11 = ai1;
                s20 = d;
                r0 = -g0;
                r1 = -g1;
                r2 = (double)(0);
                generaterotation(s00, s20, &cs, &sn, &v, _state);
                s00 = v;
                v0 = s01;
                s01 = cs*v0;
                s21 = -sn*v0;
                v0 = r0;
                v1 = r2;
                r0 = cs*v0+sn*v1;
                r2 = -sn*v0+cs*v1;
                generaterotation(s11, s21, &cs, &sn, &v, _state);
                s11 = v;
                v0 = r1;
                v1 = r2;
                r1 = cs*v0+sn*v1;
                r2 = -sn*v0+cs*v1;
                ae_assert(ae_fp_neq(s11,(double)(0)), "PRESOLVE: integrity check 781139 failed", _state);
                laglc->ptr.p_double[rowidx] = r1/s11;
                lagbc->ptr.p_double[col0] = (r0-s01*laglc->ptr.p_double[rowidx])/s00;
                lagbc->ptr.p_double[col1] = (double)(0);
                if( needstats )
                {
                    
                    /*
                     * Determine constraint status; the linear equality constraint is always
                     * active, the question is whether box constraint is actually active or
                     * not. We compare the rating for the activity (Lagrangian multiplier value)
                     * with the rating for inactivity (a distance towards corresponding bound),
                     * and depending on that we set constraint status.
                     */
                    stats->ptr.p_int[col0] = 0;
                    stats->ptr.p_int[col1] = 0;
                    if( ae_isfinite(bndl0, _state)&&ae_maxreal(-lagbc->ptr.p_double[col0], 0.0, _state)>x->ptr.p_double[col0]-bndl0 )
                    {
                        stats->ptr.p_int[col0] = -1;
                    }
                    if( ae_isfinite(bndu0, _state)&&ae_maxreal(lagbc->ptr.p_double[col0], 0.0, _state)>bndu0-x->ptr.p_double[col0] )
                    {
                        stats->ptr.p_int[col0] = 1;
                    }
                    stats->ptr.p_int[s->n+rowidx] = ipossign(laglc->ptr.p_double[rowidx], _state);
                }
                continue;
            }
            if( relevantboundactivatedby==lpqppresolve_actlowerboundonk1||relevantboundactivatedby==lpqppresolve_actupperboundonk1 )
            {
                
                /*
                 * The system is
                 *
                 *     [    Ai0 ]             [ -(G0 + SUM(lagLC[j]*Aj0)) ]
                 *     [        ] [ lagBC ]   [                           ]
                 *     [ 1  Ai1 ]*[       ] = [ -(G1 + SUM(lagLC[j]*Aj1)) ]
                 *     [        ] [ lagLC ]   [                           ]
                 *     [ D      ]             [             0             ]
                 *
                 * where D=|X1-BNDL1|+eps or D=|X1-BNDU1|+eps, depending on RelevantBoundActivatedBy.
                 * The first two rows come from requirement for Lagrangian gradient to be zero,
                 * the last row comes from complementarity conditions for lagBC, with EPS being added
                 * to help regularize the problem.
                 *
                 * The system is rewritten as
                 *
                 *     [     S01 ]             [ R0 ]
                 *     [         ] [ lagBC ]   [    ]
                 *     [ S10 S11 ]*[       ] = [ R1 ]
                 *     [         ] [ lagLC ]   [    ]
                 *     [ S20     ]             [ R2 ]
                 *
                 * and is solved by applying a sequence of rotations that converts it to a
                 * triangular form.
                 */
                d = ae_fabs(x->ptr.p_double[col1]-rcase2(relevantboundactivatedby==lpqppresolve_actlowerboundonk1, bndl1, bndu1, _state), _state)+eps;
                s10 = (double)(1);
                s01 = ai0;
                s11 = ai1;
                s20 = d;
                r0 = -g0;
                r1 = -g1;
                r2 = (double)(0);
                generaterotation(s10, s20, &cs, &sn, &v, _state);
                s10 = v;
                v0 = s11;
                s11 = cs*v0;
                s21 = -sn*v0;
                v0 = r1;
                v1 = r2;
                r1 = cs*v0+sn*v1;
                r2 = -sn*v0+cs*v1;
                generaterotation(s01, s21, &cs, &sn, &v, _state);
                s01 = v;
                v0 = r0;
                v1 = r2;
                r0 = cs*v0+sn*v1;
                r2 = -sn*v0+cs*v1;
                laglc->ptr.p_double[rowidx] = r0/s01;
                lagbc->ptr.p_double[col0] = (double)(0);
                lagbc->ptr.p_double[col1] = (r1-s11*laglc->ptr.p_double[rowidx])/s10;
                if( needstats )
                {
                    
                    /*
                     * Determine constraint status; the linear equality constraint is always
                     * active, the question is whether box constraint is actually active or
                     * not. We compare the rating for the activity (Lagrangian multiplier value)
                     * with the rating for inactivity (a distance towards corresponding bound),
                     * and depending on that we set constraint status.
                     */
                    stats->ptr.p_int[col0] = 0;
                    stats->ptr.p_int[col1] = 0;
                    if( ae_isfinite(bndl1, _state)&&ae_maxreal(-lagbc->ptr.p_double[col1], 0.0, _state)>x->ptr.p_double[col1]-bndl1 )
                    {
                        stats->ptr.p_int[col1] = -1;
                    }
                    if( ae_isfinite(bndu1, _state)&&ae_maxreal(lagbc->ptr.p_double[col1], 0.0, _state)>bndu1-x->ptr.p_double[col1] )
                    {
                        stats->ptr.p_int[col1] = 1;
                    }
                    stats->ptr.p_int[s->n+rowidx] = ipossign(laglc->ptr.p_double[rowidx], _state);
                }
                continue;
            }
            if( relevantboundactivatedby==lpqppresolve_actnone )
            {
                
                /*
                 * No box constraints active at the solution, determine Lagrange multiplier for
                 * the linear constraints by solving a least-squares system
                 *
                 *     [ Ai0 ] [ lagBC ]   [ -(G0 + SUM(lagLC[j]*Aj0)) ]
                 *     [     ] [       ] = [                           ]
                 *     [ Ai1 ] [ lagLC ]   [ -(G1 + SUM(lagLC[j]*Aj1)) ]
                 */
                lagbc->ptr.p_double[col0] = (double)(0);
                lagbc->ptr.p_double[col1] = (double)(0);
                laglc->ptr.p_double[rowidx] = (-g0*ai0-g1*ai1)/(ai0*ai0+ai1*ai1);
                if( needstats )
                {
                    stats->ptr.p_int[col0] = 0;
                    stats->ptr.p_int[col1] = 0;
                    stats->ptr.p_int[s->n+rowidx] = ipossign(laglc->ptr.p_double[rowidx], _state);
                }
                continue;
            }
            ae_assert(ae_false, "PRESOLVER: integrity check 8410 failed", _state);
        }
        ae_assert(ae_false, "PresolverRestoreSolution: unexpected transform type", _state);
    }
}


/*************************************************************************
Convert sparse H given by a single triangle into a full symmetric matrix.

INPUT PARAMETERS:
    H           -   lower/upper triangular quadratic term
    IsUpper     -   whether H is upper or lower triangular (the other triangle
                    is ignored)
    sparseTmp   -   possibly preallocated buffer; memory is reused if possible
    FullH       -   possibly preallocated matrix; memory is reused if possible
                    
OUTPUT PARAMETERS:
    FullH       -   NxN CRS matrix, fully symmetric H

  -- ALGLIB --
     Copyright 01.06.2024 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_copyexpandh(const sparsematrix* rawh,
     ae_bool isupper,
     sparsematrix* fullh,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t n;
    ae_int_t j;
    ae_int_t k;
    double v;


    ae_assert(sparseiscrs(rawh, _state)&&rawh->n==rawh->m, "PRESOLVE: integrity check 0935 failed", _state);
    n = rawh->n;
    
    /*
     * Compute row sizes for an expanded matrix
     */
    isetallocv(n+1, 0, &fullh->ridx, _state);
    for(i=0; i<=n-1; i++)
    {
        if( isupper )
        {
            j0 = rawh->uidx.ptr.p_int[i];
            j1 = rawh->ridx.ptr.p_int[i+1]-1;
        }
        else
        {
            j0 = rawh->ridx.ptr.p_int[i];
            j1 = rawh->didx.ptr.p_int[i]-1;
        }
        fullh->ridx.ptr.p_int[i+1] = fullh->ridx.ptr.p_int[i+1]+(j1-j0+1);
        for(jj=j0; jj<=j1; jj++)
        {
            j = rawh->idx.ptr.p_int[jj];
            fullh->ridx.ptr.p_int[j+1] = fullh->ridx.ptr.p_int[j+1]+1;
        }
        fullh->ridx.ptr.p_int[i+1] = fullh->ridx.ptr.p_int[i+1]+1;
    }
    for(i=0; i<=n-1; i++)
    {
        fullh->ridx.ptr.p_int[i+1] = fullh->ridx.ptr.p_int[i+1]+fullh->ridx.ptr.p_int[i];
    }
    
    /*
     * Copy/transpose/merge, use UIdx[] as a temporary buffer to store insert position
     */
    iallocv(fullh->ridx.ptr.p_int[n], &fullh->idx, _state);
    rallocv(fullh->ridx.ptr.p_int[n], &fullh->vals, _state);
    iallocv(n, &fullh->didx, _state);
    iallocv(n, &fullh->uidx, _state);
    if( isupper )
    {
        icopyvx(n, &fullh->ridx, 1, &fullh->uidx, 0, _state);
        for(i=n-1; i>=0; i--)
        {
            j0 = rawh->uidx.ptr.p_int[i];
            j1 = rawh->ridx.ptr.p_int[i+1]-1;
            for(jj=j1; jj>=j0; jj--)
            {
                j = rawh->idx.ptr.p_int[jj];
                v = rawh->vals.ptr.p_double[jj];
                k = fullh->uidx.ptr.p_int[i]-1;
                fullh->idx.ptr.p_int[k] = j;
                fullh->vals.ptr.p_double[k] = v;
                fullh->uidx.ptr.p_int[i] = k;
                k = fullh->uidx.ptr.p_int[j]-1;
                fullh->idx.ptr.p_int[k] = i;
                fullh->vals.ptr.p_double[k] = v;
                fullh->uidx.ptr.p_int[j] = k;
            }
            k = fullh->uidx.ptr.p_int[i]-1;
            fullh->idx.ptr.p_int[k] = i;
            if( rawh->uidx.ptr.p_int[i]>rawh->didx.ptr.p_int[i] )
            {
                fullh->vals.ptr.p_double[k] = rawh->vals.ptr.p_double[rawh->didx.ptr.p_int[i]];
            }
            else
            {
                fullh->vals.ptr.p_double[k] = 0.0;
            }
            fullh->didx.ptr.p_int[i] = k;
            fullh->uidx.ptr.p_int[i] = k;
        }
    }
    else
    {
        icopyvx(n, &fullh->ridx, 0, &fullh->uidx, 0, _state);
        for(i=0; i<=n-1; i++)
        {
            j0 = rawh->ridx.ptr.p_int[i];
            j1 = rawh->didx.ptr.p_int[i]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                j = rawh->idx.ptr.p_int[jj];
                v = rawh->vals.ptr.p_double[jj];
                k = fullh->uidx.ptr.p_int[i];
                fullh->idx.ptr.p_int[k] = j;
                fullh->vals.ptr.p_double[k] = v;
                fullh->uidx.ptr.p_int[i] = k+1;
                k = fullh->uidx.ptr.p_int[j];
                fullh->idx.ptr.p_int[k] = i;
                fullh->vals.ptr.p_double[k] = v;
                fullh->uidx.ptr.p_int[j] = k+1;
            }
            k = fullh->uidx.ptr.p_int[i];
            fullh->idx.ptr.p_int[k] = i;
            if( rawh->uidx.ptr.p_int[i]>rawh->didx.ptr.p_int[i] )
            {
                fullh->vals.ptr.p_double[k] = rawh->vals.ptr.p_double[rawh->didx.ptr.p_int[i]];
            }
            else
            {
                fullh->vals.ptr.p_double[k] = 0.0;
            }
            fullh->didx.ptr.p_int[i] = k;
            fullh->uidx.ptr.p_int[i] = k+1;
        }
    }
    
    /*
     * Recompute UIdx[]
     */
    for(i=0; i<=n-1; i++)
    {
        fullh->uidx.ptr.p_int[i] = fullh->didx.ptr.p_int[i]+1;
    }
    
    /*
     * Set up sparse matrix metrics
     */
    fullh->matrixtype = 1;
    fullh->m = n;
    fullh->n = n;
    fullh->ninitialized = fullh->ridx.ptr.p_int[n];
}


/*************************************************************************
Prepare temporaries

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_presolvebuffersinit(presolvebuffers* buf,
     ae_int_t n,
     ae_int_t m,
     ae_state *_state)
{


    if( n>0 )
    {
        nisinitemptyslow(n, &buf->setn, _state);
        nisinitemptyslow(n, &buf->setn2, _state);
        nisinitemptyslow(n, &buf->setn3, _state);
    }
    if( m>0 )
    {
        nisinitemptyslow(m, &buf->setm, _state);
    }
    else
    {
        nisinitemptyslow(1, &buf->setm, _state);
    }
}


/*************************************************************************
This function drops empty columns from the matrix. It may detect unbounded
problems (when unboundedness is caused by unconstrained column).

INPUT PARAMETERS:
    C, BndL, BndU  -   array[N], current cost and box constraints.
    IsDroppedCol-   array[N], column statuses; only non-dropped ones are examined.
    LagrangeFromResidual- array[N], whether we want to compute Lagrange coeffs
                    for box constraints using residual costs
    H           -   if HasH=True, a full symmetric H with both triangles;
                    diagonal elements are always present, even if zero.
                    If HasH=False, ignored.
    N           -   variables count (including both fixed and non-fixed)
    A, AT       -   current A and AT, dynamic CRS matrices
    AL, AU      -   array[M], lower/upper bounds for linear constraints
    M           -   linear constraints count
    Eps         -   unboundedness is checked subject to small dual feasibility
                    error tolerance
    DoTrace     -   whether tracing is needed or not
    SomethingChanged-flag variable
    CntFixed    -   debug counter, updated by the function
    trfStack    -   sequence of already applied transformations
                    
OUTPUT PARAMETERS:
    IsDroppedCol-   array[N], dropped columns are marked
    LagrangeFromResidual-
                    array[N], dropped cols are marked
    ProblemStatus-  on failure (unboundedness detected) is set to -2,
                    unchanged otherwise
    SomethingChanged-is set to True if at least one col was dropped
                    It is not changed otherwise.
    CntDropped  -   debug counter, updated by the function
    trfStack -      updated with new transforms
    
RESULT:
    if unboundedness was detected, False is returned.
    True is returned otherwise.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static ae_bool lpqppresolve_dropemptycol(/* Real    */ ae_vector* c,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     presolvervcstats* vcstats,
     /* Boolean */ ae_vector* lagrangefromresidual,
     dynamiccrs* sparseh,
     ae_bool hash,
     ae_int_t n,
     dynamiccrs* a,
     dynamiccrs* at,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     double eps,
     ae_bool dotrace,
     presolvebuffers* buf,
     presolverstack* trfstack,
     ae_int_t* problemstatus,
     ae_bool* somethingchanged,
     ae_int_t* cntdropped,
     ae_state *_state)
{
    ae_int_t i;
    double v;
    ae_int_t statsval;
    ae_bool result;


    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "LPPRESOLVE: Eps<0", _state);
    result = ae_true;
    
    /*
     * Scan columns, search for an empty one
     */
    for(i=0; i<=n-1; i++)
    {
        
        /*
         * Skip:
         * * already dropped columns
         * * columns that have non-empty A
         * * columns that have non-empty H
         */
        if( vcstats->isdroppedcol.ptr.p_bool[i] )
        {
            continue;
        }
        if( m!=0&&at->rowbegin.ptr.p_int[i]!=at->rowend.ptr.p_int[i] )
        {
            continue;
        }
        if( hash&&((sparseh->rowbegin.ptr.p_int[i]+1!=sparseh->rowend.ptr.p_int[i]||sparseh->idx.ptr.p_int[sparseh->rowbegin.ptr.p_int[i]]!=i)||ae_fp_neq(sparseh->vals.ptr.p_double[sparseh->rowbegin.ptr.p_int[i]],0.0)) )
        {
            continue;
        }
        if( vcstats->nlcpervar.ptr.p_int[i]!=0 )
        {
            continue;
        }
        
        /*
         * First, try to detect unboundedness
         */
        if( (c->ptr.p_double[i]<-eps&&ae_isposinf(bndu->ptr.p_double[i], _state))||(c->ptr.p_double[i]>eps&&ae_isneginf(bndl->ptr.p_double[i], _state)) )
        {
            if( dotrace )
            {
                ae_trace("> the target is unbounded (column %0d is empty, C[%0d]<>0, box constraints are insufficient)\n",
                    (int)(i),
                    (int)(i));
            }
            *problemstatus = -2;
            *somethingchanged = ae_true;
            *cntdropped = *cntdropped+1;
            result = ae_false;
            return result;
        }
        
        /*
         * Mark variable as fixed and issue transformation
         */
        vcstats->isdroppedcol.ptr.p_bool[i] = ae_true;
        *somethingchanged = ae_true;
        *cntdropped = *cntdropped+1;
        v = (double)(0);
        statsval = 0;
        if( c->ptr.p_double[i]>(double)0&&ae_isfinite(bndl->ptr.p_double[i], _state) )
        {
            
            /*
             * Variable has to be fixed at the lower bound
             */
            v = bndl->ptr.p_double[i];
            statsval = -1;
        }
        if( c->ptr.p_double[i]<(double)0&&ae_isfinite(bndu->ptr.p_double[i], _state) )
        {
            
            /*
             * Variable has to be fixed at the upper bound
             */
            v = bndu->ptr.p_double[i];
            statsval = 1;
        }
        if( statsval==0 )
        {
            
            /*
             * Variable value can be chosen arbitrarily, choose as close to zero as possible
             */
            if( ae_isfinite(bndl->ptr.p_double[i], _state) )
            {
                v = ae_maxreal(v, bndl->ptr.p_double[i], _state);
            }
            if( ae_isfinite(bndu->ptr.p_double[i], _state) )
            {
                v = ae_minreal(v, bndu->ptr.p_double[i], _state);
            }
        }
        lpqppresolve_presolverappenddropemptycol(trfstack, i, v, -c->ptr.p_double[i], statsval, _state);
    }
    return result;
}


/*************************************************************************
This function drops clearly nonbinding constraines: empty ones and ones
with infinite bounds.

It may detect infeasible problems (when infeasibility is caused by
constraint ranges  that  do  not include zero - the only value possible for
an empty constraint).

INPUT PARAMETERS:
    N           -   vars count
    VCStats     -   variable/constraint statistics
    A, AT       -   current A and AT, dynamic CRS matrices
    AL, AU      -   array[M], lower/upper bounds for linear constraints
    M           -   linear constraints count
    Eps         -   small primal feasibility error is allowed
    DoTrace     -   whether tracing is needed or not
    SomethingChanged-flag variable
    TrfYLag, TrfYTgt, TrfStat, TrfStatTgt -
                    sequence of already applied transformations
                    
OUTPUT PARAMETERS:
    VCStats.IsDroppedLC/QC-   dropped rows are marked
    ProblemStatus-  on failure (infeasibility detected) is set to -3,
                    unchanged otherwise
    SomethingChanged-is set to True if at least one row was dropped
                    It is not changed otherwise.
    CntEmpty    -   debug counter, updated by the function
    CntNoBounds -   debug counter, updated by the function
    TrfYLag, TrfYTgt, TrfStat, TrfStatTgt -
                    sequence of already applied transformations
    
RESULT:
    if infeasibility was detected, False is returned.
    True is returned otherwise.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static ae_bool lpqppresolve_dropclearlynonbindingrows(ae_int_t n,
     presolvervcstats* vcstats,
     dynamiccrs* a,
     dynamiccrs* at,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     dynamiccrsqconstraints* qc,
     xconicconstraints* cc,
     double eps,
     ae_bool dotrace,
     presolvebuffers* buf,
     presolverstack* trfstack,
     ae_int_t* problemstatus,
     ae_bool* somethingchanged,
     ae_int_t* cntempty,
     ae_int_t* cntemptyqc,
     ae_int_t* cntnobounds,
     ae_int_t* cntnoboundsqc,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t ii;
    ae_int_t jj;
    dynamiccrsqconstraint *qci;
    ae_smart_ptr _qci;
    ae_int_t qcidx;
    ae_int_t qccnt;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&_qci, 0, sizeof(_qci));
    ae_smart_ptr_init(&_qci, (void**)&qci, ae_false, _state, ae_true);

    result = ae_true;
    
    /*
     * Scan linear constraints
     */
    nisclear(&buf->setn, _state);
    nisclear(&buf->setm, _state);
    for(i=0; i<=m-1; i++)
    {
        if( !vcstats->isdroppedlc.ptr.p_bool[i] )
        {
            
            /*
             * Empty row
             */
            if( a->rowbegin.ptr.p_int[i]==a->rowend.ptr.p_int[i] )
            {
                
                /*
                 * Try to detect infeasibility
                 */
                if( (ae_isfinite(al->ptr.p_double[i], _state)&&ae_fp_greater(al->ptr.p_double[i],eps))||(ae_isfinite(au->ptr.p_double[i], _state)&&ae_fp_less(au->ptr.p_double[i],-eps)) )
                {
                    if( dotrace )
                    {
                        ae_trace("> the constraints are infeasible (row %0d is empty, initially or due to reductions, updated constraint range does not include zero)\n",
                            (int)(i));
                    }
                    *problemstatus = -3;
                    *somethingchanged = ae_true;
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                
                /*
                 * Mark row as dropped and issue transformation
                 */
                vcstats->isdroppedlc.ptr.p_bool[i] = ae_true;
                *somethingchanged = ae_true;
                *cntempty = *cntempty+1;
                lpqppresolve_presolverappenddropemptyrow(trfstack, i, _state);
                continue;
            }
            
            /*
             * No bounds
             */
            if( ae_isneginf(al->ptr.p_double[i], _state)&&ae_isposinf(au->ptr.p_double[i], _state) )
            {
                
                /*
                 * Add row and column containing its elements to the cleanup list
                 */
                nisaddelement(&buf->setm, i, _state);
                for(jj=a->rowbegin.ptr.p_int[i]; jj<=a->rowend.ptr.p_int[i]-1; jj++)
                {
                    nisaddelement(&buf->setn, a->idx.ptr.p_int[jj], _state);
                }
                
                /*
                 * Mark row as dropped and issue transformation
                 */
                vcstats->isdroppedlc.ptr.p_bool[i] = ae_true;
                *somethingchanged = ae_true;
                *cntnobounds = *cntnobounds+1;
                lpqppresolve_presolverappenddropemptyrow(trfstack, i, _state);
                continue;
            }
        }
    }
    for(ii=0; ii<=buf->setm.nstored-1; ii++)
    {
        a->rowend.ptr.p_int[buf->setm.items.ptr.p_int[ii]] = a->rowbegin.ptr.p_int[buf->setm.items.ptr.p_int[ii]];
    }
    for(jj=0; jj<=buf->setn.nstored-1; jj++)
    {
        lpqppresolve_dyncrsremovesetfromrow(at, buf->setn.items.ptr.p_int[jj], &buf->setm, _state);
    }
    
    /*
     * Scan quadratic constraints
     */
    qccnt = ae_obj_array_get_length(&qc->constraints);
    for(qcidx=0; qcidx<=qccnt-1; qcidx++)
    {
        if( vcstats->isdroppedqc.ptr.p_bool[qcidx] )
        {
            continue;
        }
        ae_obj_array_get(&qc->constraints, qcidx, &_qci, _state);
        
        /*
         * Handle empty constraint
         */
        if( qci->nvars==0 )
        {
            if( (ae_isfinite(qci->cl, _state)&&ae_fp_greater(qci->cl,eps))||(ae_isfinite(qci->cu, _state)&&ae_fp_less(qci->cu,-eps)) )
            {
                if( dotrace )
                {
                    ae_trace("> the quadratic constraints are infeasible (row %0d is empty, initially or due to reductions, updated constraint range does not include zero)\n",
                        (int)(qcidx));
                }
                *problemstatus = -3;
                *somethingchanged = ae_true;
                result = ae_false;
                ae_frame_leave(_state);
                return result;
            }
            vcstats->isdroppedqc.ptr.p_bool[qcidx] = ae_true;
            *somethingchanged = ae_true;
            *cntemptyqc = *cntemptyqc+1;
            lpqppresolve_presolverappenddropemptyqc(trfstack, qcidx, _state);
            continue;
        }
        
        /*
         * Handle constraint with no bounds
         */
        if( ae_isneginf(qci->cl, _state)&&ae_isposinf(qci->cu, _state) )
        {
            for(i=0; i<=qci->nvars-1; i++)
            {
                vcstats->nlcpervar.ptr.p_int[qci->varidx.ptr.p_int[i]] = vcstats->nlcpervar.ptr.p_int[qci->varidx.ptr.p_int[i]]-1;
            }
            vcstats->isdroppedqc.ptr.p_bool[qcidx] = ae_true;
            *somethingchanged = ae_true;
            *cntnoboundsqc = *cntnoboundsqc+1;
            lpqppresolve_presolverappenddropemptyqc(trfstack, qcidx, _state);
            continue;
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function scans rows and converts singleton rows to box constraints.

INPUT PARAMETERS:
    BndL, BndU  -   array[N], box constraints.
    VCStats     -   var/constr statistics
    N           -   variables count (including both fixed and non-fixed)
    A, AT       -   current A and AT, dynamic CRS matrices
    AL, AU      -   array[M], lower/upper bounds for linear constraints
    M           -   linear constraints count
    Eps         -   tolerance used to resolve infeasibilities
    DoTrace     -   whether tracing is needed or not
    SomethingChanged-flag variable
    CntFixed    -   debug counter, updated by the function
    trfStack    -   sequence of already applied transformations
                    
OUTPUT PARAMETERS:
    IsDroppedCol-   array[N], dropped columns are marked
    ProblemStatus-  on failure (unboundedness detected) is set to -2,
                    unchanged otherwise
    SomethingChanged-is set to True if at least one col was dropped
                    It is not changed otherwise.
    CntDropped  -   debug counter, updated by the function
    trfStack -      updated with new transforms
    
RESULT:
    if unboundedness was detected, False is returned.
    True is returned otherwise.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static ae_bool lpqppresolve_singletonrowtobc(/* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     presolvervcstats* vcstats,
     ae_int_t n,
     dynamiccrs* a,
     dynamiccrs* at,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     double eps,
     ae_bool dotrace,
     presolvebuffers* buf,
     presolverstack* trfstack,
     ae_int_t* problemstatus,
     ae_bool* somethingchanged,
     ae_int_t* cntsingleton,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t ii;
    ae_int_t offs;
    double v;
    double prevbndl;
    double prevbndu;
    double wrkal;
    double wrkau;
    double swapsign;
    double midpoint;
    ae_bool lowerboundisbc;
    ae_bool upperboundisbc;
    ae_bool result;


    result = ae_true;
    for(i=0; i<=m-1; i++)
    {
        if( (!vcstats->isdroppedlc.ptr.p_bool[i]&&a->rowbegin.ptr.p_int[i]+1==a->rowend.ptr.p_int[i])&&(ae_isfinite(al->ptr.p_double[i], _state)||ae_isfinite(au->ptr.p_double[i], _state)) )
        {
            
            /*
             * Read singleton row, perform integrity checks and normalization
             */
            j = a->idx.ptr.p_int[a->rowbegin.ptr.p_int[i]];
            v = a->vals.ptr.p_double[a->rowbegin.ptr.p_int[i]];
            if( ae_fp_less_eq(ae_fabs(v, _state),lpqppresolve_smlcoeff) )
            {
                
                /*
                 * The constraint coefficient is too small, factoring it out may result in numerical instability
                 */
                continue;
            }
            ae_assert(!vcstats->isdroppedlc.ptr.p_bool[i], "SingletonRowToBC: integrity check 6363 failed", _state);
            ae_assert(!vcstats->isdroppedcol.ptr.p_bool[j], "SingletonRowToBC: integrity check 6364 failed", _state);
            ae_assert(ae_fp_neq(v,(double)(0)), "SingletonRowToBC: integrity check 6366 failed", _state);
            wrkal = al->ptr.p_double[i];
            wrkau = au->ptr.p_double[i];
            swapsign = (double)(1);
            if( ae_fp_less(v,(double)(0)) )
            {
                swapr(&wrkal, &wrkau, _state);
                wrkal = -wrkal;
                wrkau = -wrkau;
                v = -v;
                swapsign = (double)(-1);
            }
            wrkal = wrkal/v;
            wrkau = wrkau/v;
            prevbndl = bndl->ptr.p_double[j];
            prevbndu = bndu->ptr.p_double[j];
            
            /*
             * Check feasibility
             */
            if( ae_fp_greater(prevbndl,prevbndu+eps) )
            {
                if( dotrace )
                {
                    ae_trace("> the problem is infeasible (box constraint for variable %0d)\n",
                        (int)(j));
                }
                *problemstatus = -3;
                *somethingchanged = ae_true;
                result = ae_false;
                return result;
            }
            if( ae_fp_greater(wrkal,prevbndu+eps)||ae_fp_less(wrkau,prevbndl-eps) )
            {
                if( dotrace )
                {
                    ae_trace("> the problem is infeasible (singleton row %0d is incompatible with box constraints for variable %0d)\n",
                        (int)(i),
                        (int)(j));
                }
                *problemstatus = -3;
                *somethingchanged = ae_true;
                result = ae_false;
                return result;
            }
            
            /*
             * Check compatibility with conic constraints
             */
            if( ae_isfinite(wrkal, _state)&&vcstats->cntdownwardaxial.ptr.p_int[j]>0 )
            {
                
                /*
                 * Constraint enforces lower bound on a downward axial variable (a conically
                 * constrained variable with cone axis oriented towards -INF), skip.
                 */
                continue;
            }
            if( ae_isfinite(wrkau, _state)&&vcstats->cntupwardaxial.ptr.p_int[j]>0 )
            {
                
                /*
                 * Constraint enforces lower bound on a downward axial variable (a conically
                 * constrained variable with cone axis oriented towards -INF), skip.
                 */
                continue;
            }
            
            /*
             * Modify problem, perform constraint harmonization for slightly infeasible box constraints
             */
            lowerboundisbc = ae_true;
            upperboundisbc = ae_true;
            if( ae_isfinite(wrkal, _state)&&ae_fp_greater(wrkal,prevbndl) )
            {
                bndl->ptr.p_double[j] = wrkal;
                lowerboundisbc = ae_false;
            }
            if( ae_isfinite(wrkau, _state)&&ae_fp_less(wrkau,prevbndu) )
            {
                bndu->ptr.p_double[j] = wrkau;
                upperboundisbc = ae_false;
            }
            if( ae_fp_less(bndu->ptr.p_double[j],bndl->ptr.p_double[j]) )
            {
                midpoint = 0.5*(bndu->ptr.p_double[j]+bndl->ptr.p_double[j]);
                bndl->ptr.p_double[j] = midpoint;
                bndu->ptr.p_double[j] = midpoint;
            }
            a->rowend.ptr.p_int[i] = a->rowbegin.ptr.p_int[i];
            offs = at->rowbegin.ptr.p_int[j];
            for(ii=at->rowbegin.ptr.p_int[j]; ii<=at->rowend.ptr.p_int[j]-1; ii++)
            {
                if( at->idx.ptr.p_int[ii]!=i )
                {
                    at->idx.ptr.p_int[offs] = at->idx.ptr.p_int[ii];
                    at->vals.ptr.p_double[offs] = at->vals.ptr.p_double[ii];
                    offs = offs+1;
                }
            }
            at->rowend.ptr.p_int[j] = offs;
            vcstats->isdroppedlc.ptr.p_bool[i] = ae_true;
            *somethingchanged = ae_true;
            *cntsingleton = *cntsingleton+1;
            lpqppresolve_presolverappendsingletonrow(trfstack, i, j, v, swapsign, bndl->ptr.p_double[j], lowerboundisbc, bndu->ptr.p_double[j], upperboundisbc, _state);
        }
    }
    return result;
}


/*************************************************************************
This function scans rows and uses doubleton equality rows to factor out
linear/quadratic variables.

INPUT PARAMETERS:
    BndL, BndU  -   array[N], box constraints.
    VCStats     -   variable/constr stats
    H           -   if HasH=True, a full symmetric H with both triangles;
                    diagonal elements are always present, even if zero.
                    If HasH=False, ignored.
    N           -   variables count (including both fixed and non-fixed)
    A, AT       -   current A and AT, dynamic CRS matrices
    AL, AU      -   array[M], lower/upper bounds for linear constraints
    M           -   linear constraints count
    QC          -   DynCRS-based quadratic constraints
    Eps         -   tolerance used to resolve infeasibilities
    DoTrace     -   whether tracing is needed or not
    SomethingChanged-flag variable
    CntFixed    -   debug counter, updated by the function
    trfStack    -   sequence of already applied transformations
                    
OUTPUT PARAMETERS:
    VCStats     -   dropped columns and rows are marked
    ProblemStatus-  on failure (unboundedness detected) is set to -2,
                    unchanged otherwise
    SomethingChanged-is set to True if at least one col was dropped
                    It is not changed otherwise.
    CntDropped  -   debug counter, updated by the function
    trfStack -      updated with new transforms
    
RESULT:
    if unboundedness was detected, False is returned.
    True is returned otherwise.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static ae_bool lpqppresolve_doubletonrow(/* Real    */ ae_vector* c,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     presolvervcstats* vcstats,
     dynamiccrs* sparseh,
     ae_bool hash,
     ae_int_t n,
     dynamiccrs* a,
     dynamiccrs* at,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     dynamiccrsqconstraints* qc,
     xconicconstraints* cc,
     double eps,
     ae_bool dotrace,
     presolvebuffers* buf,
     presolverstack* trfstack,
     ae_int_t* problemstatus,
     ae_bool* somethingchanged,
     ae_int_t* cntdoubleton,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t ii;
    ae_int_t cnt;
    ae_int_t qcidx;
    ae_int_t ccidx;
    double v;
    double v0;
    double v1;
    double h00;
    double h01;
    double h11;
    double shift;
    double abnd;
    double proposedbndl;
    double proposedbndu;
    ae_int_t proposedactl;
    ae_int_t proposedactu;
    double oribndl0;
    double oribndl1;
    double oribndu0;
    double oribndu1;
    double wrkbndl;
    double wrkbndu;
    double alpha0;
    double alpha1;
    ae_int_t bndlactivates;
    ae_int_t bnduactivates;
    dynamiccrsqconstraint *qci;
    ae_smart_ptr _qci;
    xconicconstraint *cci;
    ae_smart_ptr _cci;
    ae_int_t localk0;
    ae_int_t localk1;
    ae_bool hadk0;
    ae_bool hadk1;
    double dummy0;
    double dummy1;
    ae_int_t addedtok1;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&_qci, 0, sizeof(_qci));
    memset(&_cci, 0, sizeof(_cci));
    ae_smart_ptr_init(&_qci, (void**)&qci, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_cci, (void**)&cci, ae_false, _state, ae_true);

    result = ae_true;
    for(i=0; i<=m-1; i++)
    {
        if( (((!vcstats->isdroppedlc.ptr.p_bool[i]&&a->rowbegin.ptr.p_int[i]+2==a->rowend.ptr.p_int[i])&&ae_isfinite(al->ptr.p_double[i], _state))&&ae_isfinite(au->ptr.p_double[i], _state))&&ae_fabs(au->ptr.p_double[i]-al->ptr.p_double[i], _state)<=eps )
        {
            
            /*
             * Read doubleton row, perform integrity checks and normalization.
             *
             * We factor out X[K0] from the problem and retain X[K1]. We arrange equations
             * in such a way that |A[i,k0||>=|A[i,k1||
             */
            k0 = a->idx.ptr.p_int[a->rowbegin.ptr.p_int[i]];
            k1 = a->idx.ptr.p_int[a->rowbegin.ptr.p_int[i]+1];
            if( lpqppresolve_isfixed(bndl->ptr.p_double[k0], bndu->ptr.p_double[k0], eps, _state)||lpqppresolve_isfixed(bndl->ptr.p_double[k1], bndu->ptr.p_double[k1], eps, _state) )
            {
                
                /*
                 * If at least one of variables is fixed, we skip doubleton transformation.
                 */
                continue;
            }
            if( vcstats->cntaxial.ptr.p_int[k0]+vcstats->cntaxial.ptr.p_int[k1]>0 )
            {
                
                /*
                 * If at least one of variables is axial for some conic constraint, we skip doubleton transformation
                 */
                continue;
            }
            v0 = a->vals.ptr.p_double[a->rowbegin.ptr.p_int[i]];
            v1 = a->vals.ptr.p_double[a->rowbegin.ptr.p_int[i]+1];
            if( ae_fp_eq(v0*v1,(double)(0)) )
            {
                continue;
            }
            ae_assert(!vcstats->isdroppedcol.ptr.p_bool[k0], "PRESOLVER: integrity check 4253 failed", _state);
            ae_assert(!vcstats->isdroppedcol.ptr.p_bool[k1], "PRESOLVER: integrity check 4254 failed", _state);
            if( ae_fp_less(ae_fabs(v0, _state),ae_fabs(v1, _state)) )
            {
                swapi(&k0, &k1, _state);
                swapr(&v0, &v1, _state);
            }
            if( ae_fp_less(ae_fabs(v0, _state),lpqppresolve_smlcoeff) )
            {
                
                /*
                 * Row magnitude is too small, do not perform substitution because it may result in
                 * the loss of numerical stability
                 */
                continue;
            }
            abnd = 0.5*(al->ptr.p_double[i]+au->ptr.p_double[i]);
            alpha0 = abnd/v0;
            alpha1 = -v1/v0;
            
            /*
             * Compute updated bounds on X[K1], for each bound compute variable being activated:
             * *  0 means that reduced variable at this bound activates lower bound on X0
             * *  1 means that reduced variable at this bound activates upper bound on X0
             * *  2 means that reduced variable at this bound activates lower bound on X1
             * *  3 means that reduced variable at this bound activates upper bound on X1
             * * -1 denotes absent bound
             *
             * Check feasibility.
             */
            oribndl0 = bndl->ptr.p_double[k0];
            oribndu0 = bndu->ptr.p_double[k0];
            oribndl1 = bndl->ptr.p_double[k1];
            oribndu1 = bndu->ptr.p_double[k1];
            wrkbndl = bndl->ptr.p_double[k1];
            wrkbndu = bndu->ptr.p_double[k1];
            bndlactivates = icase2(ae_isfinite(wrkbndl, _state), lpqppresolve_actlowerboundonk1, lpqppresolve_actnone, _state);
            bnduactivates = icase2(ae_isfinite(wrkbndu, _state), lpqppresolve_actupperboundonk1, lpqppresolve_actnone, _state);
            proposedactl = lpqppresolve_actnone;
            proposedactu = lpqppresolve_actnone;
            proposedbndl = 0.0;
            proposedbndu = 0.0;
            if( ae_fp_greater(v0*v1,(double)(0)) )
            {
                if( ae_isfinite(bndu->ptr.p_double[k0], _state) )
                {
                    proposedbndl = (abnd-v0*bndu->ptr.p_double[k0])/v1;
                    proposedactl = lpqppresolve_actupperboundonk0;
                }
                if( ae_isfinite(bndl->ptr.p_double[k0], _state) )
                {
                    proposedbndu = (abnd-v0*bndl->ptr.p_double[k0])/v1;
                    proposedactu = lpqppresolve_actlowerboundonk0;
                }
            }
            else
            {
                if( ae_isfinite(bndl->ptr.p_double[k0], _state) )
                {
                    proposedbndl = (abnd-v0*bndl->ptr.p_double[k0])/v1;
                    proposedactl = lpqppresolve_actlowerboundonk0;
                }
                if( ae_isfinite(bndu->ptr.p_double[k0], _state) )
                {
                    proposedbndu = (abnd-v0*bndu->ptr.p_double[k0])/v1;
                    proposedactu = lpqppresolve_actupperboundonk0;
                }
            }
            if( proposedactl!=lpqppresolve_actnone&&proposedbndl>wrkbndl )
            {
                wrkbndl = proposedbndl;
                bndlactivates = proposedactl;
            }
            if( proposedactu!=lpqppresolve_actnone&&proposedbndu<wrkbndu )
            {
                wrkbndu = proposedbndu;
                bnduactivates = proposedactu;
            }
            if( ae_fp_greater(wrkbndl,wrkbndu+eps) )
            {
                if( dotrace )
                {
                    ae_trace("> the problem is infeasible (equality constraint %0d is incompatible with bounds on variable %0d and %0d)\n",
                        (int)(i),
                        (int)(k0),
                        (int)(k1));
                }
                *problemstatus = -3;
                *somethingchanged = ae_true;
                result = ae_false;
                ae_frame_leave(_state);
                return result;
            }
            if( ae_fp_greater(wrkbndl,wrkbndu) )
            {
                ae_assert(ae_isfinite(wrkbndl, _state)&&ae_isfinite(wrkbndu, _state), "PRESOLVER: integrity check 2124 failed", _state);
                v = 0.5*(wrkbndl+wrkbndu);
                wrkbndl = v;
                wrkbndu = v;
            }
            
            /*
             * Modify constraints:
             * * issue transformation
             * * drop variable K0
             * * drop constraint I
             * * factor out variable K0 from all constraints where it is present
             * * change bounds for K1
             */
            lpqppresolve_presolverappenddoubletonfactorout(trfstack, vcstats, i, k0, oribndl0, oribndu0, k1, oribndl1, oribndu1, c->ptr.p_double[k0], c->ptr.p_double[k1], sparseh, hash, at, qc, v0, v1, abnd, wrkbndl, wrkbndu, bndlactivates, bnduactivates, _state);
            vcstats->isdroppedcol.ptr.p_bool[k0] = ae_true;
            vcstats->isdroppedlc.ptr.p_bool[i] = ae_true;
            lpqppresolve_dyncrsclearrow(a, i, _state);
            lpqppresolve_dyncrsremovefromrow(at, k0, i, _state);
            lpqppresolve_dyncrsremovefromrow(at, k1, i, _state);
            cnt = 0;
            iallocv(at->rowend.ptr.p_int[k0]-at->rowbegin.ptr.p_int[k0], &buf->sparseidx, _state);
            rallocv(at->rowend.ptr.p_int[k0]-at->rowbegin.ptr.p_int[k0], &buf->sparsevals, _state);
            for(ii=at->rowbegin.ptr.p_int[k0]; ii<=at->rowend.ptr.p_int[k0]-1; ii++)
            {
                k = at->idx.ptr.p_int[ii];
                lpqppresolve_dyncrsfactoroutfromrow(a, k, k0, alpha0, alpha1, k1, ae_true, &v, &shift, _state);
                buf->sparseidx.ptr.p_int[cnt] = k;
                buf->sparsevals.ptr.p_double[cnt] = v;
                cnt = cnt+1;
                if( ae_isfinite(al->ptr.p_double[k], _state) )
                {
                    al->ptr.p_double[k] = al->ptr.p_double[k]-shift;
                }
                if( ae_isfinite(au->ptr.p_double[k], _state) )
                {
                    au->ptr.p_double[k] = au->ptr.p_double[k]-shift;
                }
            }
            lpqppresolve_dyncrsinsertwithrewrite(at, k1, &buf->sparseidx, &buf->sparsevals, cnt, _state);
            lpqppresolve_dyncrsclearrow(at, k0, _state);
            bndl->ptr.p_double[k1] = wrkbndl;
            bndu->ptr.p_double[k1] = wrkbndu;
            
            /*
             * Modify linear term with an update to C[K1] from C[K0]
             */
            c->ptr.p_double[k1] = c->ptr.p_double[k1]+c->ptr.p_double[k0]*alpha1;
            
            /*
             * Handle updates coming from the quadratic term, if we have it; clear H.
             */
            if( hash )
            {
                
                /*
                 * Analyze how 2x2 quadratic [h00 h01 ; h10 h11] changes when we
                 * substitute x0 := alpha0 + alpha1*x1.
                 *
                 * We have
                 *
                 *     h00*(alpha0+alpha1*x1)^2 + 2*h01*(alpha0+alpha1*x1)*x1 + h11*x1^2 =
                 *     h00*alpha0^2 + 2*h00*alpha0*alpha1*x1 + h00*alpha1^2*x1^2   +   2*h01*alpha0*x1+2*h01*alpha1*x1^2 + h11*x1^2 =
                 *     2*(h00*alpha0*alpha1+h01*alpha0)*x1 + (h00*alpha1^2+2*h01*alpha1+h11)*x1^2
                 */
                h00 = lpqppresolve_dyncrsgetexistingelement(sparseh, k0, k0, _state);
                h11 = lpqppresolve_dyncrsgetexistingelement(sparseh, k1, k1, _state);
                h01 = lpqppresolve_dyncrsgetelementifexists(sparseh, k0, k1, _state);
                
                /*
                 * Handle updates coming from modification of elements of H that are outside of the
                 * [k0,k1]x[k0,k1] diagonal block. The constant term in X[K0]:=Alpha0+Alpha1*X[K1]
                 * updates the linear term C[], the linear term Alpha1 updates the matrix H.
                 *
                 * The matrix H is updates applied to its column K1, then to its row K1. The row update
                 * modifies the [k0,k1]x[k0,k1] diagonal block because there is no easy way to do it
                 * without touching elements that we want to process later. We handle it by saving
                 * H00, H01, H11 prior to this block and by discarding and rewriting diagonal completely
                 * after calling DynCRSAddRowTo().
                 */
                for(ii=sparseh->rowbegin.ptr.p_int[k0]; ii<=sparseh->rowend.ptr.p_int[k0]-1; ii++)
                {
                    j = sparseh->idx.ptr.p_int[ii];
                    if( j!=k0&&j!=k1 )
                    {
                        
                        /*
                         * Update C[] and column K1 of H.
                         */
                        v = sparseh->vals.ptr.p_double[ii];
                        c->ptr.p_double[j] = c->ptr.p_double[j]+v*alpha0;
                        lpqppresolve_dyncrsfactoroutfromrow(sparseh, j, k0, 0.0, alpha1, k1, ae_false, &dummy0, &dummy1, _state);
                    }
                }
                lpqppresolve_dyncrsaddrowto(sparseh, k0, alpha1, k1, ae_true, ae_false, _state);
                lpqppresolve_dyncrsclearrow(sparseh, k0, _state);
                lpqppresolve_dyncrsremovefromrow(sparseh, k1, k0, _state);
                
                /*
                 * Handle updates coming from substituting X[K0]:=Alpha0+Alpha1*X[K1] to the
                 * [k0,k1]x[k0,k1] diagonal block.
                 */
                c->ptr.p_double[k1] = c->ptr.p_double[k1]+(h00*alpha0*alpha1+h01*alpha0);
                lpqppresolve_dyncrssetexistingelement(sparseh, k1, k1, h00*alpha1*alpha1+(double)2*h01*alpha1+h11, _state);
            }
            
            /*
             * Update quadratic and conic constraints
             */
            if( vcstats->nlcpervar.ptr.p_int[k0]+vcstats->nlcpervar.ptr.p_int[k1]>0 )
            {
                
                /*
                 * Update quadratic constraints:
                 * * use VarByQC for quick navigation (no need to enumerate all constraints)
                 * * pop indexes of constraints referencing K0 from VarByQC
                 * * save indexes of constraints added to K1 in Buf.tmp0[] (we can't immediately call 
                 *   knisAddNewElement because it can invalidate pointers used for direct access)
                 * * tell VarByQC that memory occupied by the K0-th set can be reclaimed
                 */
                addedtok1 = 0;
                while(kniscountkth(&vcstats->varbyqc, k0, _state)>0)
                {
                    qcidx = knispoplast(&vcstats->varbyqc, k0, _state);
                    ae_assert(!vcstats->isdroppedqc.ptr.p_bool[qcidx], "PRESOLVER: integrity check 635245 failed", _state);
                    ae_obj_array_get(&qc->constraints, qcidx, &_qci, _state);
                    localk0 = -1;
                    localk1 = -1;
                    for(j=0; j<=qci->nvars-1; j++)
                    {
                        if( qci->varidx.ptr.p_int[j]==k0 )
                        {
                            localk0 = j;
                        }
                        if( qci->varidx.ptr.p_int[j]==k1 )
                        {
                            localk1 = j;
                        }
                    }
                    hadk1 = localk1>=0;
                    if( localk0<0 )
                    {
                        continue;
                    }
                    if( localk1<0 )
                    {
                        localk1 = ibinarysearchlft(&qci->varidx, 0, qci->nvars, k1, _state);
                        if( localk0>=localk1 )
                        {
                            localk0 = localk0+1;
                        }
                        lpqppresolve_dynqccopyinsertrowcol(qci, k1, localk1, &buf->qc, _state);
                    }
                    else
                    {
                        lpqppresolve_dynqccopy(qci, &buf->qc, _state);
                    }
                    v = lpqppresolve_factoroutfromquadratic(&buf->qc.fullq, &buf->qc.b, localk0, alpha0, alpha1, localk1, _state);
                    if( ae_isfinite(buf->qc.cl, _state) )
                    {
                        buf->qc.cl = buf->qc.cl-v;
                    }
                    if( ae_isfinite(buf->qc.cu, _state) )
                    {
                        buf->qc.cu = buf->qc.cu-v;
                    }
                    lpqppresolve_dynqccopydeleterowcol(&buf->qc, localk0, qci, _state);
                    vcstats->nlcpervar.ptr.p_int[k0] = vcstats->nlcpervar.ptr.p_int[k0]-1;
                    if( !hadk1 )
                    {
                        vcstats->nlcpervar.ptr.p_int[k1] = vcstats->nlcpervar.ptr.p_int[k1]+1;
                        igrowappendv(addedtok1+1, &buf->tmp0, qcidx, _state);
                        addedtok1 = addedtok1+1;
                    }
                }
                for(qcidx=0; qcidx<=addedtok1-1; qcidx++)
                {
                    knisaddnewelement(&vcstats->varbyqc, k1, buf->tmp0.ptr.p_int[qcidx], _state);
                }
                knisclearkthreclaim(&vcstats->varbyqc, k0, _state);
                
                /*
                 * Update conic constraints:
                 * * use VarByCC for quick navigation (no need to enumerate all constraints)
                 * * pop indexes of constraints referencing K0 from VarByCC
                 * * save indexes of constraints added to K1 in Buf.tmp0[] (we can't immediately call 
                 *   knisAddNewElement because it can invalidate pointers used for direct access)
                 * * tell VarByCC that memory occupied by the K0-th set can be reclaimed
                 */
                addedtok1 = 0;
                while(kniscountkth(&vcstats->varbycc, k0, _state)>0)
                {
                    ccidx = knispoplast(&vcstats->varbycc, k0, _state);
                    ae_assert(!vcstats->isdroppedcc.ptr.p_bool[ccidx], "PRESOLVER: integrity check 4672221 failed", _state);
                    ae_obj_array_get(&cc->constraints, ccidx, &_cci, _state);
                    xccfactoroutnonaxial(cci, k0, alpha0, alpha1, k1, &hadk0, &hadk1, _state);
                    ae_assert(hadk0, "PRESOLVER: integrity check 675222 failed", _state);
                    vcstats->cntradial.ptr.p_int[k0] = vcstats->cntradial.ptr.p_int[k0]-1;
                    vcstats->nlcpervar.ptr.p_int[k0] = vcstats->nlcpervar.ptr.p_int[k0]-1;
                    if( !hadk1 )
                    {
                        vcstats->cntradial.ptr.p_int[k1] = vcstats->cntradial.ptr.p_int[k1]+1;
                        vcstats->nlcpervar.ptr.p_int[k1] = vcstats->nlcpervar.ptr.p_int[k1]+1;
                        igrowappendv(addedtok1+1, &buf->tmp0, ccidx, _state);
                        addedtok1 = addedtok1+1;
                    }
                }
                for(ccidx=0; ccidx<=addedtok1-1; ccidx++)
                {
                    knisaddnewelement(&vcstats->varbycc, k1, buf->tmp0.ptr.p_int[ccidx], _state);
                }
                knisclearkthreclaim(&vcstats->varbycc, k0, _state);
            }
            
            /*
             * Done
             */
            *somethingchanged = ae_true;
            *cntdoubleton = *cntdoubleton+1;
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tries to process singleton cols using various heuristics:
* detect explicit slacks

INPUT PARAMETERS:
    C. BndL, BndU-  array[N], cost and box constraints.
    IsDroppedCol-   array[N], column statuses, used for integrity checks
    H           -   if HasH=True, a full symmetric H with both triangles;
                    diagonal elements are always present, even if zero.
                    If HasH=False, ignored.
    N           -   variables count (including both fixed and non-fixed)
    A, AT       -   current A and AT, dynamic CRS matrices
    AL, AU      -   array[M], lower/upper bounds for linear constraints
    M           -   linear constraints count
    Eps         -   tolerance used to resolve infeasibilities
    DoTrace     -   whether tracing is needed or not
    SomethingChanged-flag variable
    CntFixed    -   debug counter, updated by the function
    trfStack    -   sequence of already applied transformations
                    
OUTPUT PARAMETERS:
    C, BndL, BndU-  may be modified
    IsDroppedCol-   array[N], dropped columns are marked
    ProblemStatus-  on failure (unboundedness detected) is set to -2,
                    unchanged otherwise
    SomethingChanged-is set to True if at least one col was dropped
                    It is not changed otherwise.
    CntSlackVars-   debug counter, updated by the function
    trfStack -      updated with new transforms
    
RESULT:
    if infeasibility or unboundedness was detected, False is returned.
    True is returned otherwise.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static ae_bool lpqppresolve_singletoncols(/* Real    */ ae_vector* c,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     presolvervcstats* vcstats,
     dynamiccrs* sparseh,
     ae_bool hash,
     ae_int_t n,
     dynamiccrs* a,
     dynamiccrs* at,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     double eps,
     ae_bool dotrace,
     presolvebuffers* buf,
     presolverstack* trfstack,
     ae_int_t* problemstatus,
     ae_bool* somethingchanged,
     ae_int_t* cntslackvars,
     ae_int_t* cntimplicitslacks,
     ae_int_t* cntfreecolumnsingletons,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t ii;
    double preval;
    double prevau;
    double prevabnd;
    double aij;
    double aslk;
    double vslk;
    double v;
    double cj;
    ae_bool result;


    result = ae_true;
    if( m==0 )
    {
        return result;
    }
    for(j=0; j<=n-1; j++)
    {
        
        /*
         * skip:
         * * already dropped columns
         * * non-singleton columns
         * * columns with non-zero quadratic term
         */
        if( vcstats->isdroppedcol.ptr.p_bool[j] )
        {
            continue;
        }
        if( at->rowbegin.ptr.p_int[j]+1!=at->rowend.ptr.p_int[j] )
        {
            continue;
        }
        if( hash&&((sparseh->rowbegin.ptr.p_int[j]+1!=sparseh->rowend.ptr.p_int[j]||sparseh->idx.ptr.p_int[sparseh->rowbegin.ptr.p_int[j]]!=j)||ae_fp_neq(sparseh->vals.ptr.p_double[sparseh->rowbegin.ptr.p_int[j]],0.0)) )
        {
            continue;
        }
        if( vcstats->nlcpervar.ptr.p_int[j]>0 )
        {
            continue;
        }
        
        /*
         * Proceed
         */
        i = at->idx.ptr.p_int[at->rowbegin.ptr.p_int[j]];
        cj = c->ptr.p_double[j];
        aij = at->vals.ptr.p_double[at->rowbegin.ptr.p_int[j]];
        if( ae_fp_less_eq(ae_fabs(aij, _state),lpqppresolve_smlcoeff) )
        {
            continue;
        }
        
        /*
         * Skip rows with no bounds, at least one bound is expected for the transformations below
         */
        if( !(ae_isfinite(al->ptr.p_double[i], _state)||ae_isfinite(au->ptr.p_double[i], _state)) )
        {
            continue;
        }
        
        /*
         * Is it an explicit slack?
         */
        if( ae_fp_less_eq(ae_fabs(cj, _state),eps) )
        {
            
            /*
             * Clear Cj
             * Update constraint bounds
             * Issue "explicit slack" transformation
             */
            c->ptr.p_double[j] = (double)(0);
            preval = al->ptr.p_double[i];
            prevau = au->ptr.p_double[i];
            if( ae_fp_greater(aij,(double)(0)) )
            {
                if( ae_isfinite(bndl->ptr.p_double[j], _state) )
                {
                    au->ptr.p_double[i] = au->ptr.p_double[i]-aij*bndl->ptr.p_double[j];
                }
                else
                {
                    au->ptr.p_double[i] = _state->v_posinf;
                }
                if( ae_isfinite(bndu->ptr.p_double[j], _state) )
                {
                    al->ptr.p_double[i] = al->ptr.p_double[i]-aij*bndu->ptr.p_double[j];
                }
                else
                {
                    al->ptr.p_double[i] = _state->v_neginf;
                }
            }
            else
            {
                if( ae_isfinite(bndu->ptr.p_double[j], _state) )
                {
                    au->ptr.p_double[i] = au->ptr.p_double[i]-aij*bndu->ptr.p_double[j];
                }
                else
                {
                    au->ptr.p_double[i] = _state->v_posinf;
                }
                if( ae_isfinite(bndl->ptr.p_double[j], _state) )
                {
                    al->ptr.p_double[i] = al->ptr.p_double[i]-aij*bndl->ptr.p_double[j];
                }
                else
                {
                    al->ptr.p_double[i] = _state->v_neginf;
                }
            }
            lpqppresolve_presolverappendexplicitslack(trfstack, i, j, aij, bndl->ptr.p_double[j], bndu->ptr.p_double[j], preval, prevau, a, _state);
            
            /*
             * Remove variable from A and AT
             */
            lpqppresolve_dyncrsclearrow(at, j, _state);
            lpqppresolve_dyncrsremovefromrow(a, i, j, _state);
            vcstats->isdroppedcol.ptr.p_bool[j] = ae_true;
            
            /*
             * Done
             */
            *somethingchanged = ae_true;
            *cntslackvars = *cntslackvars+1;
            continue;
        }
        
        /*
         * Is it equality row? The variable can be treated as an implicit slack
         */
        if( ((ae_isfinite(al->ptr.p_double[i], _state)&&ae_isfinite(au->ptr.p_double[i], _state))&&ae_fp_less_eq(ae_fabs(al->ptr.p_double[i]-au->ptr.p_double[i], _state),eps))&&ae_fp_greater_eq(ae_fabs(aij, _state)*lpqppresolve_maxcoeffgrowth,ae_fabs(cj, _state)) )
        {
            
            /*
             * Enforce AL=AU, set Cj to zero
             */
            prevabnd = 0.5*(al->ptr.p_double[i]+au->ptr.p_double[i]);
            al->ptr.p_double[i] = prevabnd;
            au->ptr.p_double[i] = prevabnd;
            v = cj/aij;
            for(ii=a->rowbegin.ptr.p_int[i]; ii<=a->rowend.ptr.p_int[i]-1; ii++)
            {
                c->ptr.p_double[a->idx.ptr.p_int[ii]] = c->ptr.p_double[a->idx.ptr.p_int[ii]]-v*a->vals.ptr.p_double[ii];
            }
            c->ptr.p_double[j] = (double)(0);
            
            /*
             * Update constraint bounds
             * Issue "implicit slack" transformation
             */
            if( ae_fp_greater(aij,(double)(0)) )
            {
                if( ae_isfinite(bndl->ptr.p_double[j], _state) )
                {
                    au->ptr.p_double[i] = au->ptr.p_double[i]-aij*bndl->ptr.p_double[j];
                }
                else
                {
                    au->ptr.p_double[i] = _state->v_posinf;
                }
                if( ae_isfinite(bndu->ptr.p_double[j], _state) )
                {
                    al->ptr.p_double[i] = al->ptr.p_double[i]-aij*bndu->ptr.p_double[j];
                }
                else
                {
                    al->ptr.p_double[i] = _state->v_neginf;
                }
            }
            else
            {
                if( ae_isfinite(bndu->ptr.p_double[j], _state) )
                {
                    au->ptr.p_double[i] = au->ptr.p_double[i]-aij*bndu->ptr.p_double[j];
                }
                else
                {
                    au->ptr.p_double[i] = _state->v_posinf;
                }
                if( ae_isfinite(bndl->ptr.p_double[j], _state) )
                {
                    al->ptr.p_double[i] = al->ptr.p_double[i]-aij*bndl->ptr.p_double[j];
                }
                else
                {
                    al->ptr.p_double[i] = _state->v_neginf;
                }
            }
            lpqppresolve_presolverappendimplicitslack(trfstack, i, j, aij, cj, prevabnd, a, _state);
            
            /*
             * Remove variable from A and AT, set Cj to zero
             */
            lpqppresolve_dyncrsclearrow(at, j, _state);
            lpqppresolve_dyncrsremovefromrow(a, i, j, _state);
            vcstats->isdroppedcol.ptr.p_bool[j] = ae_true;
            
            /*
             * Done
             */
            *somethingchanged = ae_true;
            *cntimplicitslacks = *cntimplicitslacks+1;
            continue;
        }
        
        /*
         * Is it a free column singleton?
         */
        if( (!ae_isfinite(bndl->ptr.p_double[j], _state)&&!ae_isfinite(bndu->ptr.p_double[j], _state))&&ae_fp_greater_eq(ae_fabs(aij, _state)*lpqppresolve_maxcoeffgrowth,ae_fabs(cj, _state)) )
        {
            
            /*
             * Handle single-sided linear constraints or two-sided one.
             *
             * A general inequality/range constraint is converted to an equality one by
             * adding temporary slack variable SLK. This variable is not actually added
             * to the problem - we analytically remove it right after the addition.
             *
             * See 3.2.2 from "A modular presolve procedure for large scale linear programming"
             * by Swietanowski for more information
             */
            if( !ae_isfinite(al->ptr.p_double[i], _state)||!ae_isfinite(au->ptr.p_double[i], _state) )
            {
                
                /*
                 * Single-sided constraint
                 *
                 * Determine multiplier for the artificial temporary slack variable S.
                 * Check for unboundedness. If bounded, the slack S is zero, right-hand
                 * side of the equality constraint is not modified.
                 */
                aslk = rcase2(ae_isfinite(au->ptr.p_double[i], _state), (double)(1), (double)(-1), _state);
                if( ae_fp_greater(aslk*cj/aij,eps) )
                {
                    if( dotrace )
                    {
                        ae_trace("> the problem is unbounded (deduced from free column singleton analysis for variable %0d, row %0d)\n",
                            (int)(j),
                            (int)(i));
                    }
                    *problemstatus = -2;
                    *somethingchanged = ae_true;
                    result = ae_false;
                    return result;
                }
                vslk = (double)(0);
                if( !ae_isfinite(au->ptr.p_double[i], _state) )
                {
                    au->ptr.p_double[i] = al->ptr.p_double[i];
                }
            }
            else
            {
                
                /*
                 * Two-sided constraint
                 *
                 * Determine value of the temporary slack variable, modify right-hand side
                 * of the constraint.
                 */
                vslk = rcase2(ae_fp_greater(cj/aij,(double)(0)), au->ptr.p_double[i]-al->ptr.p_double[i], 0.0, _state);
            }
            v = cj/aij;
            for(ii=a->rowbegin.ptr.p_int[i]; ii<=a->rowend.ptr.p_int[i]-1; ii++)
            {
                c->ptr.p_double[a->idx.ptr.p_int[ii]] = c->ptr.p_double[a->idx.ptr.p_int[ii]]-v*a->vals.ptr.p_double[ii];
            }
            c->ptr.p_double[j] = (double)(0);
            lpqppresolve_presolverappendimplicitslack(trfstack, i, j, aij, cj, au->ptr.p_double[i]-vslk, a, _state);
            al->ptr.p_double[i] = _state->v_neginf;
            au->ptr.p_double[i] = _state->v_posinf;
            lpqppresolve_dyncrsclearrow(at, j, _state);
            lpqppresolve_dyncrsremovefromrow(a, i, j, _state);
            vcstats->isdroppedcol.ptr.p_bool[j] = ae_true;
            *cntfreecolumnsingletons = *cntfreecolumnsingletons+1;
            continue;
        }
    }
    return result;
}


/*************************************************************************
This function scans box constraints list and tries  to  fix  variables  by
removing them from the A and AT matrices and by  recording  transformation
in trfStack.

If infeasibility is detected, returns False.

INPUT PARAMETERS:
    BndL, BndU  -   array[N], current set of box constraints. Only entries
                    with IsDroppedCol[]=False are examined. Entries corresponding
                    to fixed variables are forced to be equal.
    VCStats     -   variable/constr stats
    H           -   if HasH=True, a full symmetric H with both triangles;
                    diagonal elements are always present, even if zero.
                    If HasH=False, ignored.
    N           -   variables count (including both fixed and non-fixed)
    A, AT       -   current A and AT, dynamic CRS matrices
    AL, AU      -   array[M], lower/upper bounds for linear constraints
    M           -   linear constraints count
    Eps         -   tolerance to resolve slightly infeasible constraints
    DoTrace     -   whether tracing is needed or not
    SomethingChanged-flag variable
    CntFixed    -   debug counter, updated by the function
                    
OUTPUT PARAMETERS:
    VCStats     -   fixed vars are marked
    A           -   columns corresponding to fixed vars are removed from A.
                    The matrix size does not change.
    AT          -   rows corresponding to fixed vars are removed from A.
                    The matrix size does not change.
    AL, AU      -   updated with fixed values
    SomethingChanged-is set to True if at least one variable was fixed.
                    It is not changed otherwise.
    CntFixed    -   debug counter, updated by the function

RESULT:
    if infeasibility is detected, returns False.
    True otherwise.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static ae_bool lpqppresolve_fixvariables(/* Real    */ ae_vector* c,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     presolvervcstats* vcstats,
     dynamiccrs* sparseh,
     ae_bool hash,
     ae_int_t n,
     dynamiccrs* a,
     dynamiccrs* at,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     dynamiccrsqconstraints* qc,
     xconicconstraints* cc,
     double eps,
     ae_bool dotrace,
     presolvebuffers* buf,
     presolverstack* trfstack,
     ae_int_t* problemstatus,
     ae_bool* somethingchanged,
     ae_int_t* cntfixed,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ii;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t k;
    ae_int_t offs;
    double v;
    double vfixed;
    ae_int_t qcidx;
    ae_int_t qccnt;
    ae_int_t ccidx;
    dynamiccrsqconstraint *qci;
    ae_smart_ptr _qci;
    xconicconstraint *cci;
    ae_smart_ptr _cci;
    ae_bool hadk0;
    ae_bool hadk1;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&_qci, 0, sizeof(_qci));
    memset(&_cci, 0, sizeof(_cci));
    ae_smart_ptr_init(&_qci, (void**)&qci, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_cci, (void**)&cci, ae_false, _state, ae_true);

    result = ae_true;
    qccnt = ae_obj_array_get_length(&qc->constraints);
    
    /*
     * Scan variables, determine rows to update
     */
    nisclear(&buf->setn, _state);
    nisclear(&buf->setm, _state);
    if( hash )
    {
        nisclear(&buf->setn2, _state);
    }
    for(i=0; i<=n-1; i++)
    {
        if( !vcstats->isdroppedcol.ptr.p_bool[i]&&lpqppresolve_isfixed(bndl->ptr.p_double[i], bndu->ptr.p_double[i], eps, _state) )
        {
            
            /*
             * Detect infeasibility
             */
            if( ae_fp_greater(bndl->ptr.p_double[i],bndu->ptr.p_double[i]+eps) )
            {
                *somethingchanged = ae_true;
                *problemstatus = -3;
                result = ae_false;
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * Ignore axial variables
             */
            if( vcstats->cntaxial.ptr.p_int[i]>0 )
            {
                continue;
            }
            
            /*
             * Fix variable and issue transformation
             */
            vfixed = boundval(0.5*(bndl->ptr.p_double[i]+bndu->ptr.p_double[i]), bndl->ptr.p_double[i], bndu->ptr.p_double[i], _state);
            *somethingchanged = ae_true;
            *cntfixed = *cntfixed+1;
            bndl->ptr.p_double[i] = vfixed;
            bndu->ptr.p_double[i] = vfixed;
            lpqppresolve_presolverappendfixedvar(trfstack, i, vfixed, c->ptr.p_double[i], sparseh, vcstats, hash, at, m!=0, qc, _state);
            vcstats->isdroppedcol.ptr.p_bool[i] = ae_true;
            
            /*
             * Modify linear term due to modification of a quadratic one, save rows of H that contain I-th variable for further cleaning at the end
             */
            if( hash )
            {
                j0 = sparseh->rowbegin.ptr.p_int[i];
                j1 = sparseh->rowend.ptr.p_int[i]-1;
                for(jj=j0; jj<=j1; jj++)
                {
                    j = sparseh->idx.ptr.p_int[jj];
                    if( vcstats->isdroppedcol.ptr.p_bool[j] )
                    {
                        continue;
                    }
                    c->ptr.p_double[j] = c->ptr.p_double[j]+vfixed*sparseh->vals.ptr.p_double[jj];
                    nisaddelement(&buf->setn2, j, _state);
                }
            }
            
            /*
             * Save dependent linear constraints to SetM
             */
            nisaddelement(&buf->setn, i, _state);
            if( m!=0 )
            {
                for(jj=at->rowbegin.ptr.p_int[i]; jj<=at->rowend.ptr.p_int[i]-1; jj++)
                {
                    nisaddelement(&buf->setm, at->idx.ptr.p_int[jj], _state);
                }
            }
            
            /*
             * Modify quadratic constraints
             */
            while(kniscountkth(&vcstats->varbyqc, i, _state)>0)
            {
                qcidx = knispoplast(&vcstats->varbyqc, i, _state);
                ae_assert(!vcstats->isdroppedqc.ptr.p_bool[qcidx], "PRESOLVER: integrity check 148217 failed", _state);
                ae_obj_array_get(&qc->constraints, qcidx, &_qci, _state);
                k = ibinarysearchlft(&qci->varidx, 0, qci->nvars, i, _state);
                ae_assert(k<qci->nvars&&qci->varidx.ptr.p_int[k]==i, "PRESOLVER: integrity check 151219 failed", _state);
                vcstats->nlcpervar.ptr.p_int[i] = vcstats->nlcpervar.ptr.p_int[i]-1;
                v = lpqppresolve_fixquadraticvariable(&qci->fullq, &qci->b, k, vfixed, _state);
                if( ae_isfinite(qci->cl, _state) )
                {
                    qci->cl = qci->cl-v;
                }
                if( ae_isfinite(qci->cu, _state) )
                {
                    qci->cu = qci->cu-v;
                }
            }
            knisclearkthreclaim(&vcstats->varbyqc, i, _state);
            
            /*
             * Modify conic constraints
             */
            while(kniscountkth(&vcstats->varbycc, i, _state)>0)
            {
                ccidx = knispoplast(&vcstats->varbycc, i, _state);
                ae_assert(!vcstats->isdroppedcc.ptr.p_bool[ccidx], "PRESOLVER: integrity check 166221 failed", _state);
                ae_obj_array_get(&cc->constraints, ccidx, &_cci, _state);
                xccfactoroutnonaxial(cci, i, vfixed, 0.0, -1, &hadk0, &hadk1, _state);
                ae_assert(hadk0, "PRESOLVER: integrity check 169221 failed", _state);
                vcstats->cntradial.ptr.p_int[i] = vcstats->cntradial.ptr.p_int[i]-1;
                vcstats->nlcpervar.ptr.p_int[i] = vcstats->nlcpervar.ptr.p_int[i]-1;
            }
            knisclearkthreclaim(&vcstats->varbycc, i, _state);
        }
    }
    if( !*somethingchanged )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * If linear constraints are present, clear them
     */
    if( m>0 )
    {
        
        /*
         * Clear columns of AT corresponding to fixed variables
         */
        for(jj=0; jj<=buf->setn.nstored-1; jj++)
        {
            i = buf->setn.items.ptr.p_int[jj];
            lpqppresolve_dyncrsclearrow(at, i, _state);
        }
        
        /*
         * Scan marked rows of A and remove fixed variables
         */
        for(ii=0; ii<=buf->setm.nstored-1; ii++)
        {
            i = buf->setm.items.ptr.p_int[ii];
            offs = a->rowbegin.ptr.p_int[i];
            v = (double)(0);
            for(jj=a->rowbegin.ptr.p_int[i]; jj<=a->rowend.ptr.p_int[i]-1; jj++)
            {
                j = a->idx.ptr.p_int[jj];
                if( buf->setn.locationof.ptr.p_int[j]>=0 )
                {
                    
                    /*
                     * Update constraint bounds with fixed variables, remove from linear constraint
                     */
                    v = v+bndl->ptr.p_double[j]*a->vals.ptr.p_double[jj];
                }
                else
                {
                    
                    /*
                     * Non-fixed variable, retain it
                     */
                    a->idx.ptr.p_int[offs] = j;
                    a->vals.ptr.p_double[offs] = a->vals.ptr.p_double[jj];
                    offs = offs+1;
                }
            }
            a->rowend.ptr.p_int[i] = offs;
            if( ae_isfinite(al->ptr.p_double[i], _state) )
            {
                al->ptr.p_double[i] = al->ptr.p_double[i]-v;
            }
            if( ae_isfinite(au->ptr.p_double[i], _state) )
            {
                au->ptr.p_double[i] = au->ptr.p_double[i]-v;
            }
        }
    }
    
    /*
     * Clear flagged rows/columns of SparseH
     */
    if( hash )
    {
        for(ii=0; ii<=buf->setn.nstored-1; ii++)
        {
            sparseh->rowend.ptr.p_int[buf->setn.items.ptr.p_int[ii]] = sparseh->rowbegin.ptr.p_int[buf->setn.items.ptr.p_int[ii]];
        }
        for(jj=0; jj<=buf->setn2.nstored-1; jj++)
        {
            lpqppresolve_dyncrsremovesetfromrow(sparseh, buf->setn2.items.ptr.p_int[jj], &buf->setn, _state);
        }
    }
    
    /*
     * Clear quadratic constraints
     */
    for(qcidx=0; qcidx<=qccnt-1; qcidx++)
    {
        if( vcstats->isdroppedqc.ptr.p_bool[qcidx] )
        {
            continue;
        }
        ae_obj_array_get(&qc->constraints, qcidx, &_qci, _state);
        nisclear(&buf->setn3, _state);
        for(j=0; j<=qci->nvars-1; j++)
        {
            if( buf->setn.locationof.ptr.p_int[qci->varidx.ptr.p_int[j]]>=0 )
            {
                nisaddelement(&buf->setn3, j, _state);
            }
        }
        if( buf->setn3.nstored==0 )
        {
            continue;
        }
        lpqppresolve_dynqccopy(qci, &buf->qc, _state);
        igrowv(buf->qc.nvars, &buf->tmp0, _state);
        lpqppresolve_dynqccopydeleterowscols(&buf->qc, &buf->setn3, qci, &buf->tmp0, _state);
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Scale cost vector and constraints, normalize cost.

  -- ALGLIB --
     Copyright 01.07.2022 by Bochkanov Sergey
*************************************************************************/
static void lpqppresolve_scaleshiftobjectiveandconstraints(/* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* xorigin,
     ae_int_t n,
     /* Real    */ ae_vector* c,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     sparsematrix* sparseh,
     ae_bool hash,
     sparsematrix* sparsea,
     /* Real    */ ae_vector* al,
     /* Real    */ ae_vector* au,
     ae_int_t m,
     xquadraticconstraints* xqc,
     xconicconstraints* xcc,
     presolverstack* trfstack,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t nv;
    double objscale;
    double v;
    double vv;
    double vshift;
    double rowscale;
    double qcscale;
    double ccscale;
    double maxscale;
    ae_int_t mqc;
    ae_int_t mcc;
    xquadraticconstraint *xqci;
    ae_smart_ptr _xqci;
    xconicconstraint *cci;
    ae_smart_ptr _cci;
    ae_vector bufs;
    ae_vector bufori;
    ae_vector bufqx;
    ae_bool done;

    ae_frame_make(_state, &_frame_block);
    memset(&_xqci, 0, sizeof(_xqci));
    memset(&_cci, 0, sizeof(_cci));
    memset(&bufs, 0, sizeof(bufs));
    memset(&bufori, 0, sizeof(bufori));
    memset(&bufqx, 0, sizeof(bufqx));
    ae_smart_ptr_init(&_xqci, (void**)&xqci, ae_false, _state, ae_true);
    ae_smart_ptr_init(&_cci, (void**)&cci, ae_false, _state, ae_true);
    ae_vector_init(&bufs, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bufori, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bufqx, 0, DT_REAL, _state, ae_true);

    mqc = xqcgetcount(xqc, _state);
    mcc = xccgetcount(xcc, _state);
    
    /*
     * Apply scaling to linear/quadratic term, determine additional scaling due to normalization 
     */
    objscale = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        c->ptr.p_double[i] = c->ptr.p_double[i]*s->ptr.p_double[i];
        objscale = ae_maxreal(objscale, ae_fabs(c->ptr.p_double[i], _state), _state);
    }
    if( hash )
    {
        for(i=0; i<=n-1; i++)
        {
            j0 = sparseh->ridx.ptr.p_int[i];
            j1 = sparseh->ridx.ptr.p_int[i+1]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                sparseh->vals.ptr.p_double[jj] = sparseh->vals.ptr.p_double[jj]*s->ptr.p_double[i]*s->ptr.p_double[sparseh->idx.ptr.p_int[jj]];
                objscale = ae_maxreal(objscale, ae_fabs(sparseh->vals.ptr.p_double[jj], _state), _state);
            }
        }
    }
    objscale = coalesce(objscale, (double)(1), _state);
    rmulv(n, (double)1/objscale, c, _state);
    if( hash )
    {
        rmulv(sparseh->ridx.ptr.p_int[n], (double)1/objscale, &sparseh->vals, _state);
    }
    lpqppresolve_presolverappendcostscaling(trfstack, (double)1/objscale, _state);
    
    /*
     * Apply column scaling/shifting to box constraints
     */
    for(i=0; i<=n-1; i++)
    {
        
        /*
         * Transform problem
         */
        lpqppresolve_presolverappendcolshifting(trfstack, i, -xorigin->ptr.p_double[i], _state);
        lpqppresolve_presolverappendcolscaling(trfstack, i, (double)1/s->ptr.p_double[i], _state);
        bndl->ptr.p_double[i] = (bndl->ptr.p_double[i]-xorigin->ptr.p_double[i])/s->ptr.p_double[i];
        bndu->ptr.p_double[i] = (bndu->ptr.p_double[i]-xorigin->ptr.p_double[i])/s->ptr.p_double[i];
    }
    
    /*
     * Process linear constraints
     */
    maxscale = (double)(0);
    for(i=0; i<=m-1; i++)
    {
        rowscale = (double)(0);
        vshift = (double)(0);
        j0 = sparsea->ridx.ptr.p_int[i];
        j1 = sparsea->ridx.ptr.p_int[i+1]-1;
        for(j=j0; j<=j1; j++)
        {
            vshift = vshift+sparsea->vals.ptr.p_double[j]*xorigin->ptr.p_double[sparsea->idx.ptr.p_int[j]];
            v = s->ptr.p_double[sparsea->idx.ptr.p_int[j]]*sparsea->vals.ptr.p_double[j];
            sparsea->vals.ptr.p_double[j] = v;
            rowscale = ae_maxreal(rowscale, ae_fabs(v, _state), _state);
        }
        al->ptr.p_double[i] = al->ptr.p_double[i]-vshift;
        au->ptr.p_double[i] = au->ptr.p_double[i]-vshift;
        maxscale = ae_maxreal(maxscale, rowscale, _state);
        rowscale = ae_maxreal(rowscale, 1.0, _state);
        
        /*
         * Apply transformation to A/AL/AU
         */
        v = (double)1/rowscale;
        for(j=j0; j<=j1; j++)
        {
            sparsea->vals.ptr.p_double[j] = v*sparsea->vals.ptr.p_double[j];
        }
        al->ptr.p_double[i] = al->ptr.p_double[i]*v;
        au->ptr.p_double[i] = au->ptr.p_double[i]*v;
        lpqppresolve_presolverappendrowscaling(trfstack, i, (double)1/rowscale, _state);
    }
    
    /*
     * Process quadratic constraints
     */
    maxscale = (double)(0);
    for(i=0; i<=mqc-1; i++)
    {
        ae_obj_array_get(&xqc->constraints, i, &_xqci, _state);
        nv = xqci->nvars;
        if( nv==0 )
        {
            continue;
        }
        rgrowv(nv, &bufs, _state);
        rgrowv(nv, &bufori, _state);
        for(j=0; j<=nv-1; j++)
        {
            bufs.ptr.p_double[j] = s->ptr.p_double[xqci->varidx.ptr.p_int[j]];
            bufori.ptr.p_double[j] = xorigin->ptr.p_double[xqci->varidx.ptr.p_int[j]];
        }
        
        /*
         * First, apply origin:
         *
         *  x := (x-ori) + ori
         *  b'(x+ori) + 0.5(x+ori)'Q(x+ori) = 
         *      = b'x + b'ori + 0.5*[ x'Qx + 2*ori'Qx + ori'*Q*ori ]
         *      = (b+Q*ori)'x + 0.5*x'Qx + [b'ori + 0.5*ori'*Q*ori]
         */
        if( !xqci->applyorigin )
        {
            rallocv(nv, &bufqx, _state);
            sparsesmv(&xqci->lowerq, ae_false, &bufori, &bufqx, _state);
            vshift = rdotv(nv, &xqci->b, &bufori, _state)+0.5*rdotv(nv, &bufori, &bufqx, _state);
            xqci->cl = xqci->cl-vshift;
            xqci->cu = xqci->cu-vshift;
            raddv(nv, 1.0, &bufqx, &xqci->b, _state);
        }
        
        /*
         * Second, apply column scales and normalization
         */
        qcscale = (double)(0);
        for(j=0; j<=nv-1; j++)
        {
            v = xqci->b.ptr.p_double[j]*bufs.ptr.p_double[j];
            xqci->b.ptr.p_double[j] = v;
            qcscale = ae_maxreal(qcscale, ae_fabs(v, _state), _state);
            j0 = xqci->lowerq.ridx.ptr.p_int[j];
            j1 = xqci->lowerq.ridx.ptr.p_int[j+1]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                v = bufs.ptr.p_double[j]*xqci->lowerq.vals.ptr.p_double[jj]*bufs.ptr.p_double[xqci->lowerq.idx.ptr.p_int[jj]];
                xqci->lowerq.vals.ptr.p_double[jj] = v;
                qcscale = ae_maxreal(qcscale, ae_fabs(v, _state), _state);
            }
        }
        maxscale = ae_maxreal(maxscale, qcscale, _state);
        qcscale = ae_maxreal(qcscale, (double)(1), _state);
        if( ae_fp_greater(qcscale,(double)(1)) )
        {
            v = (double)1/qcscale;
            rmulv(nv, v, &xqci->b, _state);
            rmulv(xqci->lowerq.ridx.ptr.p_int[nv], v, &xqci->lowerq.vals, _state);
            xqci->cl = xqci->cl*v;
            xqci->cu = xqci->cu*v;
            lpqppresolve_presolverappendqcscaling(trfstack, i, (double)1/qcscale, _state);
        }
    }
    
    /*
     * Process conic constraints
     */
    maxscale = (double)(0);
    for(i=0; i<=mcc-1; i++)
    {
        ae_obj_array_get(&xcc->constraints, i, &_cci, _state);
        ae_assert(cci->conetype>=0, "PRESOLVER: unexpected constraint type (non-canonic)", _state);
        nv = cci->nvars;
        if( nv==0 )
        {
            continue;
        }
        rgrowv(nv, &bufs, _state);
        rgrowv(nv, &bufori, _state);
        for(j=0; j<=nv-1; j++)
        {
            bufs.ptr.p_double[j] = s->ptr.p_double[cci->varidx.ptr.p_int[j]];
            bufori.ptr.p_double[j] = xorigin->ptr.p_double[cci->varidx.ptr.p_int[j]];
        }
        
        /*
         * First, handle origin, if origin is not included into the constraint
         */
        if( !cci->applyorigin )
        {
            for(j=0; j<=nv-1; j++)
            {
                cci->shftc.ptr.p_double[j] = cci->shftc.ptr.p_double[j]+bufori.ptr.p_double[j]*cci->diaga.ptr.p_double[j];
            }
        }
        
        /*
         * Second, apply column scales and normalization
         */
        done = ae_false;
        if( cci->conetype==xccprimitiveconetype(_state) )
        {
            ccscale = (double)(0);
            for(j=0; j<=nv-1; j++)
            {
                v = cci->diaga.ptr.p_double[j]*bufs.ptr.p_double[j];
                cci->diaga.ptr.p_double[j] = v;
                ccscale = ae_maxreal(ccscale, ae_fabs(v, _state), _state);
            }
            maxscale = ae_maxreal(maxscale, ccscale, _state);
            ccscale = ae_maxreal(ccscale, (double)(1), _state);
            if( ae_fp_greater(ccscale,(double)(1)) )
            {
                v = (double)1/ccscale;
                rmulv(nv, v, &cci->diaga, _state);
                rmulv(nv, v, &cci->shftc, _state);
                cci->shftc.ptr.p_double[nv] = cci->shftc.ptr.p_double[nv]*v*v;
                lpqppresolve_presolverappendccscaling(trfstack, i, (double)1/ccscale, _state);
            }
            done = ae_true;
        }
        if( cci->conetype==xccprimitivepowerconetype(_state) )
        {
            v = (double)(1);
            for(j=nv-cci->kpow; j<=nv-1; j++)
            {
                vv = coalesce(rmaxabs2(cci->diaga.ptr.p_double[j]*bufs.ptr.p_double[j], cci->shftc.ptr.p_double[j], _state), (double)(1), _state);
                v = v*ae_pow(vv, cci->alphapow.ptr.p_double[j-(nv-cci->kpow)], _state);
                cci->diaga.ptr.p_double[j] = cci->diaga.ptr.p_double[j]*bufs.ptr.p_double[j]/vv;
                cci->shftc.ptr.p_double[j] = cci->shftc.ptr.p_double[j]/vv;
            }
            ccscale = v;
            maxscale = ae_maxreal(maxscale, ccscale, _state);
            rmergemulv(nv-cci->kpow, &bufs, &cci->diaga, _state);
            rmulv(nv-cci->kpow, (double)1/ccscale, &cci->diaga, _state);
            rmulv(nv-cci->kpow, (double)1/ccscale, &cci->shftc, _state);
            cci->shftc.ptr.p_double[nv] = cci->shftc.ptr.p_double[nv]/ae_sqr(ccscale, _state);
            lpqppresolve_presolverappendccscaling(trfstack, i, (double)1/ccscale, _state);
            done = ae_true;
        }
        ae_assert(done, "PRESOLVER: integrity check 707240 failed", _state);
    }
    ae_frame_leave(_state);
}


void _dynamiccrs_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    dynamiccrs *p = (dynamiccrs*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->rowbegin, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->rowend, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->idx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->vals, 0, DT_REAL, _state, make_automatic);
}


void _dynamiccrs_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    dynamiccrs       *dst = (dynamiccrs*)_dst;
    const dynamiccrs *src = (const dynamiccrs*)_src;
    dst->m = src->m;
    dst->n = src->n;
    dst->maxallocated = src->maxallocated;
    ae_vector_init_copy(&dst->rowbegin, &src->rowbegin, _state, make_automatic);
    ae_vector_init_copy(&dst->rowend, &src->rowend, _state, make_automatic);
    ae_vector_init_copy(&dst->idx, &src->idx, _state, make_automatic);
    ae_vector_init_copy(&dst->vals, &src->vals, _state, make_automatic);
}


void _dynamiccrs_clear(void* _p)
{
    dynamiccrs *p = (dynamiccrs*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->rowbegin);
    ae_vector_clear(&p->rowend);
    ae_vector_clear(&p->idx);
    ae_vector_clear(&p->vals);
}


void _dynamiccrs_destroy(void* _p)
{
    dynamiccrs *p = (dynamiccrs*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->rowbegin);
    ae_vector_destroy(&p->rowend);
    ae_vector_destroy(&p->idx);
    ae_vector_destroy(&p->vals);
}


void _dynamiccrsqconstraint_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    dynamiccrsqconstraint *p = (dynamiccrsqconstraint*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->varidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->b, 0, DT_REAL, _state, make_automatic);
    _dynamiccrs_init(&p->fullq, _state, make_automatic);
}


void _dynamiccrsqconstraint_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    dynamiccrsqconstraint       *dst = (dynamiccrsqconstraint*)_dst;
    const dynamiccrsqconstraint *src = (const dynamiccrsqconstraint*)_src;
    dst->nvars = src->nvars;
    ae_vector_init_copy(&dst->varidx, &src->varidx, _state, make_automatic);
    ae_vector_init_copy(&dst->b, &src->b, _state, make_automatic);
    _dynamiccrs_init_copy(&dst->fullq, &src->fullq, _state, make_automatic);
    dst->cl = src->cl;
    dst->cu = src->cu;
    dst->applyorigin = src->applyorigin;
}


void _dynamiccrsqconstraint_clear(void* _p)
{
    dynamiccrsqconstraint *p = (dynamiccrsqconstraint*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->varidx);
    ae_vector_clear(&p->b);
    _dynamiccrs_clear(&p->fullq);
}


void _dynamiccrsqconstraint_destroy(void* _p)
{
    dynamiccrsqconstraint *p = (dynamiccrsqconstraint*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->varidx);
    ae_vector_destroy(&p->b);
    _dynamiccrs_destroy(&p->fullq);
}


void _dynamiccrsqconstraints_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    dynamiccrsqconstraints *p = (dynamiccrsqconstraints*)_p;
    ae_touch_ptr((void*)p);
    ae_obj_array_init(&p->constraints, _state, make_automatic);
}


void _dynamiccrsqconstraints_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    dynamiccrsqconstraints       *dst = (dynamiccrsqconstraints*)_dst;
    const dynamiccrsqconstraints *src = (const dynamiccrsqconstraints*)_src;
    ae_obj_array_init_copy(&dst->constraints, &src->constraints, _state, make_automatic);
}


void _dynamiccrsqconstraints_clear(void* _p)
{
    dynamiccrsqconstraints *p = (dynamiccrsqconstraints*)_p;
    ae_touch_ptr((void*)p);
    ae_obj_array_clear(&p->constraints);
}


void _dynamiccrsqconstraints_destroy(void* _p)
{
    dynamiccrsqconstraints *p = (dynamiccrsqconstraints*)_p;
    ae_touch_ptr((void*)p);
    ae_obj_array_destroy(&p->constraints);
}


void _presolvebuffers_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    presolvebuffers *p = (presolvebuffers*)_p;
    ae_touch_ptr((void*)p);
    _niset_init(&p->setn, _state, make_automatic);
    _niset_init(&p->setn2, _state, make_automatic);
    _niset_init(&p->setn3, _state, make_automatic);
    _niset_init(&p->setm, _state, make_automatic);
    ae_vector_init(&p->sparseidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->sparsevals, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fixedlocal, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_INT, _state, make_automatic);
    _dynamiccrsqconstraint_init(&p->qc, _state, make_automatic);
}


void _presolvebuffers_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    presolvebuffers       *dst = (presolvebuffers*)_dst;
    const presolvebuffers *src = (const presolvebuffers*)_src;
    _niset_init_copy(&dst->setn, &src->setn, _state, make_automatic);
    _niset_init_copy(&dst->setn2, &src->setn2, _state, make_automatic);
    _niset_init_copy(&dst->setn3, &src->setn3, _state, make_automatic);
    _niset_init_copy(&dst->setm, &src->setm, _state, make_automatic);
    ae_vector_init_copy(&dst->sparseidx, &src->sparseidx, _state, make_automatic);
    ae_vector_init_copy(&dst->sparsevals, &src->sparsevals, _state, make_automatic);
    ae_vector_init_copy(&dst->fixedlocal, &src->fixedlocal, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    _dynamiccrsqconstraint_init_copy(&dst->qc, &src->qc, _state, make_automatic);
}


void _presolvebuffers_clear(void* _p)
{
    presolvebuffers *p = (presolvebuffers*)_p;
    ae_touch_ptr((void*)p);
    _niset_clear(&p->setn);
    _niset_clear(&p->setn2);
    _niset_clear(&p->setn3);
    _niset_clear(&p->setm);
    ae_vector_clear(&p->sparseidx);
    ae_vector_clear(&p->sparsevals);
    ae_vector_clear(&p->fixedlocal);
    ae_vector_clear(&p->tmp0);
    _dynamiccrsqconstraint_clear(&p->qc);
}


void _presolvebuffers_destroy(void* _p)
{
    presolvebuffers *p = (presolvebuffers*)_p;
    ae_touch_ptr((void*)p);
    _niset_destroy(&p->setn);
    _niset_destroy(&p->setn2);
    _niset_destroy(&p->setn3);
    _niset_destroy(&p->setm);
    ae_vector_destroy(&p->sparseidx);
    ae_vector_destroy(&p->sparsevals);
    ae_vector_destroy(&p->fixedlocal);
    ae_vector_destroy(&p->tmp0);
    _dynamiccrsqconstraint_destroy(&p->qc);
}


void _presolverstack_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    presolverstack *p = (presolverstack*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->trftype, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->idata, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->rdata, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->idataridx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->rdataridx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->sparseidx0, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->sparseidx1, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->sparseidx2, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->sparseidx3, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->sparseval0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sparseval1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sparseval2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sparseval3, 0, DT_REAL, _state, make_automatic);
}


void _presolverstack_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    presolverstack       *dst = (presolverstack*)_dst;
    const presolverstack *src = (const presolverstack*)_src;
    dst->n = src->n;
    dst->m = src->m;
    dst->mqc = src->mqc;
    dst->mcc = src->mcc;
    dst->ntrf = src->ntrf;
    ae_vector_init_copy(&dst->trftype, &src->trftype, _state, make_automatic);
    ae_vector_init_copy(&dst->idata, &src->idata, _state, make_automatic);
    ae_vector_init_copy(&dst->rdata, &src->rdata, _state, make_automatic);
    ae_vector_init_copy(&dst->idataridx, &src->idataridx, _state, make_automatic);
    ae_vector_init_copy(&dst->rdataridx, &src->rdataridx, _state, make_automatic);
    dst->sourceidx = src->sourceidx;
    dst->isrc = src->isrc;
    dst->rsrc = src->rsrc;
    ae_vector_init_copy(&dst->sparseidx0, &src->sparseidx0, _state, make_automatic);
    ae_vector_init_copy(&dst->sparseidx1, &src->sparseidx1, _state, make_automatic);
    ae_vector_init_copy(&dst->sparseidx2, &src->sparseidx2, _state, make_automatic);
    ae_vector_init_copy(&dst->sparseidx3, &src->sparseidx3, _state, make_automatic);
    ae_vector_init_copy(&dst->sparseval0, &src->sparseval0, _state, make_automatic);
    ae_vector_init_copy(&dst->sparseval1, &src->sparseval1, _state, make_automatic);
    ae_vector_init_copy(&dst->sparseval2, &src->sparseval2, _state, make_automatic);
    ae_vector_init_copy(&dst->sparseval3, &src->sparseval3, _state, make_automatic);
}


void _presolverstack_clear(void* _p)
{
    presolverstack *p = (presolverstack*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->trftype);
    ae_vector_clear(&p->idata);
    ae_vector_clear(&p->rdata);
    ae_vector_clear(&p->idataridx);
    ae_vector_clear(&p->rdataridx);
    ae_vector_clear(&p->sparseidx0);
    ae_vector_clear(&p->sparseidx1);
    ae_vector_clear(&p->sparseidx2);
    ae_vector_clear(&p->sparseidx3);
    ae_vector_clear(&p->sparseval0);
    ae_vector_clear(&p->sparseval1);
    ae_vector_clear(&p->sparseval2);
    ae_vector_clear(&p->sparseval3);
}


void _presolverstack_destroy(void* _p)
{
    presolverstack *p = (presolverstack*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->trftype);
    ae_vector_destroy(&p->idata);
    ae_vector_destroy(&p->rdata);
    ae_vector_destroy(&p->idataridx);
    ae_vector_destroy(&p->rdataridx);
    ae_vector_destroy(&p->sparseidx0);
    ae_vector_destroy(&p->sparseidx1);
    ae_vector_destroy(&p->sparseidx2);
    ae_vector_destroy(&p->sparseidx3);
    ae_vector_destroy(&p->sparseval0);
    ae_vector_destroy(&p->sparseval1);
    ae_vector_destroy(&p->sparseval2);
    ae_vector_destroy(&p->sparseval3);
}


void _presolvervcstats_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    presolvervcstats *p = (presolvervcstats*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->isdroppedcol, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->isdroppedlc, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->isdroppedqc, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->isdroppedcc, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->cntdownwardaxial, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->cntupwardaxial, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->cntaxial, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->cntradial, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->nlcpervar, 0, DT_INT, _state, make_automatic);
    _kniset_init(&p->varbyqc, _state, make_automatic);
    _kniset_init(&p->varbycc, _state, make_automatic);
}


void _presolvervcstats_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    presolvervcstats       *dst = (presolvervcstats*)_dst;
    const presolvervcstats *src = (const presolvervcstats*)_src;
    ae_vector_init_copy(&dst->isdroppedcol, &src->isdroppedcol, _state, make_automatic);
    ae_vector_init_copy(&dst->isdroppedlc, &src->isdroppedlc, _state, make_automatic);
    ae_vector_init_copy(&dst->isdroppedqc, &src->isdroppedqc, _state, make_automatic);
    ae_vector_init_copy(&dst->isdroppedcc, &src->isdroppedcc, _state, make_automatic);
    ae_vector_init_copy(&dst->cntdownwardaxial, &src->cntdownwardaxial, _state, make_automatic);
    ae_vector_init_copy(&dst->cntupwardaxial, &src->cntupwardaxial, _state, make_automatic);
    ae_vector_init_copy(&dst->cntaxial, &src->cntaxial, _state, make_automatic);
    ae_vector_init_copy(&dst->cntradial, &src->cntradial, _state, make_automatic);
    ae_vector_init_copy(&dst->nlcpervar, &src->nlcpervar, _state, make_automatic);
    _kniset_init_copy(&dst->varbyqc, &src->varbyqc, _state, make_automatic);
    _kniset_init_copy(&dst->varbycc, &src->varbycc, _state, make_automatic);
}


void _presolvervcstats_clear(void* _p)
{
    presolvervcstats *p = (presolvervcstats*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->isdroppedcol);
    ae_vector_clear(&p->isdroppedlc);
    ae_vector_clear(&p->isdroppedqc);
    ae_vector_clear(&p->isdroppedcc);
    ae_vector_clear(&p->cntdownwardaxial);
    ae_vector_clear(&p->cntupwardaxial);
    ae_vector_clear(&p->cntaxial);
    ae_vector_clear(&p->cntradial);
    ae_vector_clear(&p->nlcpervar);
    _kniset_clear(&p->varbyqc);
    _kniset_clear(&p->varbycc);
}


void _presolvervcstats_destroy(void* _p)
{
    presolvervcstats *p = (presolvervcstats*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->isdroppedcol);
    ae_vector_destroy(&p->isdroppedlc);
    ae_vector_destroy(&p->isdroppedqc);
    ae_vector_destroy(&p->isdroppedcc);
    ae_vector_destroy(&p->cntdownwardaxial);
    ae_vector_destroy(&p->cntupwardaxial);
    ae_vector_destroy(&p->cntaxial);
    ae_vector_destroy(&p->cntradial);
    ae_vector_destroy(&p->nlcpervar);
    _kniset_destroy(&p->varbyqc);
    _kniset_destroy(&p->varbycc);
}


void _presolveinfo_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    presolveinfo *p = (presolveinfo*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->rawc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawbndu, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->rawh, _state, make_automatic);
    _sparsematrix_init(&p->rawa, _state, make_automatic);
    _xquadraticconstraints_init(&p->rawxqc, _state, make_automatic);
    _xconicconstraints_init(&p->rawxcc, _state, make_automatic);
    ae_vector_init(&p->lagrangefromresidual, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->c, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparseh, _state, make_automatic);
    _sparsematrix_init(&p->sparsea, _state, make_automatic);
    ae_vector_init(&p->al, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->au, 0, DT_REAL, _state, make_automatic);
    _xquadraticconstraints_init(&p->xqc, _state, make_automatic);
    _xconicconstraints_init(&p->xcc, _state, make_automatic);
    ae_vector_init(&p->packxperm, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->packyperm, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->packqcperm, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->packccperm, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->packstatperm, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->unpackxperm, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->unpackyperm, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->unpackqcperm, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->unpackccperm, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->unpackstatperm, 0, DT_INT, _state, make_automatic);
    _presolverstack_init(&p->trfstack, _state, make_automatic);
    ae_vector_init(&p->s1, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->bc1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->y1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->yqc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpi, 0, DT_INT, _state, make_automatic);
    _presolvebuffers_init(&p->buf, _state, make_automatic);
}


void _presolveinfo_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    presolveinfo       *dst = (presolveinfo*)_dst;
    const presolveinfo *src = (const presolveinfo*)_src;
    dst->eps = src->eps;
    dst->newn = src->newn;
    dst->oldn = src->oldn;
    dst->oldmqc = src->oldmqc;
    dst->oldmcc = src->oldmcc;
    dst->newm = src->newm;
    dst->oldm = src->oldm;
    dst->newmqc = src->newmqc;
    dst->newmcc = src->newmcc;
    ae_vector_init_copy(&dst->rawc, &src->rawc, _state, make_automatic);
    ae_vector_init_copy(&dst->rawbndl, &src->rawbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->rawbndu, &src->rawbndu, _state, make_automatic);
    dst->hash = src->hash;
    _sparsematrix_init_copy(&dst->rawh, &src->rawh, _state, make_automatic);
    _sparsematrix_init_copy(&dst->rawa, &src->rawa, _state, make_automatic);
    _xquadraticconstraints_init_copy(&dst->rawxqc, &src->rawxqc, _state, make_automatic);
    _xconicconstraints_init_copy(&dst->rawxcc, &src->rawxcc, _state, make_automatic);
    dst->problemstatus = src->problemstatus;
    ae_vector_init_copy(&dst->lagrangefromresidual, &src->lagrangefromresidual, _state, make_automatic);
    ae_vector_init_copy(&dst->c, &src->c, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparseh, &src->sparseh, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsea, &src->sparsea, _state, make_automatic);
    ae_vector_init_copy(&dst->al, &src->al, _state, make_automatic);
    ae_vector_init_copy(&dst->au, &src->au, _state, make_automatic);
    _xquadraticconstraints_init_copy(&dst->xqc, &src->xqc, _state, make_automatic);
    _xconicconstraints_init_copy(&dst->xcc, &src->xcc, _state, make_automatic);
    ae_vector_init_copy(&dst->packxperm, &src->packxperm, _state, make_automatic);
    ae_vector_init_copy(&dst->packyperm, &src->packyperm, _state, make_automatic);
    ae_vector_init_copy(&dst->packqcperm, &src->packqcperm, _state, make_automatic);
    ae_vector_init_copy(&dst->packccperm, &src->packccperm, _state, make_automatic);
    ae_vector_init_copy(&dst->packstatperm, &src->packstatperm, _state, make_automatic);
    ae_vector_init_copy(&dst->unpackxperm, &src->unpackxperm, _state, make_automatic);
    ae_vector_init_copy(&dst->unpackyperm, &src->unpackyperm, _state, make_automatic);
    ae_vector_init_copy(&dst->unpackqcperm, &src->unpackqcperm, _state, make_automatic);
    ae_vector_init_copy(&dst->unpackccperm, &src->unpackccperm, _state, make_automatic);
    ae_vector_init_copy(&dst->unpackstatperm, &src->unpackstatperm, _state, make_automatic);
    _presolverstack_init_copy(&dst->trfstack, &src->trfstack, _state, make_automatic);
    ae_vector_init_copy(&dst->s1, &src->s1, _state, make_automatic);
    ae_vector_init_copy(&dst->bc1, &src->bc1, _state, make_automatic);
    ae_vector_init_copy(&dst->x1, &src->x1, _state, make_automatic);
    ae_vector_init_copy(&dst->y1, &src->y1, _state, make_automatic);
    ae_vector_init_copy(&dst->yqc, &src->yqc, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpi, &src->tmpi, _state, make_automatic);
    _presolvebuffers_init_copy(&dst->buf, &src->buf, _state, make_automatic);
}


void _presolveinfo_clear(void* _p)
{
    presolveinfo *p = (presolveinfo*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->rawc);
    ae_vector_clear(&p->rawbndl);
    ae_vector_clear(&p->rawbndu);
    _sparsematrix_clear(&p->rawh);
    _sparsematrix_clear(&p->rawa);
    _xquadraticconstraints_clear(&p->rawxqc);
    _xconicconstraints_clear(&p->rawxcc);
    ae_vector_clear(&p->lagrangefromresidual);
    ae_vector_clear(&p->c);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    _sparsematrix_clear(&p->sparseh);
    _sparsematrix_clear(&p->sparsea);
    ae_vector_clear(&p->al);
    ae_vector_clear(&p->au);
    _xquadraticconstraints_clear(&p->xqc);
    _xconicconstraints_clear(&p->xcc);
    ae_vector_clear(&p->packxperm);
    ae_vector_clear(&p->packyperm);
    ae_vector_clear(&p->packqcperm);
    ae_vector_clear(&p->packccperm);
    ae_vector_clear(&p->packstatperm);
    ae_vector_clear(&p->unpackxperm);
    ae_vector_clear(&p->unpackyperm);
    ae_vector_clear(&p->unpackqcperm);
    ae_vector_clear(&p->unpackccperm);
    ae_vector_clear(&p->unpackstatperm);
    _presolverstack_clear(&p->trfstack);
    ae_vector_clear(&p->s1);
    ae_vector_clear(&p->bc1);
    ae_vector_clear(&p->x1);
    ae_vector_clear(&p->y1);
    ae_vector_clear(&p->yqc);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->tmpi);
    _presolvebuffers_clear(&p->buf);
}


void _presolveinfo_destroy(void* _p)
{
    presolveinfo *p = (presolveinfo*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->rawc);
    ae_vector_destroy(&p->rawbndl);
    ae_vector_destroy(&p->rawbndu);
    _sparsematrix_destroy(&p->rawh);
    _sparsematrix_destroy(&p->rawa);
    _xquadraticconstraints_destroy(&p->rawxqc);
    _xconicconstraints_destroy(&p->rawxcc);
    ae_vector_destroy(&p->lagrangefromresidual);
    ae_vector_destroy(&p->c);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    _sparsematrix_destroy(&p->sparseh);
    _sparsematrix_destroy(&p->sparsea);
    ae_vector_destroy(&p->al);
    ae_vector_destroy(&p->au);
    _xquadraticconstraints_destroy(&p->xqc);
    _xconicconstraints_destroy(&p->xcc);
    ae_vector_destroy(&p->packxperm);
    ae_vector_destroy(&p->packyperm);
    ae_vector_destroy(&p->packqcperm);
    ae_vector_destroy(&p->packccperm);
    ae_vector_destroy(&p->packstatperm);
    ae_vector_destroy(&p->unpackxperm);
    ae_vector_destroy(&p->unpackyperm);
    ae_vector_destroy(&p->unpackqcperm);
    ae_vector_destroy(&p->unpackccperm);
    ae_vector_destroy(&p->unpackstatperm);
    _presolverstack_destroy(&p->trfstack);
    ae_vector_destroy(&p->s1);
    ae_vector_destroy(&p->bc1);
    ae_vector_destroy(&p->x1);
    ae_vector_destroy(&p->y1);
    ae_vector_destroy(&p->yqc);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->tmpi);
    _presolvebuffers_destroy(&p->buf);
}


#endif
#if defined(AE_COMPILE_MINQP) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
                    CONSTRAINED QUADRATIC PROGRAMMING

The subroutine creates QP optimizer. After initial creation,  it  contains
default optimization problem with zero quadratic and linear terms  and  no
constraints.

In order to actually solve something you should:

specify objective:
* set linear term with minqpsetlinearterm()
* set quadratic term with minqpsetquadraticterm() or
  minqpsetquadratictermsparse()
  
specify constraints:
* set variable bounds with minqpsetbc() or minqpsetbcall()
* specify linear constraint matrix with one of the following functions:
  * modern API:
    * minqpsetlc2()       for sparse two-sided constraints AL <= A*x <= AU
    * minqpsetlc2dense()  for dense  two-sided constraints AL <= A*x <= AU
    * minqpsetlc2mixed()  for mixed  two-sided constraints AL <= A*x <= AU
    * minqpaddlc2dense()  to add one dense row to the dense constraint submatrix
    * minqpaddlc2()       to add one sparse row to the sparse constraint submatrix
    * minqpaddlc2sparsefromdense() to add one sparse row (passed as a dense array) to the sparse constraint submatrix
  * legacy API:
    * minqpsetlc()        for dense one-sided equality/inequality constraints
    * minqpsetlcsparse()  for sparse one-sided equality/inequality constraints
    * minqpsetlcmixed()   for mixed dense/sparse one-sided equality/inequality constraints
* add two-sided quadratic constraint(s) of the form CL <= b'x+0.5*x'Qx <= CU
  with one of the following functions:
  * minqpaddqc2()         for a quadratic constraint given by a sparse
                          matrix structure; has O(max(N,NNZ)) memory and
                          running time requirements.
  * minqpaddqc2dense()    for a quadratic constraint given by a dense
                          matrix; has O(N^2) memory and running time requirements.
  * minqpaddqc2list()     for a sparse quadratic constraint given by
                          a list of non-zero  entries;  has  O(NNZ) memory
                          and O(NNZ*logNNZ)  running   time  requirements,
                          ideal for constraints  with  much  less  than  N
                          nonzero elements.
* add second order cone constraints with:
  * minqpaddsoccprimitive()     for a primitive second order cone constraint
  * minqpaddsoccorthogonal()    for an axis-orthogonal second order cone constraint
* add power cone constraints with:
  * minqpaddpowccprimitive()    for a primitive power cone constraint
  * minqpaddpowccorthogonal()   for an axis-orthogonal power cone constraint

configure and run QP solver:
* choose appropriate QP solver and set it  and  its stopping  criteria  by
  means of minqpsetalgo??????() function
* call minqpoptimize() to run the solver and  minqpresults()  to  get  the
  solution vector and additional information.

Following solvers are recommended for  convex  and  semidefinite  problems
with box and linear constraints:
* QuickQP for small  dense  problems  with  box-only  constraints  (or  no
  constraints at all)
* DENSE-IPM-QP for  convex  or  semidefinite  problems  with   medium  (up
  to several thousands) variable count, dense/sparse  quadratic  term  and
  any number  (up  to  many  thousands)  of  dense/sparse  general  linear
  constraints
* SPARSE-IPM-QP for convex  or  semidefinite  problems  with   large (many
  thousands) variable count, sparse quadratic term AND linear constraints.
* SPARSE-ECQP for convex having only  linear  equality  constraints.  This
  specialized solver can be orders of magnitude faster than IPM.

If your problem happens to be nonconvex or has nonlinear constraints, then
you can use:
* DENSE-GENIPM or SPARSE-GENIPM  solver  which  supports  convex/nonconvex
  QP   problems  with  box,  linear,  quadratic  equality/inequality   and
  conic constraints.
* HYBRID-GENIPM solver which is optimized  for  well-conditioned  problems
  with large dense quadratic term/constraints or for problems  with  large
  sparse quadratic terms having prohibitively dense Cholesky factors.  The
  solver uses limited-memory SR1 quadratic model to accelerate Newton step.
* QuickQP for small dense nonconvex problems with box-only constraints

INPUT PARAMETERS:
    N       -   problem size
    
OUTPUT PARAMETERS:
    State   -   optimizer with zero quadratic/linear terms
                and no constraints

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpcreate(ae_int_t n, minqpstate* state, ae_state *_state)
{
    ae_int_t i;

    _minqpstate_clear(state);

    ae_assert(n>=1, "MinQPCreate: N<1", _state);
    
    /*
     * initialize QP solver
     */
    state->n = n;
    state->repterminationtype = 0;
    state->repf = (double)(0);
    state->absamax = (double)(1);
    state->absasum = (double)(1);
    state->absasum2 = (double)(1);
    state->cqmready = ae_false;
    state->akind = 1;
    state->sparseaupper = ae_false;
    state->sparsea.m = n;
    state->sparsea.n = n;
    isetallocv(n+1, 0, &state->sparsea.ridx, _state);
    sparsecreatecrsinplace(&state->sparsea, _state);
    ae_vector_set_length(&state->b, n, _state);
    ae_vector_set_length(&state->bndl, n, _state);
    ae_vector_set_length(&state->bndu, n, _state);
    ae_vector_set_length(&state->havebndl, n, _state);
    ae_vector_set_length(&state->havebndu, n, _state);
    ae_vector_set_length(&state->s, n, _state);
    ae_vector_set_length(&state->startx, n, _state);
    ae_vector_set_length(&state->xorigin, n, _state);
    ae_vector_set_length(&state->xs, n, _state);
    rvectorsetlengthatleast(&state->replagbc, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->bndl.ptr.p_double[i] = _state->v_neginf;
        state->bndu.ptr.p_double[i] = _state->v_posinf;
        state->havebndl.ptr.p_bool[i] = ae_false;
        state->havebndu.ptr.p_bool[i] = ae_false;
        state->b.ptr.p_double[i] = 0.0;
        state->startx.ptr.p_double[i] = 0.0;
        state->xorigin.ptr.p_double[i] = 0.0;
        state->s.ptr.p_double[i] = 1.0;
        state->replagbc.ptr.p_double[i] = 0.0;
    }
    state->stype = 0;
    state->havex = ae_false;
    minqpsetalgosparseipm(state, 0.0, _state);
    qqploaddefaults(n, &state->qqpsettingsuser, _state);
    qpdenseaulloaddefaults(n, &state->qpdenseaulsettingsuser, _state);
    xlcinit(n, &state->xlc, _state);
    xqcinit(n, &state->xqc, _state);
    xccinit(n, &state->xcc, _state);
    state->dbgskipconstraintnormalization = ae_false;
    state->dbgdopresolve = ae_true;
    state->veps = 0.0;
    state->vmemlen = 0;
    state->vmaxoffdiag = 0;
    state->gmaxits = 0;
    state->repn = 0;
    state->repm = 0;
    state->repmqc = 0;
    state->repmcc = 0;
    
    /*
     * Prepare buffers
     */
    ae_nxpool_alloc(&state->n1realpool, n+1, _state);
    ae_nxpool_alloc(&state->n1intpool, n+1, _state);
}


/*************************************************************************
This function sets linear term for QP solver.

By default, linear term is zero.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    B       -   linear term, array[N].

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetlinearterm(minqpstate* state,
     /* Real    */ const ae_vector* b,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(b->cnt>=n, "MinQPSetLinearTerm: Length(B)<N", _state);
    ae_assert(isfinitevector(b, n, _state), "MinQPSetLinearTerm: B contains infinite or NaN elements", _state);
    minqpsetlineartermfast(state, b, _state);
}


/*************************************************************************
This  function  sets  dense  quadratic  term  for  QP solver. By  default,
quadratic term is zero.

IMPORTANT:

This solver minimizes following  function:
    f(x) = 0.5*x'*A*x + b'*x.
Note that quadratic term has 0.5 before it. So if  you  want  to  minimize
    f(x) = x^2 + x
you should rewrite your problem as follows:
    f(x) = 0.5*(2*x^2) + x
and your matrix A will be equal to [[2.0]], not to [[1.0]]

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    A       -   matrix, array[N,N]
    IsUpper -   storage type:
                * if True, symmetric matrix  A  is  given  by  its  upper
                  triangle, and the lower triangle isn't used
                * if False, symmetric matrix  A  is  given  by  its lower
                  triangle, and the upper triangle isn't used

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetquadraticterm(minqpstate* state,
     /* Real    */ const ae_matrix* a,
     ae_bool isupper,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(a->rows>=n, "MinQPSetQuadraticTerm: Rows(A)<N", _state);
    ae_assert(a->cols>=n, "MinQPSetQuadraticTerm: Cols(A)<N", _state);
    ae_assert(isfinitertrmatrix(a, n, isupper, _state), "MinQPSetQuadraticTerm: A contains infinite or NaN elements", _state);
    minqpsetquadratictermfast(state, a, isupper, 0.0, _state);
}


/*************************************************************************
This  function  sets  sparse  quadratic  term  for  QP solver. By default,
quadratic  term  is  zero.  This  function  overrides  previous  calls  to
minqpsetquadraticterm() or minqpsetquadratictermsparse().

NOTE: dense solvers like DENSE-AUL-QP or DENSE-IPM-QP  will  convert  this
      matrix to dense storage anyway.

IMPORTANT:

This solver minimizes following  function:
    f(x) = 0.5*x'*A*x + b'*x.
Note that quadratic term has 0.5 before it. So if  you  want  to  minimize
    f(x) = x^2 + x
you should rewrite your problem as follows:
    f(x) = 0.5*(2*x^2) + x
and your matrix A will be equal to [[2.0]], not to [[1.0]]

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    A       -   matrix, array[N,N]
    IsUpper -   (optional) storage type:
                * if True, symmetric matrix  A  is  given  by  its  upper
                  triangle, and the lower triangle isn't used
                * if False, symmetric matrix  A  is  given  by  its lower
                  triangle, and the upper triangle isn't used
                * if not given, both lower and upper  triangles  must  be
                  filled.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetquadratictermsparse(minqpstate* state,
     const sparsematrix* a,
     ae_bool isupper,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t t0;
    ae_int_t t1;
    ae_int_t i;
    ae_int_t j;
    double v;


    n = state->n;
    ae_assert(sparsegetnrows(a, _state)==n, "MinQPSetQuadraticTermSparse: Rows(A)<>N", _state);
    ae_assert(sparsegetncols(a, _state)==n, "MinQPSetQuadraticTermSparse: Cols(A)<>N", _state);
    sparsecopytocrsbuf(a, &state->sparsea, _state);
    state->sparseaupper = isupper;
    state->akind = 1;
    
    /*
     * Estimate norm of A
     * (it will be used later in the quadratic penalty function)
     */
    state->absamax = (double)(0);
    state->absasum = (double)(0);
    state->absasum2 = (double)(0);
    t0 = 0;
    t1 = 0;
    while(sparseenumerate(a, &t0, &t1, &i, &j, &v, _state))
    {
        if( i==j )
        {
            
            /*
             * Diagonal terms are counted only once
             */
            state->absamax = ae_maxreal(state->absamax, v, _state);
            state->absasum = state->absasum+v;
            state->absasum2 = state->absasum2+v*v;
        }
        if( (j>i&&isupper)||(j<i&&!isupper) )
        {
            
            /*
             * Offdiagonal terms are counted twice
             */
            state->absamax = ae_maxreal(state->absamax, v, _state);
            state->absasum = state->absasum+(double)2*v;
            state->absasum2 = state->absasum2+(double)2*v*v;
        }
    }
}


/*************************************************************************
This function sets starting point for QP solver. It is useful to have good
initial approximation to the solution, because it will increase  speed  of
convergence and identification of active constraints.

NOTE: interior point solvers ignore initial point provided by user.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    X       -   starting point, array[N].

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetstartingpoint(minqpstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(x->cnt>=n, "MinQPSetStartingPoint: Length(B)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinQPSetStartingPoint: X contains infinite or NaN elements", _state);
    minqpsetstartingpointfast(state, x, _state);
}


/*************************************************************************
This  function sets origin for QP solver. By default, following QP program
is solved:

    min(0.5*x'*A*x+b'*x)
    
This function allows to solve a different problem:

    min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin))
    
Specification of non-zero origin  affects  function  being  minimized  and
quadratic/conic constraints, but not box and linear constraints which  are
still calculated without origin.
    
INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    XOrigin -   origin, array[N].

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetorigin(minqpstate* state,
     /* Real    */ const ae_vector* xorigin,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(xorigin->cnt>=n, "MinQPSetOrigin: Length(B)<N", _state);
    ae_assert(isfinitevector(xorigin, n, _state), "MinQPSetOrigin: B contains infinite or NaN elements", _state);
    minqpsetoriginfast(state, xorigin, _state);
}


/*************************************************************************
This function sets scaling coefficients.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison  with  tolerances)  and  as
preconditioner.

Scale of the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the
   function

If you do not know how to choose scales of your variables, you can:
* read www.alglib.net/optimization/scaling.php article
* use minqpsetscaleautodiag(), which calculates scale  using  diagonal  of
  the  quadratic  term:  S  is  set to 1/sqrt(diag(A)), which works well
  sometimes.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetscale(minqpstate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(s->cnt>=state->n, "MinQPSetScale: Length(S)<N", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "MinQPSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "MinQPSetScale: S contains zero elements", _state);
    }
    for(i=0; i<=state->n-1; i++)
    {
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
    state->stype = 0;
}


/*************************************************************************
This function sets automatic evaluation of variable scaling.

IMPORTANT: this function works only for  matrices  with positive  diagonal
           elements! Zero or negative elements will  result  in  -9  error
           code  being  returned.  Specify  scale  vector  manually   with
           minqpsetscale() in such cases.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison  with  tolerances)  and  as
preconditioner.

The  best  way  to  set  scaling  is  to manually specify variable scales.
However, sometimes you just need quick-and-dirty solution  -  either  when
you perform fast prototyping, or when you know your problem well  and  you
are 100% sure that this quick solution is robust enough in your case.

One such solution is to evaluate scale of I-th variable as 1/Sqrt(A[i,i]),
where A[i,i] is an I-th diagonal element of the quadratic term.

Such approach works well sometimes, but you have to be careful here.

INPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 26.12.2017 by Bochkanov Sergey
*************************************************************************/
void minqpsetscaleautodiag(minqpstate* state, ae_state *_state)
{


    state->stype = 1;
}


/*************************************************************************
This function tells QP solver to use DENSE-AUL algorithm and sets stopping
criteria for the algorithm.

This  algorithm  is  intended  for  non-convex problems with moderate  (up
to several thousands) variable count and arbitrary number  of  constraints
which are either (a) effectively convexified under constraints or (b) have
unique solution even with nonconvex target.

IMPORTANT: when DENSE-IPM solver is applicable, its performance is usually
           much better than that of DENSE-AUL.
           We recommend  you to use DENSE-AUL only when other solvers  can
           not be used.

ALGORITHM FEATURES:

* supports  box  and  dense/sparse  general   linear   equality/inequality
  constraints
* convergence is theoretically proved for positive-definite  (convex)   QP
  problems. Semidefinite and non-convex problems can be solved as long  as
  they  are   bounded  from  below  under  constraints,  although  without
  theoretical guarantees.

ALGORITHM OUTLINE:

* this  algorithm   is   an   augmented   Lagrangian   method  with  dense
  preconditioner (hence  its  name).
* it performs several outer iterations in order to refine  values  of  the
  Lagrange multipliers. Single outer  iteration  is  a  solution  of  some
  unconstrained optimization problem: first  it  performs  dense  Cholesky
  factorization of the Hessian in order to build preconditioner  (adaptive
  regularization is applied to enforce positive  definiteness),  and  then
  it uses L-BFGS optimizer to solve optimization problem.
* typically you need about 5-10 outer iterations to converge to solution
  
ALGORITHM LIMITATIONS:

* because dense Cholesky driver is used, this algorithm has O(N^2)  memory
  requirements and O(OuterIterations*N^3) minimum running time.  From  the
  practical  point  of  view,  it  limits  its  applicability  by  several
  thousands of variables.
  From  the  other  side,  variables  count  is  the most limiting factor,
  and dependence on constraint count is  much  more  lower. Assuming  that
  constraint matrix is sparse, it may handle tens of thousands  of general
  linear constraints.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsX    -   >=0, stopping criteria for inner optimizer.
                Inner  iterations  are  stopped  when  step  length  (with
                variable scaling being applied) is less than EpsX.
                See  minqpsetscale()  for  more  information  on  variable
                scaling.
    Rho     -   penalty coefficient, Rho>0:
                * large enough  that  algorithm  converges  with   desired
                  precision.
                * not TOO large to prevent ill-conditioning
                * recommended values are 100, 1000 or 10000
    ItsCnt  -   number of outer iterations:
                * recommended values: 10-15 (although  in  most  cases  it
                  converges within 5 iterations, you may need a  few  more
                  to be sure).
                * ItsCnt=0 means that small number of outer iterations  is
                  automatically chosen (10 iterations in current version).
                * ItsCnt=1 means that AUL algorithm performs just as usual
                  penalty method.
                * ItsCnt>1 means that  AUL  algorithm  performs  specified
                  number of outer iterations

IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT!

NOTE: Passing  EpsX=0  will  lead  to  automatic  step  length  selection
      (specific step length chosen may change in the future  versions  of
      ALGLIB, so it is better to specify step length explicitly).

  -- ALGLIB --
     Copyright 20.08.2016 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgodenseaul(minqpstate* state,
     double epsx,
     double rho,
     ae_int_t itscnt,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsx, _state), "MinQPSetAlgoDenseAUL: EpsX is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "MinQPSetAlgoDenseAUL: negative EpsX", _state);
    ae_assert(ae_isfinite(rho, _state), "MinQPSetAlgoDenseAUL: Rho is not finite number", _state);
    ae_assert(ae_fp_greater(rho,(double)(0)), "MinQPSetAlgoDenseAUL: non-positive Rho", _state);
    ae_assert(itscnt>=0, "MinQPSetAlgoDenseAUL: negative ItsCnt!", _state);
    state->algokind = 4;
    if( ae_fp_eq(epsx,(double)(0)) )
    {
        epsx = 1.0E-8;
    }
    if( itscnt==0 )
    {
        itscnt = 10;
    }
    state->qpdenseaulsettingsuser.epsx = epsx;
    state->qpdenseaulsettingsuser.outerits = itscnt;
    state->qpdenseaulsettingsuser.rho = rho;
}


/*************************************************************************
This function tells QP solver to  use  DENSE-IPM  QP  algorithm  and  sets
stopping criteria for the algorithm.

This  algorithm  is  intended for convex and semidefinite QP (but not QCQP
or conic) problems with moderate (up to several thousands) variable  count
and arbitrary number of linear constraints. Quadratic and conic constraints
are supported by another solver (DENSE-GENIPM).

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). It can also use  a  performance  backend
           (e.g.  Intel  MKL  or  another  platform-specific  library)  to
           accelerate dense factorization.
           
           Dense Cholesky factorization is a well  studied  and  optimized
           algorithm. ALGLIB  includes  a  good  implementation;  however,
           using a hardware vendor-provided  performance  library  usually
           results in a better performance.
           
           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.

IMPORTANT: this  algorithm  is  likely  to  fail  on  nonconvex  problems,
           furthermore, sometimes it fails without a notice. If you try to
           run DENSE-IPM on a problem  with  indefinite  matrix (a  matrix
           having  at least one negative eigenvalue) then depending on the
           circumstances it may either (a) stall at some  arbitrary point,
           or (b) throw an exception due to the failure  of  the  Cholesky
           decomposition.
           
           Use GENIPM algorithm if  your  problem  is  nonconvex  or has a
           potential of becoming  nonconvex.  The GENIPM solver  can  also
           handle problems with quadratic and conic constraints.

ALGORITHM FEATURES:

* supports  box  and  dense/sparse  general   linear   equality/inequality
  constraints

ALGORITHM OUTLINE:

* this  algorithm  is  our implementation  of  interior  point  method  as
  formulated by  R.J.Vanderbei, with minor modifications to the  algorithm
  (damped Newton directions are extensively used)
* like all interior point methods, this algorithm  tends  to  converge  in
  roughly same number of iterations (between 15 and 50) independently from
  the problem dimensionality
  
ALGORITHM LIMITATIONS:

* because a dense Cholesky driver is used, for  N-dimensional problem with
  M dense constaints this algorithm has O(N^2+N*M) memory requirements and
  O(N^3+M*N^2) running time.
  Having sparse constraints with Z nonzeros per row  relaxes  storage  and
  running time down to O(N^2+M*Z) and O(N^3+M*Z^2)
  From the practical  point  of  view,  it  limits  its  applicability  by
  several thousands of variables.
  From  the  other  side,  variables  count  is  the most limiting factor,
  and dependence on constraint count is  much  more  lower. Assuming  that
  the constraint matrix is sparse, it may  handle  tens  of  thousands  of
  general linear constraints.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Eps     -   >=0, stopping criteria. The algorithm stops  when   primal
                and dual infeasiblities as well as complementarity gap are
                less than Eps.

IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT!

NOTE: Passing EpsX=0 will lead to automatic selection of small epsilon.

===== TRACING IPM SOLVER =================================================

IPM solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'IPM'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'IPM.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'IPM'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("IPM,PREC.F6", "path/to/trace.log")
> 

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgodenseipm(minqpstate* state, double eps, ae_state *_state)
{


    ae_assert(ae_isfinite(eps, _state), "MinQPSetAlgoDenseIPM: Eps is not finite number", _state);
    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "MinQPSetAlgoDenseIPM: negative Eps", _state);
    state->algokind = 5;
    state->veps = eps;
}


/*************************************************************************
This function tells QP solver to  use  SPARSE-IPM  QP algorithm  and  sets
stopping criteria for the algorithm.

This  algorithm  is  intended for convex and semidefinite QP (but not QCQP
or conic) problems with large  variable  and  constraint  count and sparse
quadratic term and sparse linear constraints. It was successfully used for
problems with millions of variables and constraints. Quadratic  and  conic
constraints are supported by another solver (SPARSE-GENIPM).

It is possible to have some limited set of dense linear constraints - they
will be handled separately  by  the  dense  BLAS  -  but  the  more  dense
constraints you have, the more time solver needs.

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). It can also use  a  performance  backend
           (e.g. Intel PARDISO or another  platform-specific  library)  to
           accelerate sparse factorization.
           
           Specific speed-up due to parallelism  and  performance  backend
           usage heavily depends on the sparsity pattern of quadratic term
           and constraints. For some problem  types  performance  backends
           provide great speed-up. For other  ones,  ALGLIB's  own  sparse
           factorization code is the preferred option.
           
           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.

IMPORTANT: internally this solver performs large  and  sparse  (N+M)x(N+M)
           triangular factorization. So it expects both quadratic term and
           constraints to be highly sparse. However, its  running  time is
           influenced by BOTH fill factor and sparsity pattern.

           Generally we expect that no more than few nonzero  elements per
           row are present. However different sparsity patterns may result
           in completely different running  times  even  given  same  fill
           factor.
           
           In many cases this algorithm outperforms DENSE-IPM by order  of
           magnitude. However, in some cases you may  get  better  results
           with DENSE-IPM even when solving sparse task.

IMPORTANT: this algorithm won't work for nonconvex problems. If you try to
           run SPARSE-IPM  on a problem with indefinite quadratic term  (a
           matrix having at least one negative eigenvalue) then  depending
           on the circumstances it may either (a) stall  at some arbitrary
           point, or (b) throw an exception due  to  the  failure  of  the
           Cholesky decomposition.
           
           Use GENIPM algorithm if  your  problem  is  nonconvex  or has a
           potential of becoming  nonconvex.  The  GENIPM  solver can also
           handle problems with quadratic and conic constraints.

ALGORITHM FEATURES:

* supports  box  and  dense/sparse  general   linear   equality/inequality
  constraints
* specializes on large-scale sparse problems

ALGORITHM OUTLINE:

* this  algorithm  is  our implementation  of  interior  point  method  as
  formulated by  R.J.Vanderbei, with minor modifications to the  algorithm
  (damped Newton directions are extensively used)
* like all interior point methods, this algorithm  tends  to  converge  in
  roughly same number of iterations (between 15 and 50) independently from
  the problem dimensionality
  
ALGORITHM LIMITATIONS:

* this algorithm may handle moderate number  of dense constraints, usually
  no more than a thousand of dense ones without losing its efficiency.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Eps     -   >=0, stopping criteria. The algorithm stops  when   primal
                and dual infeasiblities as well as complementarity gap are
                less than Eps.

IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT!

NOTE: Passing EpsX=0 will lead to automatic selection of small epsilon.

===== TRACING IPM SOLVER =================================================

IPM solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'IPM'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'IPM.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'IPM'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("IPM,PREC.F6", "path/to/trace.log")
> 

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgosparseipm(minqpstate* state,
     double eps,
     ae_state *_state)
{


    ae_assert(ae_isfinite(eps, _state), "MinQPSetAlgoSparseIPM: Eps is not finite number", _state);
    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "MinQPSetAlgoSparseIPM: negative Eps", _state);
    state->algokind = 6;
    state->veps = eps;
}


/*************************************************************************
This function tells QP solver to  use  DENSE-GENIPM  QP algorithm and sets
stopping criteria for the algorithm.

This  algorithm  is  intended  for convex/nonconvex box/linearly/conically
constrained QP problems with moderate (up to several thousands)  variables
count and arbitrary number  of  constraints.  Use  SPARSE-GENIPM  if  your
problem is sparse.

The algorithm is a generalization of DENSE-IPM solver, capable of handling
more general constraints as well as nonconvexity of  the  target.  In  the
latter case, a local solution is found.

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). It can also use  a  performance  backend
           (e.g.  Intel  MKL  or  another  platform-specific  library)  to
           accelerate dense factorization.
           
           Dense Cholesky factorization is a well  studied  and  optimized
           algorithm.  ALGLIB  includes  a  good  implementation; however,
           using a hardware vendor-provided  performance  library  usually
           results in a better performance.
           
           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.

ALGORITHM FEATURES:

* supports box, linear equality/inequality and conic constraints 
* for convex problems returns the global (and the only) solution
* can handle non-convex  problem  (only  a  locally  optimal  solution  is
  returned in this case)
  
ALGORITHM LIMITATIONS:

* because a dense Cholesky driver is used, for  N-dimensional problem with
  M dense constaints this algorithm has O(N^2+N*M) memory requirements and
  O(N^3+M*N^2) running time.
  Having sparse constraints with Z nonzeros per row  relaxes  storage  and
  running time down to O(N^2+M*Z) and O(N^3+M*Z^2)
  From the practical  point  of  view,  it  limits  its  applicability  by
  several thousands of variables.
  From  the  other  side,  variables  count  is  the most limiting factor,
  and dependence on constraint count is  much  more  lower. Assuming  that
  the constraint matrix is sparse, it may  handle  tens  of  thousands  of
  general linear constraints.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Eps     -   >=0, stopping criteria. The algorithm stops  when   primal
                and dual infeasiblities as well as complementarity gap are
                less than Eps.

IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT!

NOTE: Passing EpsX=0 will lead to automatic selection of small epsilon.

===== TRACING GENIPM SOLVER ==============================================

GENIPM solver supports advanced tracing capabilities. You can log algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'GENIPM'      - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'GENIPM.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'GENIPM'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("GENIPM,PREC.F6", "path/to/trace.log")
> 

  -- ALGLIB --
     Copyright 01.05.2024 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgodensegenipm(minqpstate* state,
     double eps,
     ae_state *_state)
{


    ae_assert(ae_isfinite(eps, _state), "MinQPSetAlgoDenseGENIPM: Eps is not finite number", _state);
    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "MinQPSetAlgoDenseGENIPM: negative Eps", _state);
    state->algokind = 7;
    state->veps = eps;
}


/*************************************************************************
This function tells QP solver to  use SPARSE-GENIPM  QP algorithm and sets
stopping criteria for the algorithm.

This   algorithm  is intended for convex/nonconvex box/linearly/conically/
quadratically constrained QP  problems  with  sparse  quadratic  term  and
constraints. It can handle millions of variables and constraints, assuming
that the problem is sufficiently sparse. If your problem is small (several
thousands vars at most) and dense, consider using DENSE-GENIPM  as  a more
efficient alternative.

The  algorithm  is  a  generalization of the SPARSE-IPM solver, capable of
handling more general constraints as well as nonconvexity of  the  target.
In the latter case, a local solution is found.

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). Specific  speed-up  due  to  parallelism
           heavily depends on the sparsity pattern of quadratic  term  and
           constraints.
           
           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism support.
           
           This function does not use performance backends  to  accelerate
           sparse factorization because external  libraries  typically  do
           not  provide   fine  control over regularization, pivoting  and
           sparse orderings.

IMPORTANT: internally this solver performs large  and  sparse  (N+M)x(N+M)
           triangular factorization. So it expects both quadratic term and
           constraints to be highly sparse. However, its  running  time is
           influenced by BOTH fill factor and sparsity pattern.

           Generally we expect that no more than few nonzero  elements per
           row are present. However different sparsity patterns may result
           in completely different running  times  even  given  same  fill
           factor.
           
           In many cases this algorithm outperforms DENSE-IPM by order  of
           magnitude. However, in some cases you may  get  better  results
           with DENSE-IPM even when solving sparse task.

ALGORITHM FEATURES:


* supports box, linear equality/inequality constraints
* for convex problems returns the global (and the only) solution
* can handle non-convex  problem  (only  a  locally  optimal  solution  is
  returned in this case)
* specializes on large-scale sparse problems

ALGORITHM LIMITATIONS:

* this algorithm may handle moderate number  of dense constraints, usually
  no more than a thousand of dense ones without losing its efficiency.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Eps     -   >=0, stopping criteria. The algorithm stops  when   primal
                and dual infeasiblities as well as complementarity gap are
                less than Eps.

IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT!

NOTE: Passing EpsX=0 will lead to automatic selection of small epsilon.

===== TRACING GENIPM SOLVER ==============================================

GENIPM solver supports advanced tracing capabilities. You can log algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'GENIPM'      - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'GENIPM.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'IPM'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("GENIPM,PREC.F6", "path/to/trace.log")
> 

  -- ALGLIB --
     Copyright 01.05.2024 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgosparsegenipm(minqpstate* state,
     double eps,
     ae_state *_state)
{


    ae_assert(ae_isfinite(eps, _state), "MinQPSetAlgoSparseGENIPM: Eps is not finite number", _state);
    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "MinQPSetAlgoSparseGENIPM: negative Eps", _state);
    state->algokind = 8;
    state->veps = eps;
}


/*************************************************************************
This function tells QP solver to  use HYBRID-GENIPM  QP algorithm and sets
stopping criteria for the algorithm.

This  algorithm is intended for finding medium-accuracy solutions (say, no
more than 5 accurate digits) to large-scale convex/nonconvex box/linearly/
conically/quadratically constrained  QP  problems  with  objective  and/or
quadratic constraints being  well-conditioned  but  having   prohibitively
dense Cholesky factors. This includes both  dense  problems  and  problems
with sparse terms having too dense factorizations.

Being well conditioned allows us to use low rank LBFGS/LSR1 approximations
to the objective (quadratic constraints) and use sparse linear algebra for
the rest of the problem,  thus  achieving  significant  speed-up  on  many
types of problems.

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). Specific  speed-up  due  to  parallelism
           heavily depends on the sparsity pattern of quadratic  term  and
           constraints.
           
           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism support.
           
           This function does not use performance backends  to  accelerate
           sparse factorization because external  libraries  typically  do
           not  provide   fine  control over regularization, pivoting  and
           sparse orderings.
           
IMPORTANT: performance of this function depends on both  sparsity  pattern
           of the problem and on its conditioning properties.  The running
           time for a dense QP/QCQP problem grows with variables count  as
           O(N^2) while DENSE-GENIPM running time grows as O(N^3).
           
           On the other hand, DENSE/SPARSE-GENIPM running time shows  only
           moderate dependence on the condition number, while HYBRID-GENIPM
           running time often explodes with  condition  number larger than
           10000.
           
           In practice, on a dense QCQP problem HYBRID algorithm starts to
           singificantly outperform DENSE-GENIPM for N>=1000.
           
INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    
    Eps     -   >=0, stopping criteria. The algorithm stops  when   primal
                and dual infeasiblities as well as complementarity gap are
                less than Eps.
                
                This   solver  is  optimized for  finding  medium-accuracy
                solutions, with eps being between 1E-3 and 1E-5.
                
    MemLen  -   >=0, memory length for the limited memory SR1 model.  Zero
                value means automatic selection of some  small  predefined
                value  which  may  change  in  future  versions of ALGLIB.
                Optimal value is problem-dependent, with  8...32  being  a
                good initial point for the experimentation.  Values  above
                N are silently truncated down to N.
                
    MaxOffdiag- >=0, quadratic terms with more than MaxOffdiag offdiagonal
                elements are handled via quasi-Newton approximation. Terms
                with no more than MaxOffdiag elements are handled exactly,
                including strictly diagonal terms that are always  handled
                exactly (because they have the minimum number off-diagonal
                elements possible - zero). Symmetric terms appearing above
                and below diagonal are counted as one  term,  not  two.  A
                single quasi-Newton model is built, which approximates all
                eligible quadratic terms together.
                
                The following values are possible:
                * 0 - approximate every quadratic term having at least one
                  off-diagonal element with a quasi-Newton model.
                * 0<MaxOffdiag<N*(N-1)/2 - eligible  quadratic terms (ones
                  that are too dense) are approximated, sufficiently sparse
                  ones are handled precisely
                * MaxOffdiag>=N*(N-1)/2 - no quadratic  term  is  eligible
                  for quasi-Newton approximation, algorithm performance is
                  roughly similar to  that  of  SPARSE-GENIPM,  with  some
                  additional small overhead for quasi-Newton code checks.

  -- ALGLIB --
     Copyright 01.05.2024 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgohybridgenipm(minqpstate* state,
     double eps,
     ae_int_t memlen,
     ae_int_t maxoffdiag,
     ae_state *_state)
{


    ae_assert(ae_isfinite(eps, _state), "MinQPSetAlgoHybridGENIPM: Eps is not finite number", _state);
    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "MinQPSetAlgoHybridGENIPM: negative Eps", _state);
    ae_assert(memlen>=0, "MinQPSetAlgoHybridGENIPM: negative MemLen", _state);
    ae_assert(maxoffdiag>=0, "MinQPSetAlgoHybridGENIPM: negative MaxOffdiag", _state);
    state->algokind = 10;
    state->veps = eps;
    state->vmemlen = memlen;
    state->vmaxoffdiag = maxoffdiag;
}


/*************************************************************************
This function tells QP solver to use an ECQP algorithm.

This  algorithm  is  intended  for sparse convex problems with only linear
equality constraints. It can handle millions of variables and constraints,
assuming that the problem is sufficiently  sparse.  However,  it  can  NOT
deal with nonlinear equality constraints or inequality constraints of  any
type (including box ones), nor it can deal with nonconvex problems.

When applicable, it outperforms SPARSE-IPM by  tens  of  times.  It  is  a
regularized direct linear algebra solver that performs several  rounds  of
iterative  refinement  in  order to improve a solution. Thus, due  to  its
direct nature, it does not need stopping criteria and performs much faster
than interior point methods.

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). It can also use  a  performance  backend
           (e.g. Intel PARDISO or another  platform-specific  library)  to
           accelerate sparse factorization.
           
           Specific speed-up due to parallelism  and  performance  backend
           usage heavily depends on the sparsity pattern of quadratic term
           and constraints. For some problem  types  performance  backends
           provide great speed-up. For other  ones,  ALGLIB's  own  sparse
           factorization code is the preferred option.
           
           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.

IMPORTANT: internally this solver performs large  and  sparse  (N+M)x(N+M)
           triangular factorization. So it expects both quadratic term and
           constraints to be highly sparse. However, its  running  time is
           influenced by BOTH fill factor and sparsity pattern.

           Generally we expect that no more than few nonzero  elements per
           row are present. However different sparsity patterns may result
           in completely different running  times  even  given  same  fill
           factor.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Eps     -   >=0, stopping criteria. The algorithm stops  when   primal
                and dual infeasiblities are less than Eps.

IT IS VERY IMPORTANT TO CALL minqpsetscale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS CONVERGENCE PROPERTIES AND STOPPING CRITERIA ARE SCALE-DEPENDENT!

NOTE: Passing EpsX=0 will lead to automatic selection of small epsilon.

  -- ALGLIB --
     Copyright 01.07.2024 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgosparseecqp(minqpstate* state, ae_state *_state)
{


    state->algokind = 9;
}


/*************************************************************************
This function tells solver to use QuickQP  algorithm:  special  extra-fast
algorithm for problems with box-only constrants. It may  solve  non-convex
problems as long as they are bounded from below under constraints.

ALGORITHM FEATURES:
* several times faster than DENSE-IPM when running on box-only problem
* utilizes accelerated methods for activation of constraints.
* supports dense and sparse QP problems
* supports ONLY box constraints; general linear constraints are NOT
  supported by this solver
* can solve all types of problems  (convex,  semidefinite,  nonconvex)  as
  long as they are bounded from below under constraints.
  Say, it is possible to solve "min{-x^2} subject to -1<=x<=+1".
  In convex/semidefinite case global minimum  is  returned,  in  nonconvex
  case - algorithm returns one of the local minimums.

ALGORITHM OUTLINE:

* algorithm  performs  two kinds of iterations: constrained CG  iterations
  and constrained Newton iterations
* initially it performs small number of constrained CG  iterations,  which
  can efficiently activate/deactivate multiple constraints
* after CG phase algorithm tries to calculate Cholesky  decomposition  and
  to perform several constrained Newton steps. If  Cholesky  decomposition
  failed (matrix is indefinite even under constraints),  we  perform  more
  CG iterations until we converge to such set of constraints  that  system
  matrix becomes  positive  definite.  Constrained  Newton  steps  greatly
  increase convergence speed and precision.
* algorithm interleaves CG and Newton iterations which  allows  to  handle
  indefinite matrices (CG phase) and quickly converge after final  set  of
  constraints is found (Newton phase). Combination of CG and Newton phases
  is called "outer iteration".
* it is possible to turn off Newton  phase  (beneficial  for  semidefinite
  problems - Cholesky decomposition will fail too often)
  
ALGORITHM LIMITATIONS:

* algorithm does not support general  linear  constraints;  only  box ones
  are supported
* Cholesky decomposition for sparse problems  is  performed  with  Skyline
  Cholesky solver, which is intended for low-profile matrices. No profile-
  reducing reordering of variables is performed in this version of ALGLIB.
* problems with near-zero negative eigenvalues (or exacty zero  ones)  may
  experience about 2-3x performance penalty. The reason is  that  Cholesky
  decomposition can not be performed until we identify directions of  zero
  and negative curvature and activate corresponding boundary constraints -
  but we need a lot of trial and errors because these directions  are hard
  to notice in the matrix spectrum.
  In this case you may turn off Newton phase of algorithm.
  Large negative eigenvalues  are  not  an  issue,  so  highly  non-convex
  problems can be solved very efficiently.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsG    -   >=0
                The  subroutine  finishes  its  work   if   the  condition
                |v|<EpsG is satisfied, where:
                * |.| means Euclidian norm
                * v - scaled constrained gradient vector, v[i]=g[i]*s[i]
                * g - gradient
                * s - scaling coefficients set by MinQPSetScale()
    EpsF    -   >=0
                The  subroutine  finishes its work if exploratory steepest
                descent  step  on  k+1-th iteration  satisfies   following
                condition:  |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1}
    EpsX    -   >=0
                The  subroutine  finishes its work if exploratory steepest
                descent  step  on  k+1-th iteration  satisfies   following
                condition:  
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - step vector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinQPSetScale()
    MaxOuterIts-maximum number of OUTER iterations.  One  outer  iteration
                includes some amount of CG iterations (from 5 to  ~N)  and
                one or several (usually small amount) Newton steps.  Thus,
                one outer iteration has high cost, but can greatly  reduce
                funcation value.
                Use 0 if you do not want to limit number of outer iterations.
    UseNewton-  use Newton phase or not:
                * Newton phase improves performance of  positive  definite
                  dense problems (about 2 times improvement can be observed)
                * can result in some performance penalty  on  semidefinite
                  or slightly negative definite  problems  -  each  Newton
                  phase will bring no improvement (Cholesky failure),  but
                  still will require computational time.
                * if you doubt, you can turn off this  phase  -  optimizer
                  will retain its most of its high speed.

IT IS VERY IMPORTANT TO CALL MinQPSetScale() WHEN YOU USE THIS  ALGORITHM
BECAUSE ITS STOPPING CRITERIA ARE SCALE-DEPENDENT!

Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead
to automatic stopping criterion selection (presently it is  small    step
length, but it may change in the future versions of ALGLIB).

  -- ALGLIB --
     Copyright 22.05.2014 by Bochkanov Sergey
*************************************************************************/
void minqpsetalgoquickqp(minqpstate* state,
     double epsg,
     double epsf,
     double epsx,
     ae_int_t maxouterits,
     ae_bool usenewton,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsg, _state), "MinQPSetAlgoQuickQP: EpsG is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsg,(double)(0)), "MinQPSetAlgoQuickQP: negative EpsG", _state);
    ae_assert(ae_isfinite(epsf, _state), "MinQPSetAlgoQuickQP: EpsF is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsf,(double)(0)), "MinQPSetAlgoQuickQP: negative EpsF", _state);
    ae_assert(ae_isfinite(epsx, _state), "MinQPSetAlgoQuickQP: EpsX is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "MinQPSetAlgoQuickQP: negative EpsX", _state);
    ae_assert(maxouterits>=0, "MinQPSetAlgoQuickQP: negative MaxOuterIts!", _state);
    state->algokind = 3;
    if( ((ae_fp_eq(epsg,(double)(0))&&ae_fp_eq(epsf,(double)(0)))&&ae_fp_eq(epsx,(double)(0)))&&maxouterits==0 )
    {
        epsx = 1.0E-6;
    }
    state->qqpsettingsuser.maxouterits = maxouterits;
    state->qqpsettingsuser.epsg = epsg;
    state->qqpsettingsuser.epsf = epsf;
    state->qqpsettingsuser.epsx = epsx;
    state->qqpsettingsuser.cnphase = usenewton;
}


/*************************************************************************
This function sets box constraints for QP solver

Box constraints are inactive by default (after  initial  creation).  After
being  set,  they are  preserved until explicitly overwritten with another
minqpsetbc()  or  minqpsetbcall()  call,  or  partially  overwritten  with
minqpsetbci() call.

Following types of constraints are supported:

    DESCRIPTION         CONSTRAINT              HOW TO SPECIFY
    fixed variable      x[i]=Bnd[i]             BndL[i]=BndU[i]
    lower bound         BndL[i]<=x[i]           BndU[i]=+INF
    upper bound         x[i]<=BndU[i]           BndL[i]=-INF
    range               BndL[i]<=x[i]<=BndU[i]  ...
    free variable       -                       BndL[I]=-INF, BndU[I]+INF

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF (latter is recommended because
                it will allow solver to use better algorithm).
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF (latter is recommended because
                it will allow solver to use better algorithm).

NOTE: infinite values can be specified by means of Double.PositiveInfinity
      and  Double.NegativeInfinity  (in  C#)  and  alglib::fp_posinf   and
      alglib::fp_neginf (in C++).
      
NOTE: you may replace infinities by very small/very large values,  but  it
      is not recommended because large numbers may introduce large numerical
      errors in the algorithm.
      
NOTE: if constraints for all variables are same you may use minqpsetbcall()
      which allows to specify constraints without using arrays.
      
NOTE: BndL>BndU will result in QP problem being recognized as infeasible.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetbc(minqpstate* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_assert(bndl->cnt>=n, "MinQPSetBC: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=n, "MinQPSetBC: Length(BndU)<N", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "MinQPSetBC: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "MinQPSetBC: BndU contains NAN or -INF", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->havebndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        state->havebndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function sets box constraints for QP solver (all variables  at  once,
same constraints for all variables)

Box constraints are inactive by default (after  initial  creation).  After
being  set,  they are  preserved until explicitly overwritten with another
minqpsetbc()  or  minqpsetbcall()  call,  or  partially  overwritten  with
minqpsetbci() call.

Following types of constraints are supported:

    DESCRIPTION         CONSTRAINT              HOW TO SPECIFY
    fixed variable      x[i]=Bnd                BndL=BndU
    lower bound         BndL<=x[i]              BndU=+INF
    upper bound         x[i]<=BndU              BndL=-INF
    range               BndL<=x[i]<=BndU        ...
    free variable       -                       BndL=-INF, BndU+INF

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bound, same for all variables
    BndU    -   upper bound, same for all variables

NOTE: infinite values can be specified by means of Double.PositiveInfinity
      and  Double.NegativeInfinity  (in  C#)  and  alglib::fp_posinf   and
      alglib::fp_neginf (in C++).
      
NOTE: you may replace infinities by very small/very large values,  but  it
      is not recommended because large numbers may introduce large numerical
      errors in the algorithm.
      
NOTE: BndL>BndU will result in QP problem being recognized as infeasible.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetbcall(minqpstate* state,
     double bndl,
     double bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_assert(ae_isfinite(bndl, _state)||ae_isneginf(bndl, _state), "MinQPSetBCAll: BndL is NAN or +INF", _state);
    ae_assert(ae_isfinite(bndu, _state)||ae_isposinf(bndu, _state), "MinQPSetBCAll: BndU is NAN or -INF", _state);
    for(i=0; i<=n-1; i++)
    {
        state->bndl.ptr.p_double[i] = bndl;
        state->bndu.ptr.p_double[i] = bndu;
        state->havebndl.ptr.p_bool[i] = ae_isfinite(bndl, _state);
        state->havebndu.ptr.p_bool[i] = ae_isfinite(bndu, _state);
    }
}


/*************************************************************************
This function sets box constraints for I-th variable (other variables are
not modified).

Following types of constraints are supported:

    DESCRIPTION         CONSTRAINT              HOW TO SPECIFY
    fixed variable      x[i]=Bnd                BndL=BndU
    lower bound         BndL<=x[i]              BndU=+INF
    upper bound         x[i]<=BndU              BndL=-INF
    range               BndL<=x[i]<=BndU        ...
    free variable       -                       BndL=-INF, BndU+INF

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bound
    BndU    -   upper bound

NOTE: infinite values can be specified by means of Double.PositiveInfinity
      and  Double.NegativeInfinity  (in  C#)  and  alglib::fp_posinf   and
      alglib::fp_neginf (in C++).
      
NOTE: you may replace infinities by very small/very large values,  but  it
      is not recommended because large numbers may introduce large numerical
      errors in the algorithm.
      
NOTE: BndL>BndU will result in QP problem being recognized as infeasible.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetbci(minqpstate* state,
     ae_int_t i,
     double bndl,
     double bndu,
     ae_state *_state)
{


    ae_assert(i>=0&&i<state->n, "MinQPSetBCi: I is outside of [0,N)", _state);
    ae_assert(ae_isfinite(bndl, _state)||ae_isneginf(bndl, _state), "MinQPSetBCi: BndL is NAN or +INF", _state);
    ae_assert(ae_isfinite(bndu, _state)||ae_isposinf(bndu, _state), "MinQPSetBCi: BndU is NAN or -INF", _state);
    state->bndl.ptr.p_double[i] = bndl;
    state->bndu.ptr.p_double[i] = bndu;
    state->havebndl.ptr.p_bool[i] = ae_isfinite(bndl, _state);
    state->havebndu.ptr.p_bool[i] = ae_isfinite(bndu, _state);
}


/*************************************************************************
This function sets dense linear constraints for QP optimizer.

This  function  overrides  results  of  previous  calls  to  minqpsetlc(),
minqpsetlcsparse() and minqpsetlcmixed().  After  call  to  this  function
all non-box constraints are dropped, and you have only  those  constraints
which were specified in the present call.

If you want  to  specify  mixed  (with  dense  and  sparse  terms)  linear
constraints, you should call minqpsetlcmixed().

INPUT PARAMETERS:
    State   -   structure previously allocated with MinQPCreate call.
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

NOTE 1: linear (non-bound) constraints are satisfied only approximately  -
        there always exists some violation due  to  numerical  errors  and
        algorithmic limitations.

  -- ALGLIB --
     Copyright 19.06.2012 by Bochkanov Sergey
*************************************************************************/
void minqpsetlc(minqpstate* state,
     /* Real    */ const ae_matrix* c,
     /* Integer */ const ae_vector* ct,
     ae_int_t k,
     ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix dummyc;
    ae_vector dummyct;

    ae_frame_make(_state, &_frame_block);
    memset(&dummyc, 0, sizeof(dummyc));
    memset(&dummyct, 0, sizeof(dummyct));
    _sparsematrix_init(&dummyc, _state, ae_true);
    ae_vector_init(&dummyct, 0, DT_INT, _state, ae_true);

    minqpsetlcmixed(state, &dummyc, &dummyct, 0, c, ct, k, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
This function sets sparse linear constraints for QP optimizer.

This  function  overrides  results  of  previous  calls  to  minqpsetlc(),
minqpsetlcsparse() and minqpsetlcmixed().  After  call  to  this  function
all non-box constraints are dropped, and you have only  those  constraints
which were specified in the present call.

If you want  to  specify  mixed  (with  dense  and  sparse  terms)  linear
constraints, you should call minqpsetlcmixed().

INPUT PARAMETERS:
    State   -   structure previously allocated with MinQPCreate call.
    C       -   linear  constraints,  sparse  matrix  with  dimensions  at
                least [K,N+1]. If matrix has  larger  size,  only  leading
                Kx(N+1) rectangle is used.
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0

NOTE 1: linear (non-bound) constraints are satisfied only approximately  -
        there always exists some violation due  to  numerical  errors  and
        algorithmic limitations.

  -- ALGLIB --
     Copyright 22.08.2016 by Bochkanov Sergey
*************************************************************************/
void minqpsetlcsparse(minqpstate* state,
     const sparsematrix* c,
     /* Integer */ const ae_vector* ct,
     ae_int_t k,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix dummyc;
    ae_vector dummyct;

    ae_frame_make(_state, &_frame_block);
    memset(&dummyc, 0, sizeof(dummyc));
    memset(&dummyct, 0, sizeof(dummyct));
    ae_matrix_init(&dummyc, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dummyct, 0, DT_INT, _state, ae_true);

    minqpsetlcmixed(state, c, ct, k, &dummyc, &dummyct, 0, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
This function sets mixed linear constraints, which include a set of  dense
rows, and a set of sparse rows.

This  function  overrides  results  of  previous  calls  to  minqpsetlc(),
minqpsetlcsparse() and minqpsetlcmixed().

This function may be useful if constraint matrix includes large number  of
both types of rows - dense and sparse. If you have just a few sparse rows,
you  may  represent  them  in  dense  format  without losing  performance.
Similarly, if you have just a few dense rows, you may store them in sparse
format with almost same performance.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinQPCreate call.
    SparseC -   linear constraints, sparse  matrix with dimensions EXACTLY
                EQUAL TO [SparseK,N+1].  Each  row  of  C  represents  one
                constraint, either equality or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    SparseCT-   type of sparse constraints, array[K]:
                * if SparseCT[i]>0, then I-th constraint is SparseC[i,*]*x >= SparseC[i,n+1]
                * if SparseCT[i]=0, then I-th constraint is SparseC[i,*]*x  = SparseC[i,n+1]
                * if SparseCT[i]<0, then I-th constraint is SparseC[i,*]*x <= SparseC[i,n+1]
    SparseK -   number of sparse equality/inequality constraints, K>=0
    DenseC  -   dense linear constraints, array[K,N+1].
                Each row of DenseC represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of DenseC (including right part) must be finite.
    DenseCT -   type of constraints, array[K]:
                * if DenseCT[i]>0, then I-th constraint is DenseC[i,*]*x >= DenseC[i,n+1]
                * if DenseCT[i]=0, then I-th constraint is DenseC[i,*]*x  = DenseC[i,n+1]
                * if DenseCT[i]<0, then I-th constraint is DenseC[i,*]*x <= DenseC[i,n+1]
    DenseK  -   number of equality/inequality constraints, DenseK>=0

NOTE 1: linear (non-box) constraints  are  satisfied only approximately  -
        there always exists some violation due  to  numerical  errors  and
        algorithmic limitations.

NOTE 2: due to backward compatibility reasons SparseC can be  larger  than
        [SparseK,N+1]. In this case only leading  [SparseK,N+1]  submatrix
        will be  used.  However,  the  rest  of  ALGLIB  has  more  strict
        requirements on the input size, so we recommend you to pass sparse
        term whose size exactly matches algorithm expectations.

  -- ALGLIB --
     Copyright 22.08.2016 by Bochkanov Sergey
*************************************************************************/
void minqpsetlcmixed(minqpstate* state,
     const sparsematrix* sparsec,
     /* Integer */ const ae_vector* sparsect,
     ae_int_t sparsek,
     /* Real    */ const ae_matrix* densec,
     /* Integer */ const ae_vector* densect,
     ae_int_t densek,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    
    /*
     * First, check for errors in the inputs
     */
    ae_assert(densek>=0, "MinQPSetLCMixed: K<0", _state);
    ae_assert(densek==0||densec->cols>=n+1, "MinQPSetLCMixed: Cols(C)<N+1", _state);
    ae_assert(densec->rows>=densek, "MinQPSetLCMixed: Rows(DenseC)<DenseK", _state);
    ae_assert(densect->cnt>=densek, "MinQPSetLCMixed: Length(DenseCT)<DenseK", _state);
    ae_assert(apservisfinitematrix(densec, densek, n+1, _state), "MinQPSetLCMixed: C contains infinite or NaN values!", _state);
    ae_assert(sparsek>=0, "MinQPSetLCMixed: SparseK<0", _state);
    ae_assert(sparsek==0||sparsegetncols(sparsec, _state)>=n+1, "MinQPSetLCMixed: Cols(SparseC)<N+1", _state);
    ae_assert(sparsek==0||sparsegetnrows(sparsec, _state)>=sparsek, "MinQPSetLCMixed: Rows(SparseC)<SparseK", _state);
    ae_assert(sparsect->cnt>=sparsek, "MinQPSetLCMixed: Length(SparseCT)<SparseK", _state);
    
    /*
     * Allocate place for Lagrange multipliers, fill by zero, initialize constraints
     */
    rsetallocv(densek+sparsek, 0.0, &state->replaglc, _state);
    xlcsetlcmixed(&state->xlc, sparsec, sparsect, sparsek, densec, densect, densek, _state);
}


/*************************************************************************
This function provides legacy API for specification of mixed  dense/sparse
linear constraints.

New conventions used by ALGLIB since release  3.16.0  state  that  set  of
sparse constraints comes first,  followed  by  set  of  dense  ones.  This
convention is essential when you talk about things like order of  Lagrange
multipliers.

However, legacy API accepted mixed  constraints  in  reverse  order.  This
function is here to simplify situation with code relying on legacy API. It
simply accepts constraints in one order (old) and passes them to new  API,
now in correct order.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minqpsetlcmixedlegacy(minqpstate* state,
     /* Real    */ const ae_matrix* densec,
     /* Integer */ const ae_vector* densect,
     ae_int_t densek,
     const sparsematrix* sparsec,
     /* Integer */ const ae_vector* sparsect,
     ae_int_t sparsek,
     ae_state *_state)
{


    minqpsetlcmixed(state, sparsec, sparsect, sparsek, densec, densect, densek, _state);
}


/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU with dense
constraint matrix A.

NOTE: knowing  that  constraint  matrix  is  dense  helps  some QP solvers
      (especially modern IPM method) to utilize efficient  dense  Level  3
      BLAS for dense parts of the problem. If your problem has both  dense
      and sparse constraints, you  can  use  minqpsetlc2mixed()  function,
      which will result in dense algebra being applied to dense terms, and
      sparse sparse linear algebra applied to sparse terms.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minqpsetlc2dense(minqpstate* state,
     /* Real    */ const ae_matrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t k,
     ae_state *_state)
{


    minqpsetlc2mixed(state, &state->dummysparse, 0, a, k, al, au, _state);
}


/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
sparse constraining matrix A. Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    A       -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minqpsetlc2(minqpstate* state,
     const sparsematrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t k,
     ae_state *_state)
{


    minqpsetlc2mixed(state, a, k, &state->dummyr2, 0, al, au, _state);
}


/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
mixed constraining matrix A including sparse part (first SparseK rows) and
dense part (last DenseK rows). Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

This function may be useful if constraint matrix includes large number  of
both types of rows - dense and sparse. If you have just a few sparse rows,
you  may  represent  them  in  dense  format  without losing  performance.
Similarly, if you have just a few dense rows, you may store them in sparse
format with almost same performance.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    SparseA -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    SparseK -   number of sparse constraints, SparseK>=0
    DenseA  -   linear constraints, array[K,N], set of dense constraints.
                Each row of A represents one general linear constraint.
    DenseK  -   number of dense constraints, DenseK>=0
    AL, AU  -   lower and upper bounds, array[SparseK+DenseK], with former
                SparseK elements corresponding to sparse constraints,  and
                latter DenseK elements corresponding to dense constraints;
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minqpsetlc2mixed(minqpstate* state,
     const sparsematrix* sparsea,
     ae_int_t ksparse,
     /* Real    */ const ae_matrix* densea,
     ae_int_t kdense,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t m;


    n = state->n;
    m = kdense+ksparse;
    
    /*
     * Check input arguments
     */
    ae_assert(ksparse>=0, "MinQPSetLC2Mixed: KSparse<0", _state);
    ae_assert(ksparse==0||sparsegetncols(sparsea, _state)==n, "MinQPSetLC2: Cols(SparseA)<>N", _state);
    ae_assert(ksparse==0||sparsegetnrows(sparsea, _state)==ksparse, "MinQPSetLC2: Rows(SparseA)<>K", _state);
    ae_assert(kdense>=0, "MinQPSetLC2Mixed: KDense<0", _state);
    ae_assert(kdense==0||densea->cols>=n, "MinQPSetLC2Mixed: Cols(DenseA)<N", _state);
    ae_assert(kdense==0||densea->rows>=kdense, "MinQPSetLC2Mixed: Rows(DenseA)<K", _state);
    ae_assert(apservisfinitematrix(densea, kdense, n, _state), "MinQPSetLC2Mixed: DenseA contains infinite or NaN values!", _state);
    ae_assert(al->cnt>=kdense+ksparse, "MinQPSetLC2Mixed: Length(AL)<K", _state);
    ae_assert(au->cnt>=kdense+ksparse, "MinQPSetLC2Mixed: Length(AU)<K", _state);
    for(i=0; i<=m-1; i++)
    {
        ae_assert(ae_isfinite(al->ptr.p_double[i], _state)||ae_isneginf(al->ptr.p_double[i], _state), "MinQPSetLC2Mixed: AL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(au->ptr.p_double[i], _state)||ae_isposinf(au->ptr.p_double[i], _state), "MinQPSetLC2Mixed: AU contains NAN or -INF", _state);
    }
    
    /*
     * Allocate place for Lagrange multipliers, fill by zero, set constraints
     */
    rsetallocv(kdense+ksparse, 0.0, &state->replaglc, _state);
    xlcsetlc2mixed(&state->xlc, sparsea, ksparse, densea, kdense, al, au, _state);
}


/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
matrix of currently present dense constraints.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    A       -   linear constraint coefficient, array[N], right side is NOT
                included.
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint Ai*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint Ai*x<=AU
                * AU=+INF  => one-sided constraint AL<=Ai*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minqpaddlc2dense(minqpstate* state,
     /* Real    */ const ae_vector* a,
     double al,
     double au,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(a->cnt>=n, "MinQPAddLC2Dense: Length(A)<N", _state);
    ae_assert(isfinitevector(a, n, _state), "MinQPAddLC2Dense: A contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(al, _state)||ae_isneginf(al, _state), "MinQPAddLC2Dense: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(au, _state)||ae_isposinf(au, _state), "MinQPAddLC2Dense: AU is NAN or -INF", _state);
    xlcaddlc2dense(&state->xlc, a, al, au, _state);
    rvectorgrowto(&state->replaglc, state->xlc.nsparse+state->xlc.ndense+1, _state);
    state->replaglc.ptr.p_double[state->xlc.nsparse+state->xlc.ndense] = 0.0;
}


/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present sparse constraints.

Constraint is passed in compressed format: as list of non-zero entries  of
coefficient vector A. Such approach is more efficient than  dense  storage
for highly sparse constraint vectors.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    IdxA    -   array[NNZ], indexes of non-zero elements of A:
                * can be unsorted
                * can include duplicate indexes (corresponding entries  of
                  ValA[] will be summed)
    ValA    -   array[NNZ], values of non-zero elements of A
    NNZ     -   number of non-zero coefficients in A
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minqpaddlc2(minqpstate* state,
     /* Integer */ const ae_vector* idxa,
     /* Real    */ const ae_vector* vala,
     ae_int_t nnz,
     double al,
     double au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    
    /*
     * Check inputs
     */
    ae_assert(nnz>=0, "MinQPAddLC2: NNZ<0", _state);
    ae_assert(idxa->cnt>=nnz, "MinQPAddLC2: Length(IdxA)<NNZ", _state);
    ae_assert(vala->cnt>=nnz, "MinQPAddLC2: Length(ValA)<NNZ", _state);
    for(i=0; i<=nnz-1; i++)
    {
        ae_assert(idxa->ptr.p_int[i]>=0&&idxa->ptr.p_int[i]<n, "MinQPAddLC2: IdxA contains indexes outside of [0,N) range", _state);
    }
    ae_assert(isfinitevector(vala, nnz, _state), "MinQPAddLC2: ValA contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(al, _state)||ae_isneginf(al, _state), "MinQPAddLC2: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(au, _state)||ae_isposinf(au, _state), "MinQPAddLC2: AU is NAN or -INF", _state);
    xlcaddlc2(&state->xlc, idxa, vala, nnz, al, au, _state);
    rvectorgrowto(&state->replaglc, state->xlc.nsparse+state->xlc.ndense+1, _state);
    state->replaglc.ptr.p_double[state->xlc.nsparse+state->xlc.ndense] = 0.0;
}


/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present sparse constraints.

Constraint vector A is  passed  as  a  dense  array  which  is  internally
sparsified by this function.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    DA      -   array[N], constraint vector
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minqpaddlc2sparsefromdense(minqpstate* state,
     /* Real    */ const ae_vector* da,
     double al,
     double au,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(da->cnt>=n, "MinQPAddLC2SparseFromDense: Length(DA)<N", _state);
    ae_assert(isfinitevector(da, n, _state), "MinQPAddLC2SparseFromDense: DA contains infinities/NANs", _state);
    ae_assert(ae_isfinite(al, _state)||ae_isneginf(al, _state), "MinQPAddLC2SparseFromDense: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(au, _state)||ae_isposinf(au, _state), "MinQPAddLC2SparseFromDense: AU is NAN or -INF", _state);
    xlcaddlc2sparsefromdense(&state->xlc, da, al, au, _state);
    rvectorgrowto(&state->replaglc, state->xlc.nsparse+state->xlc.ndense+1, _state);
    state->replaglc.ptr.p_double[state->xlc.nsparse+state->xlc.ndense] = 0.0;
}


/*************************************************************************
This function clears the list of quadratic constraints. Other  constraints
are not modified.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.

  -- ALGLIB --
     Copyright 19.06.2024 by Bochkanov Sergey
*************************************************************************/
void minqpclearqc(minqpstate* state, ae_state *_state)
{


    xqcclear(&state->xqc, _state);
    state->repmqc = 0;
}


/*************************************************************************
This function appends a two-sided quadratic constraint of the form

    CL <= b'x + 0.5*x'*Q*x <= CU

or (depending on the ApplyOrigin parameter)

    CL <= b'(x-origin) + 0.5*(x-origin)'*Q*(x-origin) <= CU

to the set of currently present constraints. The linear term is  given  by
a dense array, the quadratic term is given by a sparse array.

Here CL can be finite or -INF (absense of constraint), CU can be finite or
+INF (absense of constraint), CL<=CU, with  CL=CU  denoting   an  equality
constraint. Q is an arbitrary (including indefinite) symmetric matrix.

The  function  has  O(max(N,NNZ)) memory and time requirements  because  a
dense array is used to store linear term and because  most  sparse  matrix
storage formats supported by ALGLIB need at least O(N) memory even for  an
empty quadratic constraint matrix.

Use minqpaddqc2list() if you have to add many constraints with  much  less
than N nonzero elements.
    
IMPORTANT: ALGLIB  supports  arbitrary  quadratic  constraints,  including
           nonconvex ones. However, only convex constraints (combined with
           the convex objective) result in guaranteed convergence  to  the
           global minimizer. In all other cases, only local convergence to
           a local minimum is guaranteed.
           
           A convex constraint is a  constraint  of  the  following  form:
           b'*(x-origin) + 0.5(x-origin)'*Q*(x-origin) <=CU, with Q  being
           a semidefinite matrix. All other modifications  are  nonconvex:
           * -x0^2<=1 is nonconvex
           *  x0^2>=1 is nonconvex (despite Q=1 being positive definite)
           *  x0^2 =1 is nonconvex
           
           The latter case is notable because it effectively converts a QP
           problem into a mixed integer QP program. Smooth interior  point
           solver can not efficiently handle such programs, converging  to
           a randomly chosen x0 (either +1 or -1) and  keeping  its  value
           fixed during the optimization.
           
           It is also  notable  that  larger  equality  constraints  (e.g.
           x0^2+x1^2=1) are much less difficult  to  handle  because  they
           form large connected regions within the parameters space.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    Q       -   symmetric matrix Q in a sparse matrix storage format:
                * if IsUpper=True, then the upper triangle  is  given, and
                  the lower triangle is ignored
                * if IsUpper=False, then the lower triangle is  given, and
                  the upper triangle is ignored
                * any sparse matrix storage format present in ALGLIB is
                  supported
                * the matrix must be exactly NxN
    IsUpper -   whether upper or lower triangle of Q is used
    B       -   array[N], linear term
    CL, CU  -   lower and upper bounds:
                * CL can be finite or -INF (absence of a bound)
                * CU can be finite or +INF (absence of a bound)
                * CL<=CU, with CL=CU meaning an equality constraint
                * CL=-INF, CU=+INF => constraint is ignored
    ApplyOrigin-whether origin (as specified by minqpsetorigin) is applied
                to the constraint or not. If no origin was specified, this
                parameter has no effect.
                
RESULT:
    constraint index, starting from 0

  -- ALGLIB --
     Copyright 19.07.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddqc2(minqpstate* state,
     const sparsematrix* q,
     ae_bool isupper,
     /* Real    */ const ae_vector* b,
     double cl,
     double cu,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t result;


    n = state->n;
    ae_assert(sparsegetnrows(q, _state)==n&&sparsegetncols(q, _state)==n, "MinQPAddQC2: rows(Q)<>N or cols(Q)<>N", _state);
    ae_assert(b->cnt>=n, "MinQPAddQC2: Length(B)<N", _state);
    ae_assert(isfinitevector(b, n, _state), "MinQPAddQC2: B contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(cl, _state)||ae_isneginf(cl, _state), "MinQPAddQC2: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(cu, _state)||ae_isposinf(cu, _state), "MinQPAddQC2: AU is NAN or -INF", _state);
    if( !sparseiscrs(q, _state) )
    {
        sparsecopytocrsbuf(q, &state->dummysparse, _state);
        xqcaddqc2(&state->xqc, &state->dummysparse, isupper, b, cl, cu, applyorigin, _state);
    }
    else
    {
        xqcaddqc2(&state->xqc, q, isupper, b, cl, cu, applyorigin, _state);
    }
    state->repmqc = xqcgetcount(&state->xqc, _state);
    rgrowv(state->repmqc, &state->replagqc, _state);
    state->replagqc.ptr.p_double[state->repmqc-1] = 0.0;
    result = state->repmqc-1;
    return result;
}


/*************************************************************************
This function appends a two-sided quadratic constraint of the form

    CL <= b'x + 0.5*x'*Q*x <= CU

or (depending on the ApplyOrigin parameter)

    CL <= b'(x-origin) + 0.5*(x-origin)'*Q*(x-origin) <= CU

to the set of currently present constraints.  Both  linear  and  quadratic
terms are given as lists of non-zero entries.

Here CL can be finite or -INF (absense of constraint), CU can be finite or
+INF (absense of constraint), CL<=CU, with  CL=CU  denoting   an  equality
constraint. Q is an arbitrary (including indefinite) symmetric matrix.

The function needs O(NNZ) memory for temporaries and  O(NNZ*logNNZ)  time,
where NNZ is a  total  number  of  non-zeros  in  both  lists.  For  small
constraints it can be orders of magnitude faster than  minqpaddqc2()  with
its  O(max(N,NNZ)) temporary memory or minqpaddqc2dense() with its  O(N^2)
temporaries. Thus, it is recommended if you have many small constraints.

NOTE: in  the  end,  all  quadratic  constraints  are  stored  in the same
      memory-efficient compressed format. However, you have to allocate an
      NxN  temporary  dense  matrix  when  you  pass  a  constraint  using
      minqpaddqc2dense(). Similarly, data structures used as a part of the
      API  provided  by   minqpaddqc2()   have   O(N)   temporary   memory
      requirements.
    
IMPORTANT: ALGLIB  supports  arbitrary  quadratic  constraints,  including
           nonconvex ones. However, only convex constraints (combined with
           the convex objective) result in guaranteed convergence  to  the
           global minimizer. In all other cases, only local convergence to
           a local minimum is guaranteed.
           
           A convex constraint is a  constraint  of  the  following  form:
           b'*(x-origin) + 0.5(x-origin)'*Q*(x-origin) <=CU, with Q  being
           a semidefinite matrix. All other modifications  are  nonconvex:
           * -x0^2<=1 is nonconvex
           *  x0^2>=1 is nonconvex (despite Q=1 being positive definite)
           *  x0^2 =1 is nonconvex
           
           The latter case is notable because it effectively converts a QP
           problem into a mixed integer QP program. Smooth interior  point
           solver can not efficiently handle such programs, converging  to
           a randomly chosen x0 (either +1 or -1) and  keeping  its  value
           fixed during the optimization.
           
           It is also  notable  that  larger  equality  constraints  (e.g.
           x0^2+x1^2=1) are much less difficult  to  handle  because  they
           form large connected regions within the parameters space.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    QRIdx   -   array[QNNZ], stores row indexes of QNNZ  nonzero  elements
                of a symmetric matrix Q
    QCIdx   -   array[QNNZ], stores col indexes of QNNZ  nonzero  elements
                of a symmetric matrix Q
    QVals   -   array[QNNZ], stores values of QNNZ  nonzero  elements of a
                symmetric matrix Q
    QNNZ    -   number of non-zero elements in Q, QNNZ>=0
    IsUpper -   whether upper or lower triangle of Q is used:
                * if IsUpper=True, then only elements with QRIdx[I]<=QCIdx[I]
                  are used and the rest is ignored
                * if IsUpper=False, then only elements with QRIdx[I]>=QCIdx[I]
                  are used and the rest is ignored
    BIdx    -   array[BNNZ], indexes of BNNZ nonzero elements of a linear term
    BVals   -   array[BNNZ], values of BNNZ nonzero elements of a linear term
    BNNZ    -   number of nonzero elements in B, BNNZ>=0
    CL, CU  -   lower and upper bounds:
                * CL can be finite or -INF (absence of a bound)
                * CU can be finite or +INF (absence of a bound)
                * CL<=CU, with CL=CU meaning an equality constraint
                * CL=-INF, CU=+INF => constraint is ignored
    ApplyOrigin-whether origin (as specified by minqpsetorigin) is applied
                to the constraint or not. If no origin was specified, this
                parameter has no effect.
                
RESULT:
    constraint index, starting from 0

  -- ALGLIB --
     Copyright 19.07.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddqc2list(minqpstate* state,
     /* Integer */ const ae_vector* qridx,
     /* Integer */ const ae_vector* qcidx,
     /* Real    */ const ae_vector* qvals,
     ae_int_t qnnz,
     ae_bool isupper,
     /* Integer */ const ae_vector* bidx,
     /* Real    */ const ae_vector* bvals,
     ae_int_t bnnz,
     double cl,
     double cu,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t result;


    n = state->n;
    ae_assert(qnnz>=0, "MinQPAddQC2List: QNNZ<0", _state);
    ae_assert(qridx->cnt>=qnnz, "MinQPAddQC2List: len(QRIdx)<QNNZ", _state);
    ae_assert(qcidx->cnt>=qnnz, "MinQPAddQC2List: len(QCIdx)<QNNZ", _state);
    ae_assert(qvals->cnt>=qnnz, "MinQPAddQC2List: len(QVals)<QNNZ", _state);
    for(i=0; i<=qnnz-1; i++)
    {
        if( qridx->ptr.p_int[i]<0||qridx->ptr.p_int[i]>=n )
        {
            ae_assert(ae_false, "MinQPAddQC2List: one of QRIdx[] is outside of [0,N) range", _state);
        }
        if( qcidx->ptr.p_int[i]<0||qcidx->ptr.p_int[i]>=n )
        {
            ae_assert(ae_false, "MinQPAddQC2List: one of QCIdx[] is outside of [0,N) range", _state);
        }
        if( !ae_isfinite(qvals->ptr.p_double[i], _state) )
        {
            ae_assert(ae_false, "MinQPAddQC2List: one of QVals[] is not finite", _state);
        }
    }
    ae_assert(bnnz>=0, "MinQPAddQC2List: BNNZ<0", _state);
    ae_assert(bidx->cnt>=bnnz, "MinQPAddQC2List: len(BIdx)<BNNZ", _state);
    ae_assert(bvals->cnt>=bnnz, "MinQPAddQC2List: len(BVals)<BNNZ", _state);
    for(i=0; i<=bnnz-1; i++)
    {
        if( bidx->ptr.p_int[i]<0||bidx->ptr.p_int[i]>=n )
        {
            ae_assert(ae_false, "MinQPAddQC2List: one of BIdx[] is outside of [0,N) range", _state);
        }
        if( !ae_isfinite(bvals->ptr.p_double[i], _state) )
        {
            ae_assert(ae_false, "MinQPAddQC2List: one of BVals[] is not finite", _state);
        }
    }
    xqcaddqc2list(&state->xqc, qridx, qcidx, qvals, qnnz, isupper, bidx, bvals, bnnz, cl, cu, applyorigin, _state);
    state->repmqc = xqcgetcount(&state->xqc, _state);
    rgrowv(state->repmqc, &state->replagqc, _state);
    state->replagqc.ptr.p_double[state->repmqc-1] = 0.0;
    result = state->repmqc-1;
    return result;
}


/*************************************************************************
This function appends a two-sided quadratic constraint of the form

    CL <= b'x + 0.5*x'*Q*x <= CU

or (depending on the ApplyOrigin parameter)

    CL <= b'(x-origin) + 0.5*(x-origin)'*Q*(x-origin) <= CU

to the set of currently present constraints. The linear and quadratic terms
are given by dense arrays.

Here CL can be finite or -INF (absense of constraint), CU can be finite or
+INF (absense of constraint), CL<=CU, with  CL=CU  denoting   an  equality
constraint. Q is an arbitrary (including indefinite) symmetric matrix.

This function trades convenience of using dense arrays for the efficiency.
Because dense NxN storage is used, merely calling this function has O(N^2)
complexity, no matter how sparse the Q is.

Use minqpaddqc2() or minqpaddqc2list() if you have sparse  Q  and/or  many
constraints to handle.
    
IMPORTANT: ALGLIB  supports  arbitrary  quadratic  constraints,  including
           nonconvex ones. However, only convex constraints (combined with
           the convex objective) result in guaranteed convergence  to  the
           global minimizer. In all other cases, only local convergence to
           a local minimum is guaranteed.
           
           A convex constraint is a  constraint  of  the  following  form:
           b'*(x-origin) + 0.5(x-origin)'*Q*(x-origin) <=CU, with Q  being
           a semidefinite matrix. All other modifications  are  nonconvex:
           * -x0^2<=1 is nonconvex
           *  x0^2>=1 is nonconvex (despite Q=1 being positive definite)
           *  x0^2 =1 is nonconvex
           
           The latter case is notable because it effectively converts a QP
           problem into a mixed integer QP program. Smooth interior  point
           solver can not efficiently handle such programs, converging  to
           a randomly chosen x0 (either +1 or -1) and  keeping  its  value
           fixed during the optimization.
           
           It is also  notable  that  larger  equality  constraints  (e.g.
           x0^2+x1^2=1) are much less difficult  to  handle  because  they
           form large connected regions within the parameters space.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    Q       -   array[N,N], symmetric matrix Q:
                * if IsUpper=True, then the upper triangle  is  given, and
                  the lower triangle is ignored
                * if IsUpper=False, then the lower triangle is  given, and
                  the upper triangle is ignored
                * if more than N rows/cols are present, only leading N
                  elements are used
    IsUpper -   whether upper or lower triangle of Q is used
    B       -   array[N], linear term
    CL, CU  -   lower and upper bounds:
                * CL can be finite or -INF (absence of a bound)
                * CU can be finite or +INF (absence of a bound)
                * CL<=CU, with CL=CU meaning an equality constraint
                * CL=-INF, CU=+INF => constraint is ignored
    ApplyOrigin-whether origin (as specified by minqpsetorigin) is applied
                to the constraint or not. If no origin was specified, this
                parameter has no effect.
                
RESULT:
    constraint index, starting from 0

  -- ALGLIB --
     Copyright 19.06.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddqc2dense(minqpstate* state,
     /* Real    */ const ae_matrix* q,
     ae_bool isupper,
     /* Real    */ const ae_vector* b,
     double cl,
     double cu,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t result;


    n = state->n;
    ae_assert(q->rows>=n&&q->cols>=n, "MinQPAddQC2Dense: rows(Q)<N or cols(Q)<N", _state);
    ae_assert(isfinitertrmatrix(q, n, isupper, _state), "MinQPAddQC2Dense: Q contains infinite or NaN values!", _state);
    ae_assert(b->cnt>=n, "MinQPAddQC2Dense: Length(B)<N", _state);
    ae_assert(isfinitevector(b, n, _state), "MinQPAddQC2Dense: B contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(cl, _state)||ae_isneginf(cl, _state), "MinQPAddQC2Dense: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(cu, _state)||ae_isposinf(cu, _state), "MinQPAddQC2Dense: AU is NAN or -INF", _state);
    xqcaddqc2dense(&state->xqc, q, isupper, b, cl, cu, applyorigin, _state);
    state->repmqc = xqcgetcount(&state->xqc, _state);
    rgrowv(state->repmqc, &state->replagqc, _state);
    state->replagqc.ptr.p_double[state->repmqc-1] = 0.0;
    result = state->repmqc-1;
    return result;
}


/*************************************************************************
This function clears the list of conic constraints. Other  constraints are
not modified.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.

  -- ALGLIB --
     Copyright 19.06.2024 by Bochkanov Sergey
*************************************************************************/
void minqpclearcc(minqpstate* state, ae_state *_state)
{


    xccclear(&state->xcc, _state);
    state->repmcc = 0;
}


/*************************************************************************
This function appends a primitive second-order  conic  constraint  of  the
form

        (                                                  )
    sqrt(x[range0]^2 + x[range0+1]^2 + ... + x[range1-1]^2 ) <= x[axisidx]
        (                                                  )

with 'primitive' meaning that there are no per-variable  scales  and  that
variables under the square root have sequential indexes. More general form
of conic constraints can be specified with minqpaddsoccorthogonal().

Alternatively, if ApplyOrigin parameter  is  True,  x[i]  is  replaced  by
x[i]-origin[i] (applies to all variables).

Unlike  many  other  conic  solvers,  ALGLIB  allows  conic constraints to
overlap, i.e. it allows  a  variable  to  be  a  part  of  multiple  conic
constraints.

NOTE: second-order conic constraints are always  convex,  so  having  them
      preserves convexity of the QP problem.
      
NOTE: A starting point that is strictly feasible with respect to both  box
      and conic constraints greatly helps the solver to power up; however,
      it will work even without such a point,  albeit  at  somewhat  lower
      performance.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    Range0,
    Range1  -   0<=range0<=range1<=N, variable range for the LHS;
                * squared variables x[range0]...x[range1-1] are summed up
                  under the square root.
                * range0=range1 means that the constraint is interpreted
                  as x[AxisIdx]>=0.
    AxisIdx -   RHS variable index:
                * 0<=AxisIdx<N
                * either AxisIdx<range0 or AxisAdx>=Range1.
                
RESULT:
    constraint index in a conic constraints list, starting from 0

  -- ALGLIB --
     Copyright 09.09.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddsoccprimitive(minqpstate* state,
     ae_int_t range0,
     ae_int_t range1,
     ae_int_t axisidx,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_vector idx;
    ae_vector diag;
    ae_vector shft;
    ae_int_t result;

    ae_frame_make(_state, &_frame_block);
    memset(&idx, 0, sizeof(idx));
    memset(&diag, 0, sizeof(diag));
    memset(&shft, 0, sizeof(shft));
    ae_vector_init(&idx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&diag, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&shft, 0, DT_REAL, _state, ae_true);

    n = state->n;
    ae_assert(axisidx>=0&&axisidx<n, "MinQPAddSOCCPrimitive: range0 is outside of [0,N)", _state);
    ae_assert(range0>=0&&range0<=n, "MinQPAddSOCCPrimitive: range0 is outside of [0,N)", _state);
    ae_assert(range1>=0&&range1<=n, "MinQPAddSOCCPrimitive: range1 is outside of [0,N]", _state);
    ae_assert(range0<=range1, "MinQPAddSOCCPrimitive: range1<range0", _state);
    ae_assert(axisidx<range0||axisidx>=range1, "MinQPAddSOCCPrimitive: AxisIdx intersects with the range", _state);
    
    /*
     * Retrieve temporaries from pools
     */
    ae_nxpool_retrieve(&state->n1intpool, &idx, _state);
    ae_nxpool_retrieve(&state->n1realpool, &diag, _state);
    ae_nxpool_retrieve(&state->n1realpool, &shft, _state);
    
    /*
     * Prepare cone description
     */
    for(i=range0; i<=range1-1; i++)
    {
        idx.ptr.p_int[i-range0] = i;
        diag.ptr.p_double[i-range0] = 1.0;
        shft.ptr.p_double[i-range0] = 0.0;
    }
    idx.ptr.p_int[range1-range0] = axisidx;
    diag.ptr.p_double[range1-range0] = 1.0;
    shft.ptr.p_double[range1-range0] = 0.0;
    
    /*
     * Add cone
     */
    xccaddsoccprimitivecanonic(&state->xcc, &idx, &diag, &shft, range1-range0+1, applyorigin, _state);
    state->repmcc = xccgetcount(&state->xcc, _state);
    result = state->repmcc-1;
    
    /*
     * Recycle temporaries
     */
    ae_nxpool_recycle(&state->n1intpool, &idx, _state);
    ae_nxpool_recycle(&state->n1realpool, &diag, _state);
    ae_nxpool_recycle(&state->n1realpool, &shft, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function appends an axis-orthogonal second-order  conic constraint of
the form

         ( k-2 (                     )^2          )
    sqrt ( SUM ( a[i]*x[idx[i]]+c[i] )  + theta^2 ) <= a[k-1]*x[idx[k-1]]+c[k-1]
         ( i=0 (                     )            )

Alternatively, if ApplyOrigin parameter  is  True,  x[i]  is  replaced  by
x[i]-origin[i] (applies to all variables).

Unlike many other conic solvers, ALGLIB provides a flexible conic API that
allows a[] to have zero elements at arbitrary positions (e.g.,  |x|<=const
can be handled just as easy as |x|<=y). Furthermore, ALGLIB  allows  conic
constraints to overlap, i.e. it allows a variable to be a part of multiple
conic constraints, or to appear multiple times in the same constraint.

NOTE: second-order conic constraints are always  convex,  so  having  them
      preserves convexity of the QP problem.
      
NOTE: A starting point that is strictly feasible with respect to both  box
      and conic constraints greatly helps the solver to power up; however,
      it will work even without such a point,  albeit  at  somewhat  lower
      performance.

INPUT PARAMETERS:
    State   -   structure previously allocated  with  minqpcreate()  call.
    Idx     -   array[K] (or larger, only leading  K  elements  are  used)
                storing variable indexes. Indexes can be  unsorted  and/or
                non-distinct.
    A       -   array[K]  (or larger, only leading  K  elements are used),
                variable multipliers. Can contain zero values.
    C       -   array[K] (or larger, only leading  K  elements  are used),
                variable shifts.
    K       -   cone dimensionality, K>=1. It is possible to have K>N.
    Theta   -   additional constant term, can be zero
                
RESULT:
    constraint index in a conic constraints list, starting from 0

  -- ALGLIB --
     Copyright 09.09.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddsoccorthogonal(minqpstate* state,
     /* Integer */ const ae_vector* idx,
     /* Real    */ const ae_vector* a,
     /* Real    */ const ae_vector* c,
     ae_int_t k,
     double theta,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t result;


    n = state->n;
    ae_assert(k>=1, "MinQPAddSOCCOrthogonal: K<1", _state);
    ae_assert(idx->cnt>=k, "MinQPAddSOCCOrthogonal: len(idx)<k", _state);
    ae_assert(a->cnt>=k, "MinQPAddSOCCOrthogonal: len(a)<k", _state);
    ae_assert(c->cnt>=k, "MinQPAddSOCCOrthogonal: len(c)<k", _state);
    for(i=0; i<=k-1; i++)
    {
        ae_assert(idx->ptr.p_int[i]>=0&&idx->ptr.p_int[i]<n, "MinQPAddSOCCOrthogonal: idx[] contains values outside of [0,N)", _state);
        ae_assert(ae_isfinite(a->ptr.p_double[i], _state), "MinQPAddSOCCOrthogonal: a[] contains infinite values", _state);
        ae_assert(ae_isfinite(c->ptr.p_double[i], _state), "MinQPAddSOCCOrthogonal: c[] contains infinite values", _state);
    }
    ae_assert(ae_isfinite(theta, _state), "MinQPAddSOCCOrthogonal: theta is not a finite number", _state);
    
    /*
     * Add cone
     */
    xccaddsoccorthogonalnoncanonic(&state->xcc, idx, a, c, k, theta, applyorigin, _state);
    state->repmcc = xccgetcount(&state->xcc, _state);
    result = state->repmcc-1;
    return result;
}


/*************************************************************************
This function appends a primitive power cone constraint of the form

        (                                                  )
    sqrt(x[range0]^2 + x[range0+1]^2 + ... + x[range1-1]^2 ) <= x[axisidx]^alpha
        (                                                  )

or, written in another form,

    (    (                                                  ))^(1/alpha)
    (sqrt(x[range0]^2 + x[range0+1]^2 + ... + x[range1-1]^2 ))           <= x[axisidx]
    (    (                                                  ))

where

    0<alpha<=1

with 'primitive' meaning that there are no per-variable  scales  and  that
variables under the square root have sequential indexes. More general form
of power cone constraints can be specified with minqpaddpowccorthogonal().

Alternatively, if ApplyOrigin parameter  is  True,  x[i]  is  replaced  by
x[i]-origin[i] (applies to all variables).

Unlike many other conic solvers, ALGLIB provides a flexible conic API that
allows alpha to be any positive value less than or equal to 1 (e.g.  it is
possible to formulate |x|<z^0.33 without using  slack vars that are  fixed
at 1.0). Furthermore, ALGLIB allows conic constraints to overlap, i.e.  it
allows a variable to be a part of multiple conic constraints.

INPUT PARAMETERS:
    State   -   structure previously allocated with minqpcreate() call.
    Range0,
    Range1  -   0<=range0<=range1<=N, variable range for the LHS;
                * squared variables x[range0]...x[range1-1] are summed up
                  under the square root.
                * range0=range1 means that the constraint is interpreted
                  as x[AxisIdx]>=0.
    AxisIdx -   RHS variable index:
                * 0<=AxisIdx<N
                * either AxisIdx<range0 or AxisAdx>=Range1.
    Alpha   -   power parameter, 0<alpha<=1
                
RESULT:
    constraint index in a conic constraints list, starting from 0

NOTE: power cone constraints are always  convex,  so having them preserves
      convexity of the QP problem.
      
NOTE: A starting point that is strictly feasible with respect to both  box
      and conic constraints greatly helps the solver to power up; however,
      it will work even without such a point,  albeit  at  somewhat  lower
      performance.
      
NOTE: a power cone with alpha<1 is sensitive to numerical errors near  the
      origin.  Suppose,  for  example,  that  we  have a constraint of the
      form |y|<=x^alpha with alpha=0.25 and that x is zero at the solution
      Furthermore, suppose that we perturbed x by as little  as  eps=1E-8.
      Because of alpha=0.25 the constraint is  perturbed  by  as  much  as
      eps^(1/4)=0.01!
      
      Such great sensitivity is  explained  by  the  non-differentiability
      of x^alpha for alpha<1 near x=0.  It  does  not  prevent  the  conic
      solver  from  converging  to   the precise solution, it merely makes
      constraints  extremely  sensitive  to  small   errors,   e.g.   ones
      introduced during the presolve/postsolve, like discussed below.
      
      The conic solver sometimes has to insert slack variables, most often
      because of constraints referring the same right-hand  side  variable
      twice.   For   example,  for  sqrt(x0^2+x1^2)<=(y0-1)^0.5*(y0+1)^0.5
      it will automatically add t=y0+1 (a linear equality constraint)  and
      will rewrite the  constraint  as  sqrt(x0^2+x1^2)<=(y0-1)^0.5*t^0.5.
      The solver will have no  difficulty  solving  the  problem,  even if
      the optimal t is close to zero. However, the equality y0=t-1 will be
      satisfied only approximately, and even a tiny error will be  greatly
      magnified when evaluating the constraint violation.
      
      Thus,   one  has  to  be  very  careful  when  evaluating constraint
      violation errors  for  power  cones.  Having  high  error  does  not
      necessarily mean that the solver has failed.

  -- ALGLIB --
     Copyright 19.11.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddpowccprimitive(minqpstate* state,
     ae_int_t range0,
     ae_int_t range1,
     ae_int_t axisidx,
     double alpha,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_vector idx;
    ae_vector diag;
    ae_vector shft;
    ae_vector alphavec;
    ae_int_t result;

    ae_frame_make(_state, &_frame_block);
    memset(&idx, 0, sizeof(idx));
    memset(&diag, 0, sizeof(diag));
    memset(&shft, 0, sizeof(shft));
    memset(&alphavec, 0, sizeof(alphavec));
    ae_vector_init(&idx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&diag, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&shft, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&alphavec, 0, DT_REAL, _state, ae_true);

    n = state->n;
    ae_assert(axisidx>=0&&axisidx<n, "MinQPAddPOWCCPrimitive: range0 is outside of [0,N)", _state);
    ae_assert(range0>=0&&range0<=n, "MinQPAddPOWCCPrimitive: range0 is outside of [0,N)", _state);
    ae_assert(range1>=0&&range1<=n, "MinQPAddPOWCCPrimitive: range1 is outside of [0,N]", _state);
    ae_assert(range0<=range1, "MinQPAddPOWCCPrimitive: range1<range0", _state);
    ae_assert(axisidx<range0||axisidx>=range1, "MinQPAddPOWCCPrimitive: AxisIdx intersects with the range", _state);
    ae_assert(ae_isfinite(alpha, _state), "MinQPAddPOWCCPrimitive: Alpha is not finite", _state);
    ae_assert(ae_fp_greater(alpha,(double)(0))&&ae_fp_less_eq(alpha,(double)(1)), "MinQPAddPOWCCPrimitive: Alpha is outside of (0,1]", _state);
    
    /*
     * Retrieve temporaries from pools
     */
    ae_nxpool_retrieve(&state->n1intpool, &idx, _state);
    ae_nxpool_retrieve(&state->n1realpool, &diag, _state);
    ae_nxpool_retrieve(&state->n1realpool, &shft, _state);
    ae_nxpool_retrieve(&state->n1realpool, &alphavec, _state);
    
    /*
     * Prepare cone description
     */
    for(i=range0; i<=range1-1; i++)
    {
        idx.ptr.p_int[i-range0] = i;
        diag.ptr.p_double[i-range0] = 1.0;
        shft.ptr.p_double[i-range0] = 0.0;
    }
    idx.ptr.p_int[range1-range0] = axisidx;
    diag.ptr.p_double[range1-range0] = 1.0;
    shft.ptr.p_double[range1-range0] = 0.0;
    alphavec.ptr.p_double[0] = ae_minreal(alpha, 1.0, _state);
    
    /*
     * Add cone
     */
    xccaddpowccprimitivecanonic(&state->xcc, &idx, &diag, &shft, range1-range0+1, &alphavec, 1, applyorigin, _state);
    state->repmcc = xccgetcount(&state->xcc, _state);
    result = state->repmcc-1;
    
    /*
     * Recycle temporaries
     */
    ae_nxpool_recycle(&state->n1intpool, &idx, _state);
    ae_nxpool_recycle(&state->n1realpool, &diag, _state);
    ae_nxpool_recycle(&state->n1realpool, &shft, _state);
    ae_nxpool_recycle(&state->n1realpool, &alphavec, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function appends an axis-orthogonal power cone constraint of the form

         (          k-kp-1      )     k-1
    sqrt ( theta^2 + SUM y[i]^2 ) <=  MUL y[i]^alpha[i]
         (           i=0        )    i=k-kp
 
where
    
    y[i] = a[i]*x[idx[i]]+c[i], y[i]>=0
    
    0<alpha[i]<1, with SUM(alpha[i])<=1
    
    1<=kp<=k, with kp=k meaning that we have MUL(y[i]^alpha[i])>=|theta|

Alternatively, if ApplyOrigin parameter  is  True,  x[i]  is  replaced  by
x[i]-origin[i] (applies to all variables).

Unlike many other conic solvers, ALGLIB provides a flexible conic API that
allows alpha[] to sum up to any positive value less than  or  equal  to  1
(e.g. it is possible to formulate |x|<z^0.33 without  using  slack  vars).
Furthermore, ALGLIB allows conic constraints to overlap, i.e. it allows  a
variable to be a part of multiple conic constraints, or to appear multiple
times in the same constraint.

INPUT PARAMETERS:
    State   -   structure previously allocated  with  minqpcreate()  call.
    Idx     -   array[K] (or larger, only leading  K  elements  are  used)
                storing variable indexes. Indexes can be  unsorted  and/or
                non-distinct.
    A       -   array[K]  (or larger, only leading  K  elements are used),
                variable multipliers. Can contain zero values.
    C       -   array[K] (or larger, only leading  K  elements  are used),
                variable shifts.
    K       -   cone dimensionality, K>=1. It is possible to have K>N.
    Theta   -   additional constant term, can be zero
    AlphaV  -   array[KPow], power coefficients:
                * 0<AlphaV[i]<=1
                * 0<SUM(AlphaV[])<=1
    KPow    -   1<=KPow<=K, with KPow=K being correctly handled.
                
RESULT:
    constraint index in a conic constraints list, starting from 0

NOTE: power cone constraints are always  convex,  so having them preserves
      convexity of the QP problem.
      
NOTE: A starting point that is strictly feasible with respect to both  box
      and conic constraints greatly helps the solver to power up; however,
      it will work even without such a point,  albeit  at  somewhat  lower
      performance.
      
NOTE: a power cone with alpha<1 is sensitive to numerical errors near  the
      origin.  Suppose,  for  example,  that  we  have a constraint of the
      form |y|<=x^alpha with alpha=0.25 and that x is zero at the solution
      Furthermore, suppose that we perturbed x by as little  as  eps=1E-8.
      Because of alpha=0.25 the constraint is  perturbed  by  as  much  as
      eps^(1/4)=0.01!
      
      Such great sensitivity is  explained  by  the  non-differentiability
      of x^alpha for alpha<1 near x=0.  It  does  not  prevent  the  conic
      solver  from  converging  to   the precise solution, it merely makes
      constraints  extremely  sensitive  to  small   errors,   e.g.   ones
      introduced during the presolve/postsolve, like discussed below.
      
      The conic solver sometimes has to insert slack variables, most often
      because of constraints referring the same right-hand  side  variable
      twice.   For   example,  for  sqrt(x0^2+x1^2)<=(y0-1)^0.5*(y0+1)^0.5
      it will automatically add t=y0+1 (a linear equality constraint)  and
      will rewrite the  constraint  as  sqrt(x0^2+x1^2)<=(y0-1)^0.5*t^0.5.
      The solver will have no  difficulty  solving  the  problem,  even if
      the optimal t is close to zero. However, the equality y0=t-1 will be
      satisfied only approximately, and even a tiny error will be  greatly
      magnified when evaluating the constraint violation.
      
      Thus,   one  has  to  be  very  careful  when  evaluating constraint
      violation errors  for  power  cones.  Having  high  error  does  not
      necessarily mean that the solver has failed.

  -- ALGLIB --
     Copyright 09.09.2024 by Bochkanov Sergey
*************************************************************************/
ae_int_t minqpaddpowccorthogonal(minqpstate* state,
     /* Integer */ const ae_vector* idx,
     /* Real    */ const ae_vector* a,
     /* Real    */ const ae_vector* c,
     ae_int_t k,
     double theta,
     /* Real    */ const ae_vector* alphav,
     ae_int_t kpow,
     ae_bool applyorigin,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    double vs;
    ae_int_t result;


    n = state->n;
    ae_assert(k>=1, "MinQPAddPOWCCOrthogonal: K<1", _state);
    ae_assert(kpow>=1, "MinQPAddPOWCCOrthogonal: KPow<1", _state);
    ae_assert(kpow<=k, "MinQPAddPOWCCOrthogonal: KPow>K", _state);
    ae_assert(idx->cnt>=k, "MinQPAddPOWCCOrthogonal: len(idx)<k", _state);
    ae_assert(a->cnt>=k, "MinQPAddPOWCCOrthogonal: len(a)<k", _state);
    ae_assert(c->cnt>=k, "MinQPAddPOWCCOrthogonal: len(c)<k", _state);
    for(i=0; i<=k-1; i++)
    {
        ae_assert(idx->ptr.p_int[i]>=0&&idx->ptr.p_int[i]<n, "MinQPAddPOWCCOrthogonal: idx[] contains values outside of [0,N)", _state);
        ae_assert(ae_isfinite(a->ptr.p_double[i], _state), "MinQPAddPOWCCOrthogonal: a[] contains infinite values", _state);
        ae_assert(ae_isfinite(c->ptr.p_double[i], _state), "MinQPAddPOWCCOrthogonal: c[] contains infinite values", _state);
    }
    ae_assert(ae_isfinite(theta, _state), "MinQPAddPOWCCOrthogonal: theta is not a finite number", _state);
    vs = 0.0;
    for(i=0; i<=kpow-1; i++)
    {
        ae_assert(ae_isfinite(alphav->ptr.p_double[i], _state), "MinQPAddPOWCCOrthogonal: alphav[] contains infinite values", _state);
        ae_assert(ae_fp_greater(alphav->ptr.p_double[i],(double)(0))&&ae_fp_less_eq(alphav->ptr.p_double[i],(double)(1)), "MinQPAddPOWCCOrthogonal: one of alphav[] elements is outside of (0,1] range", _state);
        vs = vs+alphav->ptr.p_double[i];
    }
    ae_assert(ae_fp_greater(vs,(double)(0))&&ae_fp_less_eq(vs,(double)1+(double)(10*kpow)*ae_machineepsilon), "MinQPAddPOWCCOrthogonal: alphav[] elements do not sum to a value in the (0,1] range", _state);
    
    /*
     * Add cone
     */
    xccaddpowccorthogonalnoncanonic(&state->xcc, idx, a, c, k, theta, alphav, kpow, applyorigin, _state);
    state->repmcc = xccgetcount(&state->xcc, _state);
    result = state->repmcc-1;
    return result;
}


/*************************************************************************
This function solves quadratic programming problem.

Prior to calling this function you should choose solver by means of one of
the following functions:

* minqpsetalgoquickqp()     - for QuickQP solver
* minqpsetalgodenseaul()    - for Dense-AUL-QP solver
* minqpsetalgodenseipm()    - for convex Dense-IPM-QP solver
* minqpsetalgosparseipm()   - for convex Sparse-IPM-QP solver
* minqpsetalgodensegenipm() - for convex/nonconvex Dense-IPM-QP solver with conic constraints
* minqpsetalgosparsegenipm()- for convex/nonconvex Sparse-IPM-QP solver with conic constraints

These functions also allow you to control stopping criteria of the solver.
If you did not set solver,  MinQP  subpackage  will  automatically  select
solver for your problem and will run it with default stopping criteria.

However, it is better to set explicitly solver and its stopping criteria.

INPUT PARAMETERS:
    State   -   algorithm state

You should use MinQPResults() function to access results after calls
to this function.

  -- ALGLIB --
     Copyright 2011-2024 by Bochkanov Sergey.
     Special thanks to Elvira Illarionova  for  important  suggestions  on
     the linearly constrained QP algorithm.
*************************************************************************/
void minqpoptimize(minqpstate* state, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t m;
    ae_int_t mqc;
    ae_int_t mcc;
    ae_int_t i;
    ae_int_t nbc;
    ae_bool dotracepresolve;
    ae_vector lagbcmin;
    ae_vector lagbcmax;
    ae_vector mods;
    ae_vector modorigin;
    ae_vector modbndl;
    ae_vector modbndu;
    ae_vector modb;
    ae_vector modxs;
    ae_int_t modn;
    ae_int_t modm;

    ae_frame_make(_state, &_frame_block);
    memset(&lagbcmin, 0, sizeof(lagbcmin));
    memset(&lagbcmax, 0, sizeof(lagbcmax));
    memset(&mods, 0, sizeof(mods));
    memset(&modorigin, 0, sizeof(modorigin));
    memset(&modbndl, 0, sizeof(modbndl));
    memset(&modbndu, 0, sizeof(modbndu));
    memset(&modb, 0, sizeof(modb));
    memset(&modxs, 0, sizeof(modxs));
    ae_vector_init(&lagbcmin, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lagbcmax, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&mods, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&modorigin, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&modbndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&modbndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&modb, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&modxs, 0, DT_REAL, _state, ae_true);

    n = state->n;
    m = state->xlc.ndense+state->xlc.nsparse;
    mqc = xqcgetcount(&state->xqc, _state);
    mcc = xccgetcount(&state->xcc, _state);
    state->repterminationtype = -5;
    state->repinneriterationscount = 0;
    state->repouteriterationscount = 0;
    state->repncholesky = 0;
    state->repnmv = 0;
    state->repn = n;
    state->repm = m;
    dotracepresolve = (ae_is_trace_enabled("IPM")||ae_is_trace_enabled("GENIPM"))||ae_is_trace_enabled("GIPM");
    
    /*
     * Zero-fill Lagrange multipliers (their initial value)
     */
    ae_assert(mqc==state->repmqc, "MINQP: integrity check 1221 failed", _state);
    ae_assert(mcc==state->repmcc, "MINQP: integrity check 1222 failed", _state);
    state->repf = 0.0;
    for(i=0; i<=n-1; i++)
    {
        state->replagbc.ptr.p_double[i] = 0.0;
    }
    for(i=0; i<=m-1; i++)
    {
        state->replaglc.ptr.p_double[i] = 0.0;
    }
    rsetv(mqc, 0.0, &state->replagqc, _state);
    
    /*
     * Initial point:
     * * if we have starting point in StartX, we just have to bound it
     * * if we do not have StartX, deduce initial point from boundary constraints
     */
    if( state->havex )
    {
        for(i=0; i<=n-1; i++)
        {
            state->xs.ptr.p_double[i] = state->startx.ptr.p_double[i];
            if( state->havebndl.ptr.p_bool[i]&&ae_fp_less(state->xs.ptr.p_double[i],state->bndl.ptr.p_double[i]) )
            {
                state->xs.ptr.p_double[i] = state->bndl.ptr.p_double[i];
            }
            if( state->havebndu.ptr.p_bool[i]&&ae_fp_greater(state->xs.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                state->xs.ptr.p_double[i] = state->bndu.ptr.p_double[i];
            }
        }
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            state->xs.ptr.p_double[i] = state->xorigin.ptr.p_double[i];
            if( state->havebndl.ptr.p_bool[i] )
            {
                state->xs.ptr.p_double[i] = ae_maxreal(state->xs.ptr.p_double[i], state->bndl.ptr.p_double[i], _state);
            }
            if( state->havebndu.ptr.p_bool[i] )
            {
                state->xs.ptr.p_double[i] = ae_minreal(state->xs.ptr.p_double[i], state->bndu.ptr.p_double[i], _state);
            }
        }
    }
    
    /*
     * check correctness of constraints
     */
    for(i=0; i<=n-1; i++)
    {
        if( state->havebndl.ptr.p_bool[i]&&state->havebndu.ptr.p_bool[i] )
        {
            if( ae_fp_greater(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                state->repterminationtype = -3;
                ae_frame_leave(_state);
                return;
            }
        }
    }
    
    /*
     * count number of bound and linear constraints
     */
    nbc = 0;
    for(i=0; i<=n-1; i++)
    {
        if( state->havebndl.ptr.p_bool[i] )
        {
            nbc = nbc+1;
        }
        if( state->havebndu.ptr.p_bool[i] )
        {
            nbc = nbc+1;
        }
    }
    
    /*
     * Effective scale
     */
    rvectorsetlengthatleast(&state->effectives, n, _state);
    if( state->stype==0 )
    {
        
        /*
         * User scale (or default one)
         */
        for(i=0; i<=n-1; i++)
        {
            state->effectives.ptr.p_double[i] = state->s.ptr.p_double[i];
        }
    }
    else
    {
        if( state->stype==1 )
        {
            
            /*
             * Diagonal is used for scaling:
             * * unpack
             * * convert to scale, return error on failure
             */
            if( state->akind==0 )
            {
                
                /*
                 * Unpack CQM structure
                 */
                cqmgetdiaga(&state->a, &state->effectives, _state);
            }
            else
            {
                if( state->akind==1 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        state->effectives.ptr.p_double[i] = sparseget(&state->sparsea, i, i, _state);
                    }
                }
                else
                {
                    ae_assert(ae_false, "MinQPOptimize: integrity check failed", _state);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                if( ae_fp_less_eq(state->effectives.ptr.p_double[i],(double)(0)) )
                {
                    state->repterminationtype = -9;
                    ae_frame_leave(_state);
                    return;
                }
                state->effectives.ptr.p_double[i] = (double)1/ae_sqrt(state->effectives.ptr.p_double[i], _state);
            }
        }
        else
        {
            ae_assert(ae_false, "MinQPOptimize: integrity check failed", _state);
        }
    }
    
    /*
     * Solvers which can not handle new two-sided constraints need them to be
     * converted into legacy equality/inequality one-sided format
     */
    if( state->algokind==4 )
    {
        if( xqcgetcount(&state->xqc, _state)+xccgetcount(&state->xcc, _state)>0 )
        {
            state->repterminationtype = -5;
            ae_frame_leave(_state);
            return;
        }
        xlcconverttoold(&state->xlc, _state);
        qpdenseauloptimize(&state->a, &state->sparsea, state->akind, state->sparseaupper, &state->b, &state->bndl, &state->bndu, &state->effectives, &state->xorigin, n, &state->xlc.cleic, state->xlc.nec, state->xlc.nic, &state->dummysparse, 0, 0, !state->dbgskipconstraintnormalization, &state->qpdenseaulsettingsuser, &state->qpdenseaulbuf, &state->xs, &state->replagbc, &state->elaglc, &state->repterminationtype, _state);
        for(i=0; i<=state->xlc.nec+state->xlc.nic-1; i++)
        {
            state->replaglc.ptr.p_double[state->xlc.lcsrcidx.ptr.p_int[i]] = state->replaglc.ptr.p_double[state->xlc.lcsrcidx.ptr.p_int[i]]+state->elaglc.ptr.p_double[i]*state->xlc.lcsrcmult.ptr.p_double[i];
        }
        state->repinneriterationscount = state->qpdenseaulbuf.repinneriterationscount;
        state->repouteriterationscount = state->qpdenseaulbuf.repouteriterationscount;
        state->repncholesky = state->qpdenseaulbuf.repncholesky;
        if( state->repterminationtype>0 )
        {
            if( state->akind==0 )
            {
                state->repf = cqmeval(&state->a, &state->xs, _state);
            }
            if( state->akind==1 )
            {
                state->repf = 0.5*sparsevsmv(&state->sparsea, state->sparseaupper, &state->xs, _state);
            }
            state->repf = state->repf+rdotv(n, &state->b, &state->xs, _state);
        }
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * QuickQP solver
     */
    if( state->algokind==3 )
    {
        if( state->xlc.ndense+state->xlc.nsparse+xqcgetcount(&state->xqc, _state)+xccgetcount(&state->xcc, _state)>0 )
        {
            state->repterminationtype = -5;
            ae_frame_leave(_state);
            return;
        }
        qqpoptimize(&state->a, &state->sparsea, &state->dummyr2, state->akind, state->sparseaupper, &state->b, &state->bndl, &state->bndu, &state->effectives, &state->xorigin, n, &state->qqpsettingsuser, &state->qqpbuf, &state->xs, &state->repterminationtype, _state);
        state->repinneriterationscount = state->qqpbuf.repinneriterationscount;
        state->repouteriterationscount = state->qqpbuf.repouteriterationscount;
        state->repncholesky = state->qqpbuf.repncholesky;
        if( state->repterminationtype>0 )
        {
            if( state->akind==0 )
            {
                state->repf = cqmeval(&state->a, &state->xs, _state);
            }
            if( state->akind==1 )
            {
                state->repf = 0.5*sparsevsmv(&state->sparsea, state->sparseaupper, &state->xs, _state);
            }
            state->repf = state->repf+rdotv(n, &state->b, &state->xs, _state);
        }
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * QP-DENSE-IPM and QP-SPARSE-IPM solvers
     */
    if( state->algokind==5||state->algokind==6 )
    {
        
        /*
         * Check quadratic constraints
         */
        if( xqcgetcount(&state->xqc, _state)+xccgetcount(&state->xcc, _state)>0 )
        {
            state->repterminationtype = -5;
            rsetallocv(n, 0.0, &state->xs, _state);
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Run presolver
         */
        xlcconverttosparse(&state->xlc, _state);
        ae_assert(state->akind==0||state->akind==1, "MinQPOptimize: unexpected AKind", _state);
        if( state->akind==0 )
        {
            cqmgeta(&state->a, &state->tmpr2, _state);
            sparsecreatecrsfromdensebuf(&state->tmpr2, n, n, &state->sparsea, _state);
        }
        if( state->dbgdopresolve )
        {
            presolveqp(&state->effectives, &state->xorigin, &state->b, &state->bndl, &state->bndu, &state->sparsea, state->sparseaupper, ae_true, n, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, m, &state->xqc, &state->xcc, dotracepresolve, &state->presolver, _state);
        }
        else
        {
            presolvenonescaleuser(&state->effectives, &state->xorigin, &state->b, &state->bndl, &state->bndu, &state->sparsea, state->sparseaupper, ae_true, n, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, m, &state->xqc, &state->xcc, dotracepresolve, &state->presolver, _state);
        }
        if( state->presolver.problemstatus==-3||state->presolver.problemstatus==-2 )
        {
            state->repterminationtype = state->presolver.problemstatus;
            rsetallocv(n, 0.0, &state->xs, _state);
            ae_frame_leave(_state);
            return;
        }
        ae_assert(state->presolver.problemstatus==0, "MINQP: integrity check 6035 failed", _state);
        
        /*
         * Run the solver
         */
        if( state->presolver.newn>0 )
        {
            if( state->algokind==5 )
            {
                rsetallocv(state->presolver.newn, 0.0, &state->tmp0, _state);
                rsetallocv(state->presolver.newn, 1.0, &state->tmp1, _state);
                vipminitdense(&state->vsolver, &state->tmp1, &state->tmp0, state->presolver.newn, ae_false, _state);
                vipmsetquadraticlinear(&state->vsolver, &state->tmpr2, &state->presolver.sparseh, 1, ae_false, &state->presolver.c, _state);
                vipmsetconstraints(&state->vsolver, &state->presolver.bndl, &state->presolver.bndu, &state->presolver.sparsea, state->presolver.newm, &state->dummyr2, 0, &state->presolver.al, &state->presolver.au, _state);
                vipmsetcond(&state->vsolver, state->veps, state->veps, state->veps, _state);
                vipmoptimize(&state->vsolver, ae_true, &state->xs, &state->replagbc, &state->replaglc, &state->repterminationtype, _state);
                state->repinneriterationscount = state->vsolver.repiterationscount;
                state->repouteriterationscount = state->repinneriterationscount;
                state->repncholesky = state->vsolver.repncholesky;
            }
            else
            {
                
                /*
                 * If presolver did NOT remove all variables (NewN>0), call the current solver
                 */
                rsetallocv(state->presolver.newn, 0.0, &state->tmp0, _state);
                rsetallocv(state->presolver.newn, 1.0, &state->tmp1, _state);
                ipm2init(&state->ipm2, &state->tmp1, &state->tmp0, state->presolver.newn, &state->tmpr2, &state->presolver.sparseh, 1, ae_false, &state->dummyr2, &state->dummyr, 0, &state->presolver.c, 0.0, &state->presolver.bndl, &state->presolver.bndu, &state->presolver.sparsea, state->presolver.newm, &state->dummyr2, 0, &state->presolver.al, &state->presolver.au, ae_false, ae_true, _state);
                ipm2setcond(&state->ipm2, state->veps, state->veps, state->veps, _state);
                ipm2optimize(&state->ipm2, ae_true, &state->xs, &state->replagbc, &state->replaglc, &state->repterminationtype, _state);
                state->repinneriterationscount = state->ipm2.repiterationscount;
                state->repouteriterationscount = state->ipm2.repiterationscount;
                state->repncholesky = state->ipm2.repiterationscount;
            }
        }
        else
        {
            
            /*
             * Presolver removed all variables, manually set up XS and Lagrange multipliers
             */
            rsetallocv(state->presolver.newn, 0.0, &state->replagbc, _state);
            rsetallocv(state->presolver.newm, 0.0, &state->replaglc, _state);
            rsetallocv(state->presolver.newmqc, 0.0, &state->replagqc, _state);
            state->repterminationtype = 1;
            state->repinneriterationscount = 0;
            state->repouteriterationscount = 0;
        }
        presolvebwd(&state->presolver, &state->xs, &state->tmpi, ae_false, &state->replagbc, &state->replaglc, &state->replagqc, _state);
        state->repn = n;
        state->repm = m;
        if( state->repterminationtype>0 )
        {
            if( state->akind==0 )
            {
                state->repf = cqmeval(&state->a, &state->xs, _state);
            }
            if( state->akind==1 )
            {
                state->repf = 0.5*sparsevsmv(&state->sparsea, state->sparseaupper, &state->xs, _state);
            }
            state->repf = state->repf+rdotv(n, &state->b, &state->xs, _state);
        }
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * QP-DENSE-GENIPM and QP-SPARSE-GENIPM solvers
     */
    if( (state->algokind==7||state->algokind==8)||state->algokind==10 )
    {
        
        /*
         * Either run presolver directly on the original problem (when no conic constraints are
         * present) or canonicalize conic constraints first and run presolver on the canonicalized
         * problem.
         */
        xlcconverttosparse(&state->xlc, _state);
        ae_assert(state->akind==0||state->akind==1, "MinQPOptimize: unexpected AKind", _state);
        if( state->akind==0 )
        {
            cqmgeta(&state->a, &state->tmpr2, _state);
            sparsecreatecrsfromdensebuf(&state->tmpr2, n, n, &state->sparsea, _state);
        }
        if( xccgetcount(&state->xcc, _state)==0 )
        {
            
            /*
             * No conic constraints, we can use constraint information directly as is, without
             * creating intermediary copies
             */
            if( state->dbgdopresolve )
            {
                presolveqp(&state->effectives, &state->xorigin, &state->b, &state->bndl, &state->bndu, &state->sparsea, state->sparseaupper, ae_true, n, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, m, &state->xqc, &state->xcc, dotracepresolve, &state->presolver, _state);
            }
            else
            {
                presolvenonescaleuser(&state->effectives, &state->xorigin, &state->b, &state->bndl, &state->bndu, &state->sparsea, state->sparseaupper, ae_true, n, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, m, &state->xqc, &state->xcc, dotracepresolve, &state->presolver, _state);
            }
            if( state->presolver.problemstatus==-3||state->presolver.problemstatus==-2 )
            {
                state->repterminationtype = state->presolver.problemstatus;
                rsetallocv(n, 0.0, &state->xs, _state);
                ae_frame_leave(_state);
                return;
            }
            rsetallocv(n, -ae_maxrealnumber, &lagbcmin, _state);
            rsetallocv(n, ae_maxrealnumber, &lagbcmax, _state);
        }
        else
        {
            
            /*
             * Canonicalize constraints by adding slack variables and modifying variable bounds if necessary.
             *
             * We modify XLC.EffSparseA/AL/AU inplace; however, we have to create new copies of
             * S, XOrigin, Bnd/L, BndU, SparseA.
             */
            rcopyallocv(n, &state->effectives, &mods, _state);
            rcopyallocv(n, &state->xorigin, &modorigin, _state);
            rcopyallocv(n, &state->bndl, &modbndl, _state);
            rcopyallocv(n, &state->bndu, &modbndu, _state);
            rcopyallocv(n, &state->b, &modb, _state);
            rcopyallocv(n, &state->xs, &modxs, _state);
            sparsecopybuf(&state->sparsea, &state->modsparsea, _state);
            xcccopy(&state->xcc, &state->modxcc, _state);
            modn = n;
            modm = m;
            gqpipmcanonicalizeconicconstraints(&mods, &modorigin, &modb, &modbndl, &modbndu, &modxs, &state->modsparsea, state->sparseaupper, ae_true, &modn, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, &modm, &state->xqc, &state->modxcc, &lagbcmin, &lagbcmax, _state);
            
            /*
             * Run presolver on the modified problem
             */
            if( state->dbgdopresolve )
            {
                presolveqp(&mods, &modorigin, &modb, &modbndl, &modbndu, &state->modsparsea, state->sparseaupper, ae_true, modn, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, modm, &state->xqc, &state->modxcc, dotracepresolve, &state->presolver, _state);
            }
            else
            {
                presolvenonescaleuser(&mods, &modorigin, &modb, &modbndl, &modbndu, &state->modsparsea, state->sparseaupper, ae_true, modn, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, modm, &state->xqc, &state->modxcc, dotracepresolve, &state->presolver, _state);
            }
            rcopyallocv(modn, &modxs, &state->xs, _state);
            
            /*
             * Done
             */
            if( state->presolver.problemstatus==-3||state->presolver.problemstatus==-2 )
            {
                state->repterminationtype = state->presolver.problemstatus;
                rsetallocv(n, 0.0, &state->xs, _state);
                ae_frame_leave(_state);
                return;
            }
        }
        ae_assert(state->presolver.problemstatus==0, "MINQP: integrity check 7828 failed", _state);
        
        /*
         * Run the solver
         */
        if( state->presolver.newn>0 )
        {
            presolvefwd(&state->presolver, &state->xs, _state);
            gqpipminitbuf(&state->presolver.bndl, &state->presolver.bndu, &state->xs, state->presolver.newn, state->algokind==7, state->algokind==10, state->veps, state->gmaxits, &state->genipm, _state);
            gqpipmsetquadraticlinear(&state->genipm, &state->tmpr2, &state->presolver.sparseh, 1, &state->presolver.c, _state);
            gqpipmsetconstraints(&state->genipm, &state->presolver.sparsea, &state->presolver.al, &state->presolver.au, state->presolver.newm, &state->presolver.xqc, &state->presolver.xcc, _state);
            if( state->algokind==10 )
            {
                gqpipmsetquasinewtonparams(&state->genipm, state->vmemlen, state->vmaxoffdiag, _state);
            }
            gqpipmoptimize(&state->genipm, &state->xs, &state->replagbc, &state->replaglc, &state->replagqc, &state->repterminationtype, _state);
            state->repinneriterationscount = state->genipm.repiterationscount;
            state->repouteriterationscount = state->genipm.repiterationscount;
            state->repncholesky = state->genipm.repiterationscount;
        }
        else
        {
            
            /*
             * Presolver removed all variables, manually set up XS and Lagrange multipliers
             */
            rsetallocv(state->presolver.newn, 0.0, &state->replagbc, _state);
            rsetallocv(state->presolver.newm, 0.0, &state->replaglc, _state);
            rsetallocv(state->presolver.newmqc, 0.0, &state->replagqc, _state);
            state->repterminationtype = 1;
            state->repinneriterationscount = 0;
            state->repouteriterationscount = 0;
        }
        presolvebwd(&state->presolver, &state->xs, &state->tmpi, ae_false, &state->replagbc, &state->replaglc, &state->replagqc, _state);
        rmergemaxv(n, &lagbcmin, &state->replagbc, _state);
        rmergeminv(n, &lagbcmax, &state->replagbc, _state);
        state->repn = n;
        state->repm = m;
        if( state->repterminationtype>0 )
        {
            if( state->akind==0 )
            {
                state->repf = cqmeval(&state->a, &state->xs, _state);
            }
            if( state->akind==1 )
            {
                state->repf = 0.5*sparsevsmv(&state->sparsea, state->sparseaupper, &state->xs, _state);
            }
            state->repf = state->repf+rdotv(n, &state->b, &state->xs, _state);
        }
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * ECQP
     */
    if( state->algokind==9 )
    {
        
        /*
         * Check constraints
         */
        xlcconverttosparse(&state->xlc, _state);
        if( xqcgetcount(&state->xqc, _state)+xccgetcount(&state->xcc, _state)>0 )
        {
            state->repterminationtype = -5;
            rsetallocv(n, 0.0, &state->xs, _state);
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            if( !ae_isfinite(state->bndl.ptr.p_double[i], _state)&&!ae_isfinite(state->bndu.ptr.p_double[i], _state) )
            {
                continue;
            }
            if( (ae_isfinite(state->bndl.ptr.p_double[i], _state)&&ae_isfinite(state->bndu.ptr.p_double[i], _state))&&ae_fp_eq(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                continue;
            }
            state->repterminationtype = -5;
            rsetallocv(n, 0.0, &state->xs, _state);
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=m-1; i++)
        {
            if( !ae_isfinite(state->xlc.effal.ptr.p_double[i], _state)&&!ae_isfinite(state->xlc.effau.ptr.p_double[i], _state) )
            {
                continue;
            }
            if( (ae_isfinite(state->xlc.effal.ptr.p_double[i], _state)&&ae_isfinite(state->xlc.effau.ptr.p_double[i], _state))&&ae_fp_eq(state->xlc.effal.ptr.p_double[i],state->xlc.effau.ptr.p_double[i]) )
            {
                continue;
            }
            state->repterminationtype = -5;
            rsetallocv(n, 0.0, &state->xs, _state);
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Run presolver
         */
        ae_assert(state->akind==0||state->akind==1, "MinQPOptimize: unexpected AKind", _state);
        if( state->akind==0 )
        {
            cqmgeta(&state->a, &state->tmpr2, _state);
            sparsecreatecrsfromdensebuf(&state->tmpr2, n, n, &state->sparsea, _state);
        }
        if( state->dbgdopresolve )
        {
            presolveqp(&state->effectives, &state->xorigin, &state->b, &state->bndl, &state->bndu, &state->sparsea, state->sparseaupper, ae_true, n, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, m, &state->xqc, &state->xcc, dotracepresolve, &state->presolver, _state);
        }
        else
        {
            presolvenonescaleuser(&state->effectives, &state->xorigin, &state->b, &state->bndl, &state->bndu, &state->sparsea, state->sparseaupper, ae_true, n, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, m, &state->xqc, &state->xcc, dotracepresolve, &state->presolver, _state);
        }
        if( state->presolver.problemstatus==-3||state->presolver.problemstatus==-2 )
        {
            state->repterminationtype = state->presolver.problemstatus;
            rsetallocv(n, 0.0, &state->xs, _state);
            ae_frame_leave(_state);
            return;
        }
        ae_assert(state->presolver.problemstatus==0, "MINQP: integrity check 8938 failed", _state);
        
        /*
         * Run the solver
         */
        if( state->presolver.newn>0 )
        {
            
            /*
             * If presolver did NOT remove all variables (NewN>0), call the current solver.
             * Below we assume that the presolver returned an equality constrained problem.
             */
            ecqpsolve(&state->ecqp, state->presolver.newn, &state->presolver.sparseh, &state->presolver.c, &state->presolver.sparsea, &state->presolver.al, state->presolver.newm, &state->xs, &state->replaglc, &state->repterminationtype, _state);
            state->repinneriterationscount = state->ecqp.repiterationscount;
            state->repouteriterationscount = state->ecqp.repiterationscount;
            state->repncholesky = state->ecqp.repiterationscount;
        }
        else
        {
            
            /*
             * Presolver removed all variables, manually set up XS and Lagrange multipliers
             */
            rsetallocv(state->presolver.newn, 0.0, &state->replagbc, _state);
            rsetallocv(state->presolver.newm, 0.0, &state->replaglc, _state);
            rsetallocv(state->presolver.newmqc, 0.0, &state->replagqc, _state);
            state->repterminationtype = 1;
            state->repinneriterationscount = 0;
            state->repouteriterationscount = 0;
        }
        presolvebwd(&state->presolver, &state->xs, &state->tmpi, ae_false, &state->replagbc, &state->replaglc, &state->replagqc, _state);
        state->repn = n;
        state->repm = m;
        if( state->repterminationtype>0 )
        {
            if( state->akind==0 )
            {
                state->repf = cqmeval(&state->a, &state->xs, _state);
            }
            if( state->akind==1 )
            {
                state->repf = 0.5*sparsevsmv(&state->sparsea, state->sparseaupper, &state->xs, _state);
            }
            state->repf = state->repf+rdotv(n, &state->b, &state->xs, _state);
        }
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Integrity check failed - unknown solver
     */
    ae_assert(ae_false, "MinQPOptimize: integrity check failed - unknown solver", _state);
    ae_frame_leave(_state);
}


/*************************************************************************
QP solver results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution (on failure - the best point found
                so far).
    Rep     -   optimization report, contains:
                * completion code in Rep.TerminationType (positive  values
                  denote some kind of success, negative - failures)
                * Lagrange multipliers - for QP solvers which support them
                * other statistics
                See comments on minqpreport structure for more information
                
Following completion codes are returned in Rep.TerminationType:
* -9    failure of the automatic scale evaluation:  one  of  the  diagonal
        elements of the quadratic term is non-positive.  Specify  variable
        scales manually!
* -5    inappropriate solver was used:
        * QuickQP solver for problem with general linear constraints
        * QuickQP/DENSE-AUL/DENSE-IPM/SPARSE-IPM for a problem with
          quadratic/conic constraints
* -4    the function is unbounded from below even under constraints,
        no meaningful minimum can be found.
* -3    inconsistent constraints (or, maybe, feasible point is too hard to
        find).
* -2    IPM solver has difficulty finding primal/dual feasible point.
        It is likely that the problem is either infeasible or unbounded,
        but it is difficult to determine exact reason for termination.
        X contains best point found so far.
*  >0   success
*  7    stopping conditions are too stringent,
        further improvement is impossible,
        X contains best point found so far.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpresults(const minqpstate* state,
     /* Real    */ ae_vector* x,
     minqpreport* rep,
     ae_state *_state)
{

    ae_vector_clear(x);
    _minqpreport_clear(rep);

    minqpresultsbuf(state, x, rep, _state);
}


/*************************************************************************
QP results

Buffered implementation of MinQPResults() which uses pre-allocated  buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpresultsbuf(const minqpstate* state,
     /* Real    */ ae_vector* x,
     minqpreport* rep,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(state->xs.cnt>=state->n, "MinQPResultsBuf: integrity check failed", _state);
    ae_assert(state->replagbc.cnt>=state->n, "MinQPResultsBuf: integrity check failed", _state);
    ae_assert(state->replaglc.cnt>=state->xlc.ndense+state->xlc.nsparse, "MinQPResultsBuf: integrity check failed", _state);
    ae_assert(state->replagqc.cnt>=state->repmqc, "MinQPResultsBuf: integrity check failed", _state);
    rvectorsetlengthatleast(x, state->n, _state);
    rvectorsetlengthatleast(&rep->lagbc, state->n, _state);
    rvectorsetlengthatleast(&rep->laglc, state->xlc.ndense+state->xlc.nsparse, _state);
    rcopyallocv(state->repmqc, &state->replagqc, &rep->lagqc, _state);
    for(i=0; i<=state->n-1; i++)
    {
        x->ptr.p_double[i] = state->xs.ptr.p_double[i];
        rep->lagbc.ptr.p_double[i] = state->replagbc.ptr.p_double[i];
    }
    for(i=0; i<=state->xlc.ndense+state->xlc.nsparse-1; i++)
    {
        rep->laglc.ptr.p_double[i] = state->replaglc.ptr.p_double[i];
    }
    rep->inneriterationscount = state->repinneriterationscount;
    rep->outeriterationscount = state->repouteriterationscount;
    rep->nmv = state->repnmv;
    rep->ncholesky = state->repncholesky;
    rep->terminationtype = state->repterminationtype;
    rep->f = state->repf;
}


/*************************************************************************
Export current QP problem stored in the solver into  QPXProblem  instance.
This  instance  can  be  serialized  into   ALGLIB-specific   format   and
unserialized from several widely acknowledged formats.

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    P       -   QPXProblem instance storing current objective and
                constraints.

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void minqpexport(minqpstate* state, qpxproblem* p, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool done;
    ae_bool isdifferent;
    ae_int_t i;
    ae_matrix tmpr2;
    sparsematrix tmpa;

    ae_frame_make(_state, &_frame_block);
    memset(&tmpr2, 0, sizeof(tmpr2));
    memset(&tmpa, 0, sizeof(tmpa));
    _qpxproblem_clear(p);
    ae_matrix_init(&tmpr2, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&tmpa, _state, ae_true);

    qpxproblemcreate(state->n, p, _state);
    
    /*
     * Optional terms: scales, origin, initial point
     */
    if( state->havex )
    {
        qpxproblemsetinitialpoint(p, &state->startx, _state);
    }
    isdifferent = ae_false;
    for(i=0; i<=state->n-1; i++)
    {
        isdifferent = isdifferent||state->s.ptr.p_double[i]!=1.0;
    }
    if( isdifferent )
    {
        qpxproblemsetscale(p, &state->s, _state);
    }
    isdifferent = ae_false;
    for(i=0; i<=state->n-1; i++)
    {
        isdifferent = isdifferent||state->xorigin.ptr.p_double[i]!=0.0;
    }
    if( isdifferent )
    {
        qpxproblemsetorigin(p, &state->xorigin, _state);
    }
    
    /*
     * Mandatory terms: objective, box constraints, linear and quadratic constraints
     */
    qpxproblemsetlinearterm(p, &state->b, _state);
    done = ae_false;
    if( state->akind==0 )
    {
        cqmgeta(&state->a, &tmpr2, _state);
        sparsecreatecrsfromdensebuf(&tmpr2, state->n, state->n, &tmpa, _state);
        qpxproblemsetquadraticterm(p, &tmpa, ae_false, _state);
        done = ae_true;
    }
    if( state->akind==1 )
    {
        qpxproblemsetquadraticterm(p, &state->sparsea, state->sparseaupper, _state);
        done = ae_true;
    }
    ae_assert(done, "MinQPExport: unexpected AKind", _state);
    qpxproblemsetbc(p, &state->bndl, &state->bndu, _state);
    if( state->xlc.nsparse+state->xlc.ndense>0 )
    {
        xlcconverttosparse(&state->xlc, _state);
        qpxproblemsetlc2(p, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, state->xlc.nsparse+state->xlc.ndense, _state);
    }
    if( xqcgetcount(&state->xqc, _state)>0 )
    {
        qpxproblemsetxqc(p, &state->xqc, _state);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Imports QP problem, as defined by QPXProblem instance, creating a QP solver
with objective/constraints/scales/origin set to that stored in the instance.

INPUT PARAMETERS:
    P       -   QPXProblem instance storing current objective and
                constraints.

OUTPUT PARAMETERS:
    State   -   newly created solver

  -- ALGLIB --
     Copyright 25.08.2024 by Bochkanov Sergey
*************************************************************************/
void minqpimport(qpxproblem* p, minqpstate* s, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t n;
    ae_int_t m;
    double v0;
    double v1;
    ae_bool b0;
    ae_vector t1;
    ae_vector t1a;
    sparsematrix s2;
    ae_bool isupper;

    ae_frame_make(_state, &_frame_block);
    memset(&t1, 0, sizeof(t1));
    memset(&t1a, 0, sizeof(t1a));
    memset(&s2, 0, sizeof(s2));
    _minqpstate_clear(s);
    ae_vector_init(&t1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t1a, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&s2, _state, ae_true);

    ae_assert(qpxproblemisquadraticobjective(p, _state), "MinQPImport: nonquadratic objectives are not supported", _state);
    ae_assert(qpxproblemgettotalconstraints(p, _state)==qpxproblemgetmlc(p, _state)+qpxproblemgetmqc(p, _state)+qpxproblemgetmcc(p, _state), "MinQPImport: unknown constraint type detected", _state);
    
    /*
     * Create the solver
     */
    n = qpxproblemgetn(p, _state);
    minqpcreate(n, s, _state);
    
    /*
     * Optional terms: scales, origin, initial point
     */
    if( qpxproblemhasinitialpoint(p, _state) )
    {
        qpxproblemgetinitialpoint(p, &t1, _state);
        minqpsetstartingpoint(s, &t1, _state);
    }
    if( qpxproblemhasscale(p, _state) )
    {
        qpxproblemgetscale(p, &t1, _state);
        minqpsetscale(s, &t1, _state);
    }
    if( qpxproblemhasorigin(p, _state) )
    {
        qpxproblemgetorigin(p, &t1, _state);
        minqpsetorigin(s, &t1, _state);
    }
    
    /*
     * Mandatory terms: objective, box constraints, linear and quadratic constraints
     */
    qpxproblemgetlinearterm(p, &t1, _state);
    minqpsetlinearterm(s, &t1, _state);
    qpxproblemgetquadraticterm(p, &s2, &isupper, _state);
    minqpsetquadratictermsparse(s, &s2, isupper, _state);
    qpxproblemgetbc(p, &t1, &t1a, _state);
    minqpsetbc(s, &t1, &t1a, _state);
    if( qpxproblemgetmlc(p, _state)>0 )
    {
        qpxproblemgetlc2(p, &s2, &t1, &t1a, &m, _state);
        minqpsetlc2(s, &s2, &t1, &t1a, m, _state);
    }
    if( qpxproblemgetmqc(p, _state)>0 )
    {
        m = qpxproblemgetmqc(p, _state);
        for(i=0; i<=m-1; i++)
        {
            qpxproblemgetqc2i(p, i, &s2, &isupper, &t1, &v0, &v1, &b0, _state);
            minqpaddqc2(s, &s2, isupper, &t1, v0, v1, b0, _state);
        }
    }
    ae_assert(qpxproblemgetmcc(p, _state)==0, "MinQPImport: conic constraints are not supported yet", _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Fast version of MinQPSetLinearTerm(), which doesn't check its arguments.
For internal use only.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetlineartermfast(minqpstate* state,
     /* Real    */ const ae_vector* b,
     ae_state *_state)
{


    ae_v_move(&state->b.ptr.p_double[0], 1, &b->ptr.p_double[0], 1, ae_v_len(0,state->n-1));
}


/*************************************************************************
Fast version of MinQPSetQuadraticTerm(), which doesn't check its arguments.

It accepts additional parameter - shift S, which allows to "shift"  matrix
A by adding s*I to A. S must be positive (although it is not checked).

For internal use only.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetquadratictermfast(minqpstate* state,
     /* Real    */ const ae_matrix* a,
     ae_bool isupper,
     double s,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    double v;
    ae_int_t j0;
    ae_int_t j1;


    n = state->n;
    state->akind = 0;
    if( !state->cqmready )
    {
        cqminit(n, &state->a, _state);
        state->cqmready = ae_true;
    }
    cqmseta(&state->a, a, isupper, 1.0, _state);
    if( ae_fp_greater(s,(double)(0)) )
    {
        rvectorsetlengthatleast(&state->tmp0, n, _state);
        for(i=0; i<=n-1; i++)
        {
            state->tmp0.ptr.p_double[i] = a->ptr.pp_double[i][i]+s;
        }
        cqmrewritedensediagonal(&state->a, &state->tmp0, _state);
    }
    
    /*
     * Estimate norm of A
     * (it will be used later in the quadratic penalty function)
     */
    state->absamax = (double)(0);
    state->absasum = (double)(0);
    state->absasum2 = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        if( isupper )
        {
            j0 = i;
            j1 = n-1;
        }
        else
        {
            j0 = 0;
            j1 = i;
        }
        for(j=j0; j<=j1; j++)
        {
            v = ae_fabs(a->ptr.pp_double[i][j], _state);
            state->absamax = ae_maxreal(state->absamax, v, _state);
            state->absasum = state->absasum+v;
            state->absasum2 = state->absasum2+v*v;
        }
    }
}


/*************************************************************************
Internal function which allows to rewrite diagonal of quadratic term.
For internal use only.

This function can be used only when you have dense A and already made
MinQPSetQuadraticTerm(Fast) call.

  -- ALGLIB --
     Copyright 16.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqprewritediagonal(minqpstate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{


    cqmrewritedensediagonal(&state->a, s, _state);
}


/*************************************************************************
Fast version of MinQPSetStartingPoint(), which doesn't check its arguments.
For internal use only.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetstartingpointfast(minqpstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_v_move(&state->startx.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->havex = ae_true;
}


/*************************************************************************
Fast version of MinQPSetOrigin(), which doesn't check its arguments.
For internal use only.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minqpsetoriginfast(minqpstate* state,
     /* Real    */ const ae_vector* xorigin,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_v_move(&state->xorigin.ptr.p_double[0], 1, &xorigin->ptr.p_double[0], 1, ae_v_len(0,n-1));
}


void _minqpstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minqpstate *p = (minqpstate*)_p;
    ae_touch_ptr((void*)p);
    _qqpsettings_init(&p->qqpsettingsuser, _state, make_automatic);
    _qpdenseaulsettings_init(&p->qpdenseaulsettingsuser, _state, make_automatic);
    _convexquadraticmodel_init(&p->a, _state, make_automatic);
    _sparsematrix_init(&p->sparsea, _state, make_automatic);
    ae_vector_init(&p->b, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->havebndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->havebndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->xorigin, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->startx, 0, DT_REAL, _state, make_automatic);
    _xlinearconstraints_init(&p->xlc, _state, make_automatic);
    _xquadraticconstraints_init(&p->xqc, _state, make_automatic);
    _xconicconstraints_init(&p->xcc, _state, make_automatic);
    ae_vector_init(&p->xs, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replagbc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replaglc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replagqc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->effectives, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpi, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->elaglc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dummyr, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->dummyr2, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->dummysparse, _state, make_automatic);
    ae_matrix_init(&p->tmpr2, 0, 0, DT_REAL, _state, make_automatic);
    _presolveinfo_init(&p->presolver, _state, make_automatic);
    ae_nxpool_init(&p->n1realpool, DT_REAL, _state, make_automatic);
    ae_nxpool_init(&p->n1intpool, DT_INT, _state, make_automatic);
    _qqpbuffers_init(&p->qqpbuf, _state, make_automatic);
    _qpdenseaulbuffers_init(&p->qpdenseaulbuf, _state, make_automatic);
    _vipmstate_init(&p->vsolver, _state, make_automatic);
    _ipm2state_init(&p->ipm2, _state, make_automatic);
    _ecqpstate_init(&p->ecqp, _state, make_automatic);
    _gqpipmstate_init(&p->genipm, _state, make_automatic);
    _sparsematrix_init(&p->modsparsea, _state, make_automatic);
    _xconicconstraints_init(&p->modxcc, _state, make_automatic);
}


void _minqpstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minqpstate       *dst = (minqpstate*)_dst;
    const minqpstate *src = (const minqpstate*)_src;
    dst->n = src->n;
    _qqpsettings_init_copy(&dst->qqpsettingsuser, &src->qqpsettingsuser, _state, make_automatic);
    _qpdenseaulsettings_init_copy(&dst->qpdenseaulsettingsuser, &src->qpdenseaulsettingsuser, _state, make_automatic);
    dst->veps = src->veps;
    dst->vmemlen = src->vmemlen;
    dst->vmaxoffdiag = src->vmaxoffdiag;
    dst->gmaxits = src->gmaxits;
    dst->dbgskipconstraintnormalization = src->dbgskipconstraintnormalization;
    dst->dbgdopresolve = src->dbgdopresolve;
    dst->algokind = src->algokind;
    dst->akind = src->akind;
    _convexquadraticmodel_init_copy(&dst->a, &src->a, _state, make_automatic);
    dst->cqmready = src->cqmready;
    _sparsematrix_init_copy(&dst->sparsea, &src->sparsea, _state, make_automatic);
    dst->sparseaupper = src->sparseaupper;
    dst->absamax = src->absamax;
    dst->absasum = src->absasum;
    dst->absasum2 = src->absasum2;
    ae_vector_init_copy(&dst->b, &src->b, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    dst->stype = src->stype;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->havebndl, &src->havebndl, _state, make_automatic);
    ae_vector_init_copy(&dst->havebndu, &src->havebndu, _state, make_automatic);
    ae_vector_init_copy(&dst->xorigin, &src->xorigin, _state, make_automatic);
    ae_vector_init_copy(&dst->startx, &src->startx, _state, make_automatic);
    dst->havex = src->havex;
    _xlinearconstraints_init_copy(&dst->xlc, &src->xlc, _state, make_automatic);
    _xquadraticconstraints_init_copy(&dst->xqc, &src->xqc, _state, make_automatic);
    _xconicconstraints_init_copy(&dst->xcc, &src->xcc, _state, make_automatic);
    ae_vector_init_copy(&dst->xs, &src->xs, _state, make_automatic);
    dst->repf = src->repf;
    dst->repinneriterationscount = src->repinneriterationscount;
    dst->repouteriterationscount = src->repouteriterationscount;
    dst->repncholesky = src->repncholesky;
    dst->repnmv = src->repnmv;
    dst->repterminationtype = src->repterminationtype;
    ae_vector_init_copy(&dst->replagbc, &src->replagbc, _state, make_automatic);
    ae_vector_init_copy(&dst->replaglc, &src->replaglc, _state, make_automatic);
    ae_vector_init_copy(&dst->replagqc, &src->replagqc, _state, make_automatic);
    dst->repn = src->repn;
    dst->repm = src->repm;
    dst->repmqc = src->repmqc;
    dst->repmcc = src->repmcc;
    ae_vector_init_copy(&dst->effectives, &src->effectives, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpi, &src->tmpi, _state, make_automatic);
    ae_vector_init_copy(&dst->elaglc, &src->elaglc, _state, make_automatic);
    ae_vector_init_copy(&dst->dummyr, &src->dummyr, _state, make_automatic);
    ae_matrix_init_copy(&dst->dummyr2, &src->dummyr2, _state, make_automatic);
    _sparsematrix_init_copy(&dst->dummysparse, &src->dummysparse, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpr2, &src->tmpr2, _state, make_automatic);
    _presolveinfo_init_copy(&dst->presolver, &src->presolver, _state, make_automatic);
    ae_nxpool_init_copy(&dst->n1realpool, &src->n1realpool, _state, make_automatic);
    ae_nxpool_init_copy(&dst->n1intpool, &src->n1intpool, _state, make_automatic);
    _qqpbuffers_init_copy(&dst->qqpbuf, &src->qqpbuf, _state, make_automatic);
    _qpdenseaulbuffers_init_copy(&dst->qpdenseaulbuf, &src->qpdenseaulbuf, _state, make_automatic);
    _vipmstate_init_copy(&dst->vsolver, &src->vsolver, _state, make_automatic);
    _ipm2state_init_copy(&dst->ipm2, &src->ipm2, _state, make_automatic);
    _ecqpstate_init_copy(&dst->ecqp, &src->ecqp, _state, make_automatic);
    _gqpipmstate_init_copy(&dst->genipm, &src->genipm, _state, make_automatic);
    _sparsematrix_init_copy(&dst->modsparsea, &src->modsparsea, _state, make_automatic);
    _xconicconstraints_init_copy(&dst->modxcc, &src->modxcc, _state, make_automatic);
}


void _minqpstate_clear(void* _p)
{
    minqpstate *p = (minqpstate*)_p;
    ae_touch_ptr((void*)p);
    _qqpsettings_clear(&p->qqpsettingsuser);
    _qpdenseaulsettings_clear(&p->qpdenseaulsettingsuser);
    _convexquadraticmodel_clear(&p->a);
    _sparsematrix_clear(&p->sparsea);
    ae_vector_clear(&p->b);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->havebndl);
    ae_vector_clear(&p->havebndu);
    ae_vector_clear(&p->xorigin);
    ae_vector_clear(&p->startx);
    _xlinearconstraints_clear(&p->xlc);
    _xquadraticconstraints_clear(&p->xqc);
    _xconicconstraints_clear(&p->xcc);
    ae_vector_clear(&p->xs);
    ae_vector_clear(&p->replagbc);
    ae_vector_clear(&p->replaglc);
    ae_vector_clear(&p->replagqc);
    ae_vector_clear(&p->effectives);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmp1);
    ae_vector_clear(&p->tmpi);
    ae_vector_clear(&p->elaglc);
    ae_vector_clear(&p->dummyr);
    ae_matrix_clear(&p->dummyr2);
    _sparsematrix_clear(&p->dummysparse);
    ae_matrix_clear(&p->tmpr2);
    _presolveinfo_clear(&p->presolver);
    ae_nxpool_clear(&p->n1realpool);
    ae_nxpool_clear(&p->n1intpool);
    _qqpbuffers_clear(&p->qqpbuf);
    _qpdenseaulbuffers_clear(&p->qpdenseaulbuf);
    _vipmstate_clear(&p->vsolver);
    _ipm2state_clear(&p->ipm2);
    _ecqpstate_clear(&p->ecqp);
    _gqpipmstate_clear(&p->genipm);
    _sparsematrix_clear(&p->modsparsea);
    _xconicconstraints_clear(&p->modxcc);
}


void _minqpstate_destroy(void* _p)
{
    minqpstate *p = (minqpstate*)_p;
    ae_touch_ptr((void*)p);
    _qqpsettings_destroy(&p->qqpsettingsuser);
    _qpdenseaulsettings_destroy(&p->qpdenseaulsettingsuser);
    _convexquadraticmodel_destroy(&p->a);
    _sparsematrix_destroy(&p->sparsea);
    ae_vector_destroy(&p->b);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->havebndl);
    ae_vector_destroy(&p->havebndu);
    ae_vector_destroy(&p->xorigin);
    ae_vector_destroy(&p->startx);
    _xlinearconstraints_destroy(&p->xlc);
    _xquadraticconstraints_destroy(&p->xqc);
    _xconicconstraints_destroy(&p->xcc);
    ae_vector_destroy(&p->xs);
    ae_vector_destroy(&p->replagbc);
    ae_vector_destroy(&p->replaglc);
    ae_vector_destroy(&p->replagqc);
    ae_vector_destroy(&p->effectives);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmp1);
    ae_vector_destroy(&p->tmpi);
    ae_vector_destroy(&p->elaglc);
    ae_vector_destroy(&p->dummyr);
    ae_matrix_destroy(&p->dummyr2);
    _sparsematrix_destroy(&p->dummysparse);
    ae_matrix_destroy(&p->tmpr2);
    _presolveinfo_destroy(&p->presolver);
    ae_nxpool_destroy(&p->n1realpool);
    ae_nxpool_destroy(&p->n1intpool);
    _qqpbuffers_destroy(&p->qqpbuf);
    _qpdenseaulbuffers_destroy(&p->qpdenseaulbuf);
    _vipmstate_destroy(&p->vsolver);
    _ipm2state_destroy(&p->ipm2);
    _ecqpstate_destroy(&p->ecqp);
    _gqpipmstate_destroy(&p->genipm);
    _sparsematrix_destroy(&p->modsparsea);
    _xconicconstraints_destroy(&p->modxcc);
}


void _minqpreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minqpreport *p = (minqpreport*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->lagbc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->laglc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagqc, 0, DT_REAL, _state, make_automatic);
}


void _minqpreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minqpreport       *dst = (minqpreport*)_dst;
    const minqpreport *src = (const minqpreport*)_src;
    dst->inneriterationscount = src->inneriterationscount;
    dst->outeriterationscount = src->outeriterationscount;
    dst->nmv = src->nmv;
    dst->ncholesky = src->ncholesky;
    dst->terminationtype = src->terminationtype;
    dst->f = src->f;
    ae_vector_init_copy(&dst->lagbc, &src->lagbc, _state, make_automatic);
    ae_vector_init_copy(&dst->laglc, &src->laglc, _state, make_automatic);
    ae_vector_init_copy(&dst->lagqc, &src->lagqc, _state, make_automatic);
}


void _minqpreport_clear(void* _p)
{
    minqpreport *p = (minqpreport*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->lagbc);
    ae_vector_clear(&p->laglc);
    ae_vector_clear(&p->lagqc);
}


void _minqpreport_destroy(void* _p)
{
    minqpreport *p = (minqpreport*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->lagbc);
    ae_vector_destroy(&p->laglc);
    ae_vector_destroy(&p->lagqc);
}


#endif
#if defined(AE_COMPILE_NLCFSQP) || !defined(AE_PARTIAL_BUILD)


void minfsqpinitbuf(/* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* x0,
     ae_int_t n,
     const sparsematrix* sparsea,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     /* Integer */ const ae_vector* lcsrcidx,
     ae_int_t lccnt,
     /* Real    */ const ae_vector* nl,
     /* Real    */ const ae_vector* nu,
     ae_int_t nnlc,
     const nlpstoppingcriteria* criteria,
     ae_bool usedensebfgs,
     minfsqpstate* state,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    double v;
    double vv;
    ae_int_t nslack;


    nslack = n+2*lccnt+2*nnlc;
    state->n = n;
    state->lccnt = lccnt;
    state->nnlc = nnlc;
    state->additsforctol = 0;
    state->ctol = (double)(0);
    
    /*
     * Prepare RCOMM state
     */
    ae_vector_set_length(&state->rstate.ia, 4+1, _state);
    ae_vector_set_length(&state->rstate.ba, 14+1, _state);
    ae_vector_set_length(&state->rstate.ra, 17+1, _state);
    state->rstate.stage = -1;
    state->needfisj = ae_false;
    state->xupdated = ae_false;
    rallocv(n, &state->x, _state);
    rallocv(1+nnlc, &state->fi, _state);
    
    /*
     * Allocate memory.
     */
    rallocv(n, &state->x0, _state);
    rallocv(n, &state->xprev, _state);
    rvectorsetlengthatleast(&state->s, n, _state);
    bvectorsetlengthatleast(&state->hasbndl, n, _state);
    bvectorsetlengthatleast(&state->hasbndu, n, _state);
    rvectorsetlengthatleast(&state->scaledbndl, n, _state);
    rvectorsetlengthatleast(&state->scaledbndu, n, _state);
    rvectorsetlengthatleast(&state->dtrial, nslack, _state);
    rvectorsetlengthatleast(&state->d0, nslack, _state);
    rvectorsetlengthatleast(&state->dmu, nslack, _state);
    rvectorsetlengthatleast(&state->lagbcmult, n, _state);
    rvectorsetlengthatleast(&state->dummylagbcmult, n, _state);
    rvectorsetlengthatleast(&state->lagxcmult, lccnt+nnlc, _state);
    rvectorsetlengthatleast(&state->dummylagxcmult, lccnt+nnlc, _state);
    ballocv(nnlc, &state->hasnl, _state);
    ballocv(nnlc, &state->hasnu, _state);
    rallocv(nnlc, &state->rawnl, _state);
    rallocv(nnlc, &state->rawnu, _state);
    
    /*
     * Prepare scaled problem
     */
    for(i=0; i<=n-1; i++)
    {
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
        if( state->hasbndl.ptr.p_bool[i] )
        {
            state->scaledbndl.ptr.p_double[i] = bndl->ptr.p_double[i]/s->ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i] )
        {
            state->scaledbndu.ptr.p_double[i] = bndu->ptr.p_double[i]/s->ptr.p_double[i];
        }
        if( state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i] )
        {
            ae_assert(ae_fp_less_eq(bndl->ptr.p_double[i],bndu->ptr.p_double[i]), "SQP: integrity check failed, box constraints are inconsistent", _state);
        }
        state->x0.ptr.p_double[i] = x0->ptr.p_double[i]/s->ptr.p_double[i];
        state->s.ptr.p_double[i] = s->ptr.p_double[i];
    }
    if( lccnt>0 )
    {
        rsetallocv(lccnt, 1.0, &state->lcscales, _state);
        iallocv(lccnt, &state->lcsrcidx, _state);
        bsetallocv(lccnt, ae_false, &state->hasal, _state);
        bsetallocv(lccnt, ae_false, &state->hasau, _state);
        rallocv(lccnt, &state->sclal, _state);
        rallocv(lccnt, &state->sclau, _state);
        sparsecopytocrsbuf(sparsea, &state->scla, _state);
        for(i=0; i<=lccnt-1; i++)
        {
            state->lcsrcidx.ptr.p_int[i] = lcsrcidx->ptr.p_int[i];
            
            /*
             * Scale and normalize linear constraints
             */
            vv = 0.0;
            j0 = state->scla.ridx.ptr.p_int[i];
            j1 = state->scla.ridx.ptr.p_int[i+1]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                v = state->scla.vals.ptr.p_double[jj]*s->ptr.p_double[state->scla.idx.ptr.p_int[jj]];
                state->scla.vals.ptr.p_double[jj] = v;
                vv = vv+v*v;
            }
            vv = coalesce(ae_sqrt(vv, _state), (double)(1), _state);
            state->lcscales.ptr.p_double[i] = vv;
            vv = (double)1/vv;
            for(jj=j0; jj<=j1; jj++)
            {
                state->scla.vals.ptr.p_double[jj] = state->scla.vals.ptr.p_double[jj]*vv;
            }
            if( ae_isfinite(al->ptr.p_double[i], _state) )
            {
                state->hasal.ptr.p_bool[i] = ae_true;
                state->sclal.ptr.p_double[i] = al->ptr.p_double[i]*vv;
            }
            else
            {
                state->sclal.ptr.p_double[i] = _state->v_neginf;
            }
            if( ae_isfinite(au->ptr.p_double[i], _state) )
            {
                state->hasau.ptr.p_bool[i] = ae_true;
                state->sclau.ptr.p_double[i] = au->ptr.p_double[i]*vv;
            }
            else
            {
                state->sclau.ptr.p_double[i] = _state->v_posinf;
            }
        }
    }
    for(i=0; i<=nnlc-1; i++)
    {
        state->hasnl.ptr.p_bool[i] = ae_isfinite(nl->ptr.p_double[i], _state);
        state->hasnu.ptr.p_bool[i] = ae_isfinite(nu->ptr.p_double[i], _state);
        state->rawnl.ptr.p_double[i] = nl->ptr.p_double[i];
        state->rawnu.ptr.p_double[i] = nu->ptr.p_double[i];
    }
    
    /*
     * Initial enforcement of box constraints
     */
    for(i=0; i<=n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i] )
        {
            state->x0.ptr.p_double[i] = ae_maxreal(state->x0.ptr.p_double[i], state->scaledbndl.ptr.p_double[i], _state);
        }
        if( state->hasbndu.ptr.p_bool[i] )
        {
            state->x0.ptr.p_double[i] = ae_minreal(state->x0.ptr.p_double[i], state->scaledbndu.ptr.p_double[i], _state);
        }
    }
    
    /*
     * Stopping criteria and settings
     */
    critcopy(criteria, &state->criteria, _state);
    state->bfgsresetfreq = nlcfsqp_defaultbfgsresetfreq;
    state->usedensebfgs = usedensebfgs;
    state->inittrustrad = nlcfsqp_definittrustrad;
    
    /*
     * Report fields
     */
    state->repterminationtype = 0;
    state->repbcerr = (double)(0);
    state->repbcidx = -1;
    state->replcerr = (double)(0);
    state->replcidx = -1;
    state->repnlcerr = (double)(0);
    state->repnlcidx = -1;
    state->repsclerr = (double)(0);
    state->repiterationscount = 0;
    
    /*
     * Integrity checks
     */
    ae_assert(ae_fp_less(nlcfsqp_sqpdeltadecrease,nlcfsqp_sqpdeltaincrease), "MinSQP: integrity check failed", _state);
}


/*************************************************************************
This function sets the number additional iterations that can be  performed
after reaching MaxIts limit, if the current  point  is  infeasible (scaled
constraint violation is above CTol), but there was a feasible point in the
optimization history.

Up to AddIts iterations will be performed, with iterations being terminated
immediately after reaching CTol level of error.

INPUT PARAMETERS:
    State           -   solver state
    CTol            -   scaled constraint violation tolerance. Points with
                        violation below or at CTol are considered feasible
                        
                        Do not use zero value  because  it  is  likely  to
                        reject points with even a tiny violation of
                        constraints.

  -- ALGLIB --
     Copyright 05.03.2018 by Bochkanov Sergey
*************************************************************************/
void minfsqpsetadditsforctol(minfsqpstate* state,
     ae_int_t addits,
     double ctol,
     ae_state *_state)
{


    state->additsforctol = addits;
    state->ctol = ctol;
}


/*************************************************************************
This function sets initial trust radius

INPUT PARAMETERS:
    State           -   solver state
    Rad0            -   initial trust radius, Rad0>0

  -- ALGLIB --
     Copyright 05.03.2018 by Bochkanov Sergey
*************************************************************************/
void minfsqpsetinittrustrad(minfsqpstate* state,
     double rad0,
     ae_state *_state)
{


    state->inittrustrad = rad0;
}


/*************************************************************************
This function performs actual processing for  SQP  algorithm.  It  expects
that caller redirects its reverse communication  requests NeedFiJ/XUpdated
to external user who will provide analytic derivative (or  handle  reports
about progress).

In case external user does not have analytic derivative, it is responsibility
of caller to intercept NeedFiJ request and  replace  it  with  appropriate
numerical differentiation scheme.

Results are stored:
* point - in State.StepKX.X, State.StepKX.Fi

IMPORTANT: this function works with scaled problem formulation; it is
           responsibility of the caller to unscale request and scale
           Jacobian.
           
NOTE: SMonitor is expected to be correctly initialized smoothness monitor.

  -- ALGLIB --
     Copyright 05.03.2018 by Bochkanov Sergey
*************************************************************************/
ae_bool minfsqpiteration(minfsqpstate* state,
     smoothnessmonitor* smonitor,
     ae_bool userterminationneeded,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t lccnt;
    ae_int_t nnlc;
    ae_int_t i;
    ae_int_t j;
    double v;
    double vv;
    double mx;
    double deltamax;
    double multiplyby;
    double setscaleto;
    double prevtrustrad;
    ae_bool trustradstagnated;
    double relativetargetdecrease;
    ae_bool dotrace;
    ae_bool dotracelaconic;
    ae_bool doprobingalways;
    ae_bool doprobingonfailure;
    ae_bool dodetailedtrace;
    ae_bool meritfstagnated;
    double tgt0;
    double h0;
    double tgt1;
    double h1;
    double tol;
    double stepklagval;
    double stepknlagval;
    double d2trustradratio;
    double predictedchangemodel;
    double predictedchangepenalty;
    ae_bool infinitiesdetected;
    ae_bool failedtorecoverfrominfinities;
    ae_bool isinfeasible;
    ae_bool feasibilityrestoration;
    ae_bool firstrestorationiteration;
    ae_bool stepfailed;
    ae_bool restartiteration;
    ae_bool leavefeasibilityrestoration;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        n = state->rstate.ia.ptr.p_int[0];
        lccnt = state->rstate.ia.ptr.p_int[1];
        nnlc = state->rstate.ia.ptr.p_int[2];
        i = state->rstate.ia.ptr.p_int[3];
        j = state->rstate.ia.ptr.p_int[4];
        trustradstagnated = state->rstate.ba.ptr.p_bool[0];
        dotrace = state->rstate.ba.ptr.p_bool[1];
        dotracelaconic = state->rstate.ba.ptr.p_bool[2];
        doprobingalways = state->rstate.ba.ptr.p_bool[3];
        doprobingonfailure = state->rstate.ba.ptr.p_bool[4];
        dodetailedtrace = state->rstate.ba.ptr.p_bool[5];
        meritfstagnated = state->rstate.ba.ptr.p_bool[6];
        infinitiesdetected = state->rstate.ba.ptr.p_bool[7];
        failedtorecoverfrominfinities = state->rstate.ba.ptr.p_bool[8];
        isinfeasible = state->rstate.ba.ptr.p_bool[9];
        feasibilityrestoration = state->rstate.ba.ptr.p_bool[10];
        firstrestorationiteration = state->rstate.ba.ptr.p_bool[11];
        stepfailed = state->rstate.ba.ptr.p_bool[12];
        restartiteration = state->rstate.ba.ptr.p_bool[13];
        leavefeasibilityrestoration = state->rstate.ba.ptr.p_bool[14];
        v = state->rstate.ra.ptr.p_double[0];
        vv = state->rstate.ra.ptr.p_double[1];
        mx = state->rstate.ra.ptr.p_double[2];
        deltamax = state->rstate.ra.ptr.p_double[3];
        multiplyby = state->rstate.ra.ptr.p_double[4];
        setscaleto = state->rstate.ra.ptr.p_double[5];
        prevtrustrad = state->rstate.ra.ptr.p_double[6];
        relativetargetdecrease = state->rstate.ra.ptr.p_double[7];
        tgt0 = state->rstate.ra.ptr.p_double[8];
        h0 = state->rstate.ra.ptr.p_double[9];
        tgt1 = state->rstate.ra.ptr.p_double[10];
        h1 = state->rstate.ra.ptr.p_double[11];
        tol = state->rstate.ra.ptr.p_double[12];
        stepklagval = state->rstate.ra.ptr.p_double[13];
        stepknlagval = state->rstate.ra.ptr.p_double[14];
        d2trustradratio = state->rstate.ra.ptr.p_double[15];
        predictedchangemodel = state->rstate.ra.ptr.p_double[16];
        predictedchangepenalty = state->rstate.ra.ptr.p_double[17];
    }
    else
    {
        n = 359;
        lccnt = -58;
        nnlc = -919;
        i = -909;
        j = 81;
        trustradstagnated = ae_true;
        dotrace = ae_false;
        dotracelaconic = ae_false;
        doprobingalways = ae_true;
        doprobingonfailure = ae_true;
        dodetailedtrace = ae_false;
        meritfstagnated = ae_true;
        infinitiesdetected = ae_false;
        failedtorecoverfrominfinities = ae_true;
        isinfeasible = ae_true;
        feasibilityrestoration = ae_false;
        firstrestorationiteration = ae_false;
        stepfailed = ae_false;
        restartiteration = ae_false;
        leavefeasibilityrestoration = ae_false;
        v = -229.0;
        vv = -536.0;
        mx = 487.0;
        deltamax = -115.0;
        multiplyby = 886.0;
        setscaleto = 346.0;
        prevtrustrad = -722.0;
        relativetargetdecrease = -413.0;
        tgt0 = -461.0;
        h0 = 927.0;
        tgt1 = 201.0;
        h1 = 922.0;
        tol = -154.0;
        stepklagval = 306.0;
        stepknlagval = -1011.0;
        d2trustradratio = 951.0;
        predictedchangemodel = -463.0;
        predictedchangepenalty = 88.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    
    /*
     * Routine body
     */
    n = state->n;
    lccnt = state->lccnt;
    nnlc = state->nnlc;
    dotracelaconic = ae_is_trace_enabled("SQP.LACONIC");
    dotrace = ae_is_trace_enabled("SQP")&&!dotracelaconic;
    dodetailedtrace = dotrace&&ae_is_trace_enabled("SQP.DETAILED");
    doprobingalways = dotrace&&ae_is_trace_enabled("SQP.PROBING");
    doprobingonfailure = dotrace&&ae_is_trace_enabled("SQP.PROBINGONFAILURE");
    
    /*
     * Prepare rcomm interface
     */
    state->needfisj = ae_false;
    state->xupdated = ae_false;
    
    /*
     * Prepare debug timers
     */
    stimerinit(&state->timertotal, _state);
    stimerinit(&state->timerqpgen, _state);
    stimerinit(&state->timeripminit, _state);
    stimerinit(&state->timeripmsolve, _state);
    stimerinit(&state->timercallback, _state);
    stimerstart(&state->timertotal, _state);
    
    /*
     * Initialize algorithm data:
     * * Lagrangian and "Big C" estimates
     * * trust region
     * * initial function scales (vector of 1's)
     * * current approximation of the Hessian matrix H (unit matrix)
     * * initial linearized constraints
     * * initial violation of linear/nonlinear constraints
     */
    state->werepointsbelowctol = ae_false;
    state->fstagnationcnt = 0;
    state->trustradstagnationcnt = 0;
    state->trustrad = state->inittrustrad;
    state->trustradgrowth = nlcfsqp_deftrustradgrowth;
    rsetallocv(n, 0.0, &state->replagbc, _state);
    rsetallocv(lccnt, 0.0, &state->replaglc, _state);
    rsetallocv(nnlc, 0.0, &state->replagnlc, _state);
    
    /*
     * Evaluate function vector and Jacobian at X0, send first location report.
     * Compute initial violation of constraints.
     */
    rsetallocv(1+nnlc, 1.0, &state->fscaleszzz, _state);
    rsetallocv(1+nnlc, 1.0, &state->invfscales, _state);
    state->sqpbigscale = nlcfsqp_defsqpbigscale;
    state->sqpsmallscale = nlcfsqp_defsqpsmallscale;
    rcopyallocv(nnlc, &state->rawnl, &state->scalednl, _state);
    rcopyallocv(nnlc, &state->rawnu, &state->scalednu, _state);
    vfjallocsparse(n, 1+nnlc, &state->stepk, _state);
    vfjallocsparse(n, 1+nnlc, &state->cand, _state);
    vfjallocsparse(n, 1+nnlc, &state->probe, _state);
    rcopyv(n, &state->x0, &state->stepk.x, _state);
    nlcfsqp_sqpsendx(state, &state->stepk.x, _state);
    state->needfisj = ae_true;
    if( dotrace )
    {
        stimerstart(&state->timercallback, _state);
    }
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    if( dotrace )
    {
        stimerstop(&state->timercallback, _state);
    }
    state->needfisj = ae_false;
    if( !nlcfsqp_sqpretrievefij(state, &state->stepk, _state) )
    {
        
        /*
         * Failed to retrieve function/Jacobian, infinities detected!
         */
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    nlcfsqp_sqpsendx(state, &state->stepk.x, _state);
    state->f = state->stepk.fi.ptr.p_double[0]*state->fscaleszzz.ptr.p_double[0];
    state->xupdated = ae_true;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->xupdated = ae_false;
    checklc2violation(&state->scla, &state->sclal, &state->sclau, &state->lcsrcidx, lccnt, &state->stepk.x, &state->replcerr, &state->replcidx, _state);
    unscaleandchecknlc2violation(&state->stepk.fi, &state->fscaleszzz, &state->rawnl, &state->rawnu, nnlc, &state->repnlcerr, &state->repnlcidx, _state);
    checknlc2violation(&state->stepk.fi, &state->scalednl, &state->scalednu, nnlc, &state->repsclerr, &i, _state);
    state->repsclerr = rmaxabs3(state->repsclerr, state->repbcerr, state->replcerr, _state);
    nlcfsqp_targetandconstraints(state, &state->stepk, &tgt0, &h0, _state);
    nlpfinit((double)10*(h0+(double)1), &state->filter, _state);
    state->filter.gammaf = 0.001;
    state->filter.gammah = 0.999;
    state->werepointsbelowctol = state->werepointsbelowctol||ae_fp_less_eq(state->repsclerr,state->ctol);
    
    /*
     * Trace output (if needed)
     */
    if( dotrace )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("//  FILTER SQP SOLVER STARTED                                                                     //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("N             = %6d\n",
            (int)(n));
        ae_trace("cntLC         = %6d\n",
            (int)(lccnt));
        ae_trace("cntNLC        = %6d\n",
            (int)(nnlc));
    }
    if( dotracelaconic )
    {
        ae_trace("> ALGLIB filter SQP solver started, %0d variables, %0d linear constraints, %0d general constraints\n",
            (int)(n),
            (int)(lccnt),
            (int)(nnlc));
        ae_trace("   ITS        TR.RAD                  OBJECTIVE          FEAS.ERR           TIME\n");
    }
    
    /*
     * Perform outer (NLC) iterations
     */
    feasibilityrestoration = ae_false;
    firstrestorationiteration = ae_false;
    infinitiesdetected = ae_false;
    failedtorecoverfrominfinities = ae_false;
    relativetargetdecrease = 1.0E50;
    if( state->usedensebfgs )
    {
        hessianinitbfgs(&state->hess, n, state->bfgsresetfreq, 0.1*nlcfsqp_sqpdeltadecrease*ae_maxreal(critgeteps(&state->criteria, _state), ae_sqrt(ae_machineepsilon, _state), _state), _state);
    }
    else
    {
        hessianinitlowranksr1(&state->hess, n, ae_minint(n, nlcfsqp_xbfgsmemlen, _state), 0.1*nlcfsqp_sqpdeltadecrease*ae_maxreal(critgeteps(&state->criteria, _state), ae_sqrt(ae_machineepsilon, _state), _state), nlcfsqp_xbfgsmaxhess, _state);
        state->hess.mincrv = 0.0001;
    }
    rallocv(n, &state->varscales, _state);
    for(i=0; i<=n-1; i++)
    {
        state->varscales.ptr.p_double[i] = nlcfsqp_gettrustregionk(&state->stepk.x, i, 1.0, _state);
    }
    nlcfsqp_initqpsubsolver(state, &state->subsolver, _state);
lbl_5:
    if( ae_false )
    {
        goto lbl_6;
    }
    
    /*
     * Compute current function and variable scales, send to Hessian and filter
     */
    nlcfsqp_autoscalevarstargetconstraints(state, &state->stepk, ae_false, dotrace, _state);
    nlcfsqp_targetandconstraints(state, &state->stepk, &tgt0, &h0, _state);
    
    /*
     * Before beginning new outer iteration:
     * * check stopping criteria
     * * renormalize target function and/or constraints, if some of them have too large magnitudes
     * * save initial point for the outer iteration
     */
    if( ae_fp_less_eq(relativetargetdecrease,critgetepsf(&state->criteria, _state)) )
    {
        state->repterminationtype = 1;
        if( dotrace||dotracelaconic )
        {
            ae_trace("> stopping condition met: relative change in target is less than %0.3e\n",
                (double)(critgetepsf(&state->criteria, _state)));
        }
        goto lbl_6;
    }
    if( ae_fp_less_eq(state->trustrad,critgeteps(&state->criteria, _state)) )
    {
        state->repterminationtype = 2;
        if( dotrace||dotracelaconic )
        {
            ae_trace("> stopping condition met: trust radius is smaller than %0.3e\n",
                (double)(critgeteps(&state->criteria, _state)));
        }
        goto lbl_6;
    }
    v = ae_sqrt(ae_machineepsilon, _state);
    if( ae_fp_less_eq(state->trustrad,v) )
    {
        state->repterminationtype = 7;
        if( dotrace||dotracelaconic )
        {
            ae_trace("> stopping condition met: trust radius is smaller than the absolute minimum %0.3e\n",
                (double)(v));
        }
        goto lbl_6;
    }
    if( critgetmaxits(&state->criteria, _state)>0&&state->repiterationscount>=nlcfsqp_geteffectivemaxits(state, _state) )
    {
        state->repterminationtype = 5;
        if( dotrace||dotracelaconic )
        {
            ae_trace("> stopping condition met: %0d iterations performed\n",
                (int)(state->repiterationscount));
        }
        goto lbl_6;
    }
    if( state->fstagnationcnt>=nlcfsqp_fstagnationlimit )
    {
        state->repterminationtype = 7;
        if( dotrace||dotracelaconic )
        {
            ae_trace("> stopping criteria are too stringent: F stagnated for %0d its, stopping\n",
                (int)(state->fstagnationcnt));
        }
        goto lbl_6;
    }
    
    /*
     * Trace output (if needed)
     */
    if( dotrace )
    {
        ae_trace("\n=== OUTER ITERATION %5d STARTED ==================================================================\n",
            (int)(state->repiterationscount));
        if( dodetailedtrace )
        {
            ae_trace("> printing raw data (prior to applying variable and function scales)\n");
            ae_trace("X (raw)       = ");
            tracevectorunscaledunshiftedautoprec(&state->stepk.x, n, &state->s, ae_true, &state->s, ae_false, _state);
            ae_trace("\n");
            ae_trace("> printing scaled data (after applying variable and function scales)\n");
            ae_trace("X (scaled)    = ");
            tracevectorautoprec(&state->stepk.x, 0, n, _state);
            ae_trace("\n");
            ae_trace("FScales       = ");
            tracevectorautoprec(&state->fscaleszzz, 0, 1+nnlc, _state);
            ae_trace("\n");
            ae_trace("Fi (scaled)   = ");
            tracevectorautoprec(&state->stepk.fi, 0, 1+nnlc, _state);
            ae_trace("\n");
        }
        mx = (double)(0);
        for(i=0; i<=nnlc-1; i++)
        {
            if( state->hasnl.ptr.p_bool[i] )
            {
                mx = ae_maxreal(mx, state->scalednl.ptr.p_double[i]-state->stepk.fi.ptr.p_double[1+i], _state);
            }
            if( state->hasnu.ptr.p_bool[i] )
            {
                mx = ae_maxreal(mx, state->stepk.fi.ptr.p_double[1+i]-state->scalednu.ptr.p_double[i], _state);
            }
        }
        ae_trace("trustRad      = %0.3e\n",
            (double)(state->trustrad));
        ae_trace("lin.violation = %0.3e    (scaled violation of linear constraints)\n",
            (double)(state->replcerr));
        ae_trace("nlc.violation = %0.3e    (scaled violation of nonlinear constraints)\n",
            (double)(mx));
        ae_trace("|x|inf        = %0.3e\n",
            (double)(rmaxabsv(n, &state->stepk.x, _state)));
    }
    if( dotracelaconic )
    {
        i = 0;
        if( state->repiterationscount<=10 )
        {
            i = 1;
        }
        if( state->repiterationscount<=100&&state->repiterationscount%5==0 )
        {
            i = 1;
        }
        if( state->repiterationscount%50==0 )
        {
            i = 1;
        }
        if( i>0 )
        {
            ae_trace("%6d    %10.2e    %23.15e    %14.6e    %10.3fs\n",
                (int)(state->repiterationscount),
                (double)(state->trustrad),
                (double)(tgt0*state->fscaleszzz.ptr.p_double[0]),
                (double)(h0),
                (double)(0.001*stimergetmsrunning(&state->timertotal, _state)));
        }
    }
    
    /*
     * Start of the SQP iteration
     */
    rcopyv(n, &state->stepk.x, &state->xprev, _state);
    rsetv(n, 0.0, &state->d0, _state);
    if( dotrace )
    {
        if( feasibilityrestoration )
        {
            ae_trace("\n--- feasibility restoration step -------------------------------------------------------------------\n");
        }
        else
        {
            ae_trace("\n--- filter SQP step --------------------------------------------------------------------------------\n");
        }
    }
    
    /*
     * Default values of the flag variables
     */
    stepfailed = ae_false;
    restartiteration = ae_false;
    leavefeasibilityrestoration = ae_false;
    meritfstagnated = ae_false;
    relativetargetdecrease = 1.0E50;
    
    /*
     * Determine step direction
     */
    if( !feasibilityrestoration )
    {
        if( !nlcfsqp_qpsubproblemsolvesqp(state, &state->subsolver, &state->stepk, &state->hess, &state->d0, &state->lagbcmult, &state->lagxcmult, dotrace, &isinfeasible, &j, &predictedchangemodel, &predictedchangepenalty, &d2trustradratio, _state) )
        {
            if( dotrace )
            {
                ae_trace("> [WARNING] QP subproblem failed with TerminationType=%0d\n> decreasing trust radius\n",
                    (int)(j));
            }
            state->trustrad = 0.1*state->trustrad;
            inc(&state->repiterationscount, _state);
            goto lbl_5;
        }
        if( dotrace )
        {
            ae_trace("> the QP subproblem was solved with TerminationType=%0d, max|lagBoxMult|=%0.2e, max|lagNonBoxMult|=%0.2e\n",
                (int)(j),
                (double)(rmaxabsv(n, &state->lagbcmult, _state)),
                (double)(rmaxabsv(lccnt+nnlc, &state->lagxcmult, _state)));
        }
        if( isinfeasible )
        {
            nlpfappend(&state->filter, tgt0, h0, _state);
            feasibilityrestoration = ae_true;
            firstrestorationiteration = ae_true;
            inc(&state->repiterationscount, _state);
            if( dotrace )
            {
                ae_trace(">> the QP subproblem is infeasible (constraint violation delta=%0.2e, ratio=%0.3f), entering feasibility restoration phase\n>> the initial point was added to the filter (%0d entries in the filter)\n>> restarting iteration\n\n",
                    (double)(predictedchangepenalty),
                    (double)(state->subsolver.penalty1/state->subsolver.penalty0),
                    (int)(state->filter.filtersize));
            }
            goto lbl_5;
        }
        
        /*
         * The problem was solved and is feasible.
         * Save most recent Lagrange multipliers
         */
        rcopyv(n, &state->lagbcmult, &state->replagbc, _state);
        rmergedivv(n, &state->s, &state->replagbc, _state);
        rmulv(n, state->fscaleszzz.ptr.p_double[0], &state->replagbc, _state);
        rcopyv(lccnt, &state->lagxcmult, &state->replaglc, _state);
        rmergedivv(lccnt, &state->lcscales, &state->replaglc, _state);
        rmulv(lccnt, state->fscaleszzz.ptr.p_double[0], &state->replaglc, _state);
        rcopyvx(nnlc, &state->lagxcmult, lccnt, &state->replagnlc, 0, _state);
        for(i=0; i<=nnlc-1; i++)
        {
            state->replagnlc.ptr.p_double[i] = state->replagnlc.ptr.p_double[i]/state->fscaleszzz.ptr.p_double[1+i]*state->fscaleszzz.ptr.p_double[0];
        }
    }
    else
    {
        if( !nlcfsqp_qpsubproblemsolveelastic(state, &state->subsolver, &state->stepk, &state->hess, &state->d0, &state->lagbcmult, &state->lagxcmult, dotrace, &isinfeasible, &j, &predictedchangemodel, &predictedchangepenalty, &d2trustradratio, _state) )
        {
            if( dotrace )
            {
                ae_trace("> [WARNING] QP subproblem failed with TerminationType=%0d\n> decreasing trust radius\n",
                    (int)(j));
            }
            state->trustrad = 0.1*state->trustrad;
            inc(&state->repiterationscount, _state);
            goto lbl_5;
        }
        if( dotrace )
        {
            ae_trace("> elastic QP subproblem was solved with TerminationType=%0d, max|lagBoxMult|=%0.2e, max|lagNonBoxMult|=%0.2e\n\n",
                (int)(j),
                (double)(rmaxabsv(n, &state->lagbcmult, _state)),
                (double)(rmaxabsv(lccnt+nnlc, &state->lagxcmult, _state)));
        }
    }
    rcopyv(n, &state->d0, &state->dtrial, _state);
    
    /*
     * Perform merit function line search.
     *
     * First, we try unit step. If it does not decrease merit function,
     * a second-order correction is tried (helps to combat Maratos effect).
     */
    rcopyv(n, &state->stepk.x, &state->cand.x, _state);
    raddv(n, 1.0, &state->dtrial, &state->cand.x, _state);
    nlcfsqp_sqpsendx(state, &state->cand.x, _state);
    state->needfisj = ae_true;
    if( dotrace )
    {
        stimerstart(&state->timercallback, _state);
    }
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    if( dotrace )
    {
        stimerstop(&state->timercallback, _state);
    }
    state->needfisj = ae_false;
    if( !nlcfsqp_sqpretrievefij(state, &state->cand, _state) )
    {
        
        /*
         * Failed to retrieve func/Jac, infinities detected
         */
        if( dotrace )
        {
            ae_trace("[WARNING] infinities in target/constraints are detected, forcing trust radius decrease and restarting iteration\n");
        }
        state->trustrad = state->trustrad*nlcfsqp_maxtrustraddecay;
        trustradresetmomentum(&state->trustradgrowth, nlcfsqp_deftrustradgrowth, _state);
        infinitiesdetected = ae_true;
        failedtorecoverfrominfinities = ae_true;
        inc(&state->repiterationscount, _state);
        goto lbl_5;
    }
    nlcfsqp_targetandconstraints(state, &state->cand, &tgt1, &h1, _state);
    if( dotrace )
    {
        rallocv(n, &state->tmplaggrad, _state);
        rallocv(n, &state->tmpcandlaggrad, _state);
        nlcfsqp_lagrangianfg(state, &state->stepk.x, state->trustrad, &state->stepk.fi, &state->stepk.sj, &state->lagbcmult, &state->lagxcmult, ae_true, &stepklagval, &state->tmplaggrad, _state);
        nlcfsqp_lagrangianfg(state, &state->cand.x, state->trustrad, &state->cand.fi, &state->cand.sj, &state->lagbcmult, &state->lagxcmult, ae_true, &stepknlagval, &state->tmpcandlaggrad, _state);
        ae_trace("> proposed step with relative len %0.6f (compared to trust radius)\n",
            (double)(d2trustradratio));
        ae_trace(">> change in the target function:       predicted=%0.2e, actual=%0.2e, ratio=%5.2f\n",
            (double)(predictedchangemodel),
            (double)(tgt1-tgt0),
            (double)((tgt1-tgt0)/predictedchangemodel));
        ae_trace(">> change in the constraints violation: predicted=%0.2e, actual=%0.2e, ratio=%5.2f\n",
            (double)(predictedchangepenalty),
            (double)(h1-h0),
            (double)((h1-h0)/predictedchangepenalty));
        ae_trace(">> target and constraints are:\n");
        ae_trace("targetF:        %17.9e -> %17.9e (delta=%11.3e, raw)\n",
            (double)(state->fscaleszzz.ptr.p_double[0]*state->stepk.fi.ptr.p_double[0]),
            (double)(state->fscaleszzz.ptr.p_double[0]*state->cand.fi.ptr.p_double[0]),
            (double)(state->fscaleszzz.ptr.p_double[0]*(state->cand.fi.ptr.p_double[0]-state->stepk.fi.ptr.p_double[0])));
        ae_trace("meritF:         %17.9e -> %17.9e (delta=%11.3e, scaled penalized target)\n",
            (double)(tgt0),
            (double)(tgt1),
            (double)(tgt1-tgt0));
        ae_trace("sum-of-constr:  %17.9e -> %17.9e (delta=%11.3e, scaled)\n",
            (double)(h0),
            (double)(h1),
            (double)(h1-h0));
        ae_trace("|grad L|:       %17.9e -> %17.9e\n",
            (double)(rmaxabsv(n, &state->tmplaggrad, _state)),
            (double)(rmaxabsv(n, &state->tmpcandlaggrad, _state)));
        ae_trace("\n");
    }
    enforceboundaryconstraints(&state->cand.x, &state->scaledbndl, &state->hasbndl, &state->scaledbndu, &state->hasbndu, n, 0, _state);
    if( userterminationneeded )
    {
        
        /*
         * User requested termination, break before we move to new point
         */
        state->repterminationtype = icase2(failedtorecoverfrominfinities, -8, 8, _state);
        if( dotrace||dotracelaconic )
        {
            ae_trace("> user requested termination\n");
        }
        goto lbl_6;
    }
    
    /*
     * Update Hessian
     */
    if( !feasibilityrestoration )
    {
        rallocv(n, &state->tmplaggrad, _state);
        rallocv(n, &state->tmpcandlaggrad, _state);
        nlcfsqp_lagrangianfg(state, &state->stepk.x, state->trustrad, &state->stepk.fi, &state->stepk.sj, &state->lagbcmult, &state->lagxcmult, ae_true, &stepklagval, &state->tmplaggrad, _state);
        nlcfsqp_lagrangianfg(state, &state->cand.x, state->trustrad, &state->cand.fi, &state->cand.sj, &state->lagbcmult, &state->lagxcmult, ae_true, &stepknlagval, &state->tmpcandlaggrad, _state);
        hessianupdatev2(&state->hess, &state->stepk.x, &state->tmplaggrad, &state->cand.x, &state->tmpcandlaggrad, 2, ae_false, dotrace, 1, _state);
        if( dotrace )
        {
            hessiangetdiagonal(&state->hess, &state->tmphdiag, _state);
            ae_trace(">> printing information about Hessian:\n");
            v = state->tmphdiag.ptr.p_double[0];
            for(i=0; i<=n-1; i++)
            {
                v = ae_minreal(v, state->tmphdiag.ptr.p_double[i], _state);
            }
            ae_trace("mindiag(Bk) = %0.3e\n",
                (double)(v));
            v = state->tmphdiag.ptr.p_double[0];
            for(i=0; i<=n-1; i++)
            {
                v = ae_maxreal(v, state->tmphdiag.ptr.p_double[i], _state);
            }
            ae_trace("maxdiag(Bk) = %0.3e\n",
                (double)(v));
            ae_trace("\n");
        }
    }
    
    /*
     * Analyze current step and decide what to do: to continue iteration or to restart it.
     *
     * The latter option has two possible choices: to restart it with trust radius decreased
     * (StepFailed=True) or with the same trust radius (StepFailed=False, performed when exiting
     * from the feasibility restoration phase).
     *
     * Depending on the outcome, we set/clear FeasibilityRestoration, RestartIteration and StepFailed.
     * Then we do some other work like printing trace logs, and finally restart iteration if needed.
     */
    if( feasibilityrestoration )
    {
        
        /*
         * Feasibility restoration phase
         */
        if( (!firstrestorationiteration&&!isinfeasible)&&nlpfisacceptable1(&state->filter, tgt0, h0, _state) )
        {
            
            /*
             * Exit restoration if initial point is feasible and acceptable, and we performed at least one restoration iteration
             */
            if( dotrace )
            {
                ae_trace("> the initial point is feasible and accepted by the filter, exiting feasibility restoration phase\n");
            }
            trustradresetmomentum(&state->trustradgrowth, nlcfsqp_deftrustradgrowth, _state);
            leavefeasibilityrestoration = ae_true;
            restartiteration = ae_true;
        }
        else
        {
            
            /*
             * Check sufficient decrease condition for modified penalty
             *
             * NOTE: it is essential to have strict ">" for both conditions below in order to be able
             *       to properly handle situations with zero predicted change
             */
            if( ae_fp_greater(predictedchangepenalty,(double)(0))||ae_fp_greater(h1-h0,nlcfsqp_sufficientdecreasesigma*predictedchangepenalty) )
            {
                if( dotrace )
                {
                    ae_trace("> the sufficient decrease condition does not hold, decreasing trust radius\n");
                }
                restartiteration = ae_true;
                stepfailed = ae_true;
            }
        }
        meritfstagnated = ae_fp_less_eq(ae_fabs(h1-h0, _state),nlcfsqp_stagnationepsf*ae_maxreal(h0, h1, _state));
        firstrestorationiteration = ae_false;
    }
    else
    {
        
        /*
         * SQP phase
         */
        if( !nlcfsqp_isacceptable(state, tgt0, h0, tgt1, h1, predictedchangemodel, predictedchangepenalty, dotrace, _state) )
        {
            
            /*
             * The point is not acceptable, decrease trust radius and retry
             */
            if( dotrace )
            {
                ae_trace("> decreasing trust radius\n");
            }
            restartiteration = ae_true;
            stepfailed = ae_true;
        }
        meritfstagnated = ae_fp_less_eq(ae_fabs(tgt1-tgt0, _state),nlcfsqp_stagnationepsf*ae_fabs(tgt0, _state))&&ae_fp_less_eq(ae_fabs(h1-h0, _state),nlcfsqp_stagnationepsf*ae_maxreal(h0, h1, _state));
    }
    if( !dotrace )
    {
        goto lbl_7;
    }
    
    /*
     * Perform agressive probing of the search direction - additional function evaluations
     * which help us to determine possible discontinuity and nonsmoothness of the problem
     */
    if( !(doprobingalways||(doprobingonfailure&&stepfailed)) )
    {
        goto lbl_9;
    }
    smoothnessmonitorstartlagrangianprobing(smonitor, &state->stepk.x, &state->dtrial, 1.0, state->repiterationscount, -1, _state);
lbl_11:
    if( !smoothnessmonitorprobelagrangian(smonitor, _state) )
    {
        goto lbl_12;
    }
    for(j=0; j<=n-1; j++)
    {
        state->probe.x.ptr.p_double[j] = smonitor->lagprobx.ptr.p_double[j];
        if( state->hasbndl.ptr.p_bool[j] )
        {
            state->probe.x.ptr.p_double[j] = ae_maxreal(state->probe.x.ptr.p_double[j], state->scaledbndl.ptr.p_double[j], _state);
        }
        if( state->hasbndu.ptr.p_bool[j] )
        {
            state->probe.x.ptr.p_double[j] = ae_minreal(state->probe.x.ptr.p_double[j], state->scaledbndu.ptr.p_double[j], _state);
        }
    }
    nlcfsqp_sqpsendx(state, &state->probe.x, _state);
    state->needfisj = ae_true;
    if( dotrace )
    {
        stimerstart(&state->timercallback, _state);
    }
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    if( dotrace )
    {
        stimerstop(&state->timercallback, _state);
    }
    state->needfisj = ae_false;
    if( !nlcfsqp_sqpretrievefij(state, &state->probe, _state) )
    {
        goto lbl_12;
    }
    rcopyallocv(1+nnlc, &state->probe.fi, &smonitor->lagprobfi, _state);
    nlcfsqp_sparse2dense(&state->probe.sj, &smonitor->lagprobj, _state);
    smonitor->lagprobrawlag = nlcfsqp_rawlagrangian(state, &state->probe.x, &state->probe.fi, &state->lagbcmult, &state->lagxcmult, _state);
    goto lbl_11;
lbl_12:
    ae_trace("*** ------------------------------------------------------------\n");
    ae_trace("*** |                 probing search direction                 |\n");
    ae_trace("*** |          suggested by first-order QP subproblem          |\n");
    ae_trace("*** |                                                          |\n");
    ae_trace("*** |          note: the target/Lagrangian  may  increase      |\n");
    ae_trace("*** |                along the search  direction  due  to      |\n");
    ae_trace("*** |                nonmonotonic steps of the filter SQP      |\n");
    ae_trace("*** |                However, they  MUST  be  smooth  and      |\n");
    ae_trace("*** |                continuous.                               |\n");
    ae_trace("*** ------------------------------------------------------------\n");
    ae_trace("*** |  Step  | Lagrangian (unaugmentd)|    Target  function    |\n");
    ae_trace("*** |along  D|     must be smooth     |     must be smooth     |\n");
    ae_trace("*** |        | function   |    slope  | function   |    slope  |\n");
    smoothnessmonitortracelagrangianprobingresults(smonitor, _state);
    ae_trace("\n");
lbl_9:
    
    /*
     * Output other information
     */
lbl_7:
    if( leavefeasibilityrestoration )
    {
        feasibilityrestoration = ae_false;
    }
    if( restartiteration )
    {
        if( stepfailed )
        {
            state->trustrad = state->trustrad*0.5;
            trustradresetmomentum(&state->trustradgrowth, nlcfsqp_deftrustradgrowth, _state);
        }
        if( dotrace )
        {
            ae_trace("> restarting iteration\n\n");
        }
        inc(&state->repiterationscount, _state);
        goto lbl_5;
    }
    if( dotrace )
    {
        ae_trace("> the step was successfully performed\n");
    }
    if( (!feasibilityrestoration&&(ae_fp_greater_eq(predictedchangemodel,(double)(0))||ae_fp_greater(tgt1-tgt0,nlcfsqp_sufficientdecreasesigma*predictedchangemodel)))&&ae_fp_greater(h0,(double)(0)) )
    {
        nlpfappend(&state->filter, tgt0, h0, _state);
        if( dotrace )
        {
            ae_trace(">> the initial point was added to the filter (%0d entries in the filter)\n",
                (int)(state->filter.filtersize));
        }
    }
    if( dotrace )
    {
        ae_trace("\n");
    }
    
    /*
     * Analyze merit F for stagnation
     */
    relativetargetdecrease = ae_maxreal(ae_fabs(tgt1-tgt0, _state)/rmaxabs3(tgt0, tgt1, (double)(1), _state), ae_fabs(h1-h0, _state)/rmaxabs3(h0, h1, (double)(1), _state), _state);
    
    /*
     * Move to new point
     * Report one more inner iteration
     * Update constraint violations
     */
    vfjcopy(&state->cand, &state->stepk, _state);
    failedtorecoverfrominfinities = ae_false;
    nlcfsqp_sqpsendx(state, &state->stepk.x, _state);
    state->f = state->stepk.fi.ptr.p_double[0]*state->fscaleszzz.ptr.p_double[0];
    state->xupdated = ae_true;
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    state->xupdated = ae_false;
    checklc2violation(&state->scla, &state->sclal, &state->sclau, &state->lcsrcidx, lccnt, &state->stepk.x, &state->replcerr, &state->replcidx, _state);
    unscaleandchecknlc2violation(&state->stepk.fi, &state->fscaleszzz, &state->rawnl, &state->rawnu, nnlc, &state->repnlcerr, &state->repnlcidx, _state);
    checknlc2violation(&state->stepk.fi, &state->scalednl, &state->scalednu, nnlc, &state->repsclerr, &i, _state);
    state->repsclerr = rmaxabs3(state->repsclerr, state->repbcerr, state->replcerr, _state);
    state->werepointsbelowctol = state->werepointsbelowctol||ae_fp_less_eq(state->repsclerr,state->ctol);
    
    /*
     * Update trust region.
     *
     * NOTE: when trust region radius remains fixed for a long time it may mean that we 
     *       stagnated in eternal loop. In such cases we decrease it slightly in order
     *       to break possible loop. If such decrease was unnecessary, it may be easily
     *       fixed within few iterations.
     */
    trustradstagnated = ae_true;
    prevtrustrad = state->trustrad;
    deltamax = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        deltamax = ae_maxreal(deltamax, ae_fabs(state->xprev.ptr.p_double[i]-state->stepk.x.ptr.p_double[i], _state)/nlcfsqp_gettrustregionk(&state->xprev, i, state->trustrad, _state), _state);
    }
    if( ae_fp_less_eq(deltamax,nlcfsqp_sqpdeltadecrease) )
    {
        state->trustrad = state->trustrad*ae_maxreal(deltamax/nlcfsqp_sqpdeltadecrease, nlcfsqp_maxtrustraddecay, _state);
        trustradstagnated = ae_false;
        trustradresetmomentum(&state->trustradgrowth, nlcfsqp_deftrustradgrowth, _state);
    }
    if( ae_fp_greater_eq(deltamax,nlcfsqp_sqpdeltaincrease) )
    {
        state->trustrad = state->trustrad*state->trustradgrowth;
        trustradstagnated = ae_false;
        trustradincreasemomentum(&state->trustradgrowth, nlcfsqp_momentumgrowth, nlcfsqp_maxtrustradgrowth, _state);
    }
    else
    {
        trustradresetmomentum(&state->trustradgrowth, nlcfsqp_deftrustradgrowth, _state);
    }
    state->trustradstagnationcnt = icase2(trustradstagnated, state->trustradstagnationcnt+1, 0, _state);
    if( state->trustradstagnationcnt>=nlcfsqp_trustradstagnationlimit )
    {
        
        /*
         * Trust radius stagnated for too long, decrease it and reset stagnation counter
         */
        if( dotrace )
        {
            ae_trace("> detected stagnation of a trust radius, forcing decrease\n");
        }
        state->trustrad = state->trustrad*0.5;
        state->trustradstagnationcnt = 0;
        trustradresetmomentum(&state->trustradgrowth, nlcfsqp_deftrustradgrowth, _state);
    }
    
    /*
     * Trace
     */
    if( dotrace )
    {
        ae_trace("\n--- iteration ends ---------------------------------------------------------------------------------\n");
        ae_trace("deltaMax    = %0.3f (ratio of step length to trust radius)\n",
            (double)(deltamax));
        ae_trace("newTrustRad = %0.3e",
            (double)(state->trustrad));
        if( ae_fp_greater(state->trustrad,prevtrustrad) )
        {
            ae_trace(", trust radius increased");
        }
        if( ae_fp_less(state->trustrad,prevtrustrad) )
        {
            ae_trace(", trust radius decreased");
        }
        ae_trace("\n");
    }
    
    /*
     * Advance outer iteration counter
     */
    inc(&state->repiterationscount, _state);
    state->fstagnationcnt = icase2(meritfstagnated, state->fstagnationcnt+1, 0, _state);
    goto lbl_5;
lbl_6:
    
    /*
     * Almost done
     */
    if( infinitiesdetected )
    {
        state->repterminationtype = icase2(failedtorecoverfrominfinities, -8, state->repterminationtype+800, _state);
    }
    if( dotrace )
    {
        stimerstop(&state->timertotal, _state);
        ae_trace("\n=== STOPPED ========================================================================================\n");
        if( infinitiesdetected&&failedtorecoverfrominfinities )
        {
            ae_trace("> infinities were detected, but we failed to recover by decreasing trust radius; declaring failure with the code -8\n");
        }
        ae_trace("raw target:     %20.12e\n",
            (double)(state->stepk.fi.ptr.p_double[0]*state->fscaleszzz.ptr.p_double[0]));
        ae_trace("total time:     %10.1f ms\n",
            (double)(stimergetms(&state->timertotal, _state)));
        ae_trace("* cbck:         %10.1f ms\n",
            (double)(stimergetms(&state->timercallback, _state)));
        ae_trace("* QP gen:       %10.1f ms\n",
            (double)(stimergetms(&state->timerqpgen, _state)));
        ae_trace("* IPM init:     %10.1f ms\n",
            (double)(stimergetms(&state->timeripminit, _state)));
        ae_trace("* IPM solve:    %10.1f ms\n",
            (double)(stimergetms(&state->timeripmsolve, _state)));
    }
    if( dotracelaconic )
    {
        nlcfsqp_targetandconstraints(state, &state->stepk, &tgt0, &h0, _state);
        ae_trace("STOPPED                 %23.15e    %14.6e    %10.3fs\n",
            (double)(tgt0*state->fscaleszzz.ptr.p_double[0]),
            (double)(h0),
            (double)(0.001*stimergetmsrunning(&state->timertotal, _state)));
    }
    smoothnessmonitortracestatus(smonitor, dotrace, _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = n;
    state->rstate.ia.ptr.p_int[1] = lccnt;
    state->rstate.ia.ptr.p_int[2] = nnlc;
    state->rstate.ia.ptr.p_int[3] = i;
    state->rstate.ia.ptr.p_int[4] = j;
    state->rstate.ba.ptr.p_bool[0] = trustradstagnated;
    state->rstate.ba.ptr.p_bool[1] = dotrace;
    state->rstate.ba.ptr.p_bool[2] = dotracelaconic;
    state->rstate.ba.ptr.p_bool[3] = doprobingalways;
    state->rstate.ba.ptr.p_bool[4] = doprobingonfailure;
    state->rstate.ba.ptr.p_bool[5] = dodetailedtrace;
    state->rstate.ba.ptr.p_bool[6] = meritfstagnated;
    state->rstate.ba.ptr.p_bool[7] = infinitiesdetected;
    state->rstate.ba.ptr.p_bool[8] = failedtorecoverfrominfinities;
    state->rstate.ba.ptr.p_bool[9] = isinfeasible;
    state->rstate.ba.ptr.p_bool[10] = feasibilityrestoration;
    state->rstate.ba.ptr.p_bool[11] = firstrestorationiteration;
    state->rstate.ba.ptr.p_bool[12] = stepfailed;
    state->rstate.ba.ptr.p_bool[13] = restartiteration;
    state->rstate.ba.ptr.p_bool[14] = leavefeasibilityrestoration;
    state->rstate.ra.ptr.p_double[0] = v;
    state->rstate.ra.ptr.p_double[1] = vv;
    state->rstate.ra.ptr.p_double[2] = mx;
    state->rstate.ra.ptr.p_double[3] = deltamax;
    state->rstate.ra.ptr.p_double[4] = multiplyby;
    state->rstate.ra.ptr.p_double[5] = setscaleto;
    state->rstate.ra.ptr.p_double[6] = prevtrustrad;
    state->rstate.ra.ptr.p_double[7] = relativetargetdecrease;
    state->rstate.ra.ptr.p_double[8] = tgt0;
    state->rstate.ra.ptr.p_double[9] = h0;
    state->rstate.ra.ptr.p_double[10] = tgt1;
    state->rstate.ra.ptr.p_double[11] = h1;
    state->rstate.ra.ptr.p_double[12] = tol;
    state->rstate.ra.ptr.p_double[13] = stepklagval;
    state->rstate.ra.ptr.p_double[14] = stepknlagval;
    state->rstate.ra.ptr.p_double[15] = d2trustradratio;
    state->rstate.ra.ptr.p_double[16] = predictedchangemodel;
    state->rstate.ra.ptr.p_double[17] = predictedchangepenalty;
    return result;
}


/*************************************************************************
This function initializes SQP subproblem.
Should be called once in the beginning of the optimization.

INPUT PARAMETERS:
    SState          -   solver state
    Subsolver       -   SQP subproblem to initialize
                        
                        
RETURN VALUE:
    True on success
    False on failure of the QP solver (unexpected... but possible due to numerical errors)
                        

  -- ALGLIB --
     Copyright 05.03.2018 by Bochkanov Sergey
*************************************************************************/
static void nlcfsqp_initqpsubsolver(const minfsqpstate* sstate,
     minfsqpsubsolver* subsolver,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nslack;
    ae_int_t nnlc;
    ae_int_t i;
    ae_int_t lccnt;


    n = sstate->n;
    nnlc = sstate->nnlc;
    nslack = n+2*sstate->lccnt+2*nnlc;
    lccnt = sstate->lccnt+nnlc;
    
    /*
     * Initial ordering state
     */
    iallocv(n+lccnt, &subsolver->ordering, _state);
    for(i=0; i<=n+lccnt-1; i++)
    {
        subsolver->ordering.ptr.p_int[i] = i;
    }
    iallocv(nslack+lccnt, &subsolver->elasticordering, _state);
    for(i=0; i<=nslack+lccnt-1; i++)
    {
        subsolver->elasticordering.ptr.p_int[i] = i;
    }
    sparsecreatecrsempty(n, &subsolver->pattern, _state);
    for(i=0; i<=lccnt-1; i++)
    {
        sparseappendemptyrow(&subsolver->pattern, _state);
    }
    sparsecreatecrsempty(nslack, &subsolver->elasticpattern, _state);
    for(i=0; i<=lccnt-1; i++)
    {
        sparseappendemptyrow(&subsolver->elasticpattern, _state);
    }
    
    /*
     * Allocate temporaries
     */
    rvectorsetlengthatleast(&subsolver->cural, lccnt, _state);
    rvectorsetlengthatleast(&subsolver->curau, lccnt, _state);
    rvectorsetlengthatleast(&subsolver->curbndl, nslack, _state);
    rvectorsetlengthatleast(&subsolver->curbndu, nslack, _state);
    rvectorsetlengthatleast(&subsolver->curb, nslack, _state);
    
    /*
     * Linear constraints do not change across subiterations, that's
     * why we allocate storage for them at the start of the program.
     *
     * A full set of "raw" constraints is stored; later we will filter
     * out inequality ones which are inactive anywhere in the current
     * trust region.
     *
     * NOTE: because sparserawlc object stores only linear constraint
     *       (linearizations of nonlinear ones are not stored) we
     *       allocate only minimum necessary space.
     */
    if( sstate->lccnt>0 )
    {
        sparsecopytocrsbuf(&sstate->scla, &subsolver->sparserawlc, _state);
    }
    
    /*
     * Initialize prior for regularization
     */
    rsetallocv(lccnt, 0.0, &subsolver->lastlaglc, _state);
}


/*************************************************************************
This function solves QP subproblem
    
This function returns:
* search direction D[]
* estimates of Lagrangian multipliers
* predicted change in the quadratic model of the target, and in the L1-penalty
  added to the model

Additionally it sets the following fields of the Subsolver:
* Penalty0 - sum of constraint violations at the initial point X
* Penalty1 - sum of constraint violations at X+D

  -- ALGLIB --
     Copyright 05.03.2018 by Bochkanov Sergey
*************************************************************************/
static ae_bool nlcfsqp_qpsubproblemsolvesqp(minfsqpstate* state,
     minfsqpsubsolver* subsolver,
     const varsfuncjac* model,
     xbfgshessian* hess,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_vector* lagbcmult,
     /* Real    */ ae_vector* lagxcmult,
     ae_bool dotrace,
     ae_bool* isinfeasible,
     ae_int_t* terminationtype,
     double* predictedchangemodel,
     double* predictedchangepenalty,
     double* d2trustradratio,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t lccnt;
    ae_int_t nnlc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t k;
    double v;
    double vv;
    double vright;
    double vmax;
    double vmin;
    ae_int_t xccnt;
    ae_int_t offs;
    ae_int_t nnz;
    ae_bool bndlinactive;
    ae_bool bnduinactive;
    double rescaleby;
    double targetscale;
    ae_bool result;

    *isinfeasible = ae_false;
    *terminationtype = 0;
    *predictedchangemodel = 0.0;
    *predictedchangepenalty = 0.0;
    *d2trustradratio = 0.0;

    ae_assert(!model->isdense, "FSQP: integrity check 2314 failed", _state);
    n = state->n;
    lccnt = state->lccnt;
    nnlc = state->nnlc;
    xccnt = lccnt+nnlc;
    
    /*
     * Prepare temporary structures
     */
    rvectorgrowto(&subsolver->cural, xccnt, _state);
    rvectorgrowto(&subsolver->curau, xccnt, _state);
    rallocv(xccnt, &subsolver->rescalelag, _state);
    
    /*
     * Prepare default solution: all zeros
     */
    result = ae_true;
    *terminationtype = 0;
    *predictedchangemodel = (double)(0);
    *predictedchangepenalty = (double)(0);
    *d2trustradratio = 0.0;
    *isinfeasible = ae_false;
    subsolver->penalty0 = 0.0;
    subsolver->penalty1 = 0.0;
    rsetv(n, 0.0, d, _state);
    rsetv(n, 0.0, lagbcmult, _state);
    rsetv(xccnt, 0.0, lagxcmult, _state);
    bsetallocv(xccnt, ae_false, &subsolver->isinactive, _state);
    
    /*
     * Linear term B
     */
    nlcfsqp_penalizedfg(state, &model->x, &model->fi, &model->sj, &v, &subsolver->curb, _state);
    
    /*
     * Trust radius constraints for primary variables
     */
    bsetallocv(n, ae_false, &subsolver->retainnegativebclm, _state);
    bsetallocv(n, ae_false, &subsolver->retainpositivebclm, _state);
    for(i=0; i<=n-1; i++)
    {
        subsolver->curbndl.ptr.p_double[i] = -nlcfsqp_gettrustregionk(&model->x, i, state->trustrad, _state);
        subsolver->curbndu.ptr.p_double[i] = nlcfsqp_gettrustregionk(&model->x, i, state->trustrad, _state);
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_greater(state->scaledbndl.ptr.p_double[i]-model->x.ptr.p_double[i],subsolver->curbndl.ptr.p_double[i]) )
        {
            subsolver->curbndl.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i]-model->x.ptr.p_double[i];
            subsolver->retainnegativebclm.ptr.p_bool[i] = ae_true;
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_less(state->scaledbndu.ptr.p_double[i]-model->x.ptr.p_double[i],subsolver->curbndu.ptr.p_double[i]) )
        {
            subsolver->curbndu.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i]-model->x.ptr.p_double[i];
            subsolver->retainpositivebclm.ptr.p_bool[i] = ae_true;
        }
    }
    
    /*
     * Prepare storage for the "effective" constraining matrix (one with rows guaranteed to be
     * inactive removed) and for the upcoming pattern matrix (one that is not subject to
     * removal of any rows).
     */
    nnz = 0;
    if( lccnt>0 )
    {
        nnz = nnz+subsolver->sparserawlc.ridx.ptr.p_int[lccnt];
    }
    nnz = nnz+(model->sj.ridx.ptr.p_int[1+nnlc]-model->sj.ridx.ptr.p_int[1]);
    ivectorgrowto(&subsolver->sparseefflc.ridx, xccnt+1, _state);
    rvectorgrowto(&subsolver->sparseefflc.vals, nnz, _state);
    ivectorgrowto(&subsolver->sparseefflc.idx, nnz, _state);
    ivectorsetlengthatleast(&subsolver->sparseefflc.didx, xccnt, _state);
    ivectorsetlengthatleast(&subsolver->sparseefflc.uidx, xccnt, _state);
    subsolver->sparseefflc.ridx.ptr.p_int[0] = 0;
    subsolver->sparseefflc.m = 0;
    subsolver->sparseefflc.n = n;
    subsolver->sparseefflc.matrixtype = 1;
    sparsecreatecrsemptybuf(n, &subsolver->upcomingpattern, _state);
    
    /*
     * Append linear equality/inequality constraints
     *
     * Scan sparsified linear constraints stored in sparserawlc[], skip ones
     * which are inactive anywhere in the trust region.
     */
    for(i=0; i<=lccnt-1; i++)
    {
        
        /*
         * Calculate:
         * * VRight - product of X[]  and AR[i] - Ith row of sparserawlc matrix.
         * * VMin   - minimum value of X*ARi computed over trust region
         * * VMax   - maximum value of X*ARi computed over trust region
         */
        vright = (double)(0);
        vmax = (double)(0);
        vmin = (double)(0);
        j0 = subsolver->sparserawlc.ridx.ptr.p_int[i];
        j1 = subsolver->sparserawlc.ridx.ptr.p_int[i+1]-1;
        for(k=j0; k<=j1; k++)
        {
            j = subsolver->sparserawlc.idx.ptr.p_int[k];
            v = model->x.ptr.p_double[j];
            vv = subsolver->sparserawlc.vals.ptr.p_double[k];
            vright = vright+vv*v;
            if( vv>=(double)0 )
            {
                vmax = vmax+vv*(v+subsolver->curbndu.ptr.p_double[j]);
                vmin = vmin+vv*(v+subsolver->curbndl.ptr.p_double[j]);
            }
            else
            {
                vmax = vmax+vv*(v+subsolver->curbndl.ptr.p_double[j]);
                vmin = vmin+vv*(v+subsolver->curbndu.ptr.p_double[j]);
            }
        }
        bndlinactive = !state->hasal.ptr.p_bool[i]||vmin>state->sclal.ptr.p_double[i];
        bnduinactive = !state->hasau.ptr.p_bool[i]||vmax<state->sclau.ptr.p_double[i];
        
        /*
         * Linear constraints are passed 'as is', without additional rescaling
         */
        subsolver->rescalelag.ptr.p_double[i] = 1.0;
        
        /*
         * Always append full row to the upcoming pattern; rows to the effective linear
         * constraints matrix are added subject to basic presolve (remove constraints that
         * are guaranteed to be inactive).
         */
        nlcfsqp_appendscaledsparserow(&subsolver->upcomingpattern, 1.0, &subsolver->sparserawlc, i, _state);
        
        /*
         * If constraint is an inequality one and guaranteed to be inactive
         * within trust region, it is skipped (the row itself is retained but
         * filled by zeros).
         */
        if( bndlinactive&&bnduinactive )
        {
            subsolver->sparseefflc.ridx.ptr.p_int[i+1] = subsolver->sparseefflc.ridx.ptr.p_int[i];
            subsolver->cural.ptr.p_double[i] = 0.0;
            subsolver->curau.ptr.p_double[i] = 0.0;
            subsolver->rescalelag.ptr.p_double[i] = 0.0;
            subsolver->isinactive.ptr.p_bool[i] = ae_true;
            continue;
        }
        
        /*
         * Copy constraint from sparserawlc[] to sparseefflc[]
         */
        offs = subsolver->sparseefflc.ridx.ptr.p_int[i];
        j0 = subsolver->sparserawlc.ridx.ptr.p_int[i];
        j1 = subsolver->sparserawlc.ridx.ptr.p_int[i+1]-1;
        for(k=j0; k<=j1; k++)
        {
            subsolver->sparseefflc.idx.ptr.p_int[offs] = subsolver->sparserawlc.idx.ptr.p_int[k];
            subsolver->sparseefflc.vals.ptr.p_double[offs] = subsolver->sparserawlc.vals.ptr.p_double[k];
            offs = offs+1;
        }
        subsolver->sparseefflc.ridx.ptr.p_int[i+1] = offs;
        
        /*
         * Set up bounds and slack part of D0.
         *
         * NOTE: bounds for equality and inequality constraints are
         *       handled differently
         */
        subsolver->cural.ptr.p_double[i] = _state->v_neginf;
        subsolver->curau.ptr.p_double[i] = _state->v_posinf;
        if( !bndlinactive )
        {
            subsolver->cural.ptr.p_double[i] = state->sclal.ptr.p_double[i]-vright;
        }
        if( !bnduinactive )
        {
            subsolver->curau.ptr.p_double[i] = state->sclau.ptr.p_double[i]-vright;
        }
    }
    subsolver->sparseefflc.m = subsolver->sparseefflc.m+lccnt;
    
    /*
     * Append nonlinear equality/inequality constraints
     */
    for(i=0; i<=nnlc-1; i++)
    {
        
        /*
         * Calculate:
         * * rescale coefficient used to normalize constraints
         * * VMax - maximum of constraint value over trust region
         */
        vmax = model->fi.ptr.p_double[1+i];
        vmin = model->fi.ptr.p_double[1+i];
        vv = (double)(0);
        j0 = model->sj.ridx.ptr.p_int[1+i];
        j1 = model->sj.ridx.ptr.p_int[1+i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            j = model->sj.idx.ptr.p_int[jj];
            v = model->sj.vals.ptr.p_double[jj];
            vv = vv+v*v;
            if( v>=(double)0 )
            {
                vmax = vmax+v*subsolver->curbndu.ptr.p_double[j];
                vmin = vmin+v*subsolver->curbndl.ptr.p_double[j];
            }
            else
            {
                vmax = vmax+v*subsolver->curbndl.ptr.p_double[j];
                vmin = vmin+v*subsolver->curbndu.ptr.p_double[j];
            }
        }
        rescaleby = (double)1/coalesce(ae_sqrt(vv, _state), (double)(1), _state);
        bndlinactive = !state->hasnl.ptr.p_bool[i]||vmin>state->scalednl.ptr.p_double[i];
        bnduinactive = !state->hasnu.ptr.p_bool[i]||vmax<state->scalednu.ptr.p_double[i];
        
        /*
         * Nonlinear constraints are passed with normalization
         */
        subsolver->rescalelag.ptr.p_double[lccnt+i] = rescaleby;
        
        /*
         * Always append full row to the upcoming pattern; rows to the effective linear
         * constraints matrix are added subject to basic presolve (remove constraints that
         * are guaranteed to be inactive).
         */
        nlcfsqp_appendscaledsparserow(&subsolver->upcomingpattern, rescaleby, &model->sj, 1+i, _state);
        
        /*
         * If constraint is an inequality one and guaranteed to be inactive
         * within trust region, it is skipped (row itself is retained but
         * filled by zeros).
         */
        if( bndlinactive&&bnduinactive )
        {
            subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m+i+1] = subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m+i];
            subsolver->cural.ptr.p_double[subsolver->sparseefflc.m+i] = 0.0;
            subsolver->curau.ptr.p_double[subsolver->sparseefflc.m+i] = 0.0;
            subsolver->rescalelag.ptr.p_double[lccnt+i] = 0.0;
            subsolver->isinactive.ptr.p_bool[lccnt+i] = ae_true;
            continue;
        }
        
        /*
         * Copy scaled row
         */
        j0 = model->sj.ridx.ptr.p_int[1+i];
        j1 = model->sj.ridx.ptr.p_int[1+i+1]-1;
        offs = subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m+i];
        for(jj=j0; jj<=j1; jj++)
        {
            j = model->sj.idx.ptr.p_int[jj];
            v = model->sj.vals.ptr.p_double[jj];
            subsolver->sparseefflc.vals.ptr.p_double[offs] = rescaleby*v;
            subsolver->sparseefflc.idx.ptr.p_int[offs] = j;
            offs = offs+1;
        }
        subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m+i+1] = offs;
        
        /*
         * Set bounds on linear equality/inequality constraints
         */
        subsolver->cural.ptr.p_double[subsolver->sparseefflc.m+i] = _state->v_neginf;
        subsolver->curau.ptr.p_double[subsolver->sparseefflc.m+i] = _state->v_posinf;
        if( !bndlinactive )
        {
            subsolver->cural.ptr.p_double[subsolver->sparseefflc.m+i] = rescaleby*(state->scalednl.ptr.p_double[i]-model->fi.ptr.p_double[1+i]);
        }
        if( !bnduinactive )
        {
            subsolver->curau.ptr.p_double[subsolver->sparseefflc.m+i] = rescaleby*(state->scalednu.ptr.p_double[i]-model->fi.ptr.p_double[1+i]);
        }
    }
    subsolver->sparseefflc.m = subsolver->sparseefflc.m+nnlc;
    
    /*
     * Finalize sparse matrix structure
     */
    ae_assert(subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m]<=subsolver->sparseefflc.idx.cnt, "QPSubproblemSolve: critical integrity check failed", _state);
    ae_assert(subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m]<=subsolver->sparseefflc.vals.cnt, "QPSubproblemSolve: critical integrity check failed", _state);
    subsolver->sparseefflc.ninitialized = subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m];
    sparseinitduidx(&subsolver->sparseefflc, _state);
    
    /*
     * Solve quadratic program with IPM.
     *
     * We always treat its result as a valid solution, even for TerminationType<=0.
     * In case anything is wrong with solution vector, we will detect it during line
     * search phase (merit function does not increase).
     *
     * NOTE: because we cleaned up constraints that are DEFINITELY inactive within
     *       trust region, we do not have to worry about StopOnExcessiveBounds option.
     */
    rallocv(n, &subsolver->tmps, _state);
    for(i=0; i<=n-1; i++)
    {
        subsolver->tmps.ptr.p_double[i] = nlcfsqp_gettrustregionk(&model->x, i, state->trustrad, _state);
    }
    rsetallocv(n, 0.0, &subsolver->tmporigin, _state);
    if( state->usedensebfgs )
    {
        
        /*
         * Dense BFGS is used to generate a quasi-Newton matrix,
         * dense IPM is used to solve QP subproblems.
         */
        if( dotrace )
        {
            stimerstart(&state->timerqpgen, _state);
        }
        hessiangetmatrix(hess, ae_true, &subsolver->denseh, _state);
        if( dotrace )
        {
            stimerstop(&state->timerqpgen, _state);
        }
        if( dotrace )
        {
            stimerstart(&state->timeripminit, _state);
        }
        vipminitdensewithslacks(&subsolver->ipmsolver, &subsolver->tmps, &subsolver->tmporigin, n, n, ae_true, _state);
        vipmsetquadraticlinear(&subsolver->ipmsolver, &subsolver->denseh, &subsolver->sparsedummy, 0, ae_true, &subsolver->curb, _state);
        vipmsetconstraints(&subsolver->ipmsolver, &subsolver->curbndl, &subsolver->curbndu, &subsolver->sparseefflc, subsolver->sparseefflc.m, &subsolver->densedummy, 0, &subsolver->cural, &subsolver->curau, _state);
        if( dotrace )
        {
            stimerstop(&state->timeripminit, _state);
        }
        if( dotrace )
        {
            stimerstart(&state->timeripmsolve, _state);
        }
        vipmoptimize(&subsolver->ipmsolver, ae_false, &subsolver->tmp0, &subsolver->tmp1, &subsolver->tmp2, terminationtype, _state);
        if( dotrace )
        {
            stimerstop(&state->timeripmsolve, _state);
        }
    }
    else
    {
        
        /*
         * Prepare ordering for QP subproblem or reuse previously computed one
         */
        if( xccnt>0&&!sparsetrygatherclear(&subsolver->pattern, &subsolver->upcomingpattern, _state) )
        {
            sparsemergepatterns(&subsolver->pattern, &subsolver->upcomingpattern, &subsolver->sparsetmp, _state);
            sparsecopybuf(&subsolver->sparsetmp, &subsolver->pattern, _state);
            bsetallocv(n, ae_true, &subsolver->isbounded, _state);
            ipm2proposeordering(&subsolver->ipm2, n, ae_true, &subsolver->isbounded, &subsolver->isbounded, &subsolver->pattern, &subsolver->cural, &subsolver->curau, xccnt, &subsolver->ordering, _state);
        }
        
        /*
         * Low-rank quasi-Newton matrix with positive-only corrections is used,
         * sparse IPM with low-rank updates is used to solve QP subproblems.
         */
        if( dotrace )
        {
            stimerstart(&state->timerqpgen, _state);
        }
        hessiangetlowrankstabilized(hess, &subsolver->tmpdiag, &subsolver->tmpcorrc, &subsolver->hesscorrd, &subsolver->hessrank, _state);
        subsolver->hesssparsediag.matrixtype = 1;
        subsolver->hesssparsediag.m = n;
        subsolver->hesssparsediag.n = n;
        iallocv(n+1, &subsolver->hesssparsediag.ridx, _state);
        iallocv(n, &subsolver->hesssparsediag.idx, _state);
        rallocv(n, &subsolver->hesssparsediag.vals, _state);
        subsolver->hesssparsediag.ridx.ptr.p_int[0] = 0;
        for(i=0; i<=n-1; i++)
        {
            subsolver->hesssparsediag.idx.ptr.p_int[i] = i;
            subsolver->hesssparsediag.vals.ptr.p_double[i] = subsolver->tmpdiag.ptr.p_double[i];
            subsolver->hesssparsediag.ridx.ptr.p_int[i+1] = i+1;
        }
        sparsecreatecrsinplace(&subsolver->hesssparsediag, _state);
        if( subsolver->hessrank>0 )
        {
            rcopyallocm(subsolver->hessrank, n, &subsolver->tmpcorrc, &subsolver->hesscorrc, _state);
        }
        targetscale = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = subsolver->curb.ptr.p_double[i]*state->varscales.ptr.p_double[i];
            targetscale = targetscale+v*v;
        }
        targetscale = ae_sqrt(targetscale, _state)*state->trustrad;
        targetscale = ae_maxreal(targetscale, hessiangetnrm2stab(hess, _state)*ae_sqr(state->trustrad, _state), _state);
        targetscale = coalesce(targetscale, (double)(1), _state);
        if( dotrace )
        {
            stimerstop(&state->timerqpgen, _state);
        }
        if( dotrace )
        {
            stimerstart(&state->timeripminit, _state);
        }
        ipm2init(&subsolver->ipm2, &subsolver->tmps, &subsolver->tmporigin, n, &subsolver->densedummy, &subsolver->hesssparsediag, 1, ae_true, &subsolver->hesscorrc, &subsolver->hesscorrd, subsolver->hessrank, &subsolver->curb, targetscale, &subsolver->curbndl, &subsolver->curbndu, &subsolver->sparseefflc, subsolver->sparseefflc.m, &subsolver->densedummy, 0, &subsolver->cural, &subsolver->curau, ae_true, ae_false, _state);
        ipm2setmaxits(&subsolver->ipm2, nlcfsqp_maxipmits, _state);
        ipm2setordering(&subsolver->ipm2, &subsolver->ordering, _state);
        if( dotrace )
        {
            stimerstop(&state->timeripminit, _state);
        }
        if( dotrace )
        {
            stimerstart(&state->timeripmsolve, _state);
        }
        ipm2optimize(&subsolver->ipm2, ae_false, &subsolver->tmp0, &subsolver->tmp1, &subsolver->tmp2, terminationtype, _state);
        if( dotrace )
        {
            stimerstop(&state->timeripmsolve, _state);
        }
        rcopyv(xccnt, &subsolver->tmp2, &subsolver->lastlaglc, _state);
    }
    rcopyv(n, &subsolver->tmp0, d, _state);
    
    /*
     * Analyze constraint violation
     */
    *isinfeasible = *isinfeasible||*terminationtype<=0;
    
    /*
     * Unpack results
     */
    for(i=0; i<=n-1; i++)
    {
        lagbcmult->ptr.p_double[i] = (double)(0);
        if( subsolver->retainpositivebclm.ptr.p_bool[i]&&ae_fp_greater(subsolver->tmp1.ptr.p_double[i],(double)(0)) )
        {
            lagbcmult->ptr.p_double[i] = subsolver->tmp1.ptr.p_double[i];
        }
        if( subsolver->retainnegativebclm.ptr.p_bool[i]&&ae_fp_less(subsolver->tmp1.ptr.p_double[i],(double)(0)) )
        {
            lagbcmult->ptr.p_double[i] = subsolver->tmp1.ptr.p_double[i];
        }
    }
    rcopyv(xccnt, &subsolver->tmp2, lagxcmult, _state);
    rmergemulv(xccnt, &subsolver->rescalelag, lagxcmult, _state);
    
    /*
     * Compute predicted decrease in an L1-penalized quadratic model
     */
    for(i=0; i<=lccnt-1; i++)
    {
        v = spdotvr(&model->x, &state->scla, i, _state);
        vv = v+spdotvr(d, &state->scla, i, _state);
        if( state->hasal.ptr.p_bool[i] )
        {
            subsolver->penalty0 = subsolver->penalty0+ae_maxreal(state->sclal.ptr.p_double[i]-v, (double)(0), _state);
            subsolver->penalty1 = subsolver->penalty1+ae_maxreal(state->sclal.ptr.p_double[i]-vv, (double)(0), _state);
        }
        if( state->hasau.ptr.p_bool[i] )
        {
            subsolver->penalty0 = subsolver->penalty0+ae_maxreal(v-state->sclau.ptr.p_double[i], (double)(0), _state);
            subsolver->penalty1 = subsolver->penalty1+ae_maxreal(vv-state->sclau.ptr.p_double[i], (double)(0), _state);
        }
    }
    for(i=0; i<=nnlc-1; i++)
    {
        v = model->fi.ptr.p_double[1+i];
        vv = v+spdotvr(d, &model->sj, 1+i, _state);
        if( state->hasnl.ptr.p_bool[i] )
        {
            subsolver->penalty0 = subsolver->penalty0+ae_maxreal(state->scalednl.ptr.p_double[i]-v, (double)(0), _state);
            subsolver->penalty1 = subsolver->penalty1+ae_maxreal(state->scalednl.ptr.p_double[i]-vv, (double)(0), _state);
        }
        if( state->hasnu.ptr.p_bool[i] )
        {
            subsolver->penalty0 = subsolver->penalty0+ae_maxreal(v-state->scalednu.ptr.p_double[i], (double)(0), _state);
            subsolver->penalty1 = subsolver->penalty1+ae_maxreal(vv-state->scalednu.ptr.p_double[i], (double)(0), _state);
        }
    }
    *predictedchangemodel = rdotv(n, d, &subsolver->curb, _state)+0.5*hessianvmv(hess, d, _state);
    *predictedchangepenalty = subsolver->penalty1-subsolver->penalty0;
    *isinfeasible = *isinfeasible||ae_fp_greater(subsolver->penalty1,subsolver->penalty0*nlcfsqp_sufficientdecreasesigma+ae_maxreal(critgetepsf(&state->criteria, _state), (double)100*ae_sqrt(ae_machineepsilon, _state), _state)*rmaxabs3(subsolver->penalty0, subsolver->penalty1, (double)(1), _state));
    
    /*
     * Analyze length of D
     */
    ae_assert(ae_fp_eq(*d2trustradratio,0.0), "SQP: integrity check 0157 failed", _state);
    for(i=0; i<=n-1; i++)
    {
        *d2trustradratio = ae_maxreal(*d2trustradratio, ae_fabs(d->ptr.p_double[i]/nlcfsqp_gettrustregionk(&model->x, i, state->trustrad, _state), _state), _state);
    }
    return result;
}


/*************************************************************************
This function solves elastic  QP  subproblem  arising  during  feasibility
restoration phase.

  -- ALGLIB --
     Copyright 05.03.2018 by Bochkanov Sergey
*************************************************************************/
static ae_bool nlcfsqp_qpsubproblemsolveelastic(minfsqpstate* state,
     minfsqpsubsolver* subsolver,
     const varsfuncjac* model,
     xbfgshessian* hess,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_vector* lagbcmult,
     /* Real    */ ae_vector* lagxcmult,
     ae_bool dotrace,
     ae_bool* isinfeasible,
     ae_int_t* terminationtype,
     double* predictedchangemodel,
     double* predictedchangepenalty,
     double* d2trustradratio,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nslack;
    ae_int_t lccnt;
    ae_int_t nnlc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t jj;
    double v;
    double vv;
    double vright;
    double vmax;
    double vmin;
    ae_int_t xccnt;
    ae_int_t offsslacklc;
    ae_int_t offsslacknnlc;
    ae_int_t offs;
    ae_int_t nnz;
    ae_int_t j0;
    ae_int_t j1;
    double rescaleby;
    ae_bool bndlinactive;
    ae_bool bnduinactive;
    double targetscale;
    ae_bool result;

    *isinfeasible = ae_false;
    *terminationtype = 0;
    *predictedchangemodel = 0.0;
    *predictedchangepenalty = 0.0;
    *d2trustradratio = 0.0;

    ae_assert(!model->isdense, "SQP: integrity check 8637 failed", _state);
    n = state->n;
    lccnt = state->lccnt;
    nnlc = state->nnlc;
    nslack = n+2*lccnt+2*nnlc;
    xccnt = lccnt+nnlc;
    
    /*
     * Locations of slack variables
     */
    offsslacklc = n;
    offsslacknnlc = n+2*lccnt;
    
    /*
     * Prepare temporary structures
     */
    rvectorgrowto(&subsolver->cural, xccnt, _state);
    rvectorgrowto(&subsolver->curau, xccnt, _state);
    rallocv(xccnt, &subsolver->rescalelag, _state);
    
    /*
     * Prepare default solution: all zeros
     */
    result = ae_true;
    *terminationtype = 0;
    *predictedchangemodel = (double)(0);
    *predictedchangepenalty = (double)(0);
    *d2trustradratio = 0.0;
    *isinfeasible = ae_false;
    rsetv(n, 0.0, d, _state);
    rsetv(n, 0.0, lagbcmult, _state);
    rsetv(xccnt, 0.0, lagxcmult, _state);
    bsetallocv(lccnt+nnlc, ae_false, &subsolver->isinactive, _state);
    
    /*
     * Linear term B
     */
    rsetv(n, 0.0, &subsolver->curb, _state);
    for(i=n; i<=nslack-1; i++)
    {
        subsolver->curb.ptr.p_double[i] = 1.0;
    }
    
    /*
     * Trust radius constraints for primary variables
     */
    bsetallocv(n, ae_false, &subsolver->retainnegativebclm, _state);
    bsetallocv(n, ae_false, &subsolver->retainpositivebclm, _state);
    for(i=0; i<=n-1; i++)
    {
        subsolver->curbndl.ptr.p_double[i] = -nlcfsqp_gettrustregionk(&model->x, i, state->trustrad, _state);
        subsolver->curbndu.ptr.p_double[i] = nlcfsqp_gettrustregionk(&model->x, i, state->trustrad, _state);
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_greater(state->scaledbndl.ptr.p_double[i]-model->x.ptr.p_double[i],subsolver->curbndl.ptr.p_double[i]) )
        {
            subsolver->curbndl.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i]-model->x.ptr.p_double[i];
            subsolver->retainnegativebclm.ptr.p_bool[i] = ae_true;
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_less(state->scaledbndu.ptr.p_double[i]-model->x.ptr.p_double[i],subsolver->curbndu.ptr.p_double[i]) )
        {
            subsolver->curbndu.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i]-model->x.ptr.p_double[i];
            subsolver->retainpositivebclm.ptr.p_bool[i] = ae_true;
        }
    }
    
    /*
     * Prepare storage for "effective" constraining matrix
     */
    nnz = 0;
    if( lccnt>0 )
    {
        nnz = nnz+subsolver->sparserawlc.ridx.ptr.p_int[lccnt];
    }
    nnz = nnz+(model->sj.ridx.ptr.p_int[1+nnlc]-model->sj.ridx.ptr.p_int[1]);
    nnz = nnz+2*lccnt;
    nnz = nnz+2*nnlc;
    ivectorgrowto(&subsolver->sparseefflc.ridx, xccnt+1, _state);
    rvectorgrowto(&subsolver->sparseefflc.vals, nnz, _state);
    ivectorgrowto(&subsolver->sparseefflc.idx, nnz, _state);
    ivectorsetlengthatleast(&subsolver->sparseefflc.didx, xccnt, _state);
    ivectorsetlengthatleast(&subsolver->sparseefflc.uidx, xccnt, _state);
    subsolver->sparseefflc.ridx.ptr.p_int[0] = 0;
    subsolver->sparseefflc.m = 0;
    subsolver->sparseefflc.n = nslack;
    subsolver->sparseefflc.matrixtype = 1;
    sparsecreatecrsemptybuf(nslack, &subsolver->upcomingpattern, _state);
    
    /*
     * Append linear equality/inequality constraints
     *
     * Scan sparsified linear constraints stored in sparserawlc[], skip ones
     * which are inactive anywhere in the trust region.
     */
    rvectorsetlengthatleast(&subsolver->tmp0, nslack, _state);
    for(i=0; i<=n-1; i++)
    {
        subsolver->tmp0.ptr.p_double[i] = model->x.ptr.p_double[i];
    }
    for(i=n; i<=nslack-1; i++)
    {
        subsolver->tmp0.ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=lccnt-1; i++)
    {
        
        /*
         * Calculate:
         * * VRight - product of X[] (extended with zeros up to NSlack elements)
         *            and AR[i] - Ith row of sparserawlc matrix.
         * * VMax   - maximum value of X*ARi computed over trust region
         */
        vright = (double)(0);
        vmax = (double)(0);
        vmin = (double)(0);
        j0 = subsolver->sparserawlc.ridx.ptr.p_int[i];
        j1 = subsolver->sparserawlc.ridx.ptr.p_int[i+1]-1;
        for(k=j0; k<=j1; k++)
        {
            j = subsolver->sparserawlc.idx.ptr.p_int[k];
            v = model->x.ptr.p_double[j];
            vv = subsolver->sparserawlc.vals.ptr.p_double[k];
            vright = vright+vv*v;
            if( vv>=(double)0 )
            {
                vmax = vmax+vv*(v+subsolver->curbndu.ptr.p_double[j]);
                vmin = vmin+vv*(v+subsolver->curbndl.ptr.p_double[j]);
            }
            else
            {
                vmax = vmax+vv*(v+subsolver->curbndl.ptr.p_double[j]);
                vmin = vmin+vv*(v+subsolver->curbndu.ptr.p_double[j]);
            }
        }
        bndlinactive = !state->hasal.ptr.p_bool[i]||vmin>state->sclal.ptr.p_double[i];
        bnduinactive = !state->hasau.ptr.p_bool[i]||vmax<state->sclau.ptr.p_double[i];
        
        /*
         * Linear constraints are passed 'as is', without additional rescaling
         */
        subsolver->rescalelag.ptr.p_double[i] = 1.0;
        
        /*
         * Always append full row to the upcoming pattern; rows to the effective linear
         * constraints matrix are added subject to basic presolve (remove constraints that
         * are guaranteed to be inactive).
         */
        nlcfsqp_appendscaledsparserow(&subsolver->upcomingpattern, 1.0, &subsolver->sparserawlc, i, _state);
        sparseappendelement(&subsolver->upcomingpattern, offsslacklc+2*i+0, 1.0, _state);
        sparseappendelement(&subsolver->upcomingpattern, offsslacklc+2*i+1, -1.0, _state);
        
        /*
         * If constraint is an inequality one and guaranteed to be inactive
         * within trust region, it is skipped (the row itself is retained but
         * filled by zeros).
         */
        if( bndlinactive&&bnduinactive )
        {
            offs = subsolver->sparseefflc.ridx.ptr.p_int[i];
            subsolver->sparseefflc.vals.ptr.p_double[offs+0] = (double)(1);
            subsolver->sparseefflc.vals.ptr.p_double[offs+1] = (double)(-1);
            subsolver->sparseefflc.idx.ptr.p_int[offs+0] = offsslacklc+2*i+0;
            subsolver->sparseefflc.idx.ptr.p_int[offs+1] = offsslacklc+2*i+1;
            subsolver->sparseefflc.ridx.ptr.p_int[i+1] = offs+2;
            subsolver->cural.ptr.p_double[i] = 0.0;
            subsolver->curau.ptr.p_double[i] = 0.0;
            subsolver->curbndl.ptr.p_double[offsslacklc+2*i+0] = (double)(0);
            subsolver->curbndl.ptr.p_double[offsslacklc+2*i+1] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslacklc+2*i+0] = _state->v_posinf;
            subsolver->curbndu.ptr.p_double[offsslacklc+2*i+1] = _state->v_posinf;
            subsolver->rescalelag.ptr.p_double[i] = 0.0;
            subsolver->isinactive.ptr.p_bool[i] = ae_true;
            continue;
        }
        
        /*
         * Start working on row I
         */
        offs = subsolver->sparseefflc.ridx.ptr.p_int[i];
        
        /*
         * Copy constraint from sparserawlc[] to sparseefflc[], add slack terms
         */
        j0 = subsolver->sparserawlc.ridx.ptr.p_int[i];
        j1 = subsolver->sparserawlc.ridx.ptr.p_int[i+1]-1;
        for(k=j0; k<=j1; k++)
        {
            subsolver->sparseefflc.idx.ptr.p_int[offs] = subsolver->sparserawlc.idx.ptr.p_int[k];
            subsolver->sparseefflc.vals.ptr.p_double[offs] = subsolver->sparserawlc.vals.ptr.p_double[k];
            offs = offs+1;
        }
        if( !bndlinactive )
        {
            subsolver->sparseefflc.vals.ptr.p_double[offs] = (double)(1);
            subsolver->sparseefflc.idx.ptr.p_int[offs] = offsslacklc+2*i+0;
            offs = offs+1;
        }
        if( !bnduinactive )
        {
            subsolver->sparseefflc.vals.ptr.p_double[offs] = (double)(-1);
            subsolver->sparseefflc.idx.ptr.p_int[offs] = offsslacklc+2*i+1;
            offs = offs+1;
        }
        subsolver->sparseefflc.ridx.ptr.p_int[i+1] = offs;
        
        /*
         * Set box constraints on slack variables and bounds on linear equality/inequality constraints
         */
        if( !bndlinactive )
        {
            subsolver->cural.ptr.p_double[i] = state->sclal.ptr.p_double[i]-vright;
            subsolver->curbndl.ptr.p_double[offsslacklc+2*i+0] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslacklc+2*i+0] = ae_maxreal(state->sclal.ptr.p_double[i]-vright, 0.0, _state);
        }
        else
        {
            subsolver->cural.ptr.p_double[i] = _state->v_neginf;
            subsolver->curbndl.ptr.p_double[offsslacklc+2*i+0] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslacklc+2*i+0] = _state->v_posinf;
        }
        if( !bnduinactive )
        {
            subsolver->curau.ptr.p_double[i] = state->sclau.ptr.p_double[i]-vright;
            subsolver->curbndl.ptr.p_double[offsslacklc+2*i+1] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslacklc+2*i+1] = ae_maxreal(vright-state->sclau.ptr.p_double[i], 0.0, _state);
        }
        else
        {
            subsolver->curau.ptr.p_double[i] = _state->v_posinf;
            subsolver->curbndl.ptr.p_double[offsslacklc+2*i+1] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslacklc+2*i+1] = _state->v_posinf;
        }
        
        /*
         * Additional integrity check to fix rounding bugs, to make sure that curAL<=curAU.
         */
        if( !bndlinactive&&!bnduinactive )
        {
            subsolver->curau.ptr.p_double[i] = ae_maxreal(subsolver->curau.ptr.p_double[i], subsolver->cural.ptr.p_double[i], _state);
        }
    }
    subsolver->sparseefflc.m = subsolver->sparseefflc.m+lccnt;
    
    /*
     * Append nonlinear equality/inequality constraints
     */
    for(i=0; i<=nnlc-1; i++)
    {
        
        /*
         * Calculate:
         * * rescale coefficient used to normalize constraints
         * * VMax - maximum of constraint value over trust region
         */
        vmax = model->fi.ptr.p_double[1+i];
        vmin = model->fi.ptr.p_double[1+i];
        j0 = model->sj.ridx.ptr.p_int[1+i];
        j1 = model->sj.ridx.ptr.p_int[1+i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            j = model->sj.idx.ptr.p_int[jj];
            v = model->sj.vals.ptr.p_double[jj];
            if( v>=(double)0 )
            {
                vmax = vmax+v*subsolver->curbndu.ptr.p_double[j];
                vmin = vmin+v*subsolver->curbndl.ptr.p_double[j];
            }
            else
            {
                vmax = vmax+v*subsolver->curbndl.ptr.p_double[j];
                vmin = vmin+v*subsolver->curbndu.ptr.p_double[j];
            }
        }
        rescaleby = 1.0;
        bndlinactive = !state->hasnl.ptr.p_bool[i]||vmin>state->scalednl.ptr.p_double[i];
        bnduinactive = !state->hasnu.ptr.p_bool[i]||vmax<state->scalednu.ptr.p_double[i];
        
        /*
         * Nonlinear constraints are passed with normalization
         */
        subsolver->rescalelag.ptr.p_double[lccnt+i] = rescaleby;
        
        /*
         * Always append full row to the upcoming pattern; rows to the effective linear
         * constraints matrix are added subject to basic presolve (remove constraints that
         * are guaranteed to be inactive).
         */
        nlcfsqp_appendscaledsparserow(&subsolver->upcomingpattern, rescaleby, &model->sj, 1+i, _state);
        sparseappendelement(&subsolver->upcomingpattern, offsslacknnlc+2*i+0, 1.0, _state);
        sparseappendelement(&subsolver->upcomingpattern, offsslacknnlc+2*i+1, -1.0, _state);
        
        /*
         * If constraint is an inequality one and guaranteed to be inactive
         * within trust region, it is skipped (row itself is retained but
         * filled by zeros).
         */
        if( bndlinactive&&bnduinactive )
        {
            offs = subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m+i];
            subsolver->sparseefflc.vals.ptr.p_double[offs+0] = (double)(1);
            subsolver->sparseefflc.vals.ptr.p_double[offs+1] = (double)(-1);
            subsolver->sparseefflc.idx.ptr.p_int[offs+0] = offsslacknnlc+2*i+0;
            subsolver->sparseefflc.idx.ptr.p_int[offs+1] = offsslacknnlc+2*i+1;
            subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m+i+1] = offs+2;
            subsolver->cural.ptr.p_double[subsolver->sparseefflc.m+i] = 0.0;
            subsolver->curau.ptr.p_double[subsolver->sparseefflc.m+i] = 0.0;
            subsolver->curbndl.ptr.p_double[offsslacknnlc+2*i+0] = 0.0;
            subsolver->curbndu.ptr.p_double[offsslacknnlc+2*i+0] = _state->v_posinf;
            subsolver->curbndl.ptr.p_double[offsslacknnlc+2*i+1] = 0.0;
            subsolver->curbndu.ptr.p_double[offsslacknnlc+2*i+1] = _state->v_posinf;
            subsolver->rescalelag.ptr.p_double[lccnt+i] = 0.0;
            subsolver->isinactive.ptr.p_bool[lccnt+i] = ae_true;
            continue;
        }
        
        /*
         * Copy scaled row, add slack terms
         */
        offs = subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m+i];
        j0 = model->sj.ridx.ptr.p_int[1+i];
        j1 = model->sj.ridx.ptr.p_int[1+i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            j = model->sj.idx.ptr.p_int[jj];
            v = model->sj.vals.ptr.p_double[jj];
            subsolver->sparseefflc.vals.ptr.p_double[offs] = rescaleby*v;
            subsolver->sparseefflc.idx.ptr.p_int[offs] = j;
            offs = offs+1;
        }
        if( !bndlinactive )
        {
            subsolver->sparseefflc.vals.ptr.p_double[offs] = (double)(1);
            subsolver->sparseefflc.idx.ptr.p_int[offs] = offsslacknnlc+2*i+0;
            offs = offs+1;
        }
        if( !bnduinactive )
        {
            subsolver->sparseefflc.vals.ptr.p_double[offs] = (double)(-1);
            subsolver->sparseefflc.idx.ptr.p_int[offs] = offsslacknnlc+2*i+1;
            offs = offs+1;
        }
        subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m+i+1] = offs;
        
        /*
         * Set box constraints on slack variables and bounds on linear equality/inequality constraints
         */
        if( !bndlinactive )
        {
            subsolver->cural.ptr.p_double[subsolver->sparseefflc.m+i] = rescaleby*(state->scalednl.ptr.p_double[i]-model->fi.ptr.p_double[1+i]);
            subsolver->curbndl.ptr.p_double[offsslacknnlc+2*i+0] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslacknnlc+2*i+0] = rescaleby*ae_maxreal(state->scalednl.ptr.p_double[i]-model->fi.ptr.p_double[1+i], 0.0, _state);
        }
        else
        {
            subsolver->cural.ptr.p_double[subsolver->sparseefflc.m+i] = _state->v_neginf;
            subsolver->curbndl.ptr.p_double[offsslacknnlc+2*i+0] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslacknnlc+2*i+0] = _state->v_posinf;
        }
        if( !bnduinactive )
        {
            subsolver->curau.ptr.p_double[subsolver->sparseefflc.m+i] = rescaleby*(state->scalednu.ptr.p_double[i]-model->fi.ptr.p_double[1+i]);
            subsolver->curbndl.ptr.p_double[offsslacknnlc+2*i+1] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslacknnlc+2*i+1] = rescaleby*ae_maxreal(model->fi.ptr.p_double[1+i]-state->scalednu.ptr.p_double[i], 0.0, _state);
        }
        else
        {
            subsolver->curau.ptr.p_double[subsolver->sparseefflc.m+i] = _state->v_posinf;
            subsolver->curbndl.ptr.p_double[offsslacknnlc+2*i+1] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslacknnlc+2*i+1] = _state->v_posinf;
        }
    }
    subsolver->sparseefflc.m = subsolver->sparseefflc.m+nnlc;
    
    /*
     * Finalize sparse matrix structure
     */
    ae_assert(subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m]<=subsolver->sparseefflc.idx.cnt, "QPSubproblemSolve: critical integrity check failed", _state);
    ae_assert(subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m]<=subsolver->sparseefflc.vals.cnt, "QPSubproblemSolve: critical integrity check failed", _state);
    subsolver->sparseefflc.ninitialized = subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m];
    sparseinitduidx(&subsolver->sparseefflc, _state);
    
    /*
     * Solve quadratic program with IPM.
     *
     * We always treat its result as a valid solution, even for TerminationType<=0.
     * In case anything is wrong with solution vector, we will detect it during line
     * search phase (merit function does not increase).
     *
     * NOTE: because we cleaned up constraints that are DEFINITELY inactive within
     *       trust region, we do not have to worry about StopOnExcessiveBounds option.
     */
    rsetallocv(nslack, state->trustrad, &subsolver->tmps, _state);
    for(i=0; i<=n-1; i++)
    {
        subsolver->tmps.ptr.p_double[i] = nlcfsqp_gettrustregionk(&model->x, i, state->trustrad, _state);
    }
    rsetallocv(nslack, 0.0, &subsolver->tmporigin, _state);
    if( state->usedensebfgs )
    {
        
        /*
         * Dense BFGS is used to generate a quasi-Newton matrix,
         * dense IPM is used to solve QP subproblems.
         */
        if( dotrace )
        {
            stimerstart(&state->timerqpgen, _state);
        }
        rsetallocm(n, n, 0.0, &subsolver->denseh, _state);
        if( dotrace )
        {
            stimerstop(&state->timerqpgen, _state);
        }
        if( dotrace )
        {
            stimerstart(&state->timeripminit, _state);
        }
        vipminitdensewithslacks(&subsolver->ipmsolver, &subsolver->tmps, &subsolver->tmporigin, n, nslack, ae_true, _state);
        vipmsetquadraticlinear(&subsolver->ipmsolver, &subsolver->denseh, &subsolver->sparsedummy, 0, ae_true, &subsolver->curb, _state);
        vipmsetconstraints(&subsolver->ipmsolver, &subsolver->curbndl, &subsolver->curbndu, &subsolver->sparseefflc, subsolver->sparseefflc.m, &subsolver->densedummy, 0, &subsolver->cural, &subsolver->curau, _state);
        if( dotrace )
        {
            stimerstop(&state->timeripminit, _state);
        }
        if( dotrace )
        {
            stimerstart(&state->timeripmsolve, _state);
        }
        vipmoptimize(&subsolver->ipmsolver, ae_false, &subsolver->tmp0, &subsolver->tmp1, &subsolver->tmp2, terminationtype, _state);
        if( dotrace )
        {
            stimerstop(&state->timeripmsolve, _state);
        }
    }
    else
    {
        
        /*
         * Prepare ordering for QP subproblem or reuse previously computed one
         */
        if( xccnt>0&&!sparsetrygatherclear(&subsolver->elasticpattern, &subsolver->upcomingpattern, _state) )
        {
            sparsemergepatterns(&subsolver->elasticpattern, &subsolver->upcomingpattern, &subsolver->sparsetmp, _state);
            sparsecopybuf(&subsolver->sparsetmp, &subsolver->elasticpattern, _state);
            bsetallocv(nslack, ae_true, &subsolver->isbounded, _state);
            ipm2proposeordering(&subsolver->ipm2, nslack, ae_true, &subsolver->isbounded, &subsolver->isbounded, &subsolver->elasticpattern, &subsolver->cural, &subsolver->curau, xccnt, &subsolver->elasticordering, _state);
        }
        
        /*
         * Low-rank quasi-Newton matrix with positive-only corrections is used,
         * sparse IPM with low-rank updates is used to solve QP subproblems.
         */
        if( dotrace )
        {
            stimerstart(&state->timerqpgen, _state);
        }
        subsolver->hesssparsediag.matrixtype = 1;
        subsolver->hesssparsediag.m = nslack;
        subsolver->hesssparsediag.n = nslack;
        iallocv(nslack+1, &subsolver->hesssparsediag.ridx, _state);
        iallocv(nslack, &subsolver->hesssparsediag.idx, _state);
        rallocv(nslack, &subsolver->hesssparsediag.vals, _state);
        subsolver->hesssparsediag.ridx.ptr.p_int[0] = 0;
        for(i=0; i<=nslack-1; i++)
        {
            subsolver->hesssparsediag.idx.ptr.p_int[i] = i;
            subsolver->hesssparsediag.vals.ptr.p_double[i] = ae_sqrt(ae_machineepsilon, _state)/ae_sqr(subsolver->tmps.ptr.p_double[i], _state);
            subsolver->hesssparsediag.ridx.ptr.p_int[i+1] = i+1;
        }
        targetscale = (double)(0);
        for(i=0; i<=nslack-1; i++)
        {
            v = subsolver->curb.ptr.p_double[i];
            if( i<n )
            {
                v = v*state->varscales.ptr.p_double[i];
            }
            targetscale = targetscale+v*v;
        }
        targetscale = ae_sqrt(targetscale, _state)*state->trustrad;
        sparsecreatecrsinplace(&subsolver->hesssparsediag, _state);
        if( dotrace )
        {
            stimerstop(&state->timerqpgen, _state);
        }
        if( dotrace )
        {
            stimerstart(&state->timeripminit, _state);
        }
        ipm2init(&subsolver->ipm2, &subsolver->tmps, &subsolver->tmporigin, nslack, &subsolver->densedummy, &subsolver->hesssparsediag, 1, ae_true, &subsolver->hesscorrc, &subsolver->hesscorrd, 0, &subsolver->curb, targetscale, &subsolver->curbndl, &subsolver->curbndu, &subsolver->sparseefflc, subsolver->sparseefflc.m, &subsolver->densedummy, 0, &subsolver->cural, &subsolver->curau, ae_true, ae_false, _state);
        ipm2setmaxits(&subsolver->ipm2, nlcfsqp_maxipmits, _state);
        ipm2setordering(&subsolver->ipm2, &subsolver->elasticordering, _state);
        if( dotrace )
        {
            stimerstop(&state->timeripminit, _state);
        }
        if( dotrace )
        {
            stimerstart(&state->timeripmsolve, _state);
        }
        ipm2optimize(&subsolver->ipm2, ae_false, &subsolver->tmp0, &subsolver->tmp1, &subsolver->tmp2, terminationtype, _state);
        if( dotrace )
        {
            stimerstop(&state->timeripmsolve, _state);
        }
    }
    rcopyv(n, &subsolver->tmp0, d, _state);
    *isinfeasible = !nlcfsqp_qpsubproblemisfeasible(state, subsolver, model, d, _state);
    
    /*
     * Unpack results
     */
    for(i=0; i<=n-1; i++)
    {
        lagbcmult->ptr.p_double[i] = (double)(0);
        if( subsolver->retainpositivebclm.ptr.p_bool[i]&&ae_fp_greater(subsolver->tmp1.ptr.p_double[i],(double)(0)) )
        {
            lagbcmult->ptr.p_double[i] = subsolver->tmp1.ptr.p_double[i];
        }
        if( subsolver->retainnegativebclm.ptr.p_bool[i]&&ae_fp_less(subsolver->tmp1.ptr.p_double[i],(double)(0)) )
        {
            lagbcmult->ptr.p_double[i] = subsolver->tmp1.ptr.p_double[i];
        }
    }
    rcopyv(xccnt, &subsolver->tmp2, lagxcmult, _state);
    rmergemulv(xccnt, &subsolver->rescalelag, lagxcmult, _state);
    
    /*
     * Compute predicted decrease in an L1-penalized quadratic model
     */
    nlcfsqp_penalizedfg(state, &model->x, &model->fi, &model->sj, &v, &subsolver->tmp0, _state);
    *predictedchangemodel = rdotv(n, d, &subsolver->tmp0, _state)+0.5*hessianvmv(hess, d, _state);
    *predictedchangepenalty = (double)(0);
    for(i=0; i<=lccnt-1; i++)
    {
        v = spdotvr(&model->x, &state->scla, i, _state);
        vv = v+spdotvr(d, &state->scla, i, _state);
        if( state->hasal.ptr.p_bool[i] )
        {
            *predictedchangepenalty = *predictedchangepenalty+(ae_maxreal(state->sclal.ptr.p_double[i]-vv, (double)(0), _state)-ae_maxreal(state->sclal.ptr.p_double[i]-v, (double)(0), _state));
        }
        if( state->hasau.ptr.p_bool[i] )
        {
            *predictedchangepenalty = *predictedchangepenalty+(ae_maxreal(vv-state->sclau.ptr.p_double[i], (double)(0), _state)-ae_maxreal(v-state->sclau.ptr.p_double[i], (double)(0), _state));
        }
    }
    for(i=0; i<=nnlc-1; i++)
    {
        v = model->fi.ptr.p_double[1+i];
        vv = v+spdotvr(d, &model->sj, 1+i, _state);
        if( state->hasnl.ptr.p_bool[i] )
        {
            *predictedchangepenalty = *predictedchangepenalty+(ae_maxreal(state->scalednl.ptr.p_double[i]-vv, (double)(0), _state)-ae_maxreal(state->scalednl.ptr.p_double[i]-v, (double)(0), _state));
        }
        if( state->hasnu.ptr.p_bool[i] )
        {
            *predictedchangepenalty = *predictedchangepenalty+(ae_maxreal(vv-state->scalednu.ptr.p_double[i], (double)(0), _state)-ae_maxreal(v-state->scalednu.ptr.p_double[i], (double)(0), _state));
        }
    }
    
    /*
     * Analyze length of D
     */
    ae_assert(ae_fp_eq(*d2trustradratio,0.0), "SQP: integrity check 0157 failed", _state);
    for(i=0; i<=n-1; i++)
    {
        *d2trustradratio = ae_maxreal(*d2trustradratio, ae_fabs(d->ptr.p_double[i]/nlcfsqp_gettrustregionk(&model->x, i, state->trustrad, _state), _state), _state);
    }
    return result;
}


/*************************************************************************
This function checks feasibility of a QP subproblem solution D (which
corresponds to X+D after correcting for origin)

  -- ALGLIB --
     Copyright 05.03.2018 by Bochkanov Sergey
*************************************************************************/
static ae_bool nlcfsqp_qpsubproblemisfeasible(const minfsqpstate* state,
     const minfsqpsubsolver* subsolver,
     const varsfuncjac* model,
     /* Real    */ const ae_vector* d,
     ae_state *_state)
{
    double tol;
    double v0;
    double v1;
    double v2;
    double vv;
    ae_int_t lccnt;
    ae_int_t nnlc;
    ae_int_t i;
    ae_int_t k;
    ae_int_t j0;
    ae_int_t j1;
    ae_bool isinfeasible;
    ae_bool result;


    ae_assert(!model->isdense, "SQP: integrity check 5436 failed", _state);
    lccnt = state->lccnt;
    nnlc = state->nnlc;
    tol = ae_maxreal(critgeteps(&state->criteria, _state), ae_sqrt(ae_machineepsilon, _state), _state);
    tol = ae_maxreal(tol, 1.0E-5*state->trustrad, _state);
    isinfeasible = ae_false;
    for(i=0; i<=lccnt-1; i++)
    {
        if( !subsolver->isinactive.ptr.p_bool[i] )
        {
            
            /*
             * Calculate product of X+D with I-th linear constraint
             */
            v0 = (double)(0);
            v1 = (double)(0);
            v2 = (double)(0);
            j0 = subsolver->sparserawlc.ridx.ptr.p_int[i];
            j1 = subsolver->sparserawlc.ridx.ptr.p_int[i+1]-1;
            for(k=j0; k<=j1; k++)
            {
                vv = subsolver->sparserawlc.vals.ptr.p_double[k];
                v0 = v0+vv*model->x.ptr.p_double[subsolver->sparserawlc.idx.ptr.p_int[k]];
                v1 = v1+vv*d->ptr.p_double[subsolver->sparserawlc.idx.ptr.p_int[k]];
                v2 = v2+vv*vv;
            }
            v1 = v1+v0;
            if( state->hasal.ptr.p_bool[i] )
            {
                isinfeasible = isinfeasible||ae_maxreal(state->sclal.ptr.p_double[i]-v1, 0.0, _state)/coalesce(ae_sqrt(v2, _state), (double)(1), _state)>tol;
            }
            if( state->hasau.ptr.p_bool[i] )
            {
                isinfeasible = isinfeasible||ae_maxreal(v1-state->sclau.ptr.p_double[i], 0.0, _state)/coalesce(ae_sqrt(v2, _state), (double)(1), _state)>tol;
            }
        }
    }
    for(i=0; i<=nnlc-1; i++)
    {
        if( !subsolver->isinactive.ptr.p_bool[lccnt+i] )
        {
            v1 = model->fi.ptr.p_double[1+i]+spdotvr(d, &model->sj, 1+i, _state);
            v2 = spdotr2(&model->sj, 1+i, _state);
            if( state->hasnl.ptr.p_bool[i] )
            {
                isinfeasible = isinfeasible||ae_maxreal(state->scalednl.ptr.p_double[i]-v1, 0.0, _state)/coalesce(ae_sqrt(v2, _state), (double)(1), _state)>tol;
            }
            if( state->hasnu.ptr.p_bool[i] )
            {
                isinfeasible = isinfeasible||ae_maxreal(v1-state->scalednu.ptr.p_double[i], 0.0, _state)/coalesce(ae_sqrt(v2, _state), (double)(1), _state)>tol;
            }
        }
    }
    result = !isinfeasible;
    return result;
}


/*************************************************************************
Copies X to State.X
*************************************************************************/
static void nlcfsqp_sqpsendx(minfsqpstate* state,
     /* Real    */ const ae_vector* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    for(i=0; i<=n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i]&&xs->ptr.p_double[i]<=state->scaledbndl.ptr.p_double[i] )
        {
            state->x.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i];
            continue;
        }
        if( state->hasbndu.ptr.p_bool[i]&&xs->ptr.p_double[i]>=state->scaledbndu.ptr.p_double[i] )
        {
            state->x.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i];
            continue;
        }
        state->x.ptr.p_double[i] = xs->ptr.p_double[i];
    }
}


/*************************************************************************
Retrieves F-vector and scaled Jacobian, copies them to FiS and JS.

Returns True on success, False on failure (when F or J are not finite numbers).
*************************************************************************/
static ae_bool nlcfsqp_sqpretrievefij(const minfsqpstate* state,
     varsfuncjac* vfj,
     ae_state *_state)
{
    ae_bool result;


    ae_assert(!vfj->isdense, "SQP: integrity check 0016 failed", _state);
    
    /*
     * Retrieve function vector and Jacobian
     */
    rcopyv(state->nnlc+1, &state->fi, &vfj->fi, _state);
    sparsecopybuf(&state->sj, &vfj->sj, _state);
    rmergemulv(state->nnlc+1, &state->invfscales, &vfj->fi, _state);
    sparsemultiplyrowsby(&vfj->sj, &state->invfscales, _state);
    
    /*
     * Check finiteness
     */
    result = ae_isfinite(rdotv2(state->nnlc+1, &vfj->fi, _state), _state)&&ae_isfinite(rdotv2(vfj->sj.ridx.ptr.p_int[state->nnlc+1], &vfj->sj.vals, _state), _state);
    return result;
}


/*************************************************************************
This function calculates Lagrangian of the problem (in scaled variables):
its value and gradient.

Additionally it also estimates violation of linear constraints at the point
as well as index of the most violated constraint
*************************************************************************/
static void nlcfsqp_lagrangianfg(minfsqpstate* state,
     /* Real    */ const ae_vector* x,
     double trustrad,
     /* Real    */ const ae_vector* fi,
     const sparsematrix* sj,
     /* Real    */ const ae_vector* lagbcmult,
     /* Real    */ const ae_vector* lagxcmult,
     ae_bool uselagrangeterms,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t lccnt;
    ae_int_t nnlc;
    double v;
    double vlag;
    double vact;
    double vd;
    double vs;
    double lagalpha;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;

    *f = 0.0;

    n = state->n;
    lccnt = state->lccnt;
    nnlc = state->nnlc;
    lagalpha = rcase2(uselagrangeterms, (double)(1), (double)(0), _state);
    
    /*
     * Target function
     */
    rsetv(n, 0.0, g, _state);
    *f = fi->ptr.p_double[0];
    j0 = sj->ridx.ptr.p_int[0];
    j1 = sj->ridx.ptr.p_int[1]-1;
    for(jj=j0; jj<=j1; jj++)
    {
        g->ptr.p_double[sj->idx.ptr.p_int[jj]] = sj->vals.ptr.p_double[jj];
    }
    
    /*
     * Lagrangian terms for box constraints
     */
    for(i=0; i<=n-1; i++)
    {
        *f = *f+lagalpha*lagbcmult->ptr.p_double[i]*x->ptr.p_double[i];
        g->ptr.p_double[i] = g->ptr.p_double[i]+lagalpha*lagbcmult->ptr.p_double[i];
    }
    
    /*
     * Lagrangian terms for linear constraints, constraint violations
     */
    if( lccnt>0 )
    {
        rvectorsetlengthatleast(&state->sclagtmp0, ae_maxint(lccnt, n, _state), _state);
        rvectorsetlengthatleast(&state->sclagtmp1, ae_maxint(lccnt, n, _state), _state);
        sparsemv(&state->scla, x, &state->sclagtmp0, _state);
        for(i=0; i<=lccnt-1; i++)
        {
            
            /*
             * Prepare
             */
            v = state->sclagtmp0.ptr.p_double[i];
            vlag = lagalpha*lagxcmult->ptr.p_double[i];
            state->sclagtmp1.ptr.p_double[i] = (double)(0);
            
            /*
             * Primary Lagrangian term
             */
            vact = v;
            vd = (double)(1);
            *f = *f+vlag*vact;
            state->sclagtmp1.ptr.p_double[i] = state->sclagtmp1.ptr.p_double[i]+vlag*vd;
            
            /*
             * Quadratic augmentation term
             */
            if( state->hasal.ptr.p_bool[i]&&v<state->sclal.ptr.p_double[i] )
            {
                vact = v-state->sclal.ptr.p_double[i];
                *f = *f+0.5*nlcfsqp_augmentationfactor*vact*vact;
                state->sclagtmp1.ptr.p_double[i] = state->sclagtmp1.ptr.p_double[i]+nlcfsqp_augmentationfactor*vact;
            }
            if( state->hasau.ptr.p_bool[i]&&v>state->sclau.ptr.p_double[i] )
            {
                vact = v-state->sclau.ptr.p_double[i];
                *f = *f+0.5*nlcfsqp_augmentationfactor*vact*vact;
                state->sclagtmp1.ptr.p_double[i] = state->sclagtmp1.ptr.p_double[i]+nlcfsqp_augmentationfactor*vact;
            }
        }
        sparsemtv(&state->scla, &state->sclagtmp1, &state->sclagtmp0, _state);
        for(i=0; i<=n-1; i++)
        {
            g->ptr.p_double[i] = g->ptr.p_double[i]+state->sclagtmp0.ptr.p_double[i];
        }
    }
    
    /*
     * Lagrangian terms for nonlinear constraints
     */
    rvectorsetlengthatleast(&state->sclagtmp1, nnlc, _state);
    for(i=0; i<=nnlc-1; i++)
    {
        v = fi->ptr.p_double[1+i];
        vlag = lagalpha*lagxcmult->ptr.p_double[lccnt+i];
        vs = (double)(0);
        
        /*
         * Lagrangian term
         */
        vact = v;
        vd = (double)(1);
        *f = *f+vlag*vact;
        vs = vs+vlag*vd;
        
        /*
         * Augmentation term
         */
        vact = (double)(0);
        vd = (double)(0);
        if( state->hasnl.ptr.p_bool[i]&&v<state->scalednl.ptr.p_double[i] )
        {
            vact = state->scalednl.ptr.p_double[i]-v;
            vd = (double)(-1);
        }
        if( state->hasnu.ptr.p_bool[i]&&v>state->scalednu.ptr.p_double[i] )
        {
            vact = v-state->scalednu.ptr.p_double[i];
            vd = (double)(1);
        }
        *f = *f+0.5*nlcfsqp_augmentationfactor*vact*vact;
        vs = vs+nlcfsqp_augmentationfactor*vact*vd;
        
        /*
         * Modify gradient
         */
        j0 = sj->ridx.ptr.p_int[1+i];
        j1 = sj->ridx.ptr.p_int[1+i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            g->ptr.p_double[sj->idx.ptr.p_int[jj]] = g->ptr.p_double[sj->idx.ptr.p_int[jj]]+vs*sj->vals.ptr.p_double[jj];
        }
    }
}


/*************************************************************************
This function calculates Lagrangian of the problem (in scaled variables):
its value and gradient.

Additionally it also estimates violation of linear constraints at the point
as well as index of the most violated constraint
*************************************************************************/
static void nlcfsqp_penalizedfg(minfsqpstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     const sparsematrix* sj,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t lccnt;
    ae_int_t nnlc;
    double v;
    double vact;
    double vd;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;

    *f = 0.0;

    n = state->n;
    lccnt = state->lccnt;
    nnlc = state->nnlc;
    
    /*
     * Target function
     */
    rsetv(n, 0.0, g, _state);
    *f = fi->ptr.p_double[0];
    j0 = sj->ridx.ptr.p_int[0];
    j1 = sj->ridx.ptr.p_int[1]-1;
    for(jj=j0; jj<=j1; jj++)
    {
        g->ptr.p_double[sj->idx.ptr.p_int[jj]] = sj->vals.ptr.p_double[jj];
    }
    
    /*
     * penalties for linear constraints
     */
    if( lccnt>0 )
    {
        rvectorsetlengthatleast(&state->sclagtmp0, ae_maxint(lccnt, n, _state), _state);
        rvectorsetlengthatleast(&state->sclagtmp1, ae_maxint(lccnt, n, _state), _state);
        sparsemv(&state->scla, x, &state->sclagtmp0, _state);
        for(i=0; i<=lccnt-1; i++)
        {
            v = state->sclagtmp0.ptr.p_double[i];
            state->sclagtmp1.ptr.p_double[i] = (double)(0);
            if( state->hasal.ptr.p_bool[i]&&v<state->sclal.ptr.p_double[i] )
            {
                vact = v-state->sclal.ptr.p_double[i];
                *f = *f+0.5*nlcfsqp_augmentationfactor*vact*vact;
                state->sclagtmp1.ptr.p_double[i] = state->sclagtmp1.ptr.p_double[i]+nlcfsqp_augmentationfactor*vact;
            }
            if( state->hasau.ptr.p_bool[i]&&v>state->sclau.ptr.p_double[i] )
            {
                vact = v-state->sclau.ptr.p_double[i];
                *f = *f+0.5*nlcfsqp_augmentationfactor*vact*vact;
                state->sclagtmp1.ptr.p_double[i] = state->sclagtmp1.ptr.p_double[i]+nlcfsqp_augmentationfactor*vact;
            }
        }
        sparsemtv(&state->scla, &state->sclagtmp1, &state->sclagtmp0, _state);
        for(i=0; i<=n-1; i++)
        {
            g->ptr.p_double[i] = g->ptr.p_double[i]+state->sclagtmp0.ptr.p_double[i];
        }
    }
    
    /*
     * Lagrangian terms for nonlinear constraints
     */
    for(i=0; i<=nnlc-1; i++)
    {
        v = fi->ptr.p_double[1+i];
        vact = (double)(0);
        vd = (double)(0);
        if( state->hasnl.ptr.p_bool[i]&&v<state->scalednl.ptr.p_double[i] )
        {
            vact = state->scalednl.ptr.p_double[i]-v;
            vd = (double)(-1);
        }
        if( state->hasnu.ptr.p_bool[i]&&v>state->scalednu.ptr.p_double[i] )
        {
            vact = v-state->scalednu.ptr.p_double[i];
            vd = (double)(1);
        }
        *f = *f+0.5*nlcfsqp_augmentationfactor*vact*vact;
        j0 = sj->ridx.ptr.p_int[1+i];
        j1 = sj->ridx.ptr.p_int[1+i+1]-1;
        v = nlcfsqp_augmentationfactor*vact*vd;
        for(jj=j0; jj<=j1; jj++)
        {
            g->ptr.p_double[sj->idx.ptr.p_int[jj]] = g->ptr.p_double[sj->idx.ptr.p_int[jj]]+v*sj->vals.ptr.p_double[jj];
        }
    }
}


/*************************************************************************
This function calculates target and constraint violation
*************************************************************************/
static void nlcfsqp_targetandconstraints(minfsqpstate* state,
     const varsfuncjac* vfj,
     double* tgt,
     double* cv,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t lccnt;
    ae_int_t nnlc;
    double v;
    double vv;

    *tgt = 0.0;
    *cv = 0.0;

    lccnt = state->lccnt;
    nnlc = state->nnlc;
    
    /*
     * Target and constraints: initial state
     */
    *tgt = vfj->fi.ptr.p_double[0];
    *cv = (double)(0);
    
    /*
     * Constraint violation
     */
    if( lccnt>0 )
    {
        rvectorsetlengthatleast(&state->mftmp0, lccnt, _state);
        sparsemv(&state->scla, &vfj->x, &state->mftmp0, _state);
        for(i=0; i<=lccnt-1; i++)
        {
            v = state->mftmp0.ptr.p_double[i];
            if( state->hasal.ptr.p_bool[i]&&v<state->sclal.ptr.p_double[i] )
            {
                vv = state->sclal.ptr.p_double[i]-v;
                *tgt = *tgt+0.5*nlcfsqp_augmentationfactor*vv*vv;
                *cv = *cv+vv;
            }
            if( state->hasau.ptr.p_bool[i]&&v>state->sclau.ptr.p_double[i] )
            {
                vv = v-state->sclau.ptr.p_double[i];
                *tgt = *tgt+0.5*nlcfsqp_augmentationfactor*vv*vv;
                *cv = *cv+vv;
            }
        }
    }
    for(i=0; i<=nnlc-1; i++)
    {
        v = vfj->fi.ptr.p_double[1+i];
        if( state->hasnl.ptr.p_bool[i]&&v<state->scalednl.ptr.p_double[i] )
        {
            vv = state->scalednl.ptr.p_double[i]-v;
            *cv = *cv+vv;
            *tgt = *tgt+0.5*nlcfsqp_augmentationfactor*vv*vv;
        }
        if( state->hasnu.ptr.p_bool[i]&&v>state->scalednu.ptr.p_double[i] )
        {
            vv = v-state->scalednu.ptr.p_double[i];
            *cv = *cv+vv;
            *tgt = *tgt+0.5*nlcfsqp_augmentationfactor*vv*vv;
        }
    }
}


/*************************************************************************
This function calculates raw (unaugmented and smooth) Lagrangian
*************************************************************************/
static double nlcfsqp_rawlagrangian(minfsqpstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     /* Real    */ const ae_vector* lagbcmult,
     /* Real    */ const ae_vector* lagxcmult,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t lccnt;
    ae_int_t nnlc;
    double result;


    n = state->n;
    lccnt = state->lccnt;
    nnlc = state->nnlc;
    
    /*
     * Lagrangian: primary term
     */
    result = fi->ptr.p_double[0];
    
    /*
     * Lagrangian: handle box constraints
     *
     * NOTE: we do not add box constrained term to the merit function because
     *       box constraints are handled exactly and we do not violate them.
     */
    for(i=0; i<=n-1; i++)
    {
        result = result+lagbcmult->ptr.p_double[i]*x->ptr.p_double[i];
    }
    
    /*
     * Merit function: augmentation and penalty for linear constraints
     */
    if( lccnt>0 )
    {
        rvectorsetlengthatleast(&state->mftmp0, lccnt, _state);
        sparsemv(&state->scla, x, &state->mftmp0, _state);
        for(i=0; i<=lccnt-1; i++)
        {
            result = result+lagxcmult->ptr.p_double[i]*state->mftmp0.ptr.p_double[i];
        }
    }
    
    /*
     * Merit function: augmentation and penalty for nonlinear constraints
     */
    for(i=0; i<=nnlc-1; i++)
    {
        result = result+lagxcmult->ptr.p_double[lccnt+i]*fi->ptr.p_double[1+i];
    }
    return result;
}


/*************************************************************************
Returns k-th component of the trust region

  -- ALGLIB --
     Copyright 25.09.2023 by Bochkanov Sergey
*************************************************************************/
static double nlcfsqp_gettrustregionk(/* Real    */ const ae_vector* xcur,
     ae_int_t k,
     double trustrad,
     ae_state *_state)
{
    double result;


    result = trustrad*ae_maxreal(ae_fabs(xcur->ptr.p_double[k], _state), 1.0, _state);
    return result;
}


/*************************************************************************
Checks acceptability by the filter

  -- ALGLIB --
     Copyright 25.09.2023 by Bochkanov Sergey
*************************************************************************/
static ae_bool nlcfsqp_isacceptable(minfsqpstate* state,
     double tgt0,
     double h0,
     double tgt1,
     double h1,
     double predictedchangemodel,
     double predictedchangepenalty,
     ae_bool dotraceunacceptability,
     ae_state *_state)
{
    ae_bool result;


    if( ae_fp_greater_eq(tgt1,tgt0)&&ae_fp_greater_eq(h1,h0) )
    {
        
        /*
         * Neither the target nor constraints were improved
         */
        if( dotraceunacceptability )
        {
            ae_trace("> the filter rejects step: both meritf and constraint violations increase\n");
        }
        result = ae_false;
        return result;
    }
    if( ae_fp_less(predictedchangemodel,(double)(0))&&ae_fp_greater(tgt1-tgt0,nlcfsqp_sufficientdecreasesigma*predictedchangemodel) )
    {
        
        /*
         * The model is predicted to decrease, but it does not decrease fast enough, or does not decrease at all
         */
        if( dotraceunacceptability )
        {
            ae_trace("> the filter rejects step: sufficient decrease condition for meritf does not hold\n");
        }
        result = ae_false;
        return result;
    }
    if( !nlpfisacceptable(&state->filter, tgt0, h0, tgt1, h1, _state) )
    {
        
        /*
         * Dominated by another point
         */
        if( dotraceunacceptability )
        {
            if( state->filter.violationistoohigh )
            {
                ae_trace("> the filter rejects step: constraint violation cv=%0.9e is too high (higher than maxcv=%0.9e)\n",
                    (double)(h1),
                    (double)(state->filter.maxh));
            }
            else
            {
                ae_trace("> the filter rejects step: the new point is dominated\n");
            }
        }
        result = ae_false;
        return result;
    }
    result = ae_true;
    return result;
}


/*************************************************************************
Rescale target/constraints if gradients deviate from 1.0 too much. Compute
diagonal scales for the Hessian.

Parameters:
    Model               current function vector and Jacobian, already
                        scaled according to the present scaling.
    FirstScaling        if True, this function exactly normalizes everything,
                        except for the case when norms are less than 1.
                        If False, it tries to normalize but performs only
                        limited renormalization in order to avoid destabilization
                        of the process.

  -- ALGLIB --
     Copyright 23.02.2024 by Bochkanov Sergey
*************************************************************************/
static void nlcfsqp_autoscalevarstargetconstraints(minfsqpstate* state,
     varsfuncjac* model,
     ae_bool firstscaling,
     ae_bool dotrace,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nnlc;
    ae_int_t i;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t jj;
    double nrm2;
    double v;
    double multiplyby;
    double setscaleto;
    ae_bool wasrescale;
    ae_bool bigscaletriggered;
    ae_bool smallscaletriggered;


    ae_assert(!model->isdense, "FSQP: integrity check 3219 failed", _state);
    n = state->n;
    nnlc = state->nnlc;
    
    /*
     * Hessian scale
     */
    rallocv(n, &state->varscales, _state);
    for(i=0; i<=n-1; i++)
    {
        state->varscales.ptr.p_double[i] = nlcfsqp_gettrustregionk(&state->stepk.x, i, 1.0, _state);
    }
    
    /*
     * Target/constraints
     */
    wasrescale = ae_false;
    bigscaletriggered = ae_false;
    smallscaletriggered = ae_false;
    for(i=0; i<=nnlc; i++)
    {
        
        /*
         * Determine (a) multiplicative coefficient applied to function value
         * and Jacobian row, and (b) new value of the function scale.
         */
        j0 = model->sj.ridx.ptr.p_int[i];
        j1 = model->sj.ridx.ptr.p_int[i+1]-1;
        nrm2 = (double)(0);
        for(jj=j0; jj<=j1; jj++)
        {
            v = model->sj.vals.ptr.p_double[jj]*state->varscales.ptr.p_double[model->sj.idx.ptr.p_int[jj]];
            nrm2 = nrm2+v*v;
        }
        nrm2 = ae_sqrt(nrm2, _state);
        multiplyby = 1.0;
        setscaleto = state->fscaleszzz.ptr.p_double[i];
        if( ae_fp_greater_eq(nrm2,state->sqpbigscale) )
        {
            v = nrm2;
            if( !firstscaling )
            {
                v = ae_minreal(v, nlcfsqp_sqpmaxrescale, _state);
            }
            multiplyby = (double)1/v;
            setscaleto = state->fscaleszzz.ptr.p_double[i]*v;
            bigscaletriggered = ae_true;
        }
        if( ae_fp_less_eq(nrm2,state->sqpsmallscale)&&ae_fp_greater(state->fscaleszzz.ptr.p_double[i],1.0) )
        {
            v = nrm2;
            if( !firstscaling )
            {
                v = ae_maxreal(v, nlcfsqp_sqpminrescale, _state);
            }
            if( ae_fp_greater(state->fscaleszzz.ptr.p_double[i]*v,(double)(1)) )
            {
                multiplyby = (double)1/v;
                setscaleto = state->fscaleszzz.ptr.p_double[i]*v;
            }
            else
            {
                multiplyby = state->fscaleszzz.ptr.p_double[i];
                setscaleto = 1.0;
            }
            smallscaletriggered = ae_true;
        }
        if( ae_fp_neq(multiplyby,1.0) )
        {
            model->fi.ptr.p_double[i] = model->fi.ptr.p_double[i]*multiplyby;
            j0 = model->sj.ridx.ptr.p_int[i];
            j1 = model->sj.ridx.ptr.p_int[i+1]-1;
            for(jj=j0; jj<=j1; jj++)
            {
                model->sj.vals.ptr.p_double[jj] = model->sj.vals.ptr.p_double[jj]*multiplyby;
            }
            state->fscaleszzz.ptr.p_double[i] = setscaleto;
            state->invfscales.ptr.p_double[i] = (double)1/setscaleto;
            if( i>=1 )
            {
                if( state->hasnl.ptr.p_bool[i-1] )
                {
                    state->scalednl.ptr.p_double[i-1] = state->rawnl.ptr.p_double[i-1]/setscaleto;
                }
                if( state->hasnu.ptr.p_bool[i-1] )
                {
                    state->scalednu.ptr.p_double[i-1] = state->rawnu.ptr.p_double[i-1]/setscaleto;
                }
            }
            wasrescale = ae_true;
        }
    }
    if( wasrescale )
    {
        nlpfclear(&state->filter, _state);
        if( dotrace )
        {
            ae_trace("> target/constraints were rescaled due to gradient norms deviating too much from 1.0, filter cleared");
        }
        if( dotrace&&bigscaletriggered )
        {
            ae_trace(", downscaled");
        }
        if( smallscaletriggered )
        {
            state->sqpsmallscale = state->sqpsmallscale*nlcfsqp_sqpsmallscaledecrease;
            if( dotrace )
            {
                ae_trace(", upscaled and decreased the lower bound");
            }
        }
        if( dotrace )
        {
            ae_trace("\n");
        }
    }
}


/*************************************************************************
Returns effective iteration limit (MaxIts + modification), given additional
iterations for feasibility restoration:

* 0 if MaxIts=0
* otherwise: MaxIts if AddItsForCTol=0
* otherwise: MaxIts if not WerePointsBelowCTol
* otherwise: MaxIts if RepSclErr<=CTol
: otherwise: MaxIts+AddItsForCTol

  -- ALGLIB --
     Copyright 10.06.2025 by Bochkanov Sergey
*************************************************************************/
static ae_int_t nlcfsqp_geteffectivemaxits(minfsqpstate* state,
     ae_state *_state)
{
    ae_int_t result;


    result = critgetmaxits(&state->criteria, _state);
    if( (result==0||state->additsforctol==0)||!state->werepointsbelowctol )
    {
        return result;
    }
    if( ae_fp_greater(state->repsclerr,state->ctol) )
    {
        result = result+state->additsforctol;
    }
    return result;
}


/*************************************************************************
Convert sparse matrix to a dense one
*************************************************************************/
static void nlcfsqp_sparse2dense(const sparsematrix* s,
     /* Real    */ ae_matrix* d,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;


    ae_assert((s->matrixtype==1&&s->m>=1)&&s->n>=1, "SQP: integrity check 5541 failed", _state);
    rsetallocm(s->m, s->n, 0.0, d, _state);
    for(i=0; i<=s->m-1; i++)
    {
        j0 = s->ridx.ptr.p_int[i];
        j1 = s->ridx.ptr.p_int[i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            d->ptr.pp_double[i][s->idx.ptr.p_int[jj]] = s->vals.ptr.p_double[jj];
        }
    }
}


/*************************************************************************
Append matrix row multiplied by a scaling factor; silently switches
MatrixType=-10083 to MatrixType=1
*************************************************************************/
static void nlcfsqp_appendscaledsparserow(sparsematrix* dst,
     double alpha,
     const sparsematrix* src,
     ae_int_t rowidx,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t offs;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t jj;
    ae_int_t nnz;


    ae_assert((dst->matrixtype==1||dst->matrixtype==-10083)&&dst->ninitialized==dst->ridx.ptr.p_int[dst->m], "FSQP.AppendScaledSparseRow: Dst is not completely initialized", _state);
    m = dst->m;
    offs = dst->ridx.ptr.p_int[m];
    j0 = src->ridx.ptr.p_int[rowidx];
    j1 = src->ridx.ptr.p_int[rowidx+1]-1;
    nnz = j1-j0+1;
    dst->matrixtype = 1;
    igrowv(m+1, &dst->didx, _state);
    igrowv(m+1, &dst->uidx, _state);
    igrowv(m+2, &dst->ridx, _state);
    igrowv(dst->ridx.ptr.p_int[m]+nnz, &dst->idx, _state);
    rgrowv(dst->ridx.ptr.p_int[m]+nnz, &dst->vals, _state);
    offs = dst->ridx.ptr.p_int[m];
    for(jj=j0; jj<=j1; jj++)
    {
        dst->idx.ptr.p_int[offs] = src->idx.ptr.p_int[jj];
        dst->vals.ptr.p_double[offs] = alpha*src->vals.ptr.p_double[jj];
        offs = offs+1;
    }
    dst->ridx.ptr.p_int[m+1] = offs;
    dst->didx.ptr.p_int[m] = ibinarysearchlft(&dst->idx, dst->ridx.ptr.p_int[m], dst->ridx.ptr.p_int[m+1], m, _state);
    dst->uidx.ptr.p_int[m] = dst->didx.ptr.p_int[m]+icase2(dst->didx.ptr.p_int[m]<dst->ridx.ptr.p_int[m+1]&&dst->idx.ptr.p_int[dst->didx.ptr.p_int[m]]==m, 1, 0, _state);
    dst->ninitialized = dst->ridx.ptr.p_int[m+1];
    dst->m = m+1;
}


void _minfsqpsubsolver_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minfsqpsubsolver *p = (minfsqpsubsolver*)_p;
    ae_touch_ptr((void*)p);
    _vipmstate_init(&p->ipmsolver, _state, make_automatic);
    _ipm2state_init(&p->ipm2, _state, make_automatic);
    ae_vector_init(&p->ordering, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->elasticordering, 0, DT_INT, _state, make_automatic);
    _sparsematrix_init(&p->pattern, _state, make_automatic);
    _sparsematrix_init(&p->elasticpattern, _state, make_automatic);
    _sparsematrix_init(&p->upcomingpattern, _state, make_automatic);
    ae_vector_init(&p->curb, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->curbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->curbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cural, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->curau, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparserawlc, _state, make_automatic);
    _sparsematrix_init(&p->sparseefflc, _state, make_automatic);
    ae_vector_init(&p->lastlaglc, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->denseh, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dummy1, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->densedummy, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparsetmp, _state, make_automatic);
    _sparsematrix_init(&p->sparsedummy, _state, make_automatic);
    ae_vector_init(&p->tmps, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmporigin, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->retainnegativebclm, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->retainpositivebclm, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->rescalelag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->isinactive, 0, DT_BOOL, _state, make_automatic);
    ae_matrix_init(&p->tmpcorrc, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpdiag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->isbounded, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hessdiag, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->hesscorrc, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hesscorrd, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->hesssparsediag, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasal, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasau, 0, DT_BOOL, _state, make_automatic);
}


void _minfsqpsubsolver_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minfsqpsubsolver       *dst = (minfsqpsubsolver*)_dst;
    const minfsqpsubsolver *src = (const minfsqpsubsolver*)_src;
    _vipmstate_init_copy(&dst->ipmsolver, &src->ipmsolver, _state, make_automatic);
    _ipm2state_init_copy(&dst->ipm2, &src->ipm2, _state, make_automatic);
    ae_vector_init_copy(&dst->ordering, &src->ordering, _state, make_automatic);
    ae_vector_init_copy(&dst->elasticordering, &src->elasticordering, _state, make_automatic);
    _sparsematrix_init_copy(&dst->pattern, &src->pattern, _state, make_automatic);
    _sparsematrix_init_copy(&dst->elasticpattern, &src->elasticpattern, _state, make_automatic);
    _sparsematrix_init_copy(&dst->upcomingpattern, &src->upcomingpattern, _state, make_automatic);
    ae_vector_init_copy(&dst->curb, &src->curb, _state, make_automatic);
    ae_vector_init_copy(&dst->curbndl, &src->curbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->curbndu, &src->curbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->cural, &src->cural, _state, make_automatic);
    ae_vector_init_copy(&dst->curau, &src->curau, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparserawlc, &src->sparserawlc, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparseefflc, &src->sparseefflc, _state, make_automatic);
    ae_vector_init_copy(&dst->lastlaglc, &src->lastlaglc, _state, make_automatic);
    dst->penalty0 = src->penalty0;
    dst->penalty1 = src->penalty1;
    ae_matrix_init_copy(&dst->denseh, &src->denseh, _state, make_automatic);
    ae_vector_init_copy(&dst->dummy1, &src->dummy1, _state, make_automatic);
    ae_matrix_init_copy(&dst->densedummy, &src->densedummy, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsetmp, &src->sparsetmp, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsedummy, &src->sparsedummy, _state, make_automatic);
    ae_vector_init_copy(&dst->tmps, &src->tmps, _state, make_automatic);
    ae_vector_init_copy(&dst->tmporigin, &src->tmporigin, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp2, &src->tmp2, _state, make_automatic);
    ae_vector_init_copy(&dst->retainnegativebclm, &src->retainnegativebclm, _state, make_automatic);
    ae_vector_init_copy(&dst->retainpositivebclm, &src->retainpositivebclm, _state, make_automatic);
    ae_vector_init_copy(&dst->rescalelag, &src->rescalelag, _state, make_automatic);
    ae_vector_init_copy(&dst->isinactive, &src->isinactive, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpcorrc, &src->tmpcorrc, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpdiag, &src->tmpdiag, _state, make_automatic);
    ae_vector_init_copy(&dst->isbounded, &src->isbounded, _state, make_automatic);
    ae_vector_init_copy(&dst->hessdiag, &src->hessdiag, _state, make_automatic);
    ae_matrix_init_copy(&dst->hesscorrc, &src->hesscorrc, _state, make_automatic);
    ae_vector_init_copy(&dst->hesscorrd, &src->hesscorrd, _state, make_automatic);
    dst->hessrank = src->hessrank;
    _sparsematrix_init_copy(&dst->hesssparsediag, &src->hesssparsediag, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->hasal, &src->hasal, _state, make_automatic);
    ae_vector_init_copy(&dst->hasau, &src->hasau, _state, make_automatic);
}


void _minfsqpsubsolver_clear(void* _p)
{
    minfsqpsubsolver *p = (minfsqpsubsolver*)_p;
    ae_touch_ptr((void*)p);
    _vipmstate_clear(&p->ipmsolver);
    _ipm2state_clear(&p->ipm2);
    ae_vector_clear(&p->ordering);
    ae_vector_clear(&p->elasticordering);
    _sparsematrix_clear(&p->pattern);
    _sparsematrix_clear(&p->elasticpattern);
    _sparsematrix_clear(&p->upcomingpattern);
    ae_vector_clear(&p->curb);
    ae_vector_clear(&p->curbndl);
    ae_vector_clear(&p->curbndu);
    ae_vector_clear(&p->cural);
    ae_vector_clear(&p->curau);
    _sparsematrix_clear(&p->sparserawlc);
    _sparsematrix_clear(&p->sparseefflc);
    ae_vector_clear(&p->lastlaglc);
    ae_matrix_clear(&p->denseh);
    ae_vector_clear(&p->dummy1);
    ae_matrix_clear(&p->densedummy);
    _sparsematrix_clear(&p->sparsetmp);
    _sparsematrix_clear(&p->sparsedummy);
    ae_vector_clear(&p->tmps);
    ae_vector_clear(&p->tmporigin);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmp1);
    ae_vector_clear(&p->tmp2);
    ae_vector_clear(&p->retainnegativebclm);
    ae_vector_clear(&p->retainpositivebclm);
    ae_vector_clear(&p->rescalelag);
    ae_vector_clear(&p->isinactive);
    ae_matrix_clear(&p->tmpcorrc);
    ae_vector_clear(&p->tmpdiag);
    ae_vector_clear(&p->isbounded);
    ae_vector_clear(&p->hessdiag);
    ae_matrix_clear(&p->hesscorrc);
    ae_vector_clear(&p->hesscorrd);
    _sparsematrix_clear(&p->hesssparsediag);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->hasal);
    ae_vector_clear(&p->hasau);
}


void _minfsqpsubsolver_destroy(void* _p)
{
    minfsqpsubsolver *p = (minfsqpsubsolver*)_p;
    ae_touch_ptr((void*)p);
    _vipmstate_destroy(&p->ipmsolver);
    _ipm2state_destroy(&p->ipm2);
    ae_vector_destroy(&p->ordering);
    ae_vector_destroy(&p->elasticordering);
    _sparsematrix_destroy(&p->pattern);
    _sparsematrix_destroy(&p->elasticpattern);
    _sparsematrix_destroy(&p->upcomingpattern);
    ae_vector_destroy(&p->curb);
    ae_vector_destroy(&p->curbndl);
    ae_vector_destroy(&p->curbndu);
    ae_vector_destroy(&p->cural);
    ae_vector_destroy(&p->curau);
    _sparsematrix_destroy(&p->sparserawlc);
    _sparsematrix_destroy(&p->sparseefflc);
    ae_vector_destroy(&p->lastlaglc);
    ae_matrix_destroy(&p->denseh);
    ae_vector_destroy(&p->dummy1);
    ae_matrix_destroy(&p->densedummy);
    _sparsematrix_destroy(&p->sparsetmp);
    _sparsematrix_destroy(&p->sparsedummy);
    ae_vector_destroy(&p->tmps);
    ae_vector_destroy(&p->tmporigin);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmp1);
    ae_vector_destroy(&p->tmp2);
    ae_vector_destroy(&p->retainnegativebclm);
    ae_vector_destroy(&p->retainpositivebclm);
    ae_vector_destroy(&p->rescalelag);
    ae_vector_destroy(&p->isinactive);
    ae_matrix_destroy(&p->tmpcorrc);
    ae_vector_destroy(&p->tmpdiag);
    ae_vector_destroy(&p->isbounded);
    ae_vector_destroy(&p->hessdiag);
    ae_matrix_destroy(&p->hesscorrc);
    ae_vector_destroy(&p->hesscorrd);
    _sparsematrix_destroy(&p->hesssparsediag);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->hasal);
    ae_vector_destroy(&p->hasau);
}


void _minfsqpstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minfsqpstate *p = (minfsqpstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->scla, _state, make_automatic);
    ae_vector_init(&p->hasal, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasau, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->sclal, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sclau, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lcscales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lcsrcidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->scaledbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scaledbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasnl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasnu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->rawnl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawnu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scalednl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scalednu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fscaleszzz, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->invfscales, 0, DT_REAL, _state, make_automatic);
    _nlpstoppingcriteria_init(&p->criteria, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sj, _state, make_automatic);
    _varsfuncjac_init(&p->stepk, _state, make_automatic);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xprev, 0, DT_REAL, _state, make_automatic);
    _minfsqpsubsolver_init(&p->subsolver, _state, make_automatic);
    _xbfgshessian_init(&p->hess, _state, make_automatic);
    ae_vector_init(&p->lagbcmult, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagxcmult, 0, DT_REAL, _state, make_automatic);
    _varsfuncjac_init(&p->cand, _state, make_automatic);
    _varsfuncjac_init(&p->probe, _state, make_automatic);
    ae_vector_init(&p->dtrial, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dmu, 0, DT_REAL, _state, make_automatic);
    _nlpfilter_init(&p->filter, _state, make_automatic);
    ae_vector_init(&p->replagbc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replaglc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replagnlc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->varscales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmppend, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmphd, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dummylagbcmult, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dummylagxcmult, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmplaggrad, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpcandlaggrad, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmphdiag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sclagtmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sclagtmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->mftmp0, 0, DT_REAL, _state, make_automatic);
    _stimer_init(&p->timertotal, _state, make_automatic);
    _stimer_init(&p->timerqpgen, _state, make_automatic);
    _stimer_init(&p->timeripminit, _state, make_automatic);
    _stimer_init(&p->timeripmsolve, _state, make_automatic);
    _stimer_init(&p->timercallback, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
}


void _minfsqpstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minfsqpstate       *dst = (minfsqpstate*)_dst;
    const minfsqpstate *src = (const minfsqpstate*)_src;
    dst->n = src->n;
    dst->lccnt = src->lccnt;
    dst->nnlc = src->nnlc;
    dst->usedensebfgs = src->usedensebfgs;
    dst->inittrustrad = src->inittrustrad;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    _sparsematrix_init_copy(&dst->scla, &src->scla, _state, make_automatic);
    ae_vector_init_copy(&dst->hasal, &src->hasal, _state, make_automatic);
    ae_vector_init_copy(&dst->hasau, &src->hasau, _state, make_automatic);
    ae_vector_init_copy(&dst->sclal, &src->sclal, _state, make_automatic);
    ae_vector_init_copy(&dst->sclau, &src->sclau, _state, make_automatic);
    ae_vector_init_copy(&dst->lcscales, &src->lcscales, _state, make_automatic);
    ae_vector_init_copy(&dst->lcsrcidx, &src->lcsrcidx, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledbndl, &src->scaledbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledbndu, &src->scaledbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->hasnl, &src->hasnl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasnu, &src->hasnu, _state, make_automatic);
    ae_vector_init_copy(&dst->rawnl, &src->rawnl, _state, make_automatic);
    ae_vector_init_copy(&dst->rawnu, &src->rawnu, _state, make_automatic);
    ae_vector_init_copy(&dst->scalednl, &src->scalednl, _state, make_automatic);
    ae_vector_init_copy(&dst->scalednu, &src->scalednu, _state, make_automatic);
    ae_vector_init_copy(&dst->fscaleszzz, &src->fscaleszzz, _state, make_automatic);
    ae_vector_init_copy(&dst->invfscales, &src->invfscales, _state, make_automatic);
    dst->sqpbigscale = src->sqpbigscale;
    dst->sqpsmallscale = src->sqpsmallscale;
    _nlpstoppingcriteria_init_copy(&dst->criteria, &src->criteria, _state, make_automatic);
    dst->bfgsresetfreq = src->bfgsresetfreq;
    dst->additsforctol = src->additsforctol;
    dst->ctol = src->ctol;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sj, &src->sj, _state, make_automatic);
    dst->f = src->f;
    dst->needfisj = src->needfisj;
    dst->xupdated = src->xupdated;
    dst->werepointsbelowctol = src->werepointsbelowctol;
    dst->trustrad = src->trustrad;
    dst->trustradgrowth = src->trustradgrowth;
    dst->trustradstagnationcnt = src->trustradstagnationcnt;
    dst->fstagnationcnt = src->fstagnationcnt;
    _varsfuncjac_init_copy(&dst->stepk, &src->stepk, _state, make_automatic);
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->xprev, &src->xprev, _state, make_automatic);
    _minfsqpsubsolver_init_copy(&dst->subsolver, &src->subsolver, _state, make_automatic);
    _xbfgshessian_init_copy(&dst->hess, &src->hess, _state, make_automatic);
    ae_vector_init_copy(&dst->lagbcmult, &src->lagbcmult, _state, make_automatic);
    ae_vector_init_copy(&dst->lagxcmult, &src->lagxcmult, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->cand, &src->cand, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->probe, &src->probe, _state, make_automatic);
    ae_vector_init_copy(&dst->dtrial, &src->dtrial, _state, make_automatic);
    ae_vector_init_copy(&dst->d0, &src->d0, _state, make_automatic);
    ae_vector_init_copy(&dst->dmu, &src->dmu, _state, make_automatic);
    _nlpfilter_init_copy(&dst->filter, &src->filter, _state, make_automatic);
    ae_vector_init_copy(&dst->replagbc, &src->replagbc, _state, make_automatic);
    ae_vector_init_copy(&dst->replaglc, &src->replaglc, _state, make_automatic);
    ae_vector_init_copy(&dst->replagnlc, &src->replagnlc, _state, make_automatic);
    ae_vector_init_copy(&dst->varscales, &src->varscales, _state, make_automatic);
    ae_vector_init_copy(&dst->tmppend, &src->tmppend, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphd, &src->tmphd, _state, make_automatic);
    ae_vector_init_copy(&dst->dummylagbcmult, &src->dummylagbcmult, _state, make_automatic);
    ae_vector_init_copy(&dst->dummylagxcmult, &src->dummylagxcmult, _state, make_automatic);
    ae_vector_init_copy(&dst->tmplaggrad, &src->tmplaggrad, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpcandlaggrad, &src->tmpcandlaggrad, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphdiag, &src->tmphdiag, _state, make_automatic);
    ae_vector_init_copy(&dst->sclagtmp0, &src->sclagtmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->sclagtmp1, &src->sclagtmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->mftmp0, &src->mftmp0, _state, make_automatic);
    dst->repiterationscount = src->repiterationscount;
    dst->repterminationtype = src->repterminationtype;
    dst->repbcerr = src->repbcerr;
    dst->repbcidx = src->repbcidx;
    dst->replcerr = src->replcerr;
    dst->replcidx = src->replcidx;
    dst->repnlcerr = src->repnlcerr;
    dst->repnlcidx = src->repnlcidx;
    dst->repsclerr = src->repsclerr;
    _stimer_init_copy(&dst->timertotal, &src->timertotal, _state, make_automatic);
    _stimer_init_copy(&dst->timerqpgen, &src->timerqpgen, _state, make_automatic);
    _stimer_init_copy(&dst->timeripminit, &src->timeripminit, _state, make_automatic);
    _stimer_init_copy(&dst->timeripmsolve, &src->timeripmsolve, _state, make_automatic);
    _stimer_init_copy(&dst->timercallback, &src->timercallback, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
}


void _minfsqpstate_clear(void* _p)
{
    minfsqpstate *p = (minfsqpstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->s);
    _sparsematrix_clear(&p->scla);
    ae_vector_clear(&p->hasal);
    ae_vector_clear(&p->hasau);
    ae_vector_clear(&p->sclal);
    ae_vector_clear(&p->sclau);
    ae_vector_clear(&p->lcscales);
    ae_vector_clear(&p->lcsrcidx);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->scaledbndl);
    ae_vector_clear(&p->scaledbndu);
    ae_vector_clear(&p->hasnl);
    ae_vector_clear(&p->hasnu);
    ae_vector_clear(&p->rawnl);
    ae_vector_clear(&p->rawnu);
    ae_vector_clear(&p->scalednl);
    ae_vector_clear(&p->scalednu);
    ae_vector_clear(&p->fscaleszzz);
    ae_vector_clear(&p->invfscales);
    _nlpstoppingcriteria_clear(&p->criteria);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->fi);
    _sparsematrix_clear(&p->sj);
    _varsfuncjac_clear(&p->stepk);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->xprev);
    _minfsqpsubsolver_clear(&p->subsolver);
    _xbfgshessian_clear(&p->hess);
    ae_vector_clear(&p->lagbcmult);
    ae_vector_clear(&p->lagxcmult);
    _varsfuncjac_clear(&p->cand);
    _varsfuncjac_clear(&p->probe);
    ae_vector_clear(&p->dtrial);
    ae_vector_clear(&p->d0);
    ae_vector_clear(&p->dmu);
    _nlpfilter_clear(&p->filter);
    ae_vector_clear(&p->replagbc);
    ae_vector_clear(&p->replaglc);
    ae_vector_clear(&p->replagnlc);
    ae_vector_clear(&p->varscales);
    ae_vector_clear(&p->tmppend);
    ae_vector_clear(&p->tmphd);
    ae_vector_clear(&p->dummylagbcmult);
    ae_vector_clear(&p->dummylagxcmult);
    ae_vector_clear(&p->tmplaggrad);
    ae_vector_clear(&p->tmpcandlaggrad);
    ae_vector_clear(&p->tmphdiag);
    ae_vector_clear(&p->sclagtmp0);
    ae_vector_clear(&p->sclagtmp1);
    ae_vector_clear(&p->mftmp0);
    _stimer_clear(&p->timertotal);
    _stimer_clear(&p->timerqpgen);
    _stimer_clear(&p->timeripminit);
    _stimer_clear(&p->timeripmsolve);
    _stimer_clear(&p->timercallback);
    _rcommstate_clear(&p->rstate);
}


void _minfsqpstate_destroy(void* _p)
{
    minfsqpstate *p = (minfsqpstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->s);
    _sparsematrix_destroy(&p->scla);
    ae_vector_destroy(&p->hasal);
    ae_vector_destroy(&p->hasau);
    ae_vector_destroy(&p->sclal);
    ae_vector_destroy(&p->sclau);
    ae_vector_destroy(&p->lcscales);
    ae_vector_destroy(&p->lcsrcidx);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->scaledbndl);
    ae_vector_destroy(&p->scaledbndu);
    ae_vector_destroy(&p->hasnl);
    ae_vector_destroy(&p->hasnu);
    ae_vector_destroy(&p->rawnl);
    ae_vector_destroy(&p->rawnu);
    ae_vector_destroy(&p->scalednl);
    ae_vector_destroy(&p->scalednu);
    ae_vector_destroy(&p->fscaleszzz);
    ae_vector_destroy(&p->invfscales);
    _nlpstoppingcriteria_destroy(&p->criteria);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->fi);
    _sparsematrix_destroy(&p->sj);
    _varsfuncjac_destroy(&p->stepk);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->xprev);
    _minfsqpsubsolver_destroy(&p->subsolver);
    _xbfgshessian_destroy(&p->hess);
    ae_vector_destroy(&p->lagbcmult);
    ae_vector_destroy(&p->lagxcmult);
    _varsfuncjac_destroy(&p->cand);
    _varsfuncjac_destroy(&p->probe);
    ae_vector_destroy(&p->dtrial);
    ae_vector_destroy(&p->d0);
    ae_vector_destroy(&p->dmu);
    _nlpfilter_destroy(&p->filter);
    ae_vector_destroy(&p->replagbc);
    ae_vector_destroy(&p->replaglc);
    ae_vector_destroy(&p->replagnlc);
    ae_vector_destroy(&p->varscales);
    ae_vector_destroy(&p->tmppend);
    ae_vector_destroy(&p->tmphd);
    ae_vector_destroy(&p->dummylagbcmult);
    ae_vector_destroy(&p->dummylagxcmult);
    ae_vector_destroy(&p->tmplaggrad);
    ae_vector_destroy(&p->tmpcandlaggrad);
    ae_vector_destroy(&p->tmphdiag);
    ae_vector_destroy(&p->sclagtmp0);
    ae_vector_destroy(&p->sclagtmp1);
    ae_vector_destroy(&p->mftmp0);
    _stimer_destroy(&p->timertotal);
    _stimer_destroy(&p->timerqpgen);
    _stimer_destroy(&p->timeripminit);
    _stimer_destroy(&p->timeripmsolve);
    _stimer_destroy(&p->timercallback);
    _rcommstate_destroy(&p->rstate);
}


#endif
#if defined(AE_COMPILE_MINLM) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
                IMPROVED LEVENBERG-MARQUARDT METHOD FOR
                 NON-LINEAR LEAST SQUARES OPTIMIZATION

DESCRIPTION:
This function is used to find minimum of function which is represented  as
sum of squares:
    F(x) = f[0]^2(x[0],...,x[n-1]) + ... + f[m-1]^2(x[0],...,x[n-1])
using value of function vector f[] and Jacobian of f[].


REQUIREMENTS:
This algorithm will request following information during its operation:

* function vector f[] at given point X
* function vector f[] and Jacobian of f[] (simultaneously) at given point

There are several overloaded versions of  MinLMOptimize()  function  which
correspond  to  different LM-like optimization algorithms provided by this
unit. You should choose version which accepts fvec()  and jac() callbacks.
First  one  is used to calculate f[] at given point, second one calculates
f[] and Jacobian df[i]/dx[j].

You can try to initialize MinLMState structure with VJ  function and  then
use incorrect version  of  MinLMOptimize()  (for  example,  version  which
works  with  general  form function and does not provide Jacobian), but it
will  lead  to  exception  being  thrown  after first attempt to calculate
Jacobian.


USAGE:
1. User initializes algorithm state with MinLMCreateVJ() call
2. User tunes solver parameters with MinLMSetCond(),  MinLMSetStpMax() and
   other functions
3. User calls MinLMOptimize() function which  takes algorithm  state   and
   callback functions.
4. User calls MinLMResults() to get solution
5. Optionally, user may call MinLMRestartFrom() to solve  another  problem
   with same N/M but another starting point and/or another function.
   MinLMRestartFrom() allows to reuse already initialized structure.


INPUT PARAMETERS:
    N       -   dimension, N>1
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    M       -   number of functions f[i]
    X       -   initial solution, array[0..N-1]

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTES:
1. you may tune stopping conditions with MinLMSetCond() function
2. if target function contains exp() or other fast growing functions,  and
   optimization algorithm makes too large steps which leads  to  overflow,
   use MinLMSetStpMax() function to bound algorithm's steps.

  -- ALGLIB --
     Copyright 30.03.2009 by Bochkanov Sergey
*************************************************************************/
void minlmcreatevj(ae_int_t n,
     ae_int_t m,
     /* Real    */ const ae_vector* x,
     minlmstate* state,
     ae_state *_state)
{

    _minlmstate_clear(state);

    ae_assert(n>=1, "MinLMCreateVJ: N<1!", _state);
    ae_assert(m>=1, "MinLMCreateVJ: M<1!", _state);
    ae_assert(x->cnt>=n, "MinLMCreateVJ: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, n, _state), "MinLMCreateVJ: X contains infinite or NaN values!", _state);
    
    /*
     * initialize, check parameters
     */
    state->protocolversion = 1;
    state->teststep = (double)(0);
    state->n = n;
    state->m = m;
    state->algomode = 1;
    state->nonmonotoniccnt = 0;
    state->formulatype = 3;
    
    /*
     * second stage of initialization
     */
    minlm_lmprepare(n, m, ae_false, state, _state);
    minlmsetacctype(state, 0, _state);
    minlmsetcond(state, (double)(0), 0, _state);
    minlmsetxrep(state, ae_false, _state);
    minlmsetstpmax(state, (double)(0), _state);
    minlmrestartfrom(state, x, _state);
}


/*************************************************************************
                IMPROVED LEVENBERG-MARQUARDT METHOD FOR
                 NON-LINEAR LEAST SQUARES OPTIMIZATION

DESCRIPTION:
This function is used to find minimum of function which is represented  as
sum of squares:
    F(x) = f[0]^2(x[0],...,x[n-1]) + ... + f[m-1]^2(x[0],...,x[n-1])
using value of function vector f[] only. Finite differences  are  used  to
calculate Jacobian.


REQUIREMENTS:
This algorithm will request following information during its operation:
* function vector f[] at given point X

There are several overloaded versions of  MinLMOptimize()  function  which
correspond  to  different LM-like optimization algorithms provided by this
unit. You should choose version which accepts fvec() callback.

You can try to initialize MinLMState structure with VJ  function and  then
use incorrect version  of  MinLMOptimize()  (for  example,  version  which
works with general form function and does not accept function vector), but
it will  lead  to  exception being thrown after first attempt to calculate
Jacobian.


USAGE:
1. User initializes algorithm state with MinLMCreateV() call
2. User tunes solver parameters with MinLMSetCond(),  MinLMSetStpMax() and
   other functions
3. User calls MinLMOptimize() function which  takes algorithm  state   and
   callback functions.
4. User calls MinLMResults() to get solution
5. Optionally, user may call MinLMRestartFrom() to solve  another  problem
   with same N/M but another starting point and/or another function.
   MinLMRestartFrom() allows to reuse already initialized structure.


INPUT PARAMETERS:
    N       -   dimension, N>1
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    M       -   number of functions f[i]
    X       -   initial solution, array[0..N-1]
    DiffStep-   differentiation step, >0. By  default,  symmetric  3-point
                formula which provides good accuracy is used.  It  can  be
                changed to a faster but  less  precise  2-point  one  with
                minlmsetnumdiff() function.

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

See also MinLMIteration, MinLMResults.

NOTES:
1. you may tune stopping conditions with MinLMSetCond() function
2. if target function contains exp() or other fast growing functions,  and
   optimization algorithm makes too large steps which leads  to  overflow,
   use MinLMSetStpMax() function to bound algorithm's steps.

  -- ALGLIB --
     Copyright 30.03.2009 by Bochkanov Sergey
*************************************************************************/
void minlmcreatev(ae_int_t n,
     ae_int_t m,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minlmstate* state,
     ae_state *_state)
{

    _minlmstate_clear(state);

    ae_assert(ae_isfinite(diffstep, _state), "MinLMCreateV: DiffStep is not finite!", _state);
    ae_assert(ae_fp_greater(diffstep,(double)(0)), "MinLMCreateV: DiffStep<=0!", _state);
    ae_assert(n>=1, "MinLMCreateV: N<1!", _state);
    ae_assert(m>=1, "MinLMCreateV: M<1!", _state);
    ae_assert(x->cnt>=n, "MinLMCreateV: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, n, _state), "MinLMCreateV: X contains infinite or NaN values!", _state);
    
    /*
     * Initialize
     */
    state->protocolversion = 1;
    state->teststep = (double)(0);
    state->n = n;
    state->m = m;
    state->algomode = 0;
    state->diffstep = diffstep;
    state->nonmonotoniccnt = 0;
    state->formulatype = 3;
    
    /*
     * Second stage of initialization
     */
    minlm_lmprepare(n, m, ae_false, state, _state);
    minlmsetacctype(state, 1, _state);
    minlmsetcond(state, (double)(0), 0, _state);
    minlmsetxrep(state, ae_false, _state);
    minlmsetstpmax(state, (double)(0), _state);
    minlmrestartfrom(state, x, _state);
}


/*************************************************************************
This function sets stopping conditions for Levenberg-Marquardt optimization
algorithm.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsX    -   >=0
                The subroutine finishes its work if  on  k+1-th  iteration
                the condition |v|<=EpsX is fulfilled, where:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - ste pvector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinLMSetScale()
                Recommended values: 1E-9 ... 1E-12.
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations   is    unlimited.   Only   Levenberg-Marquardt
                iterations  are  counted  (L-BFGS/CG  iterations  are  NOT
                counted because their cost is very low compared to that of
                LM).

Passing  EpsX=0  and  MaxIts=0  (simultaneously)  will  lead  to automatic
stopping criterion selection (small EpsX).

NOTE: it is not recommended to set large EpsX (say, 0.001). Because LM  is
      a second-order method, it performs very precise steps anyway.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlmsetcond(minlmstate* state,
     double epsx,
     ae_int_t maxits,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsx, _state), "MinLMSetCond: EpsX is not finite number!", _state);
    ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "MinLMSetCond: negative EpsX!", _state);
    ae_assert(maxits>=0, "MinLMSetCond: negative MaxIts!", _state);
    if( ae_fp_eq(epsx,(double)(0))&&maxits==0 )
    {
        epsx = 1.0E-9;
    }
    state->epsx = epsx;
    state->maxits = maxits;
}


/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinLMOptimize(). Both Levenberg-Marquardt and internal  L-BFGS
iterations are reported.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlmsetxrep(minlmstate* state, ae_bool needxrep, ae_state *_state)
{


    state->xrep = needxrep;
}


/*************************************************************************
This function sets maximum step length

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    StpMax  -   maximum step length, >=0. Set StpMax to 0.0,  if you don't
                want to limit step length.

Use this subroutine when you optimize target function which contains exp()
or  other  fast  growing  functions,  and optimization algorithm makes too
large  steps  which  leads  to overflow. This function allows us to reject
steps  that  are  too  large  (and  therefore  expose  us  to the possible
overflow) without actually calculating function value at the x+stp*d.

NOTE: non-zero StpMax leads to moderate  performance  degradation  because
intermediate  step  of  preconditioned L-BFGS optimization is incompatible
with limits on step size.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minlmsetstpmax(minlmstate* state, double stpmax, ae_state *_state)
{


    ae_assert(ae_isfinite(stpmax, _state), "MinLMSetStpMax: StpMax is not finite!", _state);
    ae_assert(ae_fp_greater_eq(stpmax,(double)(0)), "MinLMSetStpMax: StpMax<0!", _state);
    state->stpmax = stpmax;
}


/*************************************************************************
This function sets scaling coefficients for LM optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Generally, scale is NOT considered to be a form of preconditioner.  But LM
optimizer is unique in that it uses scaling matrix both  in  the  stopping
condition tests and as Marquardt damping factor.

Proper scaling is very important for the algorithm performance. It is less
important for the quality of results, but still has some influence (it  is
easier  to  converge  when  variables  are  properly  scaled, so premature
stopping is possible when very badly scalled variables are  combined  with
relaxed stopping conditions).

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minlmsetscale(minlmstate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(s->cnt>=state->n, "MinLMSetScale: Length(S)<N", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "MinLMSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "MinLMSetScale: S contains zero elements", _state);
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function sets boundary constraints for LM optimizer

Boundary constraints are inactive by default (after initial creation).
They are preserved until explicitly turned off with another SetBC() call.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF (latter is recommended because
                it will allow solver to use better algorithm).
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF (latter is recommended because
                it will allow solver to use better algorithm).

NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

NOTE 2: this solver has following useful properties:
* bound constraints are always satisfied exactly
* function is evaluated only INSIDE area specified by bound constraints
  or at its boundary

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minlmsetbc(minlmstate* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_assert(bndl->cnt>=n, "MinLMSetBC: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=n, "MinLMSetBC: Length(BndU)<N", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "MinLMSetBC: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "MinLMSetBC: BndU contains NAN or -INF", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->havebndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        state->havebndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function sets general linear constraints for LM optimizer

Linear constraints are inactive by default (after initial creation).  They
are preserved until explicitly turned off with another minlmsetlc() call.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

IMPORTANT: if you have linear constraints, it is strongly  recommended  to
           set scale of variables with minlmsetscale(). QP solver which is
           used to calculate linearly constrained steps heavily relies  on
           good scaling of input problems.

IMPORTANT: solvers created with minlmcreatefgh()  do  not  support  linear
           constraints.
           
NOTE: linear  (non-bound)  constraints are satisfied only approximately  -
      there  always  exists some violation due  to  numerical  errors  and
      algorithmic limitations.

NOTE: general linear constraints  add  significant  overhead  to  solution
      process. Although solver performs roughly same amount of  iterations
      (when compared  with  similar  box-only  constrained  problem), each
      iteration   now    involves  solution  of  linearly  constrained  QP
      subproblem, which requires ~3-5 times more Cholesky  decompositions.
      Thus, if you can reformulate your problem in such way  this  it  has
      only box constraints, it may be beneficial to do so.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minlmsetlc(minlmstate* state,
     /* Real    */ const ae_matrix* c,
     /* Integer */ const ae_vector* ct,
     ae_int_t k,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    
    /*
     * First, check for errors in the inputs
     */
    ae_assert(k>=0, "MinLMSetLC: K<0", _state);
    ae_assert(c->cols>=n+1||k==0, "MinLMSetLC: Cols(C)<N+1", _state);
    ae_assert(c->rows>=k, "MinLMSetLC: Rows(C)<K", _state);
    ae_assert(ct->cnt>=k, "MinLMSetLC: Length(CT)<K", _state);
    ae_assert(apservisfinitematrix(c, k, n+1, _state), "MinLMSetLC: C contains infinite or NaN values!", _state);
    
    /*
     * Handle zero K
     */
    if( k==0 )
    {
        state->nec = 0;
        state->nic = 0;
        return;
    }
    
    /*
     * Equality constraints are stored first, in the upper
     * NEC rows of State.CLEIC matrix. Inequality constraints
     * are stored in the next NIC rows.
     *
     * NOTE: we convert inequality constraints to the form
     * A*x<=b before copying them.
     */
    rmatrixsetlengthatleast(&state->cleic, k, n+1, _state);
    state->nec = 0;
    state->nic = 0;
    for(i=0; i<=k-1; i++)
    {
        if( ct->ptr.p_int[i]==0 )
        {
            ae_v_move(&state->cleic.ptr.pp_double[state->nec][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
            state->nec = state->nec+1;
        }
    }
    for(i=0; i<=k-1; i++)
    {
        if( ct->ptr.p_int[i]!=0 )
        {
            if( ct->ptr.p_int[i]>0 )
            {
                ae_v_moveneg(&state->cleic.ptr.pp_double[state->nec+state->nic][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
            }
            else
            {
                ae_v_move(&state->cleic.ptr.pp_double[state->nec+state->nic][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
            }
            state->nic = state->nic+1;
        }
    }
}


/*************************************************************************
This function is used to change acceleration settings

You can choose between three acceleration strategies:
* AccType=0, no acceleration.
* AccType=1, secant updates are used to update quadratic model after  each
  iteration. After fixed number of iterations (or after  model  breakdown)
  we  recalculate  quadratic  model  using  analytic  Jacobian  or  finite
  differences. Number of secant-based iterations depends  on  optimization
  settings: about 3 iterations - when we have analytic Jacobian, up to 2*N
  iterations - when we use finite differences to calculate Jacobian.

AccType=1 is recommended when Jacobian  calculation  cost is prohibitively
high (several Mx1 function vector calculations  followed  by  several  NxN
Cholesky factorizations are faster than calculation of one M*N  Jacobian).
It should also be used when we have no Jacobian, because finite difference
approximation takes too much time to compute.

Table below list  optimization  protocols  (XYZ  protocol  corresponds  to
MinLMCreateXYZ) and acceleration types they support (and use by  default).

ACCELERATION TYPES SUPPORTED BY OPTIMIZATION PROTOCOLS:

protocol    0   1   comment
V           +   +
VJ          +   +
FGH         +

DEFAULT VALUES:

protocol    0   1   comment
V               x   without acceleration it is so slooooooooow
VJ          x
FGH         x

NOTE: this  function should be called before optimization. Attempt to call
it during algorithm iterations may result in unexpected behavior.

NOTE: attempt to call this function with unsupported protocol/acceleration
combination will result in exception being thrown.

  -- ALGLIB --
     Copyright 14.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlmsetacctype(minlmstate* state,
     ae_int_t acctype,
     ae_state *_state)
{


    ae_assert((acctype==0||acctype==1)||acctype==2, "MinLMSetAccType: incorrect AccType!", _state);
    if( acctype==2 )
    {
        acctype = 0;
    }
    if( acctype==0 )
    {
        state->maxmodelage = 0;
        state->makeadditers = ae_false;
        return;
    }
    if( acctype==1 )
    {
        if( state->algomode==0 )
        {
            state->maxmodelage = 2*state->n;
        }
        else
        {
            state->maxmodelage = minlm_smallmodelage;
        }
        state->makeadditers = ae_false;
        return;
    }
}


/*************************************************************************
This function is used  to  activate/deactivate  nonmonotonic  steps.  Such
steps may improve  convergence  on  noisy  problems  or  ones  with  minor
smoothness defects.

In its standard mode, LM solver compares value at the trial   point   f[1]
with the value at the current point f[0]. Only steps that decrease f() are
accepted.

When the nonmonotonic mode is activated, f[1]  is  compared  with  maximum
over several previous  locations:  max(f[0],f[-1],...,f[-CNT]).  We  still
accept only steps that decrease  f(),  however  our  reference  value  has
changed. The net results is that f[1]>f[0] are now allowed.

Nonmonotonic steps can help to handle minor defects in the objective (e.g.
small  noise,  discontinuous  jumps  or  nonsmoothness).  However,  it  is
important  that  the  overall  shape  of  the  problem  is  still  smooth.
It  may  also  help  to  minimize  perfectly  smooth  targets with complex
geometries by allowing to jump through curved valleys.

However, sometimes nonmonotonic steps degrade convergence by  allowing  an
optimizer to wander too far away from the solution, so this feature should
be used only after careful testing.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    Cnt     -   nonmonotonic memory length, Cnt>=0:
                * 0 for traditional monotonic steps
                * 2..3 is recommended for the nonmonotonic optimization

  -- ALGLIB --
     Copyright 07.04.2024 by Bochkanov Sergey
*************************************************************************/
void minlmsetnonmonotonicsteps(minlmstate* state,
     ae_int_t cnt,
     ae_state *_state)
{


    ae_assert(cnt>=0, "MinLMSetNonmonotonicSteps: incorrect AccType!", _state);
    state->nonmonotoniccnt = cnt;
}


/*************************************************************************
This function sets specific finite  difference  formula  to  be  used  for
numerical differentiation.

It works only for optimizers created  with  minlmcreatev()  function;   in
other cases it has no effect.

INPUT PARAMETERS:
    State       -   structure previously allocated with MinLMCreateV() call.
    FormulaType -   formula type:
                    * 3 for a 3-point formula, which is also  known  as  a
                      symmetric difference quotient (the formula  actually
                      uses only two function values per variable:  at  x+h
                      and x-h). A good choice for medium-accuracy  setups,
                      a default option.
                    * 2 for a forward (or backward, depending  on variable
                      bounds)  finite   difference  (f(x+h)-f(x))/h.  This
                      formula has the lowest accuracy. However, it  is  4x
                      faster than the 5-point formula and 2x  faster  than
                      the 3-point one because, in addition to the  central
                      value f(x), it needs only  one  additional  function
                      evaluation per variable.
    

  -- ALGLIB --
     Copyright 03.12.2024 by Bochkanov Sergey
*************************************************************************/
void minlmsetnumdiff(minlmstate* state,
     ae_int_t formulatype,
     ae_state *_state)
{


    ae_assert(formulatype==2||formulatype==3, "MinLMSetNumDiff: unexpected formula type", _state);
    state->formulatype = formulatype;
}


/*************************************************************************

CALLBACK PARALLELISM

The MINLM optimizer supports parallel parallel  numerical  differentiation
('callback parallelism'). This feature, which  is  present  in  commercial
ALGLIB  editions,  greatly   accelerates   optimization   with   numerical
differentiation of an expensive target functions.

Callback parallelism is usually  beneficial  when  computing  a  numerical
gradient requires more than several  milliseconds.  In this case  the  job
of computing individual gradient components can be split between  multiple
threads. Even inexpensive targets can benefit  from  parallelism,  if  you
have many variables.

If you solve a curve fitting problem, i.e. the function vector is actually
the same function computed at different points of a data points space,  it
may  be  better  to  use  an LSFIT curve fitting solver, which offers more
fine-grained parallelism due to knowledge of  the  problem  structure.  In
particular, it can accelerate both numerical differentiation  and problems
with user-supplied gradients.

ALGLIB Reference Manual, 'Working with commercial  version' section, tells
how to activate callback parallelism for your programming language.

  -- ALGLIB --
     Copyright 03.12.2023 by Bochkanov Sergey
*************************************************************************/
ae_bool minlmiteration(minlmstate* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_bool bflag;
    ae_int_t iflag;
    double v;
    double xl;
    double xr;
    double s;
    double t;
    double fnew;
    ae_int_t i;
    ae_int_t k;
    ae_bool dotrace;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        n = state->rstate.ia.ptr.p_int[0];
        m = state->rstate.ia.ptr.p_int[1];
        iflag = state->rstate.ia.ptr.p_int[2];
        i = state->rstate.ia.ptr.p_int[3];
        k = state->rstate.ia.ptr.p_int[4];
        bflag = state->rstate.ba.ptr.p_bool[0];
        dotrace = state->rstate.ba.ptr.p_bool[1];
        v = state->rstate.ra.ptr.p_double[0];
        xl = state->rstate.ra.ptr.p_double[1];
        xr = state->rstate.ra.ptr.p_double[2];
        s = state->rstate.ra.ptr.p_double[3];
        t = state->rstate.ra.ptr.p_double[4];
        fnew = state->rstate.ra.ptr.p_double[5];
    }
    else
    {
        n = 359;
        m = -58;
        iflag = -919;
        i = -909;
        k = 81;
        bflag = ae_true;
        dotrace = ae_false;
        v = -788.0;
        xl = 809.0;
        xr = 205.0;
        s = -838.0;
        t = 939.0;
        fnew = -526.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    if( state->rstate.stage==6 )
    {
        goto lbl_6;
    }
    if( state->rstate.stage==7 )
    {
        goto lbl_7;
    }
    if( state->rstate.stage==8 )
    {
        goto lbl_8;
    }
    if( state->rstate.stage==9 )
    {
        goto lbl_9;
    }
    if( state->rstate.stage==10 )
    {
        goto lbl_10;
    }
    if( state->rstate.stage==11 )
    {
        goto lbl_11;
    }
    if( state->rstate.stage==12 )
    {
        goto lbl_12;
    }
    if( state->rstate.stage==13 )
    {
        goto lbl_13;
    }
    if( state->rstate.stage==14 )
    {
        goto lbl_14;
    }
    if( state->rstate.stage==15 )
    {
        goto lbl_15;
    }
    if( state->rstate.stage==16 )
    {
        goto lbl_16;
    }
    if( state->rstate.stage==17 )
    {
        goto lbl_17;
    }
    if( state->rstate.stage==18 )
    {
        goto lbl_18;
    }
    if( state->rstate.stage==19 )
    {
        goto lbl_19;
    }
    if( state->rstate.stage==20 )
    {
        goto lbl_20;
    }
    if( state->rstate.stage==21 )
    {
        goto lbl_21;
    }
    if( state->rstate.stage==22 )
    {
        goto lbl_22;
    }
    if( state->rstate.stage==23 )
    {
        goto lbl_23;
    }
    if( state->rstate.stage==24 )
    {
        goto lbl_24;
    }
    if( state->rstate.stage==25 )
    {
        goto lbl_25;
    }
    
    /*
     * Routine body
     */
    
    /*
     * prepare
     */
    dotrace = ae_is_trace_enabled("LM");
    state->dotimers = dotrace;
    if( state->dotimers )
    {
        state->tstart = ae_tickcount();
        state->tqp = 0;
    }
    n = state->n;
    m = state->m;
    state->repiterationscount = 0;
    state->repterminationtype = 0;
    state->repnfunc = 0;
    state->repnjac = 0;
    state->repngrad = 0;
    state->repnhess = 0;
    state->repncholesky = 0;
    state->userterminationneeded = ae_false;
    state->fbase = ae_maxrealnumber;
    if( m>0 )
    {
        smoothnessmonitorinit(&state->smonitor, &state->s, n, m, ae_false, _state);
    }
    for(i=0; i<=n-1; i++)
    {
        state->lastscaleused.ptr.p_double[i] = state->s.ptr.p_double[i];
    }
    
    /*
     * Allocate temporaries, as mandated by the V2 protocol
     */
    if( state->protocolversion==2 )
    {
        rallocm(m, n, &state->tmpj1, _state);
        rallocv(m, &state->tmpf1, _state);
        rallocv(n, &state->tmpg1, _state);
        rallocv(n, &state->tmpx1, _state);
    }
    
    /*
     * Trace output (if needed)
     */
    if( dotrace )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("//  LM SOLVER STARTED                                                                             //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("N             = %6d\n",
            (int)(n));
        ae_trace("M             = %6d\n",
            (int)(m));
    }
    
    /*
     * Prepare LM step finder and enforce/check feasibility of constraints
     */
    if( !minlm_minlmstepfinderinit(&state->finderstate, n, m, state->maxmodelage, &state->xbase, &state->bndl, &state->bndu, &state->cleic, state->nec, state->nic, &state->s, state->stpmax, state->epsx, _state) )
    {
        state->repterminationtype = -3;
        result = ae_false;
        return result;
    }
    
    /*
     * set constraints for obsolete QP solver
     */
    minqpsetbc(&state->qpstate, &state->bndl, &state->bndu, _state);
    
    /*
     *  Check correctness of the analytic Jacobian
     */
    if( state->protocolversion==1 )
    {
        minlm_clearrequestfields(state, _state);
    }
    if( !(state->algomode==1&&ae_fp_greater(state->teststep,(double)(0))) )
    {
        goto lbl_26;
    }
    ae_assert(m>0, "MinLM: integrity check failed", _state);
lbl_28:
    if( !smoothnessmonitorcheckgradientatx0(&state->smonitor, &state->xbase, &state->s, &state->bndl, &state->bndu, ae_true, state->teststep, _state) )
    {
        goto lbl_29;
    }
    if( state->protocolversion!=2 )
    {
        goto lbl_30;
    }
    
    /*
     * Use V2 reverse communication protocol
     */
    state->requesttype = 2;
    state->querysize = 1;
    state->queryfuncs = m;
    state->queryvars = n;
    state->querydim = 0;
    rcopyallocv(n, &state->smonitor.x, &state->querydata, _state);
    rallocv(m, &state->replyfi, _state);
    rallocv(m*n, &state->replydj, _state);
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    rcopyv(m, &state->replyfi, &state->smonitor.fi, _state);
    unpackdj(m, n, &state->replydj, &state->smonitor.j, _state);
    goto lbl_31;
lbl_30:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLM: unexpected protocol, integrity check 0125 failed", _state);
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = state->smonitor.x.ptr.p_double[i];
    }
    state->needfij = ae_true;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->needfij = ae_false;
    for(i=0; i<=m-1; i++)
    {
        state->smonitor.fi.ptr.p_double[i] = state->fi.ptr.p_double[i];
        for(k=0; k<=n-1; k++)
        {
            state->smonitor.j.ptr.pp_double[i][k] = state->j.ptr.pp_double[i][k];
        }
    }
lbl_31:
    goto lbl_28;
lbl_29:
lbl_26:
    
    /*
     * Initial evaluation of the target and report of the current point
     */
    if( !state->xrep )
    {
        goto lbl_32;
    }
    if( state->protocolversion!=2 )
    {
        goto lbl_34;
    }
    
    /*
     * Issue request and perform report using V2 protocol
     */
    rcopyv(n, &state->xbase, &state->x, _state);
    state->requesttype = 4;
    state->querysize = 1;
    state->queryfuncs = m;
    state->queryvars = n;
    state->querydim = 0;
    rcopyallocv(n, &state->x, &state->querydata, _state);
    rallocv(m, &state->replyfi, _state);
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->repnfunc = state->repnfunc+1;
    rcopyv(m, &state->replyfi, &state->fi, _state);
    state->f = rdotv2(m, &state->fi, _state);
    state->fbase = state->f;
    state->requesttype = -1;
    state->queryvars = n;
    state->reportf = state->f;
    rcopyallocv(n, &state->x, &state->reportx, _state);
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    goto lbl_35;
lbl_34:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLM: unexpected protocol, integrity check 9600 failed", _state);
    rcopyv(n, &state->xbase, &state->x, _state);
    minlm_clearrequestfields(state, _state);
    state->needfi = ae_true;
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    state->needfi = ae_false;
    state->f = rdotv2(m, &state->fi, _state);
    state->fbase = state->f;
    state->repnfunc = state->repnfunc+1;
    rcopyv(n, &state->xbase, &state->x, _state);
    minlm_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    state->xupdated = ae_false;
lbl_35:
lbl_32:
    if( state->userterminationneeded )
    {
        
        /*
         * User requested termination
         */
        rcopyv(n, &state->xbase, &state->x, _state);
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    
    /*
     * Prepare control variables
     */
    state->fstagnationcnt = 0;
    state->nu = (double)(1);
    state->lambdav = -ae_maxrealnumber;
    state->modelage = state->maxmodelage+1;
    state->deltaxready = ae_false;
    state->deltafready = ae_false;
    state->nonmonotonicpos = -1;
    
    /*
     * fvector/Jacobian-based optimization mode
     *
     * Main cycle.
     *
     * We move through it until either:
     * * one of the stopping conditions is met
     * * we decide that stopping conditions are too stringent
     *   and break from cycle
     */
lbl_36:
    if( ae_false )
    {
        goto lbl_37;
    }
    if( dotrace )
    {
        ae_trace("\n=== ITERATION %5d ==================================================================================\n",
            (int)(state->repiterationscount));
    }
    
    /*
     * First, we have to prepare quadratic model for our function.
     * We use BFlag to ensure that model is prepared;
     * if it is false at the end of this block, something went wrong.
     *
     * We may either calculate brand new model or update old one.
     *
     * Before this block we have:
     * * State.XBase            - current position.
     * * State.DeltaX           - if DeltaXReady is True
     * * State.DeltaF           - if DeltaFReady is True
     *
     * After this block is over, we will have:
     * * State.XBase            - base point (unchanged)
     * * State.FBase            - F(XBase)
     * * State.GBase            - linear term
     * * State.QuadraticModel   - quadratic term
     * * State.LambdaV          - current estimate for lambda
     *
     * We also clear DeltaXReady/DeltaFReady flags
     * after initialization is done.
     */
    if( !(state->modelage>state->maxmodelage||!(state->deltaxready&&state->deltafready)) )
    {
        goto lbl_38;
    }
    
    /*
     * Refresh model (using either finite differences or analytic Jacobian)
     */
    if( state->protocolversion!=2 )
    {
        goto lbl_40;
    }
    
    /*
     * Issue request using V2 protocol
     */
    if( state->algomode!=0 )
    {
        goto lbl_42;
    }
    
    /*
     * Optimization using F values only. Use finite differences to estimate Jacobian.
     *
     * Request dense numerical Jacobian, propose 2-point finite difference formula
     */
    state->requesttype = 3;
    state->querysize = 1;
    state->queryfuncs = m;
    state->queryvars = n;
    state->querydim = 0;
    state->queryformulasize = 0;
    if( state->formulatype==2||state->formulatype==3 )
    {
        
        /*
         * either 2-point difference, or a 3-point central one
         */
        state->queryformulasize = 2;
        rallocv(n+n*2*state->queryformulasize, &state->querydata, _state);
        rcopyv(n, &state->xbase, &state->querydata, _state);
        for(k=0; k<=n-1; k++)
        {
            
            /*
             * We guard X[k] from leaving [BndL,BndU].
             * In case BndL=BndU, we assume that derivative in this direction is zero.
             */
            v = state->diffstep*state->s.ptr.p_double[k];
            xl = state->xbase.ptr.p_double[k]-v;
            if( state->havebndl.ptr.p_bool[k]&&ae_fp_less(xl,state->bndl.ptr.p_double[k]) )
            {
                xl = state->bndl.ptr.p_double[k];
            }
            xr = state->xbase.ptr.p_double[k]+v;
            if( state->havebndu.ptr.p_bool[k]&&ae_fp_greater(xr,state->bndu.ptr.p_double[k]) )
            {
                xr = state->bndu.ptr.p_double[k];
            }
            if( xl>=xr )
            {
                state->querydata.ptr.p_double[n+2*state->queryformulasize*k+0*2+0] = state->xbase.ptr.p_double[k];
                state->querydata.ptr.p_double[n+2*state->queryformulasize*k+0*2+1] = 0.0;
                state->querydata.ptr.p_double[n+2*state->queryformulasize*k+1*2+0] = state->xbase.ptr.p_double[k];
                state->querydata.ptr.p_double[n+2*state->queryformulasize*k+1*2+1] = 0.0;
                continue;
            }
            if( state->formulatype==2 )
            {
                if( ae_fp_greater(xr-state->xbase.ptr.p_double[k],state->xbase.ptr.p_double[k]-xl) )
                {
                    v = (double)1/(xr-state->xbase.ptr.p_double[k]);
                    state->querydata.ptr.p_double[n+2*state->queryformulasize*k+0*2+0] = state->xbase.ptr.p_double[k];
                    state->querydata.ptr.p_double[n+2*state->queryformulasize*k+0*2+1] = -v;
                    state->querydata.ptr.p_double[n+2*state->queryformulasize*k+1*2+0] = xr;
                    state->querydata.ptr.p_double[n+2*state->queryformulasize*k+1*2+1] = v;
                }
                else
                {
                    v = (double)1/(state->xbase.ptr.p_double[k]-xl);
                    state->querydata.ptr.p_double[n+2*state->queryformulasize*k+0*2+0] = xl;
                    state->querydata.ptr.p_double[n+2*state->queryformulasize*k+0*2+1] = -v;
                    state->querydata.ptr.p_double[n+2*state->queryformulasize*k+1*2+0] = state->xbase.ptr.p_double[k];
                    state->querydata.ptr.p_double[n+2*state->queryformulasize*k+1*2+1] = v;
                }
                continue;
            }
            if( state->formulatype==3 )
            {
                v = (double)1/(xr-xl);
                state->querydata.ptr.p_double[n+2*state->queryformulasize*k+0*2+0] = xl;
                state->querydata.ptr.p_double[n+2*state->queryformulasize*k+0*2+1] = -v;
                state->querydata.ptr.p_double[n+2*state->queryformulasize*k+1*2+0] = xr;
                state->querydata.ptr.p_double[n+2*state->queryformulasize*k+1*2+1] = v;
                continue;
            }
            ae_assert(ae_false, "MINLM: integrity check 431017 failed", _state);
        }
    }
    ae_assert(state->queryformulasize>=1, "MINLM: integrity check 397250 failed", _state);
    rallocv(m, &state->replyfi, _state);
    rallocv(m*n, &state->replydj, _state);
    state->rstate.stage = 6;
    goto lbl_rcomm;
lbl_6:
    rcopyv(m, &state->replyfi, &state->fi, _state);
    unpackdj(m, n, &state->replydj, &state->j, _state);
    state->repnfunc = state->repnfunc+1+n*state->queryformulasize;
    state->repnjac = state->repnjac+1;
    
    /*
     * New model
     */
    state->modelage = 0;
    goto lbl_43;
lbl_42:
    
    /*
     * Request dense analytic Jacobian
     */
    state->requesttype = 2;
    state->querysize = 1;
    state->queryfuncs = m;
    state->queryvars = n;
    state->querydim = 0;
    rcopyallocv(n, &state->xbase, &state->querydata, _state);
    rallocv(m, &state->replyfi, _state);
    rallocv(m*n, &state->replydj, _state);
    state->rstate.stage = 7;
    goto lbl_rcomm;
lbl_7:
    rcopyv(m, &state->replyfi, &state->fi, _state);
    unpackdj(m, n, &state->replydj, &state->j, _state);
    state->repnfunc = state->repnfunc+1;
    state->repnjac = state->repnjac+1;
    
    /*
     * New model
     */
    state->modelage = 0;
lbl_43:
    goto lbl_41;
lbl_40:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLM: unexpected protocol, integrity check 9600 failed", _state);
    if( state->algomode!=0 )
    {
        goto lbl_44;
    }
    
    /*
     * Optimization using F values only.
     * Use finite differences to estimate Jacobian.
     */
    k = 0;
lbl_46:
    if( k>n-1 )
    {
        goto lbl_48;
    }
    
    /*
     * We guard X[k] from leaving [BndL,BndU].
     * In case BndL=BndU, we assume that derivative in this direction is zero.
     */
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->x.ptr.p_double[k] = state->x.ptr.p_double[k]-state->s.ptr.p_double[k]*state->diffstep;
    if( state->havebndl.ptr.p_bool[k] )
    {
        state->x.ptr.p_double[k] = ae_maxreal(state->x.ptr.p_double[k], state->bndl.ptr.p_double[k], _state);
    }
    if( state->havebndu.ptr.p_bool[k] )
    {
        state->x.ptr.p_double[k] = ae_minreal(state->x.ptr.p_double[k], state->bndu.ptr.p_double[k], _state);
    }
    state->xm1 = state->x.ptr.p_double[k];
    minlm_clearrequestfields(state, _state);
    state->needfi = ae_true;
    state->rstate.stage = 8;
    goto lbl_rcomm;
lbl_8:
    state->repnfunc = state->repnfunc+1;
    ae_v_move(&state->fm1.ptr.p_double[0], 1, &state->fi.ptr.p_double[0], 1, ae_v_len(0,m-1));
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->x.ptr.p_double[k] = state->x.ptr.p_double[k]+state->s.ptr.p_double[k]*state->diffstep;
    if( state->havebndl.ptr.p_bool[k] )
    {
        state->x.ptr.p_double[k] = ae_maxreal(state->x.ptr.p_double[k], state->bndl.ptr.p_double[k], _state);
    }
    if( state->havebndu.ptr.p_bool[k] )
    {
        state->x.ptr.p_double[k] = ae_minreal(state->x.ptr.p_double[k], state->bndu.ptr.p_double[k], _state);
    }
    state->xp1 = state->x.ptr.p_double[k];
    minlm_clearrequestfields(state, _state);
    state->needfi = ae_true;
    state->rstate.stage = 9;
    goto lbl_rcomm;
lbl_9:
    state->repnfunc = state->repnfunc+1;
    ae_v_move(&state->fp1.ptr.p_double[0], 1, &state->fi.ptr.p_double[0], 1, ae_v_len(0,m-1));
    v = state->xp1-state->xm1;
    if( ae_fp_neq(v,(double)(0)) )
    {
        v = (double)1/v;
        ae_v_moved(&state->j.ptr.pp_double[0][k], state->j.stride, &state->fp1.ptr.p_double[0], 1, ae_v_len(0,m-1), v);
        ae_v_subd(&state->j.ptr.pp_double[0][k], state->j.stride, &state->fm1.ptr.p_double[0], 1, ae_v_len(0,m-1), v);
    }
    else
    {
        for(i=0; i<=m-1; i++)
        {
            state->j.ptr.pp_double[i][k] = (double)(0);
        }
    }
    k = k+1;
    goto lbl_46;
lbl_48:
    
    /*
     * Calculate F(XBase)
     */
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minlm_clearrequestfields(state, _state);
    state->needfi = ae_true;
    state->rstate.stage = 10;
    goto lbl_rcomm;
lbl_10:
    state->needfi = ae_false;
    state->repnfunc = state->repnfunc+1;
    state->repnjac = state->repnjac+1;
    
    /*
     * New model
     */
    state->modelage = 0;
    goto lbl_45;
lbl_44:
    
    /*
     * Obtain f[] and Jacobian
     */
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minlm_clearrequestfields(state, _state);
    state->needfij = ae_true;
    state->rstate.stage = 11;
    goto lbl_rcomm;
lbl_11:
    state->needfij = ae_false;
    state->repnfunc = state->repnfunc+1;
    state->repnjac = state->repnjac+1;
    
    /*
     * New model
     */
    state->modelage = 0;
lbl_45:
lbl_41:
    goto lbl_39;
lbl_38:
    
    /*
     * State.J contains Jacobian or its current approximation;
     * refresh it using secant updates:
     *
     * f(x0+dx) = f(x0) + J*dx,
     * J_new = J_old + u*h'
     * h = x_new-x_old
     * u = (f_new - f_old - J_old*h)/(h'h)
     *
     * We can explicitly generate h and u, but it is
     * preferential to do in-place calculations. Only
     * I-th row of J_old is needed to calculate u[I],
     * so we can update J row by row in one pass.
     *
     * NOTE: we expect that State.XBase contains new point,
     * State.FBase contains old point, State.DeltaX and
     * State.DeltaY contain updates from last step.
     */
    ae_assert(state->deltaxready&&state->deltafready, "MinLMIteration: uninitialized DeltaX/DeltaF", _state);
    t = ae_v_dotproduct(&state->deltax.ptr.p_double[0], 1, &state->deltax.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_assert(ae_fp_neq(t,(double)(0)), "MinLM: internal error (T=0)", _state);
    for(i=0; i<=m-1; i++)
    {
        v = ae_v_dotproduct(&state->j.ptr.pp_double[i][0], 1, &state->deltax.ptr.p_double[0], 1, ae_v_len(0,n-1));
        v = (state->deltaf.ptr.p_double[i]-v)/t;
        ae_v_addd(&state->j.ptr.pp_double[i][0], 1, &state->deltax.ptr.p_double[0], 1, ae_v_len(0,n-1), v);
    }
    ae_v_move(&state->fi.ptr.p_double[0], 1, &state->fibase.ptr.p_double[0], 1, ae_v_len(0,m-1));
    ae_v_add(&state->fi.ptr.p_double[0], 1, &state->deltaf.ptr.p_double[0], 1, ae_v_len(0,m-1));
    
    /*
     * Increase model age
     */
    state->modelage = state->modelage+1;
lbl_39:
    rmatrixgemm(n, n, m, 2.0, &state->j, 0, 0, 1, &state->j, 0, 0, 0, 0.0, &state->quadraticmodel, 0, 0, _state);
    rmatrixmv(n, m, &state->j, 0, 0, 1, &state->fi, 0, &state->gbase, 0, _state);
    ae_v_muld(&state->gbase.ptr.p_double[0], 1, ae_v_len(0,n-1), 2.0);
    v = ae_v_dotproduct(&state->fi.ptr.p_double[0], 1, &state->fi.ptr.p_double[0], 1, ae_v_len(0,m-1));
    state->fbase = v;
    ae_v_move(&state->fibase.ptr.p_double[0], 1, &state->fi.ptr.p_double[0], 1, ae_v_len(0,m-1));
    state->deltaxready = ae_false;
    state->deltafready = ae_false;
    if( dotrace )
    {
        ae_trace("ModelAge      = %9d    (model age)\n",
            (int)(state->modelage));
    }
    if( state->nonmonotonicpos<0 )
    {
        
        /*
         * Initialize nonmonotonic buffer
         */
        rsetallocv(state->nonmonotoniccnt+1, (double)2*state->fbase, &state->nonmonotonicbuf, _state);
        state->nonmonotonicbuf.ptr.p_double[0] = state->fbase;
        state->nonmonotonicpos = 1%(state->nonmonotoniccnt+1);
    }
    
    /*
     * Perform integrity check (presense of NAN/INF)
     */
    v = state->fbase;
    for(i=0; i<=n-1; i++)
    {
        v = 0.1*v+state->gbase.ptr.p_double[i];
    }
    if( !ae_isfinite(v, _state) )
    {
        
        /*
         * Break!
         */
        if( dotrace )
        {
            ae_trace("> infinities detected, abnormal termination!\n");
        }
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    
    /*
     * If Lambda is not initialized, initialize it using quadratic model
     */
    if( ae_fp_less(state->lambdav,(double)(0)) )
    {
        state->lambdav = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            state->lambdav = ae_maxreal(state->lambdav, ae_fabs(state->quadraticmodel.ptr.pp_double[i][i], _state)*ae_sqr(state->s.ptr.p_double[i], _state), _state);
        }
        state->lambdav = 0.001*state->lambdav;
        if( ae_fp_eq(state->lambdav,(double)(0)) )
        {
            state->lambdav = (double)(1);
        }
    }
    if( dotrace )
    {
        ae_trace("LambdaV       = %9.3e    (damping coefficient)\n",
            (double)(state->lambdav));
    }
    
    /*
     * Find value of Levenberg-Marquardt damping parameter which:
     * * leads to positive definite damped model
     * * within bounds specified by StpMax
     * * generates step which decreases function value
     *
     * After this block IFlag is set to:
     * * -8, if internal integrity control detected NAN/INF in function values
     * * -3, if constraints are infeasible
     * * -2, if model update is needed (either Lambda growth is too large
     *       or step is too short, but we can't rely on model and stop iterations)
     * * -1, if model is fresh, Lambda have grown too large, termination is needed
     * *  0, if everything is OK, continue iterations
     * * >0, successful termination, step is less than EpsX
     *
     * State.Nu can have any value on enter, but after exit it is set to 1.0
     */
    iflag = -99;
    minlm_minlmstepfinderstart(&state->finderstate, &state->quadraticmodel, &state->gbase, rmaxv(state->nonmonotoniccnt+1, &state->nonmonotonicbuf, _state), &state->xbase, &state->fibase, state->modelage, _state);
lbl_49:
    if( !minlm_minlmstepfinderiteration(&state->finderstate, state, &state->lambdav, &state->nu, &state->xnew, &state->deltax, &state->deltaxready, &state->deltaf, &state->deltafready, &iflag, &fnew, &state->repncholesky, _state) )
    {
        goto lbl_50;
    }
    ae_assert(state->finderstate.needfi, "MinLM: internal error 2!", _state);
    state->repnfunc = state->repnfunc+1;
    if( state->protocolversion!=2 )
    {
        goto lbl_51;
    }
    
    /*
     * Issue request and perform report using V2 protocol
     */
    state->requesttype = 4;
    state->querysize = 1;
    state->queryfuncs = m;
    state->queryvars = n;
    state->querydim = 0;
    rcopyallocv(n, &state->finderstate.x, &state->querydata, _state);
    rallocv(m, &state->replyfi, _state);
    state->rstate.stage = 12;
    goto lbl_rcomm;
lbl_12:
    rcopyv(m, &state->replyfi, &state->finderstate.fi, _state);
    goto lbl_52;
lbl_51:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLM: unexpected protocol, integrity check 5138 failed", _state);
    minlm_clearrequestfields(state, _state);
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->finderstate.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->needfi = ae_true;
    state->rstate.stage = 13;
    goto lbl_rcomm;
lbl_13:
    state->needfi = ae_false;
    ae_v_move(&state->finderstate.fi.ptr.p_double[0], 1, &state->fi.ptr.p_double[0], 1, ae_v_len(0,m-1));
lbl_52:
    goto lbl_49;
lbl_50:
    if( state->userterminationneeded )
    {
        
        /*
         * User requested termination
         */
        if( dotrace )
        {
            ae_trace("> user requested termination!\n");
        }
        ae_v_move(&state->x.ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
        state->repterminationtype = 8;
        minlm_tracefinalreport(state, dotrace||state->dotimers, _state);
        result = ae_false;
        return result;
    }
    state->nu = (double)(1);
    ae_assert(((iflag>=-3&&iflag<=0)||iflag==-8)||iflag>0, "MinLM: internal integrity check failed!", _state);
    if( iflag==-3 )
    {
        if( dotrace )
        {
            ae_trace("> constraints are inconsistent!\n");
        }
        state->repterminationtype = -3;
        minlm_tracefinalreport(state, dotrace||state->dotimers, _state);
        result = ae_false;
        return result;
    }
    if( iflag==-2 )
    {
        if( dotrace )
        {
            ae_trace("> model refresh requested\n");
        }
        state->modelage = state->maxmodelage+1;
        goto lbl_36;
    }
    if( iflag!=-1 )
    {
        goto lbl_53;
    }
    
    /*
     * Stopping conditions are too stringent
     */
    if( dotrace )
    {
        ae_trace("> stopping conditions are too stringent, terminating at the best point so far\n");
    }
    state->repterminationtype = 7;
    if( !state->xrep )
    {
        goto lbl_55;
    }
    if( state->protocolversion!=2 )
    {
        goto lbl_57;
    }
    
    /*
     * Issue request using V2 protocol
     */
    state->requesttype = -1;
    state->queryvars = n;
    state->reportf = state->fbase;
    rcopyallocv(n, &state->xbase, &state->reportx, _state);
    state->rstate.stage = 14;
    goto lbl_rcomm;
lbl_14:
    goto lbl_58;
lbl_57:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLM: unexpected protocol, integrity check 9600 failed", _state);
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->f = state->fbase;
    minlm_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 15;
    goto lbl_rcomm;
lbl_15:
    state->xupdated = ae_false;
lbl_58:
lbl_55:
    minlm_tracefinalreport(state, dotrace||state->dotimers, _state);
    result = ae_false;
    return result;
lbl_53:
    if( !(iflag==-8||iflag>0) )
    {
        goto lbl_59;
    }
    
    /*
     * Either:
     * * Integrity check failed - infinities or NANs
     * * successful termination (step size is small enough)
     */
    if( dotrace&&iflag==-8 )
    {
        ae_trace("> infinities detected, abnormal termination!\n");
    }
    if( dotrace&&iflag>0 )
    {
        ae_trace("> step size is small engough, succeful termination\n");
    }
    state->repterminationtype = iflag;
    if( !state->xrep )
    {
        goto lbl_61;
    }
    if( state->protocolversion!=2 )
    {
        goto lbl_63;
    }
    
    /*
     * Issue request using V2 protocol
     */
    state->requesttype = -1;
    state->queryvars = n;
    state->reportf = state->fbase;
    rcopyallocv(n, &state->xbase, &state->reportx, _state);
    state->rstate.stage = 16;
    goto lbl_rcomm;
lbl_16:
    goto lbl_64;
lbl_63:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLM: unexpected protocol, integrity check 4042 failed", _state);
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->f = state->fbase;
    minlm_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 17;
    goto lbl_rcomm;
lbl_17:
    state->xupdated = ae_false;
lbl_64:
lbl_61:
    minlm_tracefinalreport(state, dotrace||state->dotimers, _state);
    result = ae_false;
    return result;
lbl_59:
    state->f = fnew;
    
    /*
     * Levenberg-Marquardt step is ready.
     * Compare predicted vs. actual decrease and decide what to do with lambda.
     *
     * NOTE: we expect that State.DeltaX contains direction of step,
     * State.F contains function value at new point.
     */
    ae_assert(state->deltaxready, "MinLM: deltaX is not ready", _state);
    iflag = minlm_checkdecrease(&state->quadraticmodel, &state->gbase, rmaxv(state->nonmonotoniccnt+1, &state->nonmonotonicbuf, _state), n, &state->deltax, state->f, &state->lambdav, &state->nu, _state);
    if( iflag==0 )
    {
        goto lbl_65;
    }
    state->repterminationtype = iflag;
    if( !state->xrep )
    {
        goto lbl_67;
    }
    if( state->protocolversion!=2 )
    {
        goto lbl_69;
    }
    
    /*
     * Issue request using V2 protocol
     */
    state->requesttype = -1;
    state->queryvars = n;
    state->reportf = state->fbase;
    rcopyallocv(n, &state->xbase, &state->reportx, _state);
    state->rstate.stage = 18;
    goto lbl_rcomm;
lbl_18:
    goto lbl_70;
lbl_69:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLM: unexpected protocol, integrity check 8643 failed", _state);
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->f = state->fbase;
    minlm_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 19;
    goto lbl_rcomm;
lbl_19:
    state->xupdated = ae_false;
lbl_70:
lbl_67:
    minlm_tracefinalreport(state, dotrace||state->dotimers, _state);
    result = ae_false;
    return result;
lbl_65:
    
    /*
     * Accept step, report it and
     * test stopping conditions on iterations count and function decrease.
     *
     * NOTE: we expect that State.DeltaX contains direction of step,
     * State.F contains function value at new point.
     *
     * NOTE2: we should update XBase ONLY. In the beginning of the next
     * iteration we expect that State.FIBase is NOT updated and
     * contains old value of a function vector.
     */
    state->fstagnationcnt = icase2(ae_fp_less(ae_fabs(state->fbase-state->f, _state),minlm_stagnationfeps*rmaxabs3(state->fbase, state->f, (double)(1), _state)), state->fstagnationcnt+1, 0, _state);
    ae_v_move(&state->xbase.ptr.p_double[0], 1, &state->xnew.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->fbase = fnew;
    state->nonmonotonicbuf.ptr.p_double[state->nonmonotonicpos] = state->f;
    state->nonmonotonicpos = (state->nonmonotonicpos+1)%(state->nonmonotoniccnt+1);
    if( !state->xrep )
    {
        goto lbl_71;
    }
    if( state->protocolversion!=2 )
    {
        goto lbl_73;
    }
    
    /*
     * Issue request using V2 protocol
     */
    state->requesttype = -1;
    state->queryvars = n;
    state->reportf = state->f;
    rcopyallocv(n, &state->xbase, &state->reportx, _state);
    state->rstate.stage = 20;
    goto lbl_rcomm;
lbl_20:
    goto lbl_74;
lbl_73:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLM: unexpected protocol, integrity check 9600 failed", _state);
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minlm_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 21;
    goto lbl_rcomm;
lbl_21:
    state->xupdated = ae_false;
lbl_74:
lbl_71:
    state->repiterationscount = state->repiterationscount+1;
    if( state->repiterationscount>=state->maxits&&state->maxits>0 )
    {
        state->repterminationtype = 5;
    }
    if( state->fstagnationcnt>=minlm_stagnationfits )
    {
        state->repterminationtype = 7;
    }
    if( state->repterminationtype<=0 )
    {
        goto lbl_75;
    }
    if( !state->xrep )
    {
        goto lbl_77;
    }
    
    /*
     * Report: XBase contains new point, F contains function value at new point
     */
    if( state->protocolversion!=2 )
    {
        goto lbl_79;
    }
    
    /*
     * Issue request using V2 protocol
     */
    state->requesttype = -1;
    state->queryvars = n;
    state->reportf = state->f;
    rcopyallocv(n, &state->xbase, &state->reportx, _state);
    state->rstate.stage = 22;
    goto lbl_rcomm;
lbl_22:
    goto lbl_80;
lbl_79:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLM: unexpected protocol, integrity check 9600 failed", _state);
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minlm_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 23;
    goto lbl_rcomm;
lbl_23:
    state->xupdated = ae_false;
lbl_80:
lbl_77:
    minlm_tracefinalreport(state, dotrace||state->dotimers, _state);
    result = ae_false;
    return result;
lbl_75:
    state->modelage = state->modelage+1;
    goto lbl_36;
lbl_37:
    
    /*
     * Lambda is too large, we have to break iterations.
     */
    state->repterminationtype = 7;
    if( !state->xrep )
    {
        goto lbl_81;
    }
    if( state->protocolversion!=2 )
    {
        goto lbl_83;
    }
    
    /*
     * Issue request using V2 protocol
     */
    state->requesttype = -1;
    state->queryvars = n;
    state->reportf = state->f;
    rcopyallocv(n, &state->xbase, &state->reportx, _state);
    state->rstate.stage = 24;
    goto lbl_rcomm;
lbl_24:
    goto lbl_84;
lbl_83:
    
    /*
     * Use legacy V1 protocol
     */
    ae_assert(state->protocolversion==1, "MINLM: unexpected protocol, integrity check 9600 failed", _state);
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minlm_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 25;
    goto lbl_rcomm;
lbl_25:
    state->xupdated = ae_false;
lbl_84:
lbl_81:
    minlm_tracefinalreport(state, dotrace||state->dotimers, _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = n;
    state->rstate.ia.ptr.p_int[1] = m;
    state->rstate.ia.ptr.p_int[2] = iflag;
    state->rstate.ia.ptr.p_int[3] = i;
    state->rstate.ia.ptr.p_int[4] = k;
    state->rstate.ba.ptr.p_bool[0] = bflag;
    state->rstate.ba.ptr.p_bool[1] = dotrace;
    state->rstate.ra.ptr.p_double[0] = v;
    state->rstate.ra.ptr.p_double[1] = xl;
    state->rstate.ra.ptr.p_double[2] = xr;
    state->rstate.ra.ptr.p_double[3] = s;
    state->rstate.ra.ptr.p_double[4] = t;
    state->rstate.ra.ptr.p_double[5] = fnew;
    return result;
}


/*************************************************************************
This  function  activates/deactivates verification  of  the  user-supplied
analytic Jacobian.

Upon  activation  of  this  option  OptGuard  integrity  checker  performs
numerical differentiation of your target function vector  at  the  initial
point (note: future versions may also perform check  at  the final  point)
and compares numerical Jacobian with analytic one provided by you.

If difference is too large, an error flag is set and optimization  session
continues. After optimization session is over, you can retrieve the report
which stores  both  Jacobians,  and  specific  components  highlighted  as
suspicious by the OptGuard.

The OptGuard report can be retrieved with minlmoptguardresults().

IMPORTANT: gradient check is a high-overhead option which  will  cost  you
           about 3*N additional function evaluations. In many cases it may
           cost as much as the rest of the optimization session.
           
           YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO
           CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY.

NOTE: unlike previous incarnation of the gradient checking code,  OptGuard
      does NOT interrupt optimization even if it discovers bad gradient.
      
INPUT PARAMETERS:
    State       -   structure used to store algorithm state
    TestStep    -   verification step used for numerical differentiation:
                    * TestStep=0 turns verification off
                    * TestStep>0 activates verification
                    You should carefully choose TestStep. Value  which  is
                    too large (so large that  function  behavior  is  non-
                    cubic at this scale) will lead  to  false  alarms. Too
                    short step will result in rounding  errors  dominating
                    numerical derivative.
                    
                    You may use different step for different parameters by
                    means of setting scale with minlmsetscale().

=== EXPLANATION ==========================================================                    

In order to verify gradient algorithm performs following steps:
  * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i],
    where X[i] is i-th component of the initial point and S[i] is a  scale
    of i-th parameter
  * F(X) is evaluated at these trial points
  * we perform one more evaluation in the middle point of the interval
  * we  build  cubic  model using function values and derivatives at trial
    points and we compare its prediction with actual value in  the  middle
    point

  -- ALGLIB --
     Copyright 15.06.2014 by Bochkanov Sergey
*************************************************************************/
void minlmoptguardgradient(minlmstate* state,
     double teststep,
     ae_state *_state)
{


    ae_assert(ae_isfinite(teststep, _state), "MinLMOptGuardGradient: TestStep contains NaN or INF", _state);
    ae_assert(ae_fp_greater_eq(teststep,(double)(0)), "MinLMOptGuardGradient: invalid argument TestStep(TestStep<0)", _state);
    state->teststep = teststep;
}


/*************************************************************************
Results of OptGuard integrity check, should be called  after  optimization
session is over.

OptGuard checks analytic Jacobian  against  reference  value  obtained  by
numerical differentiation with user-specified step.

NOTE: other optimizers perform additional OptGuard checks for things  like
      C0/C1-continuity violations. However, LM optimizer  can  check  only
      for incorrect Jacobian.
      
      The reason is that unlike line search methods LM optimizer does  not
      perform extensive evaluations along the line. Thus, we simply do not
      have enough data to catch C0/C1-violations.

This check is activated with  minlmoptguardgradient() function.

Following flags are set when these errors are suspected:
* rep.badgradsuspected, and additionally:
  * rep.badgradfidx for specific function (Jacobian row) suspected
  * rep.badgradvidx for specific variable (Jacobian column) suspected
  * rep.badgradxbase, a point where gradient/Jacobian is tested
  * rep.badgraduser, user-provided gradient/Jacobian
  * rep.badgradnum, reference gradient/Jacobian obtained via numerical
    differentiation

INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    rep     -   OptGuard report
   
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minlmoptguardresults(minlmstate* state,
     optguardreport* rep,
     ae_state *_state)
{

    _optguardreport_clear(rep);

    smoothnessmonitorexportreport(&state->smonitor, rep, _state);
}


/*************************************************************************
Levenberg-Marquardt algorithm results

NOTE: if you activated OptGuard integrity checking functionality and  want
      to get OptGuard report,  it  can  be  retrieved  with  the  help  of
      minlmoptguardresults() function.

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization  report;  includes  termination   codes   and
                additional information. Termination codes are listed below,
                see comments for this structure for more info.
                
                Termination code is stored in rep.terminationtype field:
                * -8    optimizer detected NAN/INF values either in the
                        function itself, or in its Jacobian
                * -3    constraints are inconsistent
                *  2    relative step is no more than EpsX.
                *  5    MaxIts steps was taken
                *  7    stopping conditions are too stringent,
                        further improvement is impossible
                *  8    terminated by user who called minlmrequesttermination().
                        X contains point which was "current accepted" when
                        termination request was submitted.
                        
                rep.f contains SUM(f[i]^2) at X

  -- ALGLIB --
     Copyright 10.03.2009 by Bochkanov Sergey
*************************************************************************/
void minlmresults(const minlmstate* state,
     /* Real    */ ae_vector* x,
     minlmreport* rep,
     ae_state *_state)
{

    ae_vector_clear(x);
    _minlmreport_clear(rep);

    minlmresultsbuf(state, x, rep, _state);
}


/*************************************************************************
Levenberg-Marquardt algorithm results

Buffered implementation of MinLMResults(), which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 10.03.2009 by Bochkanov Sergey
*************************************************************************/
void minlmresultsbuf(const minlmstate* state,
     /* Real    */ ae_vector* x,
     minlmreport* rep,
     ae_state *_state)
{


    if( x->cnt<state->n )
    {
        ae_vector_set_length(x, state->n, _state);
    }
    ae_v_move(&x->ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,state->n-1));
    rep->iterationscount = state->repiterationscount;
    rep->terminationtype = state->repterminationtype;
    rep->nfunc = state->repnfunc;
    rep->njac = state->repnjac;
    rep->ngrad = state->repngrad;
    rep->nhess = state->repnhess;
    rep->ncholesky = state->repncholesky;
    rep->f = state->fbase;
}


/*************************************************************************
This  subroutine  restarts  LM  algorithm from new point. All optimization
parameters are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure used for reverse communication previously
                allocated with MinLMCreateXXX call.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
void minlmrestartfrom(minlmstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{


    ae_assert(x->cnt>=state->n, "MinLMRestartFrom: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, state->n, _state), "MinLMRestartFrom: X contains infinite or NaN values!", _state);
    ae_v_move(&state->xbase.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,state->n-1));
    ae_vector_set_length(&state->rstate.ia, 4+1, _state);
    ae_vector_set_length(&state->rstate.ba, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 5+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.
      
NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void minlmrequesttermination(minlmstate* state, ae_state *_state)
{


    state->userterminationneeded = ae_true;
}


/*************************************************************************
Set V1 reverse communication protocol
*************************************************************************/
void minlmsetprotocolv1(minlmstate* state, ae_state *_state)
{


    state->protocolversion = 1;
    ae_vector_set_length(&state->rstate.ia, 4+1, _state);
    ae_vector_set_length(&state->rstate.ba, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 5+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Set V2 reverse communication protocol
*************************************************************************/
void minlmsetprotocolv2(minlmstate* state, ae_state *_state)
{


    state->protocolversion = 2;
    ae_vector_set_length(&state->rstate.ia, 4+1, _state);
    ae_vector_set_length(&state->rstate.ba, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 5+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Unpacks dense Jacobian into array
*************************************************************************/
void unpackdj(ae_int_t m,
     ae_int_t n,
     /* Real    */ const ae_vector* replydj,
     /* Real    */ ae_matrix* jac,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t offs;


    if( replydj->cnt<m*n )
    {
        ae_assert(ae_false, "UnpackDJ: reply size is too small", _state);
    }
    if( jac->rows<m||jac->cols<n )
    {
        ae_assert(ae_false, "UnpackDJ: output size is too small", _state);
    }
    offs = 0;
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            jac->ptr.pp_double[i][j] = replydj->ptr.p_double[offs];
            offs = offs+1;
        }
    }
}


/*************************************************************************
Prepare internal structures (except for RComm).

Note: M must be zero for FGH mode, non-zero for V/VJ/FJ/FGJ mode.
*************************************************************************/
static void minlm_lmprepare(ae_int_t n,
     ae_int_t m,
     ae_bool havegrad,
     minlmstate* state,
     ae_state *_state)
{
    ae_int_t i;


    smoothnessmonitorinit(&state->smonitor, &state->s, 0, 0, ae_false, _state);
    if( n<=0||m<0 )
    {
        return;
    }
    if( havegrad )
    {
        ae_vector_set_length(&state->g, n, _state);
    }
    if( m!=0 )
    {
        ae_matrix_set_length(&state->j, m, n, _state);
        ae_vector_set_length(&state->fi, m, _state);
        ae_vector_set_length(&state->fibase, m, _state);
        ae_vector_set_length(&state->deltaf, m, _state);
        ae_vector_set_length(&state->fm1, m, _state);
        ae_vector_set_length(&state->fp1, m, _state);
        ae_vector_set_length(&state->fc1, m, _state);
        ae_vector_set_length(&state->gm1, m, _state);
        ae_vector_set_length(&state->gp1, m, _state);
        ae_vector_set_length(&state->gc1, m, _state);
    }
    else
    {
        ae_matrix_set_length(&state->h, n, n, _state);
    }
    ae_vector_set_length(&state->x, n, _state);
    ae_vector_set_length(&state->deltax, n, _state);
    ae_matrix_set_length(&state->quadraticmodel, n, n, _state);
    ae_vector_set_length(&state->xbase, n, _state);
    ae_vector_set_length(&state->gbase, n, _state);
    ae_vector_set_length(&state->xdir, n, _state);
    ae_vector_set_length(&state->tmp0, n, _state);
    
    /*
     * prepare internal L-BFGS
     */
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = (double)(0);
    }
    minlbfgscreate(n, ae_minint(minlm_additers, n, _state), &state->x, &state->internalstate, _state);
    minlbfgssetcond(&state->internalstate, 0.0, 0.0, 0.0, ae_minint(minlm_additers, n, _state), _state);
    
    /*
     * Prepare internal QP solver
     */
    minqpcreate(n, &state->qpstate, _state);
    minqpsetalgoquickqp(&state->qpstate, 0.0, 0.0, coalesce(0.01*state->epsx, 1.0E-12, _state), 10, ae_true, _state);
    
    /*
     * Prepare boundary constraints
     */
    ae_vector_set_length(&state->bndl, n, _state);
    ae_vector_set_length(&state->bndu, n, _state);
    ae_vector_set_length(&state->havebndl, n, _state);
    ae_vector_set_length(&state->havebndu, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->bndl.ptr.p_double[i] = _state->v_neginf;
        state->havebndl.ptr.p_bool[i] = ae_false;
        state->bndu.ptr.p_double[i] = _state->v_posinf;
        state->havebndu.ptr.p_bool[i] = ae_false;
    }
    
    /*
     * Prepare scaling matrix
     */
    ae_vector_set_length(&state->s, n, _state);
    ae_vector_set_length(&state->lastscaleused, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->s.ptr.p_double[i] = 1.0;
        state->lastscaleused.ptr.p_double[i] = 1.0;
    }
    
    /*
     * Prepare linear constraints
     */
    state->nec = 0;
    state->nic = 0;
}


/*************************************************************************
Clears request fileds (to be sure that we don't forgot to clear something)
*************************************************************************/
static void minlm_clearrequestfields(minlmstate* state, ae_state *_state)
{


    ae_assert(state->protocolversion==1, "MINLM: unexpected protocol", _state);
    state->needf = ae_false;
    state->needfg = ae_false;
    state->needfgh = ae_false;
    state->needfij = ae_false;
    state->needfi = ae_false;
    state->xupdated = ae_false;
}


/*************************************************************************
Set V1 reverse communication protocol
*************************************************************************/
static void minlm_tracefinalreport(minlmstate* state,
     ae_bool dotrace,
     ae_state *_state)
{


    if( !dotrace )
    {
        return;
    }
    ae_trace("\n\n----------------------------------------------------------------------------------------------------\n");
    ae_trace("Total running time is %0d ms, including:\n",
        (int)(ae_tickcount()-state->tstart));
    ae_trace("> QP time: %8d ms\n",
        (int)(state->tqp));
}


/*************************************************************************
Increases lambda, returns False when there is a danger of overflow
*************************************************************************/
static ae_bool minlm_increaselambda(double* lambdav,
     double* nu,
     ae_state *_state)
{
    double lnlambda;
    double lnnu;
    double lnlambdaup;
    double lnmax;
    ae_bool result;


    result = ae_false;
    lnlambda = ae_log(*lambdav, _state);
    lnlambdaup = ae_log(minlm_lambdaup, _state);
    lnnu = ae_log(*nu, _state);
    lnmax = ae_log(ae_maxrealnumber, _state);
    if( ae_fp_greater(lnlambda+lnlambdaup+lnnu,0.25*lnmax) )
    {
        return result;
    }
    if( ae_fp_greater(lnnu+ae_log((double)(2), _state),lnmax) )
    {
        return result;
    }
    *lambdav = *lambdav*minlm_lambdaup*(*nu);
    *nu = *nu*(double)2;
    result = ae_true;
    return result;
}


/*************************************************************************
Decreases lambda, but leaves it unchanged when there is danger of underflow.
*************************************************************************/
static void minlm_decreaselambda(double* lambdav,
     double* nu,
     ae_state *_state)
{


    *nu = (double)(1);
    if( ae_fp_less(ae_log(*lambdav, _state)+ae_log(minlm_lambdadown, _state),ae_log(ae_minrealnumber, _state)) )
    {
        *lambdav = ae_minrealnumber;
    }
    else
    {
        *lambdav = *lambdav*minlm_lambdadown;
    }
}


/*************************************************************************
This function compares actual decrease vs predicted decrease  and  updates
LambdaV/Nu accordingly.

INPUT PARAMETERS:
    QuadraticModel      -   array[N,N], full Hessian matrix of quadratic
                            model at deltaX=0
    GBase               -   array[N], gradient at deltaX=0
    FBase               -   F(deltaX=0)
    N                   -   size
    DeltaX              -   step vector
    FNew                -   new function value
    LambdaV             -   lambda-value, updated on exit
    Nu                  -   Nu-multiplier, updated on exit

On exit it returns:
* Result=0  - if we have to continue iterations
* Result<>0 - if termination with completion code Result is requested

  -- ALGLIB --
     Copyright 17.02.2017 by Bochkanov Sergey
*************************************************************************/
static ae_int_t minlm_checkdecrease(/* Real    */ const ae_matrix* quadraticmodel,
     /* Real    */ const ae_vector* gbase,
     double fbase,
     ae_int_t n,
     /* Real    */ const ae_vector* deltax,
     double fnew,
     double* lambdav,
     double* nu,
     ae_state *_state)
{
    ae_int_t i;
    double v;
    double t;
    double predicteddecrease;
    double actualdecrease;
    ae_int_t result;


    result = 0;
    t = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        v = ae_v_dotproduct(&quadraticmodel->ptr.pp_double[i][0], 1, &deltax->ptr.p_double[0], 1, ae_v_len(0,n-1));
        t = t+deltax->ptr.p_double[i]*gbase->ptr.p_double[i]+0.5*deltax->ptr.p_double[i]*v;
    }
    predicteddecrease = -t;
    actualdecrease = -(fnew-fbase);
    if( ae_fp_less_eq(predicteddecrease,(double)(0)) )
    {
        result = 7;
        return result;
    }
    v = actualdecrease/predicteddecrease;
    if( ae_fp_less(v,0.1) )
    {
        if( !minlm_increaselambda(lambdav, nu, _state) )
        {
            
            /*
             * Lambda is too large, we have to break iterations.
             */
            result = 7;
            return result;
        }
    }
    if( ae_fp_greater(v,0.5) )
    {
        minlm_decreaselambda(lambdav, nu, _state);
    }
    return result;
}


/*************************************************************************
This  function  initializes  step finder object  with  problem  statement;
model  parameters  specified  during  this  call  should not (and can not)
change during object lifetime (although it is  possible  to  re-initialize
object with different settings).

This function reuses internally allocated objects as much as possible.

In addition to initializing step finder, this function enforces feasibility
in initial point X passed to this function. It is important that LM iteration
starts from feasible point and performs feasible steps;

RETURN VALUE:
    True for successful initialization
    False for inconsistent constraints; you should not use step finder if
    it returned False.
*************************************************************************/
static ae_bool minlm_minlmstepfinderinit(minlmstepfinder* state,
     ae_int_t n,
     ae_int_t m,
     ae_int_t maxmodelage,
     /* Real    */ ae_vector* xbase,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_matrix* cleic,
     ae_int_t nec,
     ae_int_t nic,
     /* Real    */ const ae_vector* s,
     double stpmax,
     double epsx,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool result;


    state->n = n;
    state->m = m;
    state->maxmodelage = maxmodelage;
    state->stpmax = stpmax;
    state->epsx = epsx;
    
    /*
     * Allocate temporaries, create QP solver, select QP algorithm
     */
    rvectorsetlengthatleast(&state->bndl, n, _state);
    rvectorsetlengthatleast(&state->bndu, n, _state);
    rvectorsetlengthatleast(&state->s, n, _state);
    bvectorsetlengthatleast(&state->havebndl, n, _state);
    bvectorsetlengthatleast(&state->havebndu, n, _state);
    rvectorsetlengthatleast(&state->x, n, _state);
    rvectorsetlengthatleast(&state->xbase, n, _state);
    rvectorsetlengthatleast(&state->tmp0, n, _state);
    rvectorsetlengthatleast(&state->modeldiag, n, _state);
    ivectorsetlengthatleast(&state->tmpct, nec+nic, _state);
    rvectorsetlengthatleast(&state->xdir, n, _state);
    rvectorsetlengthatleast(&state->fi, m, _state);
    rvectorsetlengthatleast(&state->fibase, m, _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "MinLM: integrity check failed", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "MinLM: integrity check failed", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->havebndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        state->havebndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
        state->s.ptr.p_double[i] = s->ptr.p_double[i];
    }
    for(i=0; i<=nec-1; i++)
    {
        state->tmpct.ptr.p_int[i] = 0;
    }
    for(i=0; i<=nic-1; i++)
    {
        state->tmpct.ptr.p_int[nec+i] = -1;
    }
    minqpcreate(n, &state->qpstate, _state);
    if( nec+nic==0 )
    {
        minqpsetalgoquickqp(&state->qpstate, 0.0, 0.0, coalesce(0.01*epsx, 1.0E-12, _state), 10, ae_true, _state);
    }
    else
    {
        minqpsetalgodenseaul(&state->qpstate, coalesce(0.01*epsx, 1.0E-12, _state), (double)(100), 10, _state);
    }
    minqpsetbc(&state->qpstate, bndl, bndu, _state);
    minqpsetlc(&state->qpstate, cleic, &state->tmpct, nec+nic, _state);
    minqpsetscale(&state->qpstate, s, _state);
    
    /*
     * Check feasibility of constraints:
     * * check/enforce box constraints (straightforward)
     * * prepare QP subproblem which return us a feasible point
     */
    result = ae_true;
    for(i=0; i<=n-1; i++)
    {
        if( (state->havebndl.ptr.p_bool[i]&&state->havebndu.ptr.p_bool[i])&&ae_fp_greater(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            result = ae_false;
            return result;
        }
        if( state->havebndl.ptr.p_bool[i]&&ae_fp_less(xbase->ptr.p_double[i],state->bndl.ptr.p_double[i]) )
        {
            xbase->ptr.p_double[i] = state->bndl.ptr.p_double[i];
        }
        if( state->havebndu.ptr.p_bool[i]&&ae_fp_greater(xbase->ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            xbase->ptr.p_double[i] = state->bndu.ptr.p_double[i];
        }
    }
    if( nec+nic>0 )
    {
        
        /*
         * Well, we have linear constraints... let's use heavy machinery.
         *
         * We will modify QP solver state below, but everything will be
         * restored in MinLMStepFinderStart().
         */
        sparsecreate(n, n, n, &state->tmpsp, _state);
        for(i=0; i<=n-1; i++)
        {
            sparseset(&state->tmpsp, i, i, 0.5, _state);
            state->tmp0.ptr.p_double[i] = (double)(0);
        }
        minqpsetstartingpointfast(&state->qpstate, xbase, _state);
        minqpsetoriginfast(&state->qpstate, xbase, _state);
        minqpsetlineartermfast(&state->qpstate, &state->tmp0, _state);
        minqpsetquadratictermsparse(&state->qpstate, &state->tmpsp, ae_true, _state);
        minqpoptimize(&state->qpstate, _state);
        minqpresultsbuf(&state->qpstate, xbase, &state->qprep, _state);
    }
    return result;
}


/*************************************************************************
This function prepares LM step search session.
*************************************************************************/
static void minlm_minlmstepfinderstart(minlmstepfinder* state,
     /* Real    */ const ae_matrix* quadraticmodel,
     /* Real    */ const ae_vector* gbase,
     double fbase,
     /* Real    */ const ae_vector* xbase,
     /* Real    */ const ae_vector* fibase,
     ae_int_t modelage,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_vector_set_length(&state->rstate.ia, 2+1, _state);
    ae_vector_set_length(&state->rstate.ba, 0+1, _state);
    ae_vector_set_length(&state->rstate.ra, 0+1, _state);
    state->rstate.stage = -1;
    state->modelage = modelage;
    state->fbase = fbase;
    for(i=0; i<=state->m-1; i++)
    {
        state->fibase.ptr.p_double[i] = fibase->ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        state->xbase.ptr.p_double[i] = xbase->ptr.p_double[i];
        state->modeldiag.ptr.p_double[i] = quadraticmodel->ptr.pp_double[i][i];
    }
    minqpsetstartingpointfast(&state->qpstate, xbase, _state);
    minqpsetoriginfast(&state->qpstate, xbase, _state);
    minqpsetlineartermfast(&state->qpstate, gbase, _state);
    minqpsetquadratictermfast(&state->qpstate, quadraticmodel, ae_true, 0.0, _state);
}


/*************************************************************************
This function runs LM step search session.
//
// Find value of Levenberg-Marquardt damping parameter which:
// * leads to positive definite damped model
// * within bounds specified by StpMax
// * generates step which decreases function value
//
// After this block IFlag is set to:
// * -8, if infinities/NANs were detected in function values/gradient
// * -3, if constraints are infeasible
// * -2, if model update is needed (either Lambda growth is too large
//       or step is too short, but we can't rely on model and stop iterations)
// * -1, if model is fresh, Lambda have grown too large, termination is needed
// *  0, if everything is OK, continue iterations
// * >0  - successful completion (step size is small enough)
//
// State.Nu can have any value on enter, but after exit it is set to 1.0
//
*************************************************************************/
static ae_bool minlm_minlmstepfinderiteration(minlmstepfinder* state,
     minlmstate* parentstate,
     double* lambdav,
     double* nu,
     /* Real    */ ae_vector* xnew,
     /* Real    */ ae_vector* deltax,
     ae_bool* deltaxready,
     /* Real    */ ae_vector* deltaf,
     ae_bool* deltafready,
     ae_int_t* iflag,
     double* fnew,
     ae_int_t* ncholesky,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool bflag;
    double v;
    ae_int_t n;
    ae_int_t m;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        i = state->rstate.ia.ptr.p_int[0];
        n = state->rstate.ia.ptr.p_int[1];
        m = state->rstate.ia.ptr.p_int[2];
        bflag = state->rstate.ba.ptr.p_bool[0];
        v = state->rstate.ra.ptr.p_double[0];
    }
    else
    {
        i = 763;
        n = -541;
        m = -698;
        bflag = ae_false;
        v = -318.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    
    /*
     * Routine body
     */
    *iflag = -99;
    n = state->n;
    m = state->m;
lbl_1:
    if( ae_false )
    {
        goto lbl_2;
    }
    *deltaxready = ae_false;
    *deltafready = ae_false;
    
    /*
     * Do we need model update?
     */
    if( state->modelage>0&&ae_fp_greater_eq(*nu,minlm_suspiciousnu) )
    {
        *iflag = -2;
        goto lbl_2;
    }
    
    /*
     * Setup quadratic solver and solve quadratic programming problem.
     * After problem is solved we'll try to bound step by StpMax
     * (Lambda will be increased if step size is too large).
     *
     * We use BFlag variable to indicate that we have to increase Lambda.
     * If it is False, we will try to increase Lambda and move to new iteration.
     */
    bflag = ae_true;
    for(i=0; i<=n-1; i++)
    {
        state->tmp0.ptr.p_double[i] = state->modeldiag.ptr.p_double[i]+*lambdav/ae_sqr(state->s.ptr.p_double[i], _state);
    }
    if( parentstate->dotimers )
    {
        parentstate->tqp = parentstate->tqp-ae_tickcount();
    }
    minqprewritediagonal(&state->qpstate, &state->tmp0, _state);
    minqpoptimize(&state->qpstate, _state);
    minqpresultsbuf(&state->qpstate, xnew, &state->qprep, _state);
    if( parentstate->dotimers )
    {
        parentstate->tqp = parentstate->tqp+ae_tickcount();
    }
    *ncholesky = *ncholesky+state->qprep.ncholesky;
    if( state->qprep.terminationtype==-3 )
    {
        
        /*
         * Infeasible constraints
         */
        *iflag = -3;
        goto lbl_2;
    }
    if( state->qprep.terminationtype==-4||state->qprep.terminationtype==-5 )
    {
        
        /*
         * Unconstrained direction of negative curvature was detected
         */
        if( !minlm_increaselambda(lambdav, nu, _state) )
        {
            *iflag = -1;
            goto lbl_2;
        }
        goto lbl_1;
    }
    ae_assert(state->qprep.terminationtype>0, "MinLM: unexpected completion code from QP solver", _state);
    ae_v_move(&state->xdir.ptr.p_double[0], 1, &xnew->ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_sub(&state->xdir.ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
    v = 0.0;
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr(state->xdir.ptr.p_double[i]/state->s.ptr.p_double[i], _state);
    }
    if( ae_isfinite(v, _state) )
    {
        v = ae_sqrt(v, _state);
        if( ae_fp_greater(state->stpmax,(double)(0))&&ae_fp_greater(v,state->stpmax) )
        {
            bflag = ae_false;
        }
    }
    else
    {
        bflag = ae_false;
    }
    if( !bflag )
    {
        
        /*
         * Solution failed:
         * try to increase lambda to make matrix positive definite and continue.
         */
        if( !minlm_increaselambda(lambdav, nu, _state) )
        {
            *iflag = -1;
            goto lbl_2;
        }
        goto lbl_1;
    }
    
    /*
     * Step in State.XDir and it is bounded by StpMax.
     *
     * We should check stopping conditions on step size here.
     * DeltaX, which is used for secant updates, is initialized here.
     *
     * This code is a bit tricky because sometimes XDir<>0, but
     * it is so small that XDir+XBase==XBase (in finite precision
     * arithmetics). So we set DeltaX to XBase, then
     * add XDir, and then subtract XBase to get exact value of
     * DeltaX.
     *
     * Step length is estimated using DeltaX.
     *
     * NOTE: stopping conditions are tested
     * for fresh models only (ModelAge=0)
     */
    ae_v_move(&deltax->ptr.p_double[0], 1, &xnew->ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_sub(&deltax->ptr.p_double[0], 1, &state->xbase.ptr.p_double[0], 1, ae_v_len(0,n-1));
    *deltaxready = ae_true;
    v = 0.0;
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr(deltax->ptr.p_double[i]/state->s.ptr.p_double[i], _state);
    }
    v = ae_sqrt(v, _state);
    if( ae_fp_less_eq(v,state->epsx) )
    {
        if( state->modelage==0 )
        {
            
            /*
             * Step is too short, model is fresh and we can rely on it.
             * Terminating.
             */
            *iflag = 2;
            goto lbl_2;
        }
        else
        {
            
            /*
             * Step is suspiciously short, but model is not fresh
             * and we can't rely on it.
             */
            *iflag = -2;
            goto lbl_2;
        }
    }
    
    /*
     * Let's evaluate new step:
     * a) if we have Fi vector, we evaluate it using rcomm, and
     *    then we manually calculate State.F as sum of squares of Fi[]
     * b) if we have F value, we just evaluate it through rcomm interface
     *
     * We prefer (a) because we may need Fi vector for additional
     * iterations
     */
    ae_v_move(&state->x.ptr.p_double[0], 1, &xnew->ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->needf = ae_false;
    state->needfi = ae_false;
    state->needfi = ae_true;
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    state->needfi = ae_false;
    v = ae_v_dotproduct(&state->fi.ptr.p_double[0], 1, &state->fi.ptr.p_double[0], 1, ae_v_len(0,m-1));
    *fnew = v;
    ae_v_move(&deltaf->ptr.p_double[0], 1, &state->fi.ptr.p_double[0], 1, ae_v_len(0,m-1));
    ae_v_sub(&deltaf->ptr.p_double[0], 1, &state->fibase.ptr.p_double[0], 1, ae_v_len(0,m-1));
    *deltafready = ae_true;
    if( !ae_isfinite(*fnew, _state) )
    {
        
        /*
         * Integrity check failed, break!
         */
        *iflag = -8;
        goto lbl_2;
    }
    if( ae_fp_greater_eq(*fnew,state->fbase) )
    {
        
        /*
         * Increase lambda and continue
         */
        if( !minlm_increaselambda(lambdav, nu, _state) )
        {
            *iflag = -1;
            goto lbl_2;
        }
        goto lbl_1;
    }
    
    /*
     * We've found our step!
     */
    *iflag = 0;
    goto lbl_2;
    goto lbl_1;
lbl_2:
    *nu = (double)(1);
    ae_assert(((*iflag>=-3&&*iflag<=0)||*iflag==-8)||*iflag>0, "MinLM: internal integrity check failed!", _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = i;
    state->rstate.ia.ptr.p_int[1] = n;
    state->rstate.ia.ptr.p_int[2] = m;
    state->rstate.ba.ptr.p_bool[0] = bflag;
    state->rstate.ra.ptr.p_double[0] = v;
    return result;
}


void _minlmstepfinder_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minlmstepfinder *p = (minlmstepfinder*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->modeldiag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xbase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fibase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->havebndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->havebndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    ae_vector_init(&p->xdir, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->choleskybuf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpct, 0, DT_INT, _state, make_automatic);
    _minqpstate_init(&p->qpstate, _state, make_automatic);
    _minqpreport_init(&p->qprep, _state, make_automatic);
    _sparsematrix_init(&p->tmpsp, _state, make_automatic);
}


void _minlmstepfinder_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minlmstepfinder       *dst = (minlmstepfinder*)_dst;
    const minlmstepfinder *src = (const minlmstepfinder*)_src;
    dst->n = src->n;
    dst->m = src->m;
    dst->stpmax = src->stpmax;
    dst->modelage = src->modelage;
    dst->maxmodelage = src->maxmodelage;
    dst->epsx = src->epsx;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    dst->f = src->f;
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    dst->needf = src->needf;
    dst->needfi = src->needfi;
    dst->fbase = src->fbase;
    ae_vector_init_copy(&dst->modeldiag, &src->modeldiag, _state, make_automatic);
    ae_vector_init_copy(&dst->xbase, &src->xbase, _state, make_automatic);
    ae_vector_init_copy(&dst->fibase, &src->fibase, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    ae_vector_init_copy(&dst->havebndl, &src->havebndl, _state, make_automatic);
    ae_vector_init_copy(&dst->havebndu, &src->havebndu, _state, make_automatic);
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    ae_vector_init_copy(&dst->xdir, &src->xdir, _state, make_automatic);
    ae_vector_init_copy(&dst->choleskybuf, &src->choleskybuf, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpct, &src->tmpct, _state, make_automatic);
    dst->actualdecrease = src->actualdecrease;
    dst->predicteddecrease = src->predicteddecrease;
    _minqpstate_init_copy(&dst->qpstate, &src->qpstate, _state, make_automatic);
    _minqpreport_init_copy(&dst->qprep, &src->qprep, _state, make_automatic);
    _sparsematrix_init_copy(&dst->tmpsp, &src->tmpsp, _state, make_automatic);
}


void _minlmstepfinder_clear(void* _p)
{
    minlmstepfinder *p = (minlmstepfinder*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->fi);
    ae_vector_clear(&p->modeldiag);
    ae_vector_clear(&p->xbase);
    ae_vector_clear(&p->fibase);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->havebndl);
    ae_vector_clear(&p->havebndu);
    ae_vector_clear(&p->s);
    _rcommstate_clear(&p->rstate);
    ae_vector_clear(&p->xdir);
    ae_vector_clear(&p->choleskybuf);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmpct);
    _minqpstate_clear(&p->qpstate);
    _minqpreport_clear(&p->qprep);
    _sparsematrix_clear(&p->tmpsp);
}


void _minlmstepfinder_destroy(void* _p)
{
    minlmstepfinder *p = (minlmstepfinder*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->fi);
    ae_vector_destroy(&p->modeldiag);
    ae_vector_destroy(&p->xbase);
    ae_vector_destroy(&p->fibase);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->havebndl);
    ae_vector_destroy(&p->havebndu);
    ae_vector_destroy(&p->s);
    _rcommstate_destroy(&p->rstate);
    ae_vector_destroy(&p->xdir);
    ae_vector_destroy(&p->choleskybuf);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmpct);
    _minqpstate_destroy(&p->qpstate);
    _minqpreport_destroy(&p->qprep);
    _sparsematrix_destroy(&p->tmpsp);
}


void _minlmstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minlmstate *p = (minlmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->j, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->h, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->reportx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->querydata, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyfi, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replydj, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->replysj, _state, make_automatic);
    ae_vector_init(&p->tmpx1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpc1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpf1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpg1, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpj1, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->tmps1, _state, make_automatic);
    ae_vector_init(&p->xbase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fibase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gbase, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->quadraticmodel, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->havebndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->havebndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->cleic, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nonmonotonicbuf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xnew, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xdir, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->deltax, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->deltaf, 0, DT_REAL, _state, make_automatic);
    _smoothnessmonitor_init(&p->smonitor, _state, make_automatic);
    ae_vector_init(&p->lastscaleused, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    ae_vector_init(&p->choleskybuf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fm1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fc1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gm1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gc1, 0, DT_REAL, _state, make_automatic);
    _minlbfgsstate_init(&p->internalstate, _state, make_automatic);
    _minlbfgsreport_init(&p->internalrep, _state, make_automatic);
    _minqpstate_init(&p->qpstate, _state, make_automatic);
    _minqpreport_init(&p->qprep, _state, make_automatic);
    _minlmstepfinder_init(&p->finderstate, _state, make_automatic);
}


void _minlmstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minlmstate       *dst = (minlmstate*)_dst;
    const minlmstate *src = (const minlmstate*)_src;
    dst->protocolversion = src->protocolversion;
    dst->n = src->n;
    dst->m = src->m;
    dst->diffstep = src->diffstep;
    dst->epsx = src->epsx;
    dst->maxits = src->maxits;
    dst->xrep = src->xrep;
    dst->stpmax = src->stpmax;
    dst->maxmodelage = src->maxmodelage;
    dst->makeadditers = src->makeadditers;
    dst->nonmonotoniccnt = src->nonmonotoniccnt;
    dst->formulatype = src->formulatype;
    dst->userterminationneeded = src->userterminationneeded;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    dst->f = src->f;
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    ae_matrix_init_copy(&dst->j, &src->j, _state, make_automatic);
    ae_matrix_init_copy(&dst->h, &src->h, _state, make_automatic);
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    dst->needf = src->needf;
    dst->needfg = src->needfg;
    dst->needfgh = src->needfgh;
    dst->needfij = src->needfij;
    dst->needfi = src->needfi;
    dst->xupdated = src->xupdated;
    dst->requesttype = src->requesttype;
    ae_vector_init_copy(&dst->reportx, &src->reportx, _state, make_automatic);
    dst->reportf = src->reportf;
    dst->querysize = src->querysize;
    dst->queryfuncs = src->queryfuncs;
    dst->queryvars = src->queryvars;
    dst->querydim = src->querydim;
    dst->queryformulasize = src->queryformulasize;
    ae_vector_init_copy(&dst->querydata, &src->querydata, _state, make_automatic);
    ae_vector_init_copy(&dst->replyfi, &src->replyfi, _state, make_automatic);
    ae_vector_init_copy(&dst->replydj, &src->replydj, _state, make_automatic);
    _sparsematrix_init_copy(&dst->replysj, &src->replysj, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpx1, &src->tmpx1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpc1, &src->tmpc1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpf1, &src->tmpf1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpg1, &src->tmpg1, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpj1, &src->tmpj1, _state, make_automatic);
    _sparsematrix_init_copy(&dst->tmps1, &src->tmps1, _state, make_automatic);
    dst->algomode = src->algomode;
    ae_vector_init_copy(&dst->xbase, &src->xbase, _state, make_automatic);
    dst->fbase = src->fbase;
    ae_vector_init_copy(&dst->fibase, &src->fibase, _state, make_automatic);
    ae_vector_init_copy(&dst->gbase, &src->gbase, _state, make_automatic);
    ae_matrix_init_copy(&dst->quadraticmodel, &src->quadraticmodel, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    ae_vector_init_copy(&dst->havebndl, &src->havebndl, _state, make_automatic);
    ae_vector_init_copy(&dst->havebndu, &src->havebndu, _state, make_automatic);
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_matrix_init_copy(&dst->cleic, &src->cleic, _state, make_automatic);
    dst->nec = src->nec;
    dst->nic = src->nic;
    dst->lambdav = src->lambdav;
    dst->nu = src->nu;
    dst->modelage = src->modelage;
    dst->nonmonotonicpos = src->nonmonotonicpos;
    ae_vector_init_copy(&dst->nonmonotonicbuf, &src->nonmonotonicbuf, _state, make_automatic);
    ae_vector_init_copy(&dst->xnew, &src->xnew, _state, make_automatic);
    ae_vector_init_copy(&dst->xdir, &src->xdir, _state, make_automatic);
    ae_vector_init_copy(&dst->deltax, &src->deltax, _state, make_automatic);
    ae_vector_init_copy(&dst->deltaf, &src->deltaf, _state, make_automatic);
    dst->deltaxready = src->deltaxready;
    dst->deltafready = src->deltafready;
    _smoothnessmonitor_init_copy(&dst->smonitor, &src->smonitor, _state, make_automatic);
    dst->teststep = src->teststep;
    ae_vector_init_copy(&dst->lastscaleused, &src->lastscaleused, _state, make_automatic);
    dst->fstagnationcnt = src->fstagnationcnt;
    dst->repiterationscount = src->repiterationscount;
    dst->repterminationtype = src->repterminationtype;
    dst->repnfunc = src->repnfunc;
    dst->repnjac = src->repnjac;
    dst->repngrad = src->repngrad;
    dst->repnhess = src->repnhess;
    dst->repncholesky = src->repncholesky;
    dst->dotimers = src->dotimers;
    dst->tstart = src->tstart;
    dst->tqp = src->tqp;
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    ae_vector_init_copy(&dst->choleskybuf, &src->choleskybuf, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    dst->actualdecrease = src->actualdecrease;
    dst->predicteddecrease = src->predicteddecrease;
    dst->xm1 = src->xm1;
    dst->xp1 = src->xp1;
    ae_vector_init_copy(&dst->fm1, &src->fm1, _state, make_automatic);
    ae_vector_init_copy(&dst->fp1, &src->fp1, _state, make_automatic);
    ae_vector_init_copy(&dst->fc1, &src->fc1, _state, make_automatic);
    ae_vector_init_copy(&dst->gm1, &src->gm1, _state, make_automatic);
    ae_vector_init_copy(&dst->gp1, &src->gp1, _state, make_automatic);
    ae_vector_init_copy(&dst->gc1, &src->gc1, _state, make_automatic);
    _minlbfgsstate_init_copy(&dst->internalstate, &src->internalstate, _state, make_automatic);
    _minlbfgsreport_init_copy(&dst->internalrep, &src->internalrep, _state, make_automatic);
    _minqpstate_init_copy(&dst->qpstate, &src->qpstate, _state, make_automatic);
    _minqpreport_init_copy(&dst->qprep, &src->qprep, _state, make_automatic);
    _minlmstepfinder_init_copy(&dst->finderstate, &src->finderstate, _state, make_automatic);
}


void _minlmstate_clear(void* _p)
{
    minlmstate *p = (minlmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->fi);
    ae_matrix_clear(&p->j);
    ae_matrix_clear(&p->h);
    ae_vector_clear(&p->g);
    ae_vector_clear(&p->reportx);
    ae_vector_clear(&p->querydata);
    ae_vector_clear(&p->replyfi);
    ae_vector_clear(&p->replydj);
    _sparsematrix_clear(&p->replysj);
    ae_vector_clear(&p->tmpx1);
    ae_vector_clear(&p->tmpc1);
    ae_vector_clear(&p->tmpf1);
    ae_vector_clear(&p->tmpg1);
    ae_matrix_clear(&p->tmpj1);
    _sparsematrix_clear(&p->tmps1);
    ae_vector_clear(&p->xbase);
    ae_vector_clear(&p->fibase);
    ae_vector_clear(&p->gbase);
    ae_matrix_clear(&p->quadraticmodel);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->havebndl);
    ae_vector_clear(&p->havebndu);
    ae_vector_clear(&p->s);
    ae_matrix_clear(&p->cleic);
    ae_vector_clear(&p->nonmonotonicbuf);
    ae_vector_clear(&p->xnew);
    ae_vector_clear(&p->xdir);
    ae_vector_clear(&p->deltax);
    ae_vector_clear(&p->deltaf);
    _smoothnessmonitor_clear(&p->smonitor);
    ae_vector_clear(&p->lastscaleused);
    _rcommstate_clear(&p->rstate);
    ae_vector_clear(&p->choleskybuf);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->fm1);
    ae_vector_clear(&p->fp1);
    ae_vector_clear(&p->fc1);
    ae_vector_clear(&p->gm1);
    ae_vector_clear(&p->gp1);
    ae_vector_clear(&p->gc1);
    _minlbfgsstate_clear(&p->internalstate);
    _minlbfgsreport_clear(&p->internalrep);
    _minqpstate_clear(&p->qpstate);
    _minqpreport_clear(&p->qprep);
    _minlmstepfinder_clear(&p->finderstate);
}


void _minlmstate_destroy(void* _p)
{
    minlmstate *p = (minlmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->fi);
    ae_matrix_destroy(&p->j);
    ae_matrix_destroy(&p->h);
    ae_vector_destroy(&p->g);
    ae_vector_destroy(&p->reportx);
    ae_vector_destroy(&p->querydata);
    ae_vector_destroy(&p->replyfi);
    ae_vector_destroy(&p->replydj);
    _sparsematrix_destroy(&p->replysj);
    ae_vector_destroy(&p->tmpx1);
    ae_vector_destroy(&p->tmpc1);
    ae_vector_destroy(&p->tmpf1);
    ae_vector_destroy(&p->tmpg1);
    ae_matrix_destroy(&p->tmpj1);
    _sparsematrix_destroy(&p->tmps1);
    ae_vector_destroy(&p->xbase);
    ae_vector_destroy(&p->fibase);
    ae_vector_destroy(&p->gbase);
    ae_matrix_destroy(&p->quadraticmodel);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->havebndl);
    ae_vector_destroy(&p->havebndu);
    ae_vector_destroy(&p->s);
    ae_matrix_destroy(&p->cleic);
    ae_vector_destroy(&p->nonmonotonicbuf);
    ae_vector_destroy(&p->xnew);
    ae_vector_destroy(&p->xdir);
    ae_vector_destroy(&p->deltax);
    ae_vector_destroy(&p->deltaf);
    _smoothnessmonitor_destroy(&p->smonitor);
    ae_vector_destroy(&p->lastscaleused);
    _rcommstate_destroy(&p->rstate);
    ae_vector_destroy(&p->choleskybuf);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->fm1);
    ae_vector_destroy(&p->fp1);
    ae_vector_destroy(&p->fc1);
    ae_vector_destroy(&p->gm1);
    ae_vector_destroy(&p->gp1);
    ae_vector_destroy(&p->gc1);
    _minlbfgsstate_destroy(&p->internalstate);
    _minlbfgsreport_destroy(&p->internalrep);
    _minqpstate_destroy(&p->qpstate);
    _minqpreport_destroy(&p->qprep);
    _minlmstepfinder_destroy(&p->finderstate);
}


void _minlmreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minlmreport *p = (minlmreport*)_p;
    ae_touch_ptr((void*)p);
}


void _minlmreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minlmreport       *dst = (minlmreport*)_dst;
    const minlmreport *src = (const minlmreport*)_src;
    dst->iterationscount = src->iterationscount;
    dst->terminationtype = src->terminationtype;
    dst->f = src->f;
    dst->nfunc = src->nfunc;
    dst->njac = src->njac;
    dst->ngrad = src->ngrad;
    dst->nhess = src->nhess;
    dst->ncholesky = src->ncholesky;
}


void _minlmreport_clear(void* _p)
{
    minlmreport *p = (minlmreport*)_p;
    ae_touch_ptr((void*)p);
}


void _minlmreport_destroy(void* _p)
{
    minlmreport *p = (minlmreport*)_p;
    ae_touch_ptr((void*)p);
}


#endif
#if defined(AE_COMPILE_NLCAUL) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
This function initializes solver state
*************************************************************************/
void minaulinitbuf(/* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* x0,
     ae_int_t n,
     const sparsematrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     /* Integer */ const ae_vector* lcsrcidx,
     ae_int_t cntlc,
     /* Real    */ const ae_vector* nl,
     /* Real    */ const ae_vector* nu,
     ae_int_t cntnlc,
     const nlpstoppingcriteria* criteria,
     ae_int_t maxouterits,
     minaulstate* state,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(cntlc==0||a->m==cntlc, "AUL: rows(A)<>CntLC", _state);
    ae_assert(maxouterits>=0, "AUL: MaxOuterIts<0", _state);
    
    /*
     * Initialization
     */
    if( maxouterits==0 )
    {
        maxouterits = 20;
    }
    state->n = n;
    state->cntlc = cntlc;
    state->cntnlc = cntnlc;
    
    /*
     * Prepare RCOMM state
     */
    ae_vector_set_length(&state->rstate.ia, 9+1, _state);
    ae_vector_set_length(&state->rstate.ba, 4+1, _state);
    ae_vector_set_length(&state->rstate.ra, 23+1, _state);
    state->rstate.stage = -1;
    state->needsj = ae_false;
    state->precrefreshupcoming = ae_false;
    state->xupdated = ae_false;
    ae_vector_set_length(&state->x, n, _state);
    ae_vector_set_length(&state->fi, 1+cntnlc, _state);
    
    /*
     * Allocate memory.
     */
    rallocv(n, &state->x0, _state);
    rsetallocv(n, 1.0, &state->s, _state);
    rvectorsetlengthatleast(&state->fscales, 1+cntnlc, _state);
    rvectorsetlengthatleast(&state->tracegamma, 1+cntnlc, _state);
    bvectorsetlengthatleast(&state->hasbndl, n, _state);
    bvectorsetlengthatleast(&state->hasbndu, n, _state);
    rvectorsetlengthatleast(&state->scaledbndl, n, _state);
    rvectorsetlengthatleast(&state->scaledbndu, n, _state);
    rallocv(2*n, &state->lagmultbc2, _state);
    rvectorsetlengthatleast(&state->lagmultxc2, 2*(cntlc+cntnlc), _state);
    rallocv(n, &state->d, _state);
    rallocv(n, &state->du, _state);
    rallocv(n, &state->laggradcur, _state);
    rallocv(n, &state->modtgtgrad0, _state);
    rallocv(n, &state->modtgtgrad1, _state);
    
    /*
     * Prepare scaled problem
     */
    rsetallocv(n, -1.0E50, &state->finitebndl, _state);
    rsetallocv(n, 1.0E50, &state->finitebndu, _state);
    for(i=0; i<=n-1; i++)
    {
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
        if( state->hasbndl.ptr.p_bool[i] )
        {
            state->scaledbndl.ptr.p_double[i] = bndl->ptr.p_double[i]/s->ptr.p_double[i];
            state->finitebndl.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i] )
        {
            state->scaledbndu.ptr.p_double[i] = bndu->ptr.p_double[i]/s->ptr.p_double[i];
            state->finitebndu.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i];
        }
        if( state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i] )
        {
            ae_assert(ae_fp_less_eq(bndl->ptr.p_double[i],bndu->ptr.p_double[i]), "AUL: integrity check failed, box constraints are inconsistent", _state);
        }
        state->x0.ptr.p_double[i] = x0->ptr.p_double[i]/s->ptr.p_double[i];
        state->s.ptr.p_double[i] = s->ptr.p_double[i];
    }
    if( cntlc>0 )
    {
        rsetallocv(n, 0.0, &state->tmpzero, _state);
        sparsecopytocrsbuf(a, &state->sparsea, _state);
        rcopyallocv(cntlc, al, &state->al, _state);
        rcopyallocv(cntlc, au, &state->au, _state);
        icopyallocv(cntlc, lcsrcidx, &state->lcsrcidx, _state);
        scaleshiftmixedlcinplace(s, &state->tmpzero, n, &state->sparsea, cntlc, &state->dummy2, 0, &state->al, &state->au, _state);
        normalizesparselcinplace(&state->sparsea, cntlc, &state->al, &state->au, n, ae_true, &state->ascales, ae_true, _state);
        ballocv(cntlc, &state->hasal, _state);
        ballocv(cntlc, &state->hasau, _state);
        for(i=0; i<=cntlc-1; i++)
        {
            state->hasal.ptr.p_bool[i] = ae_isfinite(state->al.ptr.p_double[i], _state);
            state->hasau.ptr.p_bool[i] = ae_isfinite(state->au.ptr.p_double[i], _state);
        }
    }
    if( cntnlc>0 )
    {
        rcopyallocv(cntnlc, nl, &state->rawnl, _state);
        rcopyallocv(cntnlc, nu, &state->rawnu, _state);
        ballocv(cntnlc, &state->hasnl, _state);
        ballocv(cntnlc, &state->hasnu, _state);
        for(i=0; i<=cntnlc-1; i++)
        {
            state->hasnl.ptr.p_bool[i] = ae_isfinite(state->rawnl.ptr.p_double[i], _state);
            state->hasnu.ptr.p_bool[i] = ae_isfinite(state->rawnu.ptr.p_double[i], _state);
        }
    }
    enforceboundaryconstraints(&state->x0, &state->scaledbndl, &state->hasbndl, &state->scaledbndu, &state->hasbndu, n, 0, _state);
    
    /*
     * Stopping criteria and settings
     */
    critcopy(criteria, &state->criteria, _state);
    state->maxouterits = maxouterits;
    state->restartfreq = 5;
    
    /*
     * Report fields
     */
    state->repterminationtype = 0;
    state->repbcerr = (double)(0);
    state->repbcidx = -1;
    state->replcerr = (double)(0);
    state->replcidx = -1;
    state->repnlcerr = (double)(0);
    state->repnlcidx = -1;
    state->repiterationscount = 0;
    
    /*
     * Integrity checks
     */
    ae_assert(ae_fp_less(nlcaul_auldeltadecrease,nlcaul_auldeltaincrease), "MinAUL: integrity check failed", _state);
}


/*************************************************************************
This function performs actual processing for  AUL  algorithm.  It  expects
that caller redirects its reverse communication  requests NeedFiJ/XUpdated
to external user who will provide analytic derivative (or  handle  reports
about progress).

In case external user does not have analytic derivative, it is responsibility
of caller to intercept NeedFiJ request and  replace  it  with  appropriate
numerical differentiation scheme.

Results are stored:
* point - in State.StepKX

IMPORTANT: this function works with scaled problem formulation; it is
           responsibility of the caller to unscale request and scale
           Jacobian.
           
NOTE: SMonitor is expected to be correctly initialized smoothness monitor.

  -- ALGLIB --
     Copyright 05.03.2018 by Bochkanov Sergey
*************************************************************************/
ae_bool minauliteration(minaulstate* state,
     smoothnessmonitor* smonitor,
     ae_bool userterminationneeded,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t cntlc;
    ae_int_t cntnlc;
    ae_int_t nconstr;
    ae_bool dotrace;
    ae_bool doprobingalways;
    ae_bool dodetailedtrace;
    double lagcur;
    double lag0;
    double lag1;
    double laga;
    double lagb;
    double ci;
    double modtgt0;
    double modtgt1;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t jj;
    double mx;
    double setscaleto;
    double multiplyby;
    ae_int_t mcinfo;
    ae_bool inneriterationsstopped;
    double stpproposed;
    double p;
    double dp;
    double d2p;
    double steplengthtoprobe;
    double v;
    double vv;
    ae_bool stepaccepted;
    double errprim;
    double errdual;
    double errcmpl;
    double preverrprim;
    double preverrdual;
    double preverrcmpl;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        n = state->rstate.ia.ptr.p_int[0];
        cntlc = state->rstate.ia.ptr.p_int[1];
        cntnlc = state->rstate.ia.ptr.p_int[2];
        nconstr = state->rstate.ia.ptr.p_int[3];
        i = state->rstate.ia.ptr.p_int[4];
        j = state->rstate.ia.ptr.p_int[5];
        j0 = state->rstate.ia.ptr.p_int[6];
        j1 = state->rstate.ia.ptr.p_int[7];
        jj = state->rstate.ia.ptr.p_int[8];
        mcinfo = state->rstate.ia.ptr.p_int[9];
        dotrace = state->rstate.ba.ptr.p_bool[0];
        doprobingalways = state->rstate.ba.ptr.p_bool[1];
        dodetailedtrace = state->rstate.ba.ptr.p_bool[2];
        inneriterationsstopped = state->rstate.ba.ptr.p_bool[3];
        stepaccepted = state->rstate.ba.ptr.p_bool[4];
        lagcur = state->rstate.ra.ptr.p_double[0];
        lag0 = state->rstate.ra.ptr.p_double[1];
        lag1 = state->rstate.ra.ptr.p_double[2];
        laga = state->rstate.ra.ptr.p_double[3];
        lagb = state->rstate.ra.ptr.p_double[4];
        ci = state->rstate.ra.ptr.p_double[5];
        modtgt0 = state->rstate.ra.ptr.p_double[6];
        modtgt1 = state->rstate.ra.ptr.p_double[7];
        mx = state->rstate.ra.ptr.p_double[8];
        setscaleto = state->rstate.ra.ptr.p_double[9];
        multiplyby = state->rstate.ra.ptr.p_double[10];
        stpproposed = state->rstate.ra.ptr.p_double[11];
        p = state->rstate.ra.ptr.p_double[12];
        dp = state->rstate.ra.ptr.p_double[13];
        d2p = state->rstate.ra.ptr.p_double[14];
        steplengthtoprobe = state->rstate.ra.ptr.p_double[15];
        v = state->rstate.ra.ptr.p_double[16];
        vv = state->rstate.ra.ptr.p_double[17];
        errprim = state->rstate.ra.ptr.p_double[18];
        errdual = state->rstate.ra.ptr.p_double[19];
        errcmpl = state->rstate.ra.ptr.p_double[20];
        preverrprim = state->rstate.ra.ptr.p_double[21];
        preverrdual = state->rstate.ra.ptr.p_double[22];
        preverrcmpl = state->rstate.ra.ptr.p_double[23];
    }
    else
    {
        n = 359;
        cntlc = -58;
        cntnlc = -919;
        nconstr = -909;
        i = 81;
        j = 255;
        j0 = 74;
        j1 = -788;
        jj = 809;
        mcinfo = 205;
        dotrace = ae_false;
        doprobingalways = ae_true;
        dodetailedtrace = ae_false;
        inneriterationsstopped = ae_true;
        stepaccepted = ae_true;
        lagcur = -698.0;
        lag0 = -900.0;
        lag1 = -318.0;
        laga = -940.0;
        lagb = 1016.0;
        ci = -229.0;
        modtgt0 = -536.0;
        modtgt1 = 487.0;
        mx = -115.0;
        setscaleto = 886.0;
        multiplyby = 346.0;
        stpproposed = -722.0;
        p = -413.0;
        dp = -461.0;
        d2p = 927.0;
        steplengthtoprobe = 201.0;
        v = 922.0;
        vv = -154.0;
        errprim = 306.0;
        errdual = -1011.0;
        errcmpl = 951.0;
        preverrprim = -463.0;
        preverrdual = 88.0;
        preverrcmpl = -861.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    
    /*
     * Routine body
     */
    n = state->n;
    cntlc = state->cntlc;
    cntnlc = state->cntnlc;
    nconstr = state->cntlc+state->cntnlc;
    dotrace = ae_is_trace_enabled("AUL");
    dodetailedtrace = dotrace&&ae_is_trace_enabled("AUL.DETAILED");
    doprobingalways = dotrace&&ae_is_trace_enabled("AUL.PROBING");
    
    /*
     * Prepare rcomm interface
     */
    state->needsj = ae_false;
    state->precrefreshupcoming = ae_false;
    state->xupdated = ae_false;
    
    /*
     * Initialize algorithm state.
     *
     * Create two points that will be maintained during iterations:
     * * a 'virtual' point which is used by the AUL solver. This point is
     *   subject to box constraints, but they are enforced only approximately.
     *   Constraint violation in the intermediate points can be very large.
     *   However, the user does not see this point when we send a callback request.
     * * a 'true' point which actually sent to the user - obtained by clipping
     *   the virtual point to the box constrained area.
     */
    for(i=0; i<=cntnlc; i++)
    {
        state->fscales.ptr.p_double[i] = 1.0;
        state->tracegamma.ptr.p_double[i] = 0.0;
    }
    state->repterminationtype = 0;
    state->repiterationscount = 0;
    state->repnfev = 0;
    state->inneriterationscount = 0;
    state->outeriterationscount = 0;
    rsetv(2*n, 0.0, &state->lagmultbc2, _state);
    for(i=0; i<=n-1; i++)
    {
        if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->scaledbndl.ptr.p_double[i],state->scaledbndu.ptr.p_double[i]) )
        {
            
            /*
             * Equality constraint
             */
            state->lagmultbc2.ptr.p_double[2*i+0] = 0.0;
            state->lagmultbc2.ptr.p_double[2*i+1] = 0.0;
            continue;
        }
        if( state->hasbndl.ptr.p_bool[i] )
        {
            state->lagmultbc2.ptr.p_double[2*i+0] = nlcaul_defaultinequalitylag;
        }
        if( state->hasbndu.ptr.p_bool[i] )
        {
            state->lagmultbc2.ptr.p_double[2*i+1] = nlcaul_defaultinequalitylag;
        }
    }
    rsetv(2*(cntlc+cntnlc), 0.0, &state->lagmultxc2, _state);
    for(i=0; i<=cntlc-1; i++)
    {
        if( (state->hasal.ptr.p_bool[i]&&state->hasau.ptr.p_bool[i])&&ae_fp_eq(state->al.ptr.p_double[i],state->au.ptr.p_double[i]) )
        {
            
            /*
             * Equality constraint
             */
            state->lagmultxc2.ptr.p_double[2*i+0] = 0.0;
            state->lagmultxc2.ptr.p_double[2*i+1] = 0.0;
            continue;
        }
        if( state->hasal.ptr.p_bool[i] )
        {
            state->lagmultxc2.ptr.p_double[2*i+0] = nlcaul_defaultinequalitylag;
        }
        if( state->hasau.ptr.p_bool[i] )
        {
            state->lagmultxc2.ptr.p_double[2*i+1] = nlcaul_defaultinequalitylag;
        }
    }
    for(i=0; i<=cntnlc-1; i++)
    {
        if( (state->hasnl.ptr.p_bool[i]&&state->hasnu.ptr.p_bool[i])&&ae_fp_eq(state->rawnl.ptr.p_double[i],state->rawnu.ptr.p_double[i]) )
        {
            
            /*
             * Equality constraint
             */
            state->lagmultxc2.ptr.p_double[2*cntlc+2*i+0] = 0.0;
            state->lagmultxc2.ptr.p_double[2*cntlc+2*i+1] = 0.0;
            continue;
        }
        if( state->hasnl.ptr.p_bool[i] )
        {
            state->lagmultxc2.ptr.p_double[2*cntlc+2*i+0] = nlcaul_defaultinequalitylag;
        }
        if( state->hasnu.ptr.p_bool[i] )
        {
            state->lagmultxc2.ptr.p_double[2*cntlc+2*i+1] = nlcaul_defaultinequalitylag;
        }
    }
    vfjallocsparse(n, 1+cntnlc, &state->xvirt, _state);
    vfjallocsparse(n, 1+cntnlc, &state->xtrue, _state);
    rcopyv(n, &state->x0, &state->xvirt.x, _state);
    
    /*
     * Evaluate function vector and Jacobian at X0, send first location report.
     * Compute initial violation of constraints.
     */
    nlcaul_aulsendvirtualpoint(state, &state->xvirt.x, &state->xtrue.x, _state);
    state->needsj = ae_true;
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    state->needsj = ae_false;
    if( !nlcaul_aulretrievesj(state, &state->xvirt, &state->xtrue, _state) )
    {
        
        /*
         * Failed to retrieve function/Jacobian, infinities detected!
         */
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    nlcaul_aulsendtruepoint(state, &state->xtrue.x, _state);
    state->f = state->xtrue.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0];
    state->xupdated = ae_true;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->xupdated = ae_false;
    checklc2violation(&state->sparsea, &state->al, &state->au, &state->lcsrcidx, cntlc, &state->xtrue.x, &state->replcerr, &state->replcidx, _state);
    unscaleandchecknlc2violation(&state->xtrue.fi, &state->fscales, &state->rawnl, &state->rawnu, cntnlc, &state->repnlcerr, &state->repnlcidx, _state);
    
    /*
     * Trace output (if needed)
     */
    if( dotrace )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("//  AUL SOLVER STARTED                                                                            //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("N             = %6d\n",
            (int)(n));
        ae_trace("cntLC         = %6d\n",
            (int)(state->cntlc));
        ae_trace("cntNLC        = %6d\n",
            (int)(state->cntnlc));
    }
    
    /*
     * Perform outer (NLC) iterations
     */
    preverrprim = 1.0E50;
    preverrdual = 1.0E50;
    preverrcmpl = 1.0E50;
    state->fstagnationcnt = 0;
    state->longeststp = 0.0;
    state->rho = 500.0;
    hessianinitlowrank(&state->hessaug, n, ae_minint(n, nlcaul_xbfgsmemlen, _state), ae_maxreal(critgeteps(&state->criteria, _state), ae_sqrt(ae_machineepsilon, _state), _state), nlcaul_xbfgsmaxhess*((double)1+state->rho), _state);
    hessianinitlowrank(&state->hesstgt, n, 1, ae_maxreal(critgeteps(&state->criteria, _state), ae_sqrt(ae_machineepsilon, _state), _state), nlcaul_xbfgsmaxhess, _state);
    vfjcopy(&state->xvirt, &state->xvirtbest, _state);
    vfjcopy(&state->xtrue, &state->xtruebest, _state);
    state->besterr = ae_maxrealnumber;
    state->bestiteridx = -1;
    state->besterrnegligibleupdates = 0;
    nlcaul_preconditionerinit(state, state->restartfreq, &state->preconditioner, _state);
lbl_6:
    if( ae_false )
    {
        goto lbl_7;
    }
    if( dotrace )
    {
        ae_trace("\n=== INNER ITERATION %5d, OUTER ITERATION %5d ===================================================\n",
            (int)(state->repiterationscount),
            (int)(state->outeriterationscount));
    }
    
    /*
     * Renormalize target function and/or constraints, if some of them have too large magnitudes
     */
    for(i=0; i<=cntnlc; i++)
    {
        
        /*
         * Determine (a) multiplicative coefficient applied to function value
         * and Jacobian row, and (b) new value of the function scale.
         */
        mx = (double)(0);
        j0 = state->xvirt.sj.ridx.ptr.p_int[i];
        j1 = state->xvirt.sj.ridx.ptr.p_int[i+1]-1;
        for(j=j0; j<=j1; j++)
        {
            mx = mx+ae_sqr(state->xvirt.sj.vals.ptr.p_double[j], _state);
        }
        mx = ae_sqrt(mx, _state);
        multiplyby = 1.0;
        setscaleto = state->fscales.ptr.p_double[i];
        if( ae_fp_greater_eq(mx,nlcaul_aulbigscale) )
        {
            multiplyby = (double)1/mx;
            setscaleto = state->fscales.ptr.p_double[i]*mx;
        }
        if( ae_fp_less_eq(mx,nlcaul_aulsmallscale)&&ae_fp_greater(state->fscales.ptr.p_double[i],1.0) )
        {
            if( ae_fp_greater(state->fscales.ptr.p_double[i]*mx,(double)(1)) )
            {
                multiplyby = (double)1/mx;
                setscaleto = state->fscales.ptr.p_double[i]*mx;
            }
            else
            {
                multiplyby = state->fscales.ptr.p_double[i];
                setscaleto = 1.0;
            }
        }
        if( ae_fp_neq(multiplyby,1.0) )
        {
            
            /*
             * Function #I needs renormalization:
             * * update function vector element and Jacobian matrix row
             * * update slack variable and box constraints
             * * update FScales[] and TraceGamma[] arrays
             */
            state->xvirt.fi.ptr.p_double[i] = state->xvirt.fi.ptr.p_double[i]*multiplyby;
            state->xtrue.fi.ptr.p_double[i] = state->xtrue.fi.ptr.p_double[i]*multiplyby;
            j0 = state->xvirt.sj.ridx.ptr.p_int[i];
            j1 = state->xvirt.sj.ridx.ptr.p_int[i+1]-1;
            for(j=j0; j<=j1; j++)
            {
                state->xvirt.sj.vals.ptr.p_double[j] = state->xvirt.sj.vals.ptr.p_double[j]*multiplyby;
            }
            j0 = state->xtrue.sj.ridx.ptr.p_int[i];
            j1 = state->xtrue.sj.ridx.ptr.p_int[i+1]-1;
            for(j=j0; j<=j1; j++)
            {
                state->xtrue.sj.vals.ptr.p_double[j] = state->xtrue.sj.vals.ptr.p_double[j]*multiplyby;
            }
            if( i==0 )
            {
                
                /*
                 * Rescaling target, rescale all Lagrange multipliers
                 */
                rmulv(2*n, multiplyby, &state->lagmultbc2, _state);
                rmulv(2*(cntlc+cntnlc), multiplyby, &state->lagmultxc2, _state);
                if( dotrace )
                {
                    ae_trace("> target rescaled by %0.2e during renormalization\n",
                        (double)(multiplyby));
                }
            }
            else
            {
                
                /*
                 * Rescaling nonlinear constraint, rescale only quantities related to this constraint
                 */
                if( state->hasnl.ptr.p_bool[i-1] )
                {
                    state->rawnl.ptr.p_double[i-1] = state->rawnl.ptr.p_double[i-1]*multiplyby;
                }
                if( state->hasnu.ptr.p_bool[i-1] )
                {
                    state->rawnu.ptr.p_double[i-1] = state->rawnu.ptr.p_double[i-1]*multiplyby;
                }
                state->lagmultxc2.ptr.p_double[2*(cntlc+(i-1))+0] = state->lagmultxc2.ptr.p_double[2*(cntlc+(i-1))+0]/multiplyby;
                state->lagmultxc2.ptr.p_double[2*(cntlc+(i-1))+1] = state->lagmultxc2.ptr.p_double[2*(cntlc+(i-1))+1]/multiplyby;
            }
            state->fscales.ptr.p_double[i] = setscaleto;
            state->tracegamma.ptr.p_double[i] = state->tracegamma.ptr.p_double[i]*multiplyby;
        }
    }
    
    /*
     * Trace output (if needed)
     */
    if( dotrace )
    {
        if( dodetailedtrace )
        {
            ae_trace("> printing raw data (prior to applying variable and function scales)\n");
            ae_trace("X (raw)       = ");
            tracevectorunscaledunshiftedautoprec(&state->xvirt.x, n, &state->s, ae_true, &state->s, ae_false, _state);
            ae_trace("\n");
            ae_trace("> printing scaled data (after applying variable and function scales)\n");
            ae_trace("X (scaled)    = ");
            tracevectorautoprec(&state->xvirt.x, 0, n, _state);
            ae_trace("\n");
            ae_trace("FScales       = ");
            tracevectorautoprec(&state->fscales, 0, 1+cntnlc, _state);
            ae_trace("\n");
            ae_trace("GammaScl      = ");
            tracevectorautoprec(&state->tracegamma, 0, 1+cntnlc, _state);
            ae_trace("\n");
            ae_trace("Fi (scaled)   = ");
            tracevectorautoprec(&state->xvirt.fi, 0, 1+cntnlc, _state);
            ae_trace("\n");
            ae_trace("lagMultBC     = ");
            tracevectorautoprec(&state->lagmultbc2, 0, 2*n, _state);
            ae_trace("\n");
            ae_trace("lagMultLC     = ");
            tracevectorautoprec(&state->lagmultxc2, 0, 2*cntlc, _state);
            ae_trace("\n");
            ae_trace("lagMultNLC    = ");
            tracevectorautoprec(&state->lagmultxc2, 2*cntlc, 2*cntlc+2*cntnlc, _state);
            ae_trace("\n");
        }
        mx = (double)(0);
        for(i=1; i<=cntnlc; i++)
        {
            mx = ae_maxreal(mx, ae_fabs(state->xvirt.fi.ptr.p_double[i], _state), _state);
        }
        ae_trace("Rho           = %0.3e    (penalty for violation of constraints)\n",
            (double)(state->rho));
        ae_trace("lin.violation = %0.3e    (scaled violation of linear constraints)\n",
            (double)(state->replcerr));
        ae_trace("nlc.violation = %0.3e    (scaled violation of nonlinear constraints)\n",
            (double)(mx));
        ae_trace("gamma0        = %0.3e    (Hessian 2-norm estimate for target)\n",
            (double)(state->tracegamma.ptr.p_double[0]));
        j = 0;
        for(i=0; i<=cntnlc; i++)
        {
            if( ae_fp_greater(state->tracegamma.ptr.p_double[i],state->tracegamma.ptr.p_double[j]) )
            {
                j = i;
            }
        }
        ae_trace("gammaMax      = %0.3e    (maximum over Hessian 2-norm estimates for target/constraints)\n",
            (double)(state->tracegamma.ptr.p_double[j]));
        ae_trace("arg(gammaMax) = %0d             (function index; 0 for target, >0 for nonlinear constraints)\n",
            (int)(j));
        ae_trace("|x|inf        = %0.3e\n",
            (double)(rmaxabsv(n, &state->xvirt.x, _state)));
        ae_trace("|LagBC|inf    = %0.3e\n",
            (double)(rmaxabsv(2*n, &state->lagmultbc2, _state)));
        ae_trace("|LagXC|inf    = %0.3e\n",
            (double)(rmaxabsv(2*(cntlc+cntnlc), &state->lagmultxc2, _state)));
    }
    
    /*
     * Save current state
     */
    vfjcopy(&state->xvirt, &state->xvirtprev, _state);
    vfjcopy(&state->xtrue, &state->xtrueprev, _state);
    
    /*
     * Send most recent copy of L-BFGS Hessians to the preconditioner
     */
    nlcaul_preconditionersendupdates(state, &state->preconditioner, &state->xvirt, state->rho, &state->lagmultbc2, &state->lagmultxc2, &state->hesstgt, &state->hessaug, _state);
    
    /*
     * Compute preconditioned step direction
     */
    if( dotrace )
    {
        ae_trace("\n--- LBFGS-preconditioned gradient descent ----------------------------------------------------------\n");
    }
    nlcaul_auglagfg(state, &state->xvirt, state->rho, &state->lagmultbc2, &state->lagmultxc2, &lagcur, &state->laggradcur, _state);
    nlcaul_modtargetfg(state, &state->xvirt, state->rho, state->rho, &modtgt0, &state->modtgtgrad0, _state);
    lag0 = lagcur;
    nlcaul_preconditionersolve(state, &state->preconditioner, &state->laggradcur, &state->d, _state);
    if( dotrace )
    {
        if( dodetailedtrace )
        {
            ae_trace("grad L(X0)    = ");
            tracevectorautoprec(&state->laggradcur, 0, n, _state);
            ae_trace("\n");
            ae_trace("D             = ");
            tracevectorautoprec(&state->d, 0, n, _state);
            ae_trace("\n");
        }
        ae_trace("(D,G)         = %0.2e (directional derivative)\n",
            (double)(rdotv(n, &state->laggradcur, &state->d, _state)));
    }
    
    /*
     * Perform optimization along step directions
     */
    rallocv(n, &state->work, _state);
    state->mcstage = 0;
    state->stp = 1.0;
    rcopyv(n, &state->d, &state->du, _state);
    linminnormalized(&state->d, &state->stp, n, _state);
    stpproposed = state->stp;
    state->longeststp = coalesce(state->longeststp, state->stp, _state);
    state->stplimit = (double)10*state->longeststp;
    rcopyallocv(n, &state->laggradcur, &state->laggrad0, _state);
    lag0 = lagcur;
    mcsrch(n, &state->xvirt.x, &lagcur, &state->laggradcur, &state->d, &state->stp, state->stplimit, nlcaul_gtol, &mcinfo, &state->nfev, &state->work, &state->lstate, &state->mcstage, _state);
lbl_8:
    if( state->mcstage==0 )
    {
        goto lbl_9;
    }
    nlcaul_clearrequestfields(state, _state);
    nlcaul_aulsendvirtualpoint(state, &state->xvirt.x, &state->xtrue.x, _state);
    state->needsj = ae_true;
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->needsj = ae_false;
    if( !nlcaul_aulretrievesj(state, &state->xvirt, &state->xtrue, _state) )
    {
        
        /*
         * Failed to retrieve function/Jacobian, infinities detected!
         */
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    nlcaul_auglagfg(state, &state->xvirt, state->rho, &state->lagmultbc2, &state->lagmultxc2, &lagcur, &state->laggradcur, _state);
    mcsrch(n, &state->xvirt.x, &lagcur, &state->laggradcur, &state->d, &state->stp, state->stplimit, nlcaul_gtol, &mcinfo, &state->nfev, &state->work, &state->lstate, &state->mcstage, _state);
    goto lbl_8;
lbl_9:
    lag1 = lagcur;
    if( dotrace )
    {
        ae_trace("> line search finished with relative step %0.3f, absolute step %0.3e, %0d evals, MCINFO=%0d\n",
            (double)(state->stp/stpproposed),
            (double)(state->stp*rmaxabsv(n, &state->d, _state)),
            (int)(state->nfev),
            (int)(mcinfo));
        ae_trace("mod Tgt:        %17.9e -> %17.9e (delta=%11.3e)\n",
            (double)(state->xvirtprev.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0]),
            (double)(state->xvirt.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0]),
            (double)(state->xvirt.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0]-state->xvirtprev.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0]));
        ae_trace("raw Lag:        %17.9e -> %17.9e (delta=%11.3e)\n",
            (double)(lag0),
            (double)(lag1),
            (double)(lag1-lag0));
    }
    nlcaul_clearrequestfields(state, _state);
    nlcaul_aulsendtruepoint(state, &state->xtrue.x, _state);
    state->f = state->xtrue.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0];
    state->xupdated = ae_true;
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    state->xupdated = ae_false;
    state->repnfev = state->repnfev+state->nfev;
    
    /*
     * Update curvature information, statistics, errors
     */
    state->longeststp = ae_maxreal(state->longeststp, state->stp, _state);
    if( mcinfo==1 )
    {
        hessianupdate(&state->hessaug, &state->xvirtprev.x, &state->laggrad0, &state->xvirt.x, &state->laggradcur, dotrace, _state);
    }
    else
    {
        if( dotrace )
        {
            ae_trace("> L-BFGS update for an augmented Lagrangian is rejected (MCINFO<>1)\n");
        }
    }
    nlcaul_modtargetfg(state, &state->xvirt, state->rho, state->rho, &modtgt1, &state->modtgtgrad1, _state);
    hessianupdate(&state->hesstgt, &state->xvirtprev.x, &state->modtgtgrad0, &state->xvirt.x, &state->modtgtgrad1, dotrace, _state);
    checklc2violation(&state->sparsea, &state->al, &state->au, &state->lcsrcidx, cntlc, &state->xtrue.x, &state->replcerr, &state->replcidx, _state);
    unscaleandchecknlc2violation(&state->xtrue.fi, &state->fscales, &state->rawnl, &state->rawnu, cntnlc, &state->repnlcerr, &state->repnlcidx, _state);
    nlcaul_computeerrors(state, &state->xvirt, &errprim, &errdual, &errcmpl, _state);
    
    /*
     * Perform agressive probing of the search direction - additional function evaluations
     * which help us to determine possible discontinuity and nonsmoothness of the problem
     */
    if( !doprobingalways )
    {
        goto lbl_10;
    }
    vfjcopy(&state->xvirtprev, &state->xvirtprobe, _state);
    vfjcopy(&state->xtrueprev, &state->xtrueprobe, _state);
    steplengthtoprobe = ae_maxreal((double)5*(state->stp/stpproposed), 0.01, _state);
    smoothnessmonitorstartlagrangianprobing(smonitor, &state->xvirtprobe.x, &state->du, steplengthtoprobe, state->repiterationscount, state->outeriterationscount, _state);
lbl_12:
    if( !smoothnessmonitorprobelagrangian(smonitor, _state) )
    {
        goto lbl_13;
    }
    rcopyv(n, &smonitor->lagprobx, &state->xvirtprobe.x, _state);
    nlcaul_clearrequestfields(state, _state);
    nlcaul_aulsendvirtualpoint(state, &state->xvirtprobe.x, &state->xtrueprobe.x, _state);
    state->needsj = ae_true;
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    state->needsj = ae_false;
    if( !nlcaul_aulretrievesj(state, &state->xvirtprobe, &state->xtrueprobe, _state) )
    {
        
        /*
         * Failed to retrieve function/Jacobian, infinities detected!
         */
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    rcopyallocv(1+cntnlc, &state->xvirtprobe.fi, &smonitor->lagprobfi, _state);
    rsetallocm(1+cntnlc, n, 0.0, &smonitor->lagprobj, _state);
    for(i=0; i<=cntnlc; i++)
    {
        j0 = state->xvirtprobe.sj.ridx.ptr.p_int[i];
        j1 = state->xvirtprobe.sj.ridx.ptr.p_int[i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            smonitor->lagprobj.ptr.pp_double[i][state->xvirtprobe.sj.idx.ptr.p_int[jj]] = state->xvirtprobe.sj.vals.ptr.p_double[jj];
        }
    }
    rallocv(n, &state->tmpg, _state);
    nlcaul_auglagfg(state, &state->xvirtprobe, state->rho, &state->lagmultbc2, &state->lagmultxc2, &smonitor->lagprobrawlag, &state->tmpg, _state);
    goto lbl_12;
lbl_13:
    ae_trace("*** ------------------------------------------------------------\n");
    ae_trace("*** |                 probing search direction                 |\n");
    ae_trace("*** ------------------------------------------------------------\n");
    ae_trace("*** |  Step  | Lagrangian (unaugmentd)|   Projected  target    |\n");
    ae_trace("*** |along  D|     must be smooth     |     must be smooth     |\n");
    ae_trace("*** |        | function   |    slope  | function   |    slope  |\n");
    smoothnessmonitortracelagrangianprobingresults(smonitor, _state);
lbl_10:
    
    /*
     * Trace
     */
    if( dotrace )
    {
    }
    
    /*
     * Trace
     */
    if( dotrace )
    {
        ae_trace("\n--- inner iteration ends ---------------------------------------------------------------------------\n");
        ae_trace("> printing primal/dual/complementary errors:\n");
        ae_trace("prim.feas     = %0.3e    (primal feasibility error, inf-norm)\n",
            (double)(errprim));
        ae_trace("dual.feas     = %0.3e    (dual feasibility error, inf-norm)\n",
            (double)(errdual));
        ae_trace("cmpl.slckness = %0.3e    (complementary slackness, inf-norm)\n",
            (double)(errcmpl));
    }
    
    /*
     * Update iterations counts.
     * Check stopping criteria for inner iterations.
     */
    state->repiterationscount = state->repiterationscount+1;
    state->inneriterationscount = state->inneriterationscount+1;
    inneriterationsstopped = ae_false;
    state->fstagnationcnt = icase2(ae_fp_less_eq(ae_fabs(lag1-lag0, _state),nlcaul_stagnationepsf*rmax3(ae_fabs(lag1, _state), ae_fabs(lag0, _state), (double)(1), _state)), state->fstagnationcnt+1, 0, _state);
    v = ae_fabs(lag1-lag0, _state)/rmaxabs3(lag1, lag0, (double)(1), _state);
    vv = ae_fabs(modtgt1-modtgt0, _state)/rmaxabs3(modtgt1, modtgt0, (double)(1), _state);
    if( ae_fp_less_eq(ae_maxreal(v, vv, _state),critgetepsf(&state->criteria, _state)) )
    {
        inneriterationsstopped = ae_true;
        if( dotrace )
        {
            ae_trace("> stopping condition for inner iterations met: function change is smaller than %0.3e\n",
                (double)(critgetepsf(&state->criteria, _state)));
        }
    }
    if( ae_fp_less_eq(rmaxabsv(n, &state->d, _state)*state->stp,critgeteps(&state->criteria, _state)) )
    {
        inneriterationsstopped = ae_true;
        if( dotrace )
        {
            ae_trace("> stopping condition for inner iterations met: step is smaller than %0.3e\n",
                (double)(critgeteps(&state->criteria, _state)));
        }
    }
    if( ae_fp_less_eq(rmaxabsv(n, &state->d, _state)*state->stp,0.001*ae_sqrt(ae_machineepsilon, _state)) )
    {
        inneriterationsstopped = ae_true;
        if( dotrace )
        {
            ae_trace("> stopping condition for inner iterations met: step is smaller than the absolute minimum\n");
        }
    }
    if( critgetmaxits(&state->criteria, _state)>0&&state->inneriterationscount>=critgetmaxits(&state->criteria, _state) )
    {
        inneriterationsstopped = ae_true;
        if( dotrace )
        {
            ae_trace("> stopping condition for inner iterations met: %0d iterations performed\n",
                (int)(state->inneriterationscount));
        }
    }
    if( state->fstagnationcnt>=nlcaul_fstagnationlimit )
    {
        if( dotrace )
        {
            ae_trace("> stopping criteria are too stringent: F stagnated for %0d its, stopping\n",
                (int)(state->fstagnationcnt));
        }
        inneriterationsstopped = ae_true;
    }
    
    /*
     * Inner iterations stopped (or user requested termination)
     *
     * Update Lagrange multipliers, penalties, check stopping criteria for outer iterations.
     */
    if( inneriterationsstopped||userterminationneeded )
    {
        
        /*
         * Update best point so far
         */
        if( ae_fp_less(rmax3(errprim, errdual, errcmpl, _state),state->besterr) )
        {
            if( ae_fp_greater(rmax3(errprim, errdual, errcmpl, _state),nlcaul_besterrstagnationcoeff*state->besterr) )
            {
                state->besterrnegligibleupdates = state->besterrnegligibleupdates+1;
            }
            vfjcopy(&state->xvirt, &state->xvirtbest, _state);
            vfjcopy(&state->xtrue, &state->xtruebest, _state);
            state->besterr = rmax3(errprim, errdual, errcmpl, _state);
            state->bestiteridx = state->outeriterationscount;
        }
        
        /*
         * Advance outer iterations counter, reset inner iterations count, check stopping criteria
         */
        state->outeriterationscount = state->outeriterationscount+1;
        state->inneriterationscount = 0;
        state->fstagnationcnt = 0;
        if( (ae_fp_less_eq(errprim,(double)10*ae_sqrt(ae_machineepsilon, _state))&&ae_fp_less_eq(errdual,(double)10*ae_sqrt(ae_machineepsilon, _state)))&&ae_fp_less_eq(errcmpl,(double)10*ae_sqrt(ae_machineepsilon, _state)) )
        {
            if( dotrace )
            {
                ae_trace("> primal, dual and complementary slackness errors are small, stopping\n");
            }
            state->repterminationtype = 1;
            goto lbl_7;
        }
        if( state->besterrnegligibleupdates>=nlcaul_besterrstagnationlimit )
        {
            if( dotrace )
            {
                ae_trace("> primal/dual/complementary error had too many negligible updates, stopping\n");
            }
            state->repterminationtype = 7;
            goto lbl_7;
        }
        if( state->bestiteridx<state->outeriterationscount-nlcaul_besterrstagnationlimit )
        {
            if( dotrace )
            {
                ae_trace("> primal/dual/complementary error stagnated for too long, stopping\n");
            }
            state->repterminationtype = 7;
            goto lbl_7;
        }
        if( userterminationneeded )
        {
            if( dotrace )
            {
                ae_trace("> user requested termination, stopping\n");
            }
            state->repterminationtype = 8;
            goto lbl_7;
        }
        if( state->outeriterationscount>=state->maxouterits )
        {
            state->repterminationtype = 5;
            goto lbl_7;
        }
        
        /*
         * Save primal/dual/complementary error estimates
         */
        preverrprim = errprim;
        preverrdual = errdual;
        preverrcmpl = errcmpl;
        
        /*
         * Update Lagrange multipliers
         */
        for(i=0; i<=n-1; i++)
        {
            ae_assert(ae_fp_greater(nlcaul_aulmaxgrowth,1.0), "AUL: integrity check 2444 failed", _state);
            if( !state->hasbndl.ptr.p_bool[i]&&!state->hasbndu.ptr.p_bool[i] )
            {
                continue;
            }
            if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->scaledbndl.ptr.p_double[i],state->scaledbndu.ptr.p_double[i]) )
            {
                v = ae_minreal(nlcaul_maxlagmult, nlcaul_aulmaxgrowth*(ae_fabs(state->lagmultbc2.ptr.p_double[2*i+0], _state)+(double)1), _state);
                state->lagmultbc2.ptr.p_double[2*i+0] = boundval(state->lagmultbc2.ptr.p_double[2*i+0]+state->rho*(state->xvirt.x.ptr.p_double[i]-state->scaledbndl.ptr.p_double[i]), -v, v, _state);
                continue;
            }
            if( state->hasbndl.ptr.p_bool[i] )
            {
                inequalityshiftedbarrierfunction((state->xvirt.x.ptr.p_double[i]-state->scaledbndl.ptr.p_double[i])*state->rho+(double)1, &p, &dp, &d2p, _state);
                dp = ae_fabs(dp, _state);
                dp = ae_minreal(dp, nlcaul_aulmaxgrowth, _state);
                dp = ae_maxreal(dp, (double)1/nlcaul_aulmaxgrowth, _state);
                state->lagmultbc2.ptr.p_double[2*i+0] = boundval(state->lagmultbc2.ptr.p_double[2*i+0]*dp, nlcaul_minlagmult, nlcaul_maxlagmult, _state);
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                inequalityshiftedbarrierfunction((state->scaledbndu.ptr.p_double[i]-state->xvirt.x.ptr.p_double[i])*state->rho+(double)1, &p, &dp, &d2p, _state);
                dp = ae_fabs(dp, _state);
                dp = ae_minreal(dp, nlcaul_aulmaxgrowth, _state);
                dp = ae_maxreal(dp, (double)1/nlcaul_aulmaxgrowth, _state);
                state->lagmultbc2.ptr.p_double[2*i+1] = boundval(state->lagmultbc2.ptr.p_double[2*i+1]*dp, nlcaul_minlagmult, nlcaul_maxlagmult, _state);
            }
        }
        for(i=0; i<=cntlc-1; i++)
        {
            j0 = state->sparsea.ridx.ptr.p_int[i];
            j1 = state->sparsea.ridx.ptr.p_int[i+1]-1;
            ci = (double)(0);
            for(jj=j0; jj<=j1; jj++)
            {
                ci = ci+state->xvirt.x.ptr.p_double[state->sparsea.idx.ptr.p_int[jj]]*state->sparsea.vals.ptr.p_double[jj];
            }
            if( (state->hasal.ptr.p_bool[i]&&state->hasau.ptr.p_bool[i])&&ae_fp_eq(state->al.ptr.p_double[i],state->au.ptr.p_double[i]) )
            {
                
                /*
                 * Equality constraint
                 */
                state->lagmultxc2.ptr.p_double[2*i+0] = state->lagmultxc2.ptr.p_double[2*i+0]+state->rho*(ci-state->al.ptr.p_double[i]);
            }
            else
            {
                
                /*
                 * No constraints, inequality constraint or range constraint
                 */
                if( state->hasal.ptr.p_bool[i] )
                {
                    inequalityshiftedbarrierfunction((ci-state->al.ptr.p_double[i])*state->rho+(double)1, &p, &dp, &d2p, _state);
                    dp = ae_fabs(dp, _state);
                    dp = ae_minreal(dp, nlcaul_aulmaxgrowth, _state);
                    dp = ae_maxreal(dp, (double)1/nlcaul_aulmaxgrowth, _state);
                    state->lagmultxc2.ptr.p_double[2*i+0] = boundval(state->lagmultxc2.ptr.p_double[2*i+0]*dp, nlcaul_minlagmult, nlcaul_maxlagmult, _state);
                }
                if( state->hasau.ptr.p_bool[i] )
                {
                    inequalityshiftedbarrierfunction((state->au.ptr.p_double[i]-ci)*state->rho+(double)1, &p, &dp, &d2p, _state);
                    dp = ae_fabs(dp, _state);
                    dp = ae_minreal(dp, nlcaul_aulmaxgrowth, _state);
                    dp = ae_maxreal(dp, (double)1/nlcaul_aulmaxgrowth, _state);
                    state->lagmultxc2.ptr.p_double[2*i+1] = boundval(state->lagmultxc2.ptr.p_double[2*i+1]*dp, nlcaul_minlagmult, nlcaul_maxlagmult, _state);
                }
            }
        }
        for(i=0; i<=cntnlc-1; i++)
        {
            ci = state->xvirt.fi.ptr.p_double[1+i];
            if( (state->hasnl.ptr.p_bool[i]&&state->hasnu.ptr.p_bool[i])&&ae_fp_eq(state->rawnl.ptr.p_double[i],state->rawnu.ptr.p_double[i]) )
            {
                
                /*
                 * Equality constraint
                 */
                state->lagmultxc2.ptr.p_double[2*cntlc+2*i+0] = state->lagmultxc2.ptr.p_double[2*cntlc+2*i+0]+state->rho*(ci-state->rawnl.ptr.p_double[i]);
            }
            else
            {
                
                /*
                 * No constraints, inequality constraint or range constraint
                 */
                if( state->hasnl.ptr.p_bool[i] )
                {
                    inequalityshiftedbarrierfunction((ci-state->rawnl.ptr.p_double[i])*state->rho+(double)1, &p, &dp, &d2p, _state);
                    dp = ae_fabs(dp, _state);
                    dp = ae_minreal(dp, nlcaul_aulmaxgrowth, _state);
                    dp = ae_maxreal(dp, (double)1/nlcaul_aulmaxgrowth, _state);
                    state->lagmultxc2.ptr.p_double[2*cntlc+2*i+0] = boundval(state->lagmultxc2.ptr.p_double[2*cntlc+2*i+0]*dp, nlcaul_minlagmult, nlcaul_maxlagmult, _state);
                }
                if( state->hasnu.ptr.p_bool[i] )
                {
                    inequalityshiftedbarrierfunction((state->rawnu.ptr.p_double[i]-ci)*state->rho+(double)1, &p, &dp, &d2p, _state);
                    dp = ae_fabs(dp, _state);
                    dp = ae_minreal(dp, nlcaul_aulmaxgrowth, _state);
                    dp = ae_maxreal(dp, (double)1/nlcaul_aulmaxgrowth, _state);
                    state->lagmultxc2.ptr.p_double[2*cntlc+2*i+1] = boundval(state->lagmultxc2.ptr.p_double[2*cntlc+2*i+1]*dp, nlcaul_minlagmult, nlcaul_maxlagmult, _state);
                }
            }
        }
        
        /*
         * Update upper bound on Hessian norm
         */
        hessiansetmaxhess(&state->hessaug, nlcaul_xbfgsmaxhess*((double)1+state->rho)*ae_maxreal((double)1+rmaxabsv(2*n, &state->lagmultbc2, _state), (double)1+rmaxabsv(2*cntlc+2*cntnlc, &state->lagmultxc2, _state), _state), _state);
        state->rho = ae_minreal(ae_sqrt((double)(2), _state)*state->rho, nlcaul_rhomax, _state);
    }
    goto lbl_6;
lbl_7:
    
    /*
     * Perform the final report
     */
    nlcaul_clearrequestfields(state, _state);
    nlcaul_aulsendtruepoint(state, &state->xtruebest.x, _state);
    state->f = state->xtruebest.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0];
    state->xupdated = ae_true;
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    state->xupdated = ae_false;
    
    /*
     * Almost done
     */
    if( dotrace )
    {
        ae_trace("\n=== STOPPED ========================================================================================\n");
        ae_trace("raw target:     %20.12e\n",
            (double)(state->xtruebest.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0]));
    }
    smoothnessmonitortracestatus(smonitor, dotrace, _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = n;
    state->rstate.ia.ptr.p_int[1] = cntlc;
    state->rstate.ia.ptr.p_int[2] = cntnlc;
    state->rstate.ia.ptr.p_int[3] = nconstr;
    state->rstate.ia.ptr.p_int[4] = i;
    state->rstate.ia.ptr.p_int[5] = j;
    state->rstate.ia.ptr.p_int[6] = j0;
    state->rstate.ia.ptr.p_int[7] = j1;
    state->rstate.ia.ptr.p_int[8] = jj;
    state->rstate.ia.ptr.p_int[9] = mcinfo;
    state->rstate.ba.ptr.p_bool[0] = dotrace;
    state->rstate.ba.ptr.p_bool[1] = doprobingalways;
    state->rstate.ba.ptr.p_bool[2] = dodetailedtrace;
    state->rstate.ba.ptr.p_bool[3] = inneriterationsstopped;
    state->rstate.ba.ptr.p_bool[4] = stepaccepted;
    state->rstate.ra.ptr.p_double[0] = lagcur;
    state->rstate.ra.ptr.p_double[1] = lag0;
    state->rstate.ra.ptr.p_double[2] = lag1;
    state->rstate.ra.ptr.p_double[3] = laga;
    state->rstate.ra.ptr.p_double[4] = lagb;
    state->rstate.ra.ptr.p_double[5] = ci;
    state->rstate.ra.ptr.p_double[6] = modtgt0;
    state->rstate.ra.ptr.p_double[7] = modtgt1;
    state->rstate.ra.ptr.p_double[8] = mx;
    state->rstate.ra.ptr.p_double[9] = setscaleto;
    state->rstate.ra.ptr.p_double[10] = multiplyby;
    state->rstate.ra.ptr.p_double[11] = stpproposed;
    state->rstate.ra.ptr.p_double[12] = p;
    state->rstate.ra.ptr.p_double[13] = dp;
    state->rstate.ra.ptr.p_double[14] = d2p;
    state->rstate.ra.ptr.p_double[15] = steplengthtoprobe;
    state->rstate.ra.ptr.p_double[16] = v;
    state->rstate.ra.ptr.p_double[17] = vv;
    state->rstate.ra.ptr.p_double[18] = errprim;
    state->rstate.ra.ptr.p_double[19] = errdual;
    state->rstate.ra.ptr.p_double[20] = errcmpl;
    state->rstate.ra.ptr.p_double[21] = preverrprim;
    state->rstate.ra.ptr.p_double[22] = preverrdual;
    state->rstate.ra.ptr.p_double[23] = preverrcmpl;
    return result;
}


/*************************************************************************
Shifted barrier function for inequality constraints  which  is  multiplied
by the corresponding Lagrange multiplier.

Shift function must enter augmented Lagrangian as

    LagMult/Rho*SHIFT((x-lowerbound)*Rho+1)
    
with corresponding changes being made for upper bound or  other  kinds  of
constraints.

INPUT PARAMETERS:
    Alpha   -   function argument. Typically, if we have active constraint
                with precise Lagrange multiplier, we have Alpha  around 1.
                Large positive Alpha's correspond to  inner  area  of  the
                feasible set. Alpha<1 corresponds to  outer  area  of  the
                feasible set.
                
OUTPUT PARAMETERS:
    F       -   F(Alpha)
    DF      -   DF=dF(Alpha)/dAlpha, exact derivative
    D2F     -   second derivative

  -- ALGLIB --
     Copyright 11.07.2023 by Bochkanov Sergey
*************************************************************************/
void inequalityshiftedbarrierfunction(double alpha,
     double* f,
     double* df,
     double* d2f,
     ae_state *_state)
{

    *f = 0.0;
    *df = 0.0;
    *d2f = 0.0;

    if( alpha>=0.5 )
    {
        *f = -ae_log(alpha, _state);
        *df = -(double)1/alpha;
        *d2f = *df*(*df);
    }
    else
    {
        *f = (double)2*alpha*alpha-(double)4*alpha+(ae_log((double)(2), _state)+1.5);
        *df = (double)4*alpha-(double)4;
        *d2f = (double)(4);
    }
}


/*************************************************************************
This function prepares preconditioner for subsequent computation.

INPUT PARAMETERS:
    SState          -   solver state
    RefreshFreq     -   >=1, refresh frequency

OUTPUT PARAMETERS:
    P               -   preconditioner object, ready to be computed
                        

  -- ALGLIB --
     Copyright 13.06.2023 by Bochkanov Sergey
*************************************************************************/
static void nlcaul_preconditionerinit(const minaulstate* sstate,
     ae_int_t refreshfreq,
     minaulpreconditioner* p,
     ae_state *_state)
{


    p->refreshfreq = refreshfreq;
    p->modelage = refreshfreq;
}


/*************************************************************************
This function sends updated target and augmented Lagrangian Hessians to the
preconditioner. It is expected that this call corresponds to one update of
Hessians.

  -- ALGLIB --
     Copyright 13.06.2023 by Bochkanov Sergey
*************************************************************************/
static void nlcaul_preconditionersendupdates(minaulstate* state,
     minaulpreconditioner* prec,
     const varsfuncjac* current,
     double rho,
     /* Real    */ const ae_vector* lagmultbc2,
     /* Real    */ const ae_vector* lagmultxc2,
     xbfgshessian* hesstgt,
     xbfgshessian* hessaug,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t cntlc;
    double sigmatgt;
    double dummy;
    ae_int_t dummyupdcnt;
    double dampeps;
    ae_int_t nconstr;
    ae_int_t naug;
    ae_int_t offs;
    ae_int_t tgtrowidx;
    double vd;
    double p;
    double dp;
    double d2p;
    double vcurvature;
    double vsqrtcurvature;
    ae_int_t i;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    double ci;
    double fi;
    ae_int_t facttype;


    n = state->n;
    cntlc = state->cntlc;
    nconstr = state->cntlc+state->cntnlc;
    dampeps = (double)100*ae_sqrt(ae_machineepsilon, _state)*((double)1+rho);
    
    /*
     * Extract LBFGS memory:
     * * we extract only diagonal scaling factor Sigma from the mod-target Hessian, ignoring all updates
     * * we extract only S/Y updates from the augmented Lagrangian Hessian
     */
    hessiangetlowrankmemory(hesstgt, &sigmatgt, &prec->dummys, &prec->dummyy, &dummyupdcnt, _state);
    hessiangetlowrankmemory(hessaug, &dummy, &prec->s, &prec->y, &prec->updcnt, _state);
    ae_assert(ae_fp_greater(sigmatgt,(double)(0)), "AUL: sigma0<=0", _state);
    
    /*
     * Increase model age, skip factorization update if model age is small enough
     */
    prec->modelage = prec->modelage+1;
    if( prec->modelage<prec->refreshfreq )
    {
        return;
    }
    
    /*
     * The model needs refactorization of the initial scaling matrix
     */
    prec->modelage = 0;
    
    /*
     * Generate initial scaling matrix which is a diagonal scaling Sigma from the mod-target Hessian +
     * correction terms coming from penalized constraints.
     */
    naug = n+nconstr;
    prec->naug = naug;
    prec->augsys.matrixtype = 1;
    prec->augsys.m = naug;
    prec->augsys.n = naug;
    iallocv(naug+1, &prec->augsys.ridx, _state);
    prec->augsys.ridx.ptr.p_int[0] = 0;
    for(i=0; i<=n-1; i++)
    {
        vd = 0.0;
        if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->scaledbndl.ptr.p_double[i],state->scaledbndu.ptr.p_double[i]) )
        {
            
            /*
             * Equality constraint, quadratic penalty
             */
            vd = rho;
        }
        else
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                inequalityshiftedbarrierfunction((current->x.ptr.p_double[i]-state->scaledbndl.ptr.p_double[i])*rho+(double)1, &p, &dp, &d2p, _state);
                vd = vd+lagmultbc2->ptr.p_double[2*i+0]*d2p*rho;
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                inequalityshiftedbarrierfunction((state->scaledbndu.ptr.p_double[i]-current->x.ptr.p_double[i])*rho+(double)1, &p, &dp, &d2p, _state);
                vd = vd+lagmultbc2->ptr.p_double[2*i+1]*d2p*rho;
            }
        }
        
        /*
         * Write the diagonal Hessian + diagonal correction coming from the box constraints
         */
        offs = prec->augsys.ridx.ptr.p_int[i];
        igrowv(offs+naug, &prec->augsys.idx, _state);
        rgrowv(offs+naug, &prec->augsys.vals, _state);
        prec->augsys.idx.ptr.p_int[offs] = i;
        prec->augsys.vals.ptr.p_double[offs] = sigmatgt+vd+dampeps;
        offs = offs+1;
        prec->augsys.ridx.ptr.p_int[i+1] = offs;
    }
    for(i=0; i<=state->cntlc-1; i++)
    {
        
        /*
         * Compute curvature coming from I-th linear constraint
         */
        j0 = state->sparsea.ridx.ptr.p_int[i];
        j1 = state->sparsea.ridx.ptr.p_int[i+1]-1;
        ci = (double)(0);
        for(jj=j0; jj<=j1; jj++)
        {
            ci = ci+current->x.ptr.p_double[state->sparsea.idx.ptr.p_int[jj]]*state->sparsea.vals.ptr.p_double[jj];
        }
        vcurvature = 0.0;
        if( (state->hasal.ptr.p_bool[i]&&state->hasau.ptr.p_bool[i])&&ae_fp_eq(state->al.ptr.p_double[i],state->au.ptr.p_double[i]) )
        {
            
            /*
             * Equality constraint Ai*x = ALi = AUi
             */
            vcurvature = rho;
        }
        else
        {
            
            /*
             * Inequality/range constraint
             */
            if( state->hasal.ptr.p_bool[i] )
            {
                
                /*
                 * Add curvature from inequality constraint Ai*x >= ALi
                 */
                inequalityshiftedbarrierfunction((ci-state->al.ptr.p_double[i])*rho+(double)1, &p, &dp, &d2p, _state);
                vcurvature = vcurvature+lagmultxc2->ptr.p_double[2*i+0]*d2p*rho;
            }
            if( state->hasau.ptr.p_bool[i] )
            {
                
                /*
                 * Add curvature from inequality constraint Ai*x <= AUi
                 */
                inequalityshiftedbarrierfunction((state->au.ptr.p_double[i]-ci)*rho+(double)1, &p, &dp, &d2p, _state);
                vcurvature = vcurvature+lagmultxc2->ptr.p_double[2*i+1]*d2p*rho;
            }
        }
        
        /*
         * Add modification to the Hessian
         */
        vsqrtcurvature = ae_sqrt(ae_maxreal(vcurvature, 0.0, _state), _state);
        tgtrowidx = n+i;
        offs = prec->augsys.ridx.ptr.p_int[tgtrowidx];
        igrowv(offs+naug, &prec->augsys.idx, _state);
        rgrowv(offs+naug, &prec->augsys.vals, _state);
        j0 = state->sparsea.ridx.ptr.p_int[i];
        j1 = state->sparsea.ridx.ptr.p_int[i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            prec->augsys.idx.ptr.p_int[offs] = state->sparsea.idx.ptr.p_int[jj];
            prec->augsys.vals.ptr.p_double[offs] = vsqrtcurvature*state->sparsea.vals.ptr.p_double[jj];
            offs = offs+1;
        }
        prec->augsys.idx.ptr.p_int[offs] = tgtrowidx;
        prec->augsys.vals.ptr.p_double[offs] = -1.0;
        offs = offs+1;
        prec->augsys.ridx.ptr.p_int[tgtrowidx+1] = offs;
    }
    for(i=0; i<=state->cntnlc-1; i++)
    {
        
        /*
         * Compute curvature coming from I-th nonlinear constraint
         */
        fi = current->fi.ptr.p_double[1+i];
        vcurvature = 0.0;
        if( (state->hasnl.ptr.p_bool[i]&&state->hasnu.ptr.p_bool[i])&&ae_fp_eq(state->rawnl.ptr.p_double[i],state->rawnu.ptr.p_double[i]) )
        {
            
            /*
             * Equality constraint Ai*x = ALi = AUi
             */
            vcurvature = rho;
        }
        else
        {
            
            /*
             * Inequality/range constraint
             */
            if( state->hasnl.ptr.p_bool[i] )
            {
                
                /*
                 * Add curvature from inequality constraint Ai*x >= ALi
                 */
                inequalityshiftedbarrierfunction((fi-state->rawnl.ptr.p_double[i])*rho+(double)1, &p, &dp, &d2p, _state);
                vcurvature = vcurvature+lagmultxc2->ptr.p_double[2*cntlc+2*i+0]*d2p*rho;
            }
            if( state->hasnu.ptr.p_bool[i] )
            {
                
                /*
                 * Add curvature from inequality constraint Ai*x <= AUi
                 */
                inequalityshiftedbarrierfunction((state->rawnu.ptr.p_double[i]-fi)*rho+(double)1, &p, &dp, &d2p, _state);
                vcurvature = vcurvature+lagmultxc2->ptr.p_double[2*cntlc+2*i+1]*d2p*rho;
            }
        }
        
        /*
         * Add modification
         */
        vsqrtcurvature = ae_sqrt(ae_maxreal(vcurvature, 0.0, _state), _state);
        tgtrowidx = n+cntlc+i;
        offs = prec->augsys.ridx.ptr.p_int[tgtrowidx];
        igrowv(offs+naug, &prec->augsys.idx, _state);
        rgrowv(offs+naug, &prec->augsys.vals, _state);
        j0 = current->sj.ridx.ptr.p_int[1+i];
        j1 = current->sj.ridx.ptr.p_int[1+i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            prec->augsys.idx.ptr.p_int[offs] = current->sj.idx.ptr.p_int[jj];
            prec->augsys.vals.ptr.p_double[offs] = vsqrtcurvature*current->sj.vals.ptr.p_double[jj];
            offs = offs+1;
        }
        prec->augsys.idx.ptr.p_int[offs] = tgtrowidx;
        prec->augsys.vals.ptr.p_double[offs] = -1.0;
        offs = offs+1;
        prec->augsys.ridx.ptr.p_int[tgtrowidx+1] = offs;
    }
    sparsecreatecrsinplace(&prec->augsys, _state);
    
    /*
     * Perform analysis and factorization
     */
    facttype = 21;
    isetallocv(naug, 0, &prec->priorities, _state);
    if( !spsymmanalyze(&prec->augsys, &prec->priorities, 0.0, 0, facttype, 3, 1, &prec->analysis, _state) )
    {
        ae_assert(ae_false, "AUL: integrity check 8126 failed", _state);
    }
    if( !spsymmfactorize(&prec->analysis, _state) )
    {
        ae_assert(ae_false, "AUL: integrity check 8127 failed", _state);
    }
}


/*************************************************************************
This function computes gradient and applies preconditioner.

  -- ALGLIB --
     Copyright 13.06.2023 by Bochkanov Sergey
*************************************************************************/
static void nlcaul_preconditionersolve(minaulstate* state,
     minaulpreconditioner* prec,
     /* Real    */ const ae_vector* g,
     /* Real    */ ae_vector* d,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t naug;
    ae_int_t k;
    double betak;


    ae_assert(prec->modelage<prec->refreshfreq, "AUL: preconditioner needs at least one update in order to properly initialize itself", _state);
    n = state->n;
    naug = prec->naug;
    
    /*
     *
     */
    rallocv(prec->updcnt, &prec->alphak, _state);
    rallocv(prec->updcnt, &prec->rhok, _state);
    rcopymulv(n, -1.0, g, d, _state);
    for(k=prec->updcnt-1; k>=0; k--)
    {
        prec->rhok.ptr.p_double[k] = (double)1/rdotrr(n, &prec->s, k, &prec->y, k, _state);
        prec->alphak.ptr.p_double[k] = prec->rhok.ptr.p_double[k]*rdotvr(n, d, &prec->s, k, _state);
        raddrv(n, -prec->alphak.ptr.p_double[k], &prec->y, k, d, _state);
    }
    rsetallocv(naug, 0.0, &prec->dx, _state);
    rcopyv(n, d, &prec->dx, _state);
    spsymmsolve(&prec->analysis, &prec->dx, _state);
    rcopyv(n, &prec->dx, d, _state);
    for(k=0; k<=prec->updcnt-1; k++)
    {
        betak = prec->rhok.ptr.p_double[k]*rdotvr(n, d, &prec->y, k, _state);
        raddrv(n, prec->alphak.ptr.p_double[k]-betak, &prec->s, k, d, _state);
    }
}


/*************************************************************************
Sends virtual point to the user, enforcing box constraints on it and saving
box-constrained copy to XTrue
*************************************************************************/
static void nlcaul_aulsendvirtualpoint(minaulstate* state,
     /* Real    */ const ae_vector* xvirt,
     /* Real    */ ae_vector* xtrue,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(xvirt->cnt>=n, "AUL: integrity check 6339 failed", _state);
    ae_assert(xtrue->cnt>=n, "AUL: integrity check 6440 failed", _state);
    rcopyv(n, xvirt, &state->x, _state);
    rmergemaxv(n, &state->finitebndl, &state->x, _state);
    rmergeminv(n, &state->finitebndu, &state->x, _state);
    rcopyv(n, &state->x, xtrue, _state);
}


/*************************************************************************
Sends true point (one which is already box-constrained) to the user.
*************************************************************************/
static void nlcaul_aulsendtruepoint(minaulstate* state,
     /* Real    */ const ae_vector* xtrue,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_assert(xtrue->cnt>=n, "AUL: integrity check 0044 failed", _state);
    for(i=0; i<=n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i]&&xtrue->ptr.p_double[i]<state->scaledbndl.ptr.p_double[i] )
        {
            ae_assert(ae_false, "AUL: box constrained point falls outside of the box constrained area", _state);
        }
        if( state->hasbndu.ptr.p_bool[i]&&xtrue->ptr.p_double[i]>state->scaledbndu.ptr.p_double[i] )
        {
            ae_assert(ae_false, "AUL: box constrained point falls outside of the box constrained area", _state);
        }
        state->x.ptr.p_double[i] = xtrue->ptr.p_double[i];
    }
}


/*************************************************************************
Retrieves raw functions vector and scaled Jacobian from the user data,
fills two structures, XVirt and XTrue:

* it is assumed that XVirt.X and XTrue.X are  already  initialized  -  the
  former is initialized with the virtual point (one which is  not  subject
  to box constraints) and the latter is initialized with  the  true  point
  (one which is box constrained and passed to the user)

* XTrue.Fi and XTrue.SJ are initialized with the true values at XTrue.X

* XVirt.Fi and XVirt.Sj are initialized with the  values  of  the  virtual
  model which coincides with the true function vector at  box  constrained
  points, but linearly extrapolates box constrained projection when  XVirt
  is not box-constrained.

Returns True on success, False on failure (when F or J are not finite numbers).
*************************************************************************/
static ae_bool nlcaul_aulretrievesj(minaulstate* state,
     varsfuncjac* xvirt,
     varsfuncjac* xtrue,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    ae_int_t j1;
    double v;
    double vv;
    ae_int_t offs;
    ae_bool allsame;
    ae_bool result;


    ae_assert(!xvirt->isdense, "AULRetrieveSJ: dense output was specified", _state);
    ae_assert(!xtrue->isdense, "AULRetrieveSJ: dense output was specified", _state);
    ae_assert(state->sj.n==state->n, "AULRetrieveSJ: integrity check 2200 failed", _state);
    ae_assert(state->sj.m==1+state->cntnlc, "AULRetrieveSJ: integrity check 2300 failed", _state);
    ae_assert(xvirt->n==state->n, "AULRetrieveSJ: integrity check 2359 failed", _state);
    ae_assert(xvirt->m==1+state->cntnlc, "AULRetrieveSJ: integrity check 1125 failed", _state);
    ae_assert(xtrue->n==state->n, "AULRetrieveSJ: integrity check 1126 failed", _state);
    ae_assert(xtrue->m==1+state->cntnlc, "AULRetrieveSJ: integrity check 1127 failed", _state);
    n = xtrue->n;
    m = xtrue->m;
    
    /*
     * Fill true point with the raw data
     */
    v = (double)(0);
    xtrue->sj.matrixtype = -10080;
    xtrue->sj.m = m;
    xtrue->sj.n = n;
    iallocv(m+1, &xtrue->sj.ridx, _state);
    xtrue->sj.ridx.ptr.p_int[0] = 0;
    for(i=0; i<=state->cntnlc; i++)
    {
        
        /*
         * Function value
         */
        vv = (double)1/state->fscales.ptr.p_double[i];
        xtrue->fi.ptr.p_double[i] = vv*state->fi.ptr.p_double[i];
        v = v+xtrue->fi.ptr.p_double[i];
        
        /*
         * Sparse Jacobian row (slacks are not added)
         */
        j0 = state->sj.ridx.ptr.p_int[i];
        j1 = state->sj.ridx.ptr.p_int[i+1]-1;
        offs = xtrue->sj.ridx.ptr.p_int[i];
        igrowv(offs+(j1-j0+1), &xtrue->sj.idx, _state);
        rgrowv(offs+(j1-j0+1), &xtrue->sj.vals, _state);
        for(j=j0; j<=j1; j++)
        {
            xtrue->sj.idx.ptr.p_int[offs] = state->sj.idx.ptr.p_int[j];
            xtrue->sj.vals.ptr.p_double[offs] = vv*state->sj.vals.ptr.p_double[j];
            v = v+xtrue->sj.vals.ptr.p_double[offs];
            offs = offs+1;
        }
        
        /*
         * Done
         */
        xtrue->sj.ridx.ptr.p_int[i+1] = offs;
    }
    result = ae_isfinite(v, _state);
    
    /*
     * Compute function values at the virtual point
     */
    rsetallocv(n, 0.0, &state->tmpretrdelta, _state);
    allsame = ae_true;
    for(i=0; i<=n-1; i++)
    {
        state->tmpretrdelta.ptr.p_double[i] = xvirt->x.ptr.p_double[i]-xtrue->x.ptr.p_double[i];
        allsame = allsame&&xtrue->x.ptr.p_double[i]==xvirt->x.ptr.p_double[i];
    }
    if( allsame )
    {
        vfjcopy(xtrue, xvirt, _state);
        return result;
    }
    xvirt->sj.matrixtype = -10080;
    xvirt->sj.m = m;
    xvirt->sj.n = n;
    iallocv(m+1, &xvirt->sj.ridx, _state);
    xvirt->sj.ridx.ptr.p_int[0] = 0;
    for(i=0; i<=state->cntnlc; i++)
    {
        j0 = xtrue->sj.ridx.ptr.p_int[i];
        j1 = xtrue->sj.ridx.ptr.p_int[i+1]-1;
        
        /*
         * Function value
         */
        xvirt->fi.ptr.p_double[i] = xtrue->fi.ptr.p_double[i];
        for(j=j0; j<=j1; j++)
        {
            xvirt->fi.ptr.p_double[i] = xvirt->fi.ptr.p_double[i]+xtrue->sj.vals.ptr.p_double[j]*state->tmpretrdelta.ptr.p_double[xtrue->sj.idx.ptr.p_int[j]];
        }
        
        /*
         * Sparse Jacobian row
         */
        offs = xvirt->sj.ridx.ptr.p_int[i];
        igrowv(offs+(j1-j0+1), &xvirt->sj.idx, _state);
        rgrowv(offs+(j1-j0+1), &xvirt->sj.vals, _state);
        for(j=j0; j<=j1; j++)
        {
            xvirt->sj.idx.ptr.p_int[offs] = xtrue->sj.idx.ptr.p_int[j];
            xvirt->sj.vals.ptr.p_double[offs] = xtrue->sj.vals.ptr.p_double[j];
            offs = offs+1;
        }
        
        /*
         * Done
         */
        xvirt->sj.ridx.ptr.p_int[i+1] = offs;
    }
    return result;
}


/*************************************************************************
This function calculates target and its gradient, with possibility of
applying additional stabilizing modification which does not change
constrained solution.
*************************************************************************/
static void nlcaul_modtargetfg(minaulstate* state,
     const varsfuncjac* s,
     double rhobc,
     double rhoxc,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;

    *f = 0.0;

    n = state->n;
    ae_assert(g->cnt>=n, "AUL: integrity check 5717 failed", _state);
    ae_assert(((!s->isdense&&(s->sj.matrixtype==1||s->sj.matrixtype==-10080))&&s->sj.m>=1)&&s->sj.n==n, "AUL: integrity check 5820 failed", _state);
    
    /*
     * Target function
     */
    rsetv(n, 0.0, g, _state);
    *f = s->fi.ptr.p_double[0];
    j0 = s->sj.ridx.ptr.p_int[0];
    j1 = s->sj.ridx.ptr.p_int[1]-1;
    for(jj=j0; jj<=j1; jj++)
    {
        g->ptr.p_double[s->sj.idx.ptr.p_int[jj]] = s->sj.vals.ptr.p_double[jj];
    }
}


/*************************************************************************
This function calculates augmented (penalized) Lagrangian of the problem
*************************************************************************/
static void nlcaul_auglagfg(minaulstate* state,
     const varsfuncjac* s,
     double rho,
     /* Real    */ const ae_vector* lagmultbc2,
     /* Real    */ const ae_vector* lagmultxc2,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t cntlc;
    ae_int_t cntnlc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    double ci;
    double v;
    double aix;
    double fi;
    double p;
    double dp;
    double d2p;
    double invrho;
    double stabrho;
    double rhoci;

    *f = 0.0;

    n = state->n;
    cntlc = state->cntlc;
    cntnlc = state->cntnlc;
    ae_assert(ae_fp_greater(rho,(double)(0)), "AUL: integrity check 1459 failed", _state);
    ae_assert(g->cnt>=n, "AUL: integrity check 7517 failed", _state);
    ae_assert(((!s->isdense&&(s->sj.matrixtype==1||s->sj.matrixtype==-10080))&&s->sj.m>=1)&&s->sj.n==n, "AUL: integrity check 7720 failed", _state);
    ae_assert(state->cntlc==0||((state->sparsea.matrixtype==1&&state->sparsea.m==state->cntlc)&&state->sparsea.n==n), "AUL: integrity check 3528 failed", _state);
    invrho = (double)1/rho;
    
    /*
     * Target function
     */
    nlcaul_modtargetfg(state, s, rho, rho, f, g, _state);
    
    /*
     * Lagrangian terms for box constraints
     */
    for(i=0; i<=n-1; i++)
    {
        
        /*
         * Handle equality constraint on a variable
         */
        if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&state->scaledbndl.ptr.p_double[i]==state->scaledbndu.ptr.p_double[i] )
        {
            ci = s->x.ptr.p_double[i]-state->scaledbndl.ptr.p_double[i];
            *f = *f+0.5*rho*ci*ci+lagmultbc2->ptr.p_double[2*i+0]*ci;
            g->ptr.p_double[i] = g->ptr.p_double[i]+rho*ci+lagmultbc2->ptr.p_double[2*i+0];
            continue;
        }
        
        /*
         * Handle lower bound (inequality or range with non-zero width)
         */
        if( state->hasbndl.ptr.p_bool[i] )
        {
            inequalityshiftedbarrierfunction((s->x.ptr.p_double[i]-state->scaledbndl.ptr.p_double[i])*rho+(double)1, &p, &dp, &d2p, _state);
            *f = *f+p*invrho*lagmultbc2->ptr.p_double[2*i+0];
            g->ptr.p_double[i] = g->ptr.p_double[i]+dp*lagmultbc2->ptr.p_double[2*i+0];
        }
        
        /*
         * Handle upper bound (inequality or range with non-zero width)
         */
        if( state->hasbndu.ptr.p_bool[i] )
        {
            inequalityshiftedbarrierfunction((state->scaledbndu.ptr.p_double[i]-s->x.ptr.p_double[i])*rho+(double)1, &p, &dp, &d2p, _state);
            *f = *f+p*invrho*lagmultbc2->ptr.p_double[2*i+1];
            g->ptr.p_double[i] = g->ptr.p_double[i]-dp*lagmultbc2->ptr.p_double[2*i+1];
        }
    }
    
    /*
     * Lagrangian and penalty terms for linear constraints
     */
    for(i=0; i<=state->cntlc-1; i++)
    {
        j0 = state->sparsea.ridx.ptr.p_int[i];
        j1 = state->sparsea.ridx.ptr.p_int[i+1]-1;
        
        /*
         * Compute constraint value Ai*x
         */
        ci = (double)(0);
        for(jj=j0; jj<=j1; jj++)
        {
            ci = ci+s->x.ptr.p_double[state->sparsea.idx.ptr.p_int[jj]]*state->sparsea.vals.ptr.p_double[jj];
        }
        
        /*
         * Handle Lagrangian and penalty terms
         */
        if( (state->hasal.ptr.p_bool[i]&&state->hasau.ptr.p_bool[i])&&state->al.ptr.p_double[i]==state->au.ptr.p_double[i] )
        {
            
            /*
             * Equality constraint Ai*x = ALi = AUi
             */
            ci = ci-state->al.ptr.p_double[i];
            *f = *f+lagmultxc2->ptr.p_double[2*i+0]*ci+0.5*rho*ci*ci;
            for(jj=j0; jj<=j1; jj++)
            {
                j = state->sparsea.idx.ptr.p_int[jj];
                v = state->sparsea.vals.ptr.p_double[jj];
                g->ptr.p_double[j] = g->ptr.p_double[j]+lagmultxc2->ptr.p_double[2*i+0]*v+rho*ci*v;
            }
        }
        else
        {
            
            /*
             * Inequality/range constraint
             */
            if( state->hasal.ptr.p_bool[i] )
            {
                inequalityshiftedbarrierfunction((ci-state->al.ptr.p_double[i])*rho+(double)1, &p, &dp, &d2p, _state);
                *f = *f+lagmultxc2->ptr.p_double[2*i+0]*p*invrho;
                for(jj=j0; jj<=j1; jj++)
                {
                    j = state->sparsea.idx.ptr.p_int[jj];
                    v = state->sparsea.vals.ptr.p_double[jj];
                    g->ptr.p_double[j] = g->ptr.p_double[j]+lagmultxc2->ptr.p_double[2*i+0]*dp*v;
                }
            }
            if( state->hasau.ptr.p_bool[i] )
            {
                inequalityshiftedbarrierfunction((state->au.ptr.p_double[i]-ci)*rho+(double)1, &p, &dp, &d2p, _state);
                *f = *f+lagmultxc2->ptr.p_double[2*i+1]*p*invrho;
                for(jj=j0; jj<=j1; jj++)
                {
                    j = state->sparsea.idx.ptr.p_int[jj];
                    v = state->sparsea.vals.ptr.p_double[jj];
                    g->ptr.p_double[j] = g->ptr.p_double[j]-lagmultxc2->ptr.p_double[2*i+1]*dp*v;
                }
            }
        }
    }
    
    /*
     * Lagrangian and penalty terms for nonlinear constraints
     */
    for(i=0; i<=cntnlc-1; i++)
    {
        j0 = s->sj.ridx.ptr.p_int[1+i];
        j1 = s->sj.ridx.ptr.p_int[1+i+1]-1;
        ci = s->fi.ptr.p_double[1+i];
        
        /*
         * Handle Lagrangian and penalty terms
         */
        if( (state->hasnl.ptr.p_bool[i]&&state->hasnu.ptr.p_bool[i])&&state->rawnl.ptr.p_double[i]==state->rawnu.ptr.p_double[i] )
        {
            
            /*
             * Equality constraint Fi = NLi = NUi
             */
            ci = ci-state->rawnl.ptr.p_double[i];
            *f = *f+lagmultxc2->ptr.p_double[2*cntlc+2*i+0]*ci+0.5*rho*ci*ci;
            for(jj=j0; jj<=j1; jj++)
            {
                j = s->sj.idx.ptr.p_int[jj];
                v = s->sj.vals.ptr.p_double[jj];
                g->ptr.p_double[j] = g->ptr.p_double[j]+lagmultxc2->ptr.p_double[2*cntlc+2*i+0]*v+rho*ci*v;
            }
        }
        else
        {
            
            /*
             * Inequality/range constraint
             */
            if( state->hasnl.ptr.p_bool[i] )
            {
                inequalityshiftedbarrierfunction((ci-state->rawnl.ptr.p_double[i])*rho+(double)1, &p, &dp, &d2p, _state);
                *f = *f+lagmultxc2->ptr.p_double[2*cntlc+2*i+0]*p*invrho;
                for(jj=j0; jj<=j1; jj++)
                {
                    j = s->sj.idx.ptr.p_int[jj];
                    v = s->sj.vals.ptr.p_double[jj];
                    g->ptr.p_double[j] = g->ptr.p_double[j]+lagmultxc2->ptr.p_double[2*cntlc+2*i+0]*dp*v;
                }
            }
            if( state->hasnu.ptr.p_bool[i] )
            {
                inequalityshiftedbarrierfunction((state->rawnu.ptr.p_double[i]-ci)*rho+(double)1, &p, &dp, &d2p, _state);
                *f = *f+lagmultxc2->ptr.p_double[2*cntlc+2*i+1]*p*invrho;
                for(jj=j0; jj<=j1; jj++)
                {
                    j = s->sj.idx.ptr.p_int[jj];
                    v = s->sj.vals.ptr.p_double[jj];
                    g->ptr.p_double[j] = g->ptr.p_double[j]-lagmultxc2->ptr.p_double[2*cntlc+2*i+1]*dp*v;
                }
            }
        }
    }
    
    /*
     * Additional stabilizing penalty term which is inactive within the feasible area
     * and close to its boundaries. This term is intended to prevent long steps into
     * the infeasible area.
     */
    stabrho = nlcaul_stabilizingpoint/rho;
    for(i=0; i<=n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i] )
        {
            ci = s->x.ptr.p_double[i]-state->scaledbndl.ptr.p_double[i]+stabrho;
            if( ci<0.0 )
            {
                rhoci = rho*ci;
                *f = *f+0.5*rhoci*ci;
                g->ptr.p_double[i] = g->ptr.p_double[i]+rhoci;
            }
        }
        if( state->hasbndu.ptr.p_bool[i] )
        {
            ci = state->scaledbndu.ptr.p_double[i]-s->x.ptr.p_double[i]+stabrho;
            if( ci<0.0 )
            {
                rhoci = rho*ci;
                *f = *f+0.5*rhoci*ci;
                g->ptr.p_double[i] = g->ptr.p_double[i]-rhoci;
            }
        }
    }
    for(i=0; i<=state->cntlc-1; i++)
    {
        j0 = state->sparsea.ridx.ptr.p_int[i];
        j1 = state->sparsea.ridx.ptr.p_int[i+1]-1;
        aix = (double)(0);
        for(jj=j0; jj<=j1; jj++)
        {
            aix = aix+s->x.ptr.p_double[state->sparsea.idx.ptr.p_int[jj]]*state->sparsea.vals.ptr.p_double[jj];
        }
        if( state->hasal.ptr.p_bool[i]&&aix-state->al.ptr.p_double[i]<-stabrho )
        {
            ci = aix-state->al.ptr.p_double[i]+stabrho;
            *f = *f+0.5*rho*ci*ci;
            for(jj=j0; jj<=j1; jj++)
            {
                j = state->sparsea.idx.ptr.p_int[jj];
                v = state->sparsea.vals.ptr.p_double[jj];
                g->ptr.p_double[j] = g->ptr.p_double[j]+rho*ci*v;
            }
        }
        if( state->hasau.ptr.p_bool[i]&&state->au.ptr.p_double[i]-aix<-stabrho )
        {
            ci = state->au.ptr.p_double[i]-aix+stabrho;
            *f = *f+0.5*rho*ci*ci;
            for(jj=j0; jj<=j1; jj++)
            {
                j = state->sparsea.idx.ptr.p_int[jj];
                v = state->sparsea.vals.ptr.p_double[jj];
                g->ptr.p_double[j] = g->ptr.p_double[j]-rho*ci*v;
            }
        }
    }
    for(i=0; i<=state->cntnlc-1; i++)
    {
        j0 = s->sj.ridx.ptr.p_int[1+i];
        j1 = s->sj.ridx.ptr.p_int[1+i+1]-1;
        fi = s->fi.ptr.p_double[1+i];
        if( state->hasnl.ptr.p_bool[i] )
        {
            ci = fi-state->rawnl.ptr.p_double[i]+stabrho;
            if( ci<0.0 )
            {
                rhoci = rho*ci;
                *f = *f+0.5*rhoci*ci;
                for(jj=j0; jj<=j1; jj++)
                {
                    j = s->sj.idx.ptr.p_int[jj];
                    v = s->sj.vals.ptr.p_double[jj];
                    g->ptr.p_double[j] = g->ptr.p_double[j]+rhoci*v;
                }
            }
        }
        if( state->hasnu.ptr.p_bool[i] )
        {
            ci = state->rawnu.ptr.p_double[i]-fi+stabrho;
            if( ci<0.0 )
            {
                rhoci = rho*ci;
                *f = *f+0.5*rhoci*ci;
                for(jj=j0; jj<=j1; jj++)
                {
                    j = s->sj.idx.ptr.p_int[jj];
                    v = s->sj.vals.ptr.p_double[jj];
                    g->ptr.p_double[j] = g->ptr.p_double[j]-rhoci*v;
                }
            }
        }
    }
}


/*************************************************************************
This function calculates raw Lagrangian of the problem
*************************************************************************/
static void nlcaul_rawlagfg(minaulstate* state,
     const varsfuncjac* s,
     /* Real    */ const ae_vector* lagmultbc2,
     /* Real    */ const ae_vector* lagmultxc2,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t cntlc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    double fi;
    double ci;
    double v;

    *f = 0.0;

    n = state->n;
    cntlc = state->cntlc;
    ae_assert(g->cnt>=n, "AUL: integrity check 7517 failed", _state);
    ae_assert(((!s->isdense&&(s->sj.matrixtype==1||s->sj.matrixtype==-10080))&&s->sj.m>=1)&&s->sj.n==n, "AUL: integrity check 7720 failed", _state);
    ae_assert(state->cntlc==0||((state->sparsea.matrixtype==1&&state->sparsea.m==state->cntlc)&&state->sparsea.n==n), "AUL: integrity check 3528 failed", _state);
    
    /*
     * Target function
     */
    rsetv(n, 0.0, g, _state);
    *f = s->fi.ptr.p_double[0];
    j0 = s->sj.ridx.ptr.p_int[0];
    j1 = s->sj.ridx.ptr.p_int[1]-1;
    for(jj=j0; jj<=j1; jj++)
    {
        g->ptr.p_double[s->sj.idx.ptr.p_int[jj]] = s->sj.vals.ptr.p_double[jj];
    }
    
    /*
     * Lagrangian terms for box constraints
     */
    for(i=0; i<=n-1; i++)
    {
        
        /*
         * Handle equality constraint on a variable
         */
        if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->scaledbndl.ptr.p_double[i],state->scaledbndu.ptr.p_double[i]) )
        {
            ci = s->x.ptr.p_double[i]-state->scaledbndl.ptr.p_double[i];
            *f = *f+lagmultbc2->ptr.p_double[2*i+0]*ci;
            g->ptr.p_double[i] = g->ptr.p_double[i]+lagmultbc2->ptr.p_double[2*i+0];
            continue;
        }
        
        /*
         * Handle lower bound (inequality or range with non-zero width)
         */
        if( state->hasbndl.ptr.p_bool[i] )
        {
            *f = *f-lagmultbc2->ptr.p_double[2*i+0]*(s->x.ptr.p_double[i]-state->scaledbndl.ptr.p_double[i]);
            g->ptr.p_double[i] = g->ptr.p_double[i]-lagmultbc2->ptr.p_double[2*i+0];
        }
        
        /*
         * Handle upper bound (inequality or range with non-zero width)
         */
        if( state->hasbndu.ptr.p_bool[i] )
        {
            *f = *f-lagmultbc2->ptr.p_double[2*i+1]*(state->scaledbndu.ptr.p_double[i]-s->x.ptr.p_double[i]);
            g->ptr.p_double[i] = g->ptr.p_double[i]+lagmultbc2->ptr.p_double[2*i+1];
        }
    }
    
    /*
     * Lagrangian and penalty terms for linear constraints
     */
    for(i=0; i<=state->cntlc-1; i++)
    {
        j0 = state->sparsea.ridx.ptr.p_int[i];
        j1 = state->sparsea.ridx.ptr.p_int[i+1]-1;
        
        /*
         * Compute constraint value Ai*x
         */
        ci = (double)(0);
        for(jj=j0; jj<=j1; jj++)
        {
            ci = ci+s->x.ptr.p_double[state->sparsea.idx.ptr.p_int[jj]]*state->sparsea.vals.ptr.p_double[jj];
        }
        
        /*
         * Handle Lagrangian and penalty terms
         */
        if( (state->hasal.ptr.p_bool[i]&&state->hasau.ptr.p_bool[i])&&ae_fp_eq(state->al.ptr.p_double[i],state->au.ptr.p_double[i]) )
        {
            
            /*
             * Equality constraint Ai*x = ALi = AUi
             */
            ci = ci-state->al.ptr.p_double[i];
            *f = *f+lagmultxc2->ptr.p_double[2*i+0]*ci;
            for(jj=j0; jj<=j1; jj++)
            {
                j = state->sparsea.idx.ptr.p_int[jj];
                v = state->sparsea.vals.ptr.p_double[jj];
                g->ptr.p_double[j] = g->ptr.p_double[j]+lagmultxc2->ptr.p_double[2*i+0]*v;
            }
        }
        else
        {
            
            /*
             * Inequality/range constraint
             */
            if( state->hasal.ptr.p_bool[i] )
            {
                *f = *f-lagmultxc2->ptr.p_double[2*i+0]*(ci-state->al.ptr.p_double[i]);
                for(jj=j0; jj<=j1; jj++)
                {
                    j = state->sparsea.idx.ptr.p_int[jj];
                    v = state->sparsea.vals.ptr.p_double[jj];
                    g->ptr.p_double[j] = g->ptr.p_double[j]-lagmultxc2->ptr.p_double[2*i+0]*v;
                }
            }
            if( state->hasau.ptr.p_bool[i] )
            {
                
                /*
                 * Inequality constraint Ai*x <= AUi
                 */
                *f = *f-lagmultxc2->ptr.p_double[2*i+1]*(state->au.ptr.p_double[i]-ci);
                for(jj=j0; jj<=j1; jj++)
                {
                    j = state->sparsea.idx.ptr.p_int[jj];
                    v = state->sparsea.vals.ptr.p_double[jj];
                    g->ptr.p_double[j] = g->ptr.p_double[j]+lagmultxc2->ptr.p_double[2*i+1]*v;
                }
            }
        }
    }
    for(i=0; i<=state->cntnlc-1; i++)
    {
        j0 = s->sj.ridx.ptr.p_int[1+i];
        j1 = s->sj.ridx.ptr.p_int[1+i+1]-1;
        fi = s->fi.ptr.p_double[1+i];
        
        /*
         * Handle Lagrangian and penalty terms
         */
        if( (state->hasnl.ptr.p_bool[i]&&state->hasnu.ptr.p_bool[i])&&ae_fp_eq(state->rawnl.ptr.p_double[i],state->rawnu.ptr.p_double[i]) )
        {
            
            /*
             * Equality constraint Fi = NLi = NUi
             */
            ci = fi-state->rawnl.ptr.p_double[i];
            *f = *f+lagmultxc2->ptr.p_double[2*cntlc+2*i+0]*ci;
            for(jj=j0; jj<=j1; jj++)
            {
                j = s->sj.idx.ptr.p_int[jj];
                v = s->sj.vals.ptr.p_double[jj];
                g->ptr.p_double[j] = g->ptr.p_double[j]+lagmultxc2->ptr.p_double[2*cntlc+2*i+0]*v;
            }
        }
        else
        {
            
            /*
             * Inequality/range constraint
             */
            if( state->hasnl.ptr.p_bool[i] )
            {
                
                /*
                 * Inequality constraint Ai*x >= ALi
                 */
                *f = *f-lagmultxc2->ptr.p_double[2*cntlc+2*i+0]*(fi-state->rawnl.ptr.p_double[i]);
                for(jj=j0; jj<=j1; jj++)
                {
                    j = s->sj.idx.ptr.p_int[jj];
                    v = s->sj.vals.ptr.p_double[jj];
                    g->ptr.p_double[j] = g->ptr.p_double[j]-lagmultxc2->ptr.p_double[2*cntlc+2*i+0]*v;
                }
            }
            if( state->hasnu.ptr.p_bool[i] )
            {
                
                /*
                 * Inequality constraint Ai*x <= AUi
                 */
                *f = *f-lagmultxc2->ptr.p_double[2*cntlc+2*i+1]*(state->rawnu.ptr.p_double[i]-fi);
                for(jj=j0; jj<=j1; jj++)
                {
                    j = s->sj.idx.ptr.p_int[jj];
                    v = s->sj.vals.ptr.p_double[jj];
                    g->ptr.p_double[j] = g->ptr.p_double[j]+lagmultxc2->ptr.p_double[2*cntlc+2*i+1]*v;
                }
            }
        }
    }
}


/*************************************************************************
Clears request fileds (to be sure that we didn't forget to clear something)
*************************************************************************/
static void nlcaul_clearrequestfields(minaulstate* state,
     ae_state *_state)
{


    state->needsj = ae_false;
    state->precrefreshupcoming = ae_false;
    state->xupdated = ae_false;
}


/*************************************************************************
Compute primal/dual/complementary slackness errors
*************************************************************************/
static void nlcaul_computeerrors(minaulstate* sstate,
     const varsfuncjac* s,
     double* errprim,
     double* errdual,
     double* errcmpl,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t n;
    ae_int_t cntlc;
    ae_int_t cntnlc;
    double ci;
    double v;

    *errprim = 0.0;
    *errdual = 0.0;
    *errcmpl = 0.0;

    n = sstate->n;
    cntlc = sstate->cntlc;
    cntnlc = sstate->cntnlc;
    *errprim = 0.0;
    for(i=0; i<=n-1; i++)
    {
        if( sstate->hasbndl.ptr.p_bool[i] )
        {
            *errprim = ae_maxreal(*errprim, sstate->scaledbndl.ptr.p_double[i]-s->x.ptr.p_double[i], _state);
        }
        if( sstate->hasbndu.ptr.p_bool[i] )
        {
            *errprim = ae_maxreal(*errprim, s->x.ptr.p_double[i]-sstate->scaledbndu.ptr.p_double[i], _state);
        }
    }
    for(i=0; i<=cntlc-1; i++)
    {
        j0 = sstate->sparsea.ridx.ptr.p_int[i];
        j1 = sstate->sparsea.ridx.ptr.p_int[i+1]-1;
        ci = (double)(0);
        for(jj=j0; jj<=j1; jj++)
        {
            ci = ci+s->x.ptr.p_double[sstate->sparsea.idx.ptr.p_int[jj]]*sstate->sparsea.vals.ptr.p_double[jj];
        }
        if( sstate->hasal.ptr.p_bool[i] )
        {
            *errprim = ae_maxreal(*errprim, sstate->al.ptr.p_double[i]-ci, _state);
        }
        if( sstate->hasau.ptr.p_bool[i] )
        {
            *errprim = ae_maxreal(*errprim, ci-sstate->au.ptr.p_double[i], _state);
        }
    }
    for(i=0; i<=cntnlc-1; i++)
    {
        ci = s->fi.ptr.p_double[1+i];
        if( sstate->hasnl.ptr.p_bool[i] )
        {
            *errprim = ae_maxreal(*errprim, sstate->rawnl.ptr.p_double[i]-ci, _state);
        }
        if( sstate->hasnu.ptr.p_bool[i] )
        {
            *errprim = ae_maxreal(*errprim, ci-sstate->rawnu.ptr.p_double[i], _state);
        }
    }
    rallocv(n, &sstate->tmpg, _state);
    nlcaul_rawlagfg(sstate, s, &sstate->lagmultbc2, &sstate->lagmultxc2, &v, &sstate->tmpg, _state);
    *errdual = rmaxabsv(n, &sstate->tmpg, _state);
    *errcmpl = 0.0;
    for(i=0; i<=n-1; i++)
    {
        if( (sstate->hasbndl.ptr.p_bool[i]&&sstate->hasbndu.ptr.p_bool[i])&&ae_fp_eq(sstate->scaledbndl.ptr.p_double[i],sstate->scaledbndu.ptr.p_double[i]) )
        {
            *errcmpl = ae_maxreal(*errcmpl, ae_fabs((sstate->scaledbndl.ptr.p_double[i]-s->x.ptr.p_double[i])*sstate->lagmultbc2.ptr.p_double[2*i+0], _state), _state);
            continue;
        }
        if( sstate->hasbndl.ptr.p_bool[i] )
        {
            *errcmpl = ae_maxreal(*errcmpl, ae_fabs((sstate->scaledbndl.ptr.p_double[i]-s->x.ptr.p_double[i])*sstate->lagmultbc2.ptr.p_double[2*i+0], _state), _state);
        }
        if( sstate->hasbndu.ptr.p_bool[i] )
        {
            *errcmpl = ae_maxreal(*errcmpl, ae_fabs((s->x.ptr.p_double[i]-sstate->scaledbndu.ptr.p_double[i])*sstate->lagmultbc2.ptr.p_double[2*i+1], _state), _state);
        }
    }
    for(i=0; i<=cntlc-1; i++)
    {
        j0 = sstate->sparsea.ridx.ptr.p_int[i];
        j1 = sstate->sparsea.ridx.ptr.p_int[i+1]-1;
        ci = (double)(0);
        for(jj=j0; jj<=j1; jj++)
        {
            ci = ci+s->x.ptr.p_double[sstate->sparsea.idx.ptr.p_int[jj]]*sstate->sparsea.vals.ptr.p_double[jj];
        }
        if( (sstate->hasal.ptr.p_bool[i]&&sstate->hasau.ptr.p_bool[i])&&ae_fp_eq(sstate->al.ptr.p_double[i],sstate->au.ptr.p_double[i]) )
        {
            
            /*
             * Equality constraint
             */
            *errcmpl = ae_maxreal(*errcmpl, ae_fabs((ci-sstate->al.ptr.p_double[i])*sstate->lagmultxc2.ptr.p_double[2*i+0], _state), _state);
        }
        else
        {
            
            /*
             * No constraints, inequality constraint or range constraint
             */
            if( sstate->hasal.ptr.p_bool[i] )
            {
                *errcmpl = ae_maxreal(*errcmpl, ae_fabs((sstate->al.ptr.p_double[i]-ci)*sstate->lagmultxc2.ptr.p_double[2*i+0], _state), _state);
            }
            if( sstate->hasau.ptr.p_bool[i] )
            {
                *errcmpl = ae_maxreal(*errcmpl, ae_fabs((ci-sstate->au.ptr.p_double[i])*sstate->lagmultxc2.ptr.p_double[2*i+1], _state), _state);
            }
        }
    }
    for(i=0; i<=cntnlc-1; i++)
    {
        ci = s->fi.ptr.p_double[1+i];
        if( (sstate->hasnl.ptr.p_bool[i]&&sstate->hasnu.ptr.p_bool[i])&&ae_fp_eq(sstate->rawnl.ptr.p_double[i],sstate->rawnu.ptr.p_double[i]) )
        {
            
            /*
             * Equality constraint
             */
            *errcmpl = ae_maxreal(*errcmpl, ae_fabs((ci-sstate->rawnl.ptr.p_double[i])*sstate->lagmultxc2.ptr.p_double[2*cntlc+2*i+0], _state), _state);
        }
        else
        {
            
            /*
             * No constraints, inequality constraint or range constraint
             */
            if( sstate->hasnl.ptr.p_bool[i] )
            {
                *errcmpl = ae_maxreal(*errcmpl, ae_fabs((sstate->rawnl.ptr.p_double[i]-ci)*sstate->lagmultxc2.ptr.p_double[2*cntlc+2*i+0], _state), _state);
            }
            if( sstate->hasnu.ptr.p_bool[i] )
            {
                *errcmpl = ae_maxreal(*errcmpl, ae_fabs((ci-sstate->rawnu.ptr.p_double[i])*sstate->lagmultxc2.ptr.p_double[2*cntlc+2*i+1], _state), _state);
            }
        }
    }
}


void _minaulpreconditioner_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minaulpreconditioner *p = (minaulpreconditioner*)_p;
    ae_touch_ptr((void*)p);
    _spcholanalysis_init(&p->analysis, _state, make_automatic);
    _sparsematrix_init(&p->augsys, _state, make_automatic);
    ae_matrix_init(&p->s, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->y, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpcorrc, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpcorrd, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpdiag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->priorities, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->dx, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->dummys, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->dummyy, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->alphak, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rhok, 0, DT_REAL, _state, make_automatic);
}


void _minaulpreconditioner_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minaulpreconditioner       *dst = (minaulpreconditioner*)_dst;
    const minaulpreconditioner *src = (const minaulpreconditioner*)_src;
    dst->refreshfreq = src->refreshfreq;
    dst->modelage = src->modelage;
    _spcholanalysis_init_copy(&dst->analysis, &src->analysis, _state, make_automatic);
    _sparsematrix_init_copy(&dst->augsys, &src->augsys, _state, make_automatic);
    dst->naug = src->naug;
    ae_matrix_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_matrix_init_copy(&dst->y, &src->y, _state, make_automatic);
    dst->updcnt = src->updcnt;
    ae_matrix_init_copy(&dst->tmpcorrc, &src->tmpcorrc, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpcorrd, &src->tmpcorrd, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpdiag, &src->tmpdiag, _state, make_automatic);
    ae_vector_init_copy(&dst->priorities, &src->priorities, _state, make_automatic);
    ae_vector_init_copy(&dst->dx, &src->dx, _state, make_automatic);
    ae_matrix_init_copy(&dst->dummys, &src->dummys, _state, make_automatic);
    ae_matrix_init_copy(&dst->dummyy, &src->dummyy, _state, make_automatic);
    ae_vector_init_copy(&dst->alphak, &src->alphak, _state, make_automatic);
    ae_vector_init_copy(&dst->rhok, &src->rhok, _state, make_automatic);
}


void _minaulpreconditioner_clear(void* _p)
{
    minaulpreconditioner *p = (minaulpreconditioner*)_p;
    ae_touch_ptr((void*)p);
    _spcholanalysis_clear(&p->analysis);
    _sparsematrix_clear(&p->augsys);
    ae_matrix_clear(&p->s);
    ae_matrix_clear(&p->y);
    ae_matrix_clear(&p->tmpcorrc);
    ae_vector_clear(&p->tmpcorrd);
    ae_vector_clear(&p->tmpdiag);
    ae_vector_clear(&p->priorities);
    ae_vector_clear(&p->dx);
    ae_matrix_clear(&p->dummys);
    ae_matrix_clear(&p->dummyy);
    ae_vector_clear(&p->alphak);
    ae_vector_clear(&p->rhok);
}


void _minaulpreconditioner_destroy(void* _p)
{
    minaulpreconditioner *p = (minaulpreconditioner*)_p;
    ae_touch_ptr((void*)p);
    _spcholanalysis_destroy(&p->analysis);
    _sparsematrix_destroy(&p->augsys);
    ae_matrix_destroy(&p->s);
    ae_matrix_destroy(&p->y);
    ae_matrix_destroy(&p->tmpcorrc);
    ae_vector_destroy(&p->tmpcorrd);
    ae_vector_destroy(&p->tmpdiag);
    ae_vector_destroy(&p->priorities);
    ae_vector_destroy(&p->dx);
    ae_matrix_destroy(&p->dummys);
    ae_matrix_destroy(&p->dummyy);
    ae_vector_destroy(&p->alphak);
    ae_vector_destroy(&p->rhok);
}


void _minaulstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minaulstate *p = (minaulstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->scaledbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scaledbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitebndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitebndu, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparsea, _state, make_automatic);
    ae_vector_init(&p->al, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->au, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasal, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasau, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->rawnl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawnu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasnl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasnu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->lcsrcidx, 0, DT_INT, _state, make_automatic);
    _nlpstoppingcriteria_init(&p->criteria, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sj, _state, make_automatic);
    _varsfuncjac_init(&p->xvirt, _state, make_automatic);
    _varsfuncjac_init(&p->xvirtprev, _state, make_automatic);
    _varsfuncjac_init(&p->xvirtbest, _state, make_automatic);
    _varsfuncjac_init(&p->xtrue, _state, make_automatic);
    _varsfuncjac_init(&p->xtrueprev, _state, make_automatic);
    _varsfuncjac_init(&p->xtruebest, _state, make_automatic);
    ae_vector_init(&p->lagmultbc2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagmultxc2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->du, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fscales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tracegamma, 0, DT_REAL, _state, make_automatic);
    _minaulpreconditioner_init(&p->preconditioner, _state, make_automatic);
    _xbfgshessian_init(&p->hessaug, _state, make_automatic);
    _xbfgshessian_init(&p->hesstgt, _state, make_automatic);
    ae_vector_init(&p->laggrad0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->laggradcur, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->modtgtgrad0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->modtgtgrad1, 0, DT_REAL, _state, make_automatic);
    _linminstate_init(&p->lstate, _state, make_automatic);
    ae_vector_init(&p->ascales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpzero, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpy, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->dummy2, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpretrdelta, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpg, 0, DT_REAL, _state, make_automatic);
    _varsfuncjac_init(&p->xvirtprobe, _state, make_automatic);
    _varsfuncjac_init(&p->xtrueprobe, _state, make_automatic);
    ae_vector_init(&p->work, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
}


void _minaulstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minaulstate       *dst = (minaulstate*)_dst;
    const minaulstate *src = (const minaulstate*)_src;
    dst->n = src->n;
    dst->cntlc = src->cntlc;
    dst->cntnlc = src->cntnlc;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledbndl, &src->scaledbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledbndu, &src->scaledbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndl, &src->finitebndl, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndu, &src->finitebndu, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsea, &src->sparsea, _state, make_automatic);
    ae_vector_init_copy(&dst->al, &src->al, _state, make_automatic);
    ae_vector_init_copy(&dst->au, &src->au, _state, make_automatic);
    ae_vector_init_copy(&dst->hasal, &src->hasal, _state, make_automatic);
    ae_vector_init_copy(&dst->hasau, &src->hasau, _state, make_automatic);
    ae_vector_init_copy(&dst->rawnl, &src->rawnl, _state, make_automatic);
    ae_vector_init_copy(&dst->rawnu, &src->rawnu, _state, make_automatic);
    ae_vector_init_copy(&dst->hasnl, &src->hasnl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasnu, &src->hasnu, _state, make_automatic);
    ae_vector_init_copy(&dst->lcsrcidx, &src->lcsrcidx, _state, make_automatic);
    _nlpstoppingcriteria_init_copy(&dst->criteria, &src->criteria, _state, make_automatic);
    dst->maxouterits = src->maxouterits;
    dst->restartfreq = src->restartfreq;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sj, &src->sj, _state, make_automatic);
    dst->f = src->f;
    dst->needsj = src->needsj;
    dst->precrefreshupcoming = src->precrefreshupcoming;
    dst->xupdated = src->xupdated;
    dst->rho = src->rho;
    _varsfuncjac_init_copy(&dst->xvirt, &src->xvirt, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->xvirtprev, &src->xvirtprev, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->xvirtbest, &src->xvirtbest, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->xtrue, &src->xtrue, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->xtrueprev, &src->xtrueprev, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->xtruebest, &src->xtruebest, _state, make_automatic);
    dst->besterr = src->besterr;
    dst->bestiteridx = src->bestiteridx;
    dst->besterrnegligibleupdates = src->besterrnegligibleupdates;
    ae_vector_init_copy(&dst->lagmultbc2, &src->lagmultbc2, _state, make_automatic);
    ae_vector_init_copy(&dst->lagmultxc2, &src->lagmultxc2, _state, make_automatic);
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    ae_vector_init_copy(&dst->du, &src->du, _state, make_automatic);
    ae_vector_init_copy(&dst->fscales, &src->fscales, _state, make_automatic);
    ae_vector_init_copy(&dst->tracegamma, &src->tracegamma, _state, make_automatic);
    _minaulpreconditioner_init_copy(&dst->preconditioner, &src->preconditioner, _state, make_automatic);
    _xbfgshessian_init_copy(&dst->hessaug, &src->hessaug, _state, make_automatic);
    _xbfgshessian_init_copy(&dst->hesstgt, &src->hesstgt, _state, make_automatic);
    ae_vector_init_copy(&dst->laggrad0, &src->laggrad0, _state, make_automatic);
    ae_vector_init_copy(&dst->laggradcur, &src->laggradcur, _state, make_automatic);
    ae_vector_init_copy(&dst->modtgtgrad0, &src->modtgtgrad0, _state, make_automatic);
    ae_vector_init_copy(&dst->modtgtgrad1, &src->modtgtgrad1, _state, make_automatic);
    dst->inneriterationscount = src->inneriterationscount;
    dst->outeriterationscount = src->outeriterationscount;
    dst->fstagnationcnt = src->fstagnationcnt;
    dst->longeststp = src->longeststp;
    dst->mcstage = src->mcstage;
    _linminstate_init_copy(&dst->lstate, &src->lstate, _state, make_automatic);
    dst->stp = src->stp;
    dst->stplimit = src->stplimit;
    ae_vector_init_copy(&dst->ascales, &src->ascales, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpzero, &src->tmpzero, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpy, &src->tmpy, _state, make_automatic);
    ae_matrix_init_copy(&dst->dummy2, &src->dummy2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpretrdelta, &src->tmpretrdelta, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpg, &src->tmpg, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->xvirtprobe, &src->xvirtprobe, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->xtrueprobe, &src->xtrueprobe, _state, make_automatic);
    dst->repiterationscount = src->repiterationscount;
    dst->repterminationtype = src->repterminationtype;
    dst->repnfev = src->repnfev;
    dst->repbcerr = src->repbcerr;
    dst->repbcidx = src->repbcidx;
    dst->replcerr = src->replcerr;
    dst->replcidx = src->replcidx;
    dst->repnlcerr = src->repnlcerr;
    dst->repnlcidx = src->repnlcidx;
    dst->nfev = src->nfev;
    ae_vector_init_copy(&dst->work, &src->work, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
}


void _minaulstate_clear(void* _p)
{
    minaulstate *p = (minaulstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->scaledbndl);
    ae_vector_clear(&p->scaledbndu);
    ae_vector_clear(&p->finitebndl);
    ae_vector_clear(&p->finitebndu);
    _sparsematrix_clear(&p->sparsea);
    ae_vector_clear(&p->al);
    ae_vector_clear(&p->au);
    ae_vector_clear(&p->hasal);
    ae_vector_clear(&p->hasau);
    ae_vector_clear(&p->rawnl);
    ae_vector_clear(&p->rawnu);
    ae_vector_clear(&p->hasnl);
    ae_vector_clear(&p->hasnu);
    ae_vector_clear(&p->lcsrcidx);
    _nlpstoppingcriteria_clear(&p->criteria);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->fi);
    _sparsematrix_clear(&p->sj);
    _varsfuncjac_clear(&p->xvirt);
    _varsfuncjac_clear(&p->xvirtprev);
    _varsfuncjac_clear(&p->xvirtbest);
    _varsfuncjac_clear(&p->xtrue);
    _varsfuncjac_clear(&p->xtrueprev);
    _varsfuncjac_clear(&p->xtruebest);
    ae_vector_clear(&p->lagmultbc2);
    ae_vector_clear(&p->lagmultxc2);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->du);
    ae_vector_clear(&p->fscales);
    ae_vector_clear(&p->tracegamma);
    _minaulpreconditioner_clear(&p->preconditioner);
    _xbfgshessian_clear(&p->hessaug);
    _xbfgshessian_clear(&p->hesstgt);
    ae_vector_clear(&p->laggrad0);
    ae_vector_clear(&p->laggradcur);
    ae_vector_clear(&p->modtgtgrad0);
    ae_vector_clear(&p->modtgtgrad1);
    _linminstate_clear(&p->lstate);
    ae_vector_clear(&p->ascales);
    ae_vector_clear(&p->tmpzero);
    ae_vector_clear(&p->tmpy);
    ae_matrix_clear(&p->dummy2);
    ae_vector_clear(&p->tmpretrdelta);
    ae_vector_clear(&p->tmpg);
    _varsfuncjac_clear(&p->xvirtprobe);
    _varsfuncjac_clear(&p->xtrueprobe);
    ae_vector_clear(&p->work);
    _rcommstate_clear(&p->rstate);
}


void _minaulstate_destroy(void* _p)
{
    minaulstate *p = (minaulstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->scaledbndl);
    ae_vector_destroy(&p->scaledbndu);
    ae_vector_destroy(&p->finitebndl);
    ae_vector_destroy(&p->finitebndu);
    _sparsematrix_destroy(&p->sparsea);
    ae_vector_destroy(&p->al);
    ae_vector_destroy(&p->au);
    ae_vector_destroy(&p->hasal);
    ae_vector_destroy(&p->hasau);
    ae_vector_destroy(&p->rawnl);
    ae_vector_destroy(&p->rawnu);
    ae_vector_destroy(&p->hasnl);
    ae_vector_destroy(&p->hasnu);
    ae_vector_destroy(&p->lcsrcidx);
    _nlpstoppingcriteria_destroy(&p->criteria);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->fi);
    _sparsematrix_destroy(&p->sj);
    _varsfuncjac_destroy(&p->xvirt);
    _varsfuncjac_destroy(&p->xvirtprev);
    _varsfuncjac_destroy(&p->xvirtbest);
    _varsfuncjac_destroy(&p->xtrue);
    _varsfuncjac_destroy(&p->xtrueprev);
    _varsfuncjac_destroy(&p->xtruebest);
    ae_vector_destroy(&p->lagmultbc2);
    ae_vector_destroy(&p->lagmultxc2);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->du);
    ae_vector_destroy(&p->fscales);
    ae_vector_destroy(&p->tracegamma);
    _minaulpreconditioner_destroy(&p->preconditioner);
    _xbfgshessian_destroy(&p->hessaug);
    _xbfgshessian_destroy(&p->hesstgt);
    ae_vector_destroy(&p->laggrad0);
    ae_vector_destroy(&p->laggradcur);
    ae_vector_destroy(&p->modtgtgrad0);
    ae_vector_destroy(&p->modtgtgrad1);
    _linminstate_destroy(&p->lstate);
    ae_vector_destroy(&p->ascales);
    ae_vector_destroy(&p->tmpzero);
    ae_vector_destroy(&p->tmpy);
    ae_matrix_destroy(&p->dummy2);
    ae_vector_destroy(&p->tmpretrdelta);
    ae_vector_destroy(&p->tmpg);
    _varsfuncjac_destroy(&p->xvirtprobe);
    _varsfuncjac_destroy(&p->xtrueprobe);
    ae_vector_destroy(&p->work);
    _rcommstate_destroy(&p->rstate);
}


#endif
#if defined(AE_COMPILE_MINCG) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
        NONLINEAR CONJUGATE GRADIENT METHOD

DESCRIPTION:
The subroutine minimizes function F(x) of N arguments by using one of  the
nonlinear conjugate gradient methods.

These CG methods are globally convergent (even on non-convex functions) as
long as grad(f) is Lipschitz continuous in  a  some  neighborhood  of  the
L = { x : f(x)<=f(x0) }.


REQUIREMENTS:
Algorithm will request following information during its operation:
* function value F and its gradient G (simultaneously) at given point X


USAGE:
1. User initializes algorithm state with MinCGCreate() call
2. User tunes solver parameters with MinCGSetCond(), MinCGSetStpMax() and
   other functions
3. User calls MinCGOptimize() function which takes algorithm  state   and
   pointer (delegate, etc.) to callback function which calculates F/G.
4. User calls MinCGResults() to get solution
5. Optionally, user may call MinCGRestartFrom() to solve another  problem
   with same N but another starting point and/or another function.
   MinCGRestartFrom() allows to reuse already initialized structure.


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[0..N-1].

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 25.03.2010 by Bochkanov Sergey
*************************************************************************/
void mincgcreate(ae_int_t n,
     /* Real    */ const ae_vector* x,
     mincgstate* state,
     ae_state *_state)
{

    _mincgstate_clear(state);

    ae_assert(n>=1, "MinCGCreate: N too small!", _state);
    ae_assert(x->cnt>=n, "MinCGCreate: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, n, _state), "MinCGCreate: X contains infinite or NaN values!", _state);
    mincg_mincginitinternal(n, 0.0, state, _state);
    mincgrestartfrom(state, x, _state);
}


/*************************************************************************
The subroutine is finite difference variant of MinCGCreate(). It uses
finite differences in order to differentiate target function.

Description below contains information which is specific to this function
only. We recommend to read comments on MinCGCreate() in order to get more
information about creation of CG optimizer.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[0..N-1].
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinCGSetScale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large truncation  errors, while too small
   step will result in too large numerical  errors.  1.0E-6  can  be  good
   value to start with.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is  less  robust  and  precise.  L-BFGS  needs  exact  gradient values.
   Imprecise  gradient may slow down  convergence,  especially  on  highly
   nonlinear problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
void mincgcreatef(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     mincgstate* state,
     ae_state *_state)
{

    _mincgstate_clear(state);

    ae_assert(n>=1, "MinCGCreateF: N too small!", _state);
    ae_assert(x->cnt>=n, "MinCGCreateF: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, n, _state), "MinCGCreateF: X contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(diffstep, _state), "MinCGCreateF: DiffStep is infinite or NaN!", _state);
    ae_assert(ae_fp_greater(diffstep,(double)(0)), "MinCGCreateF: DiffStep is non-positive!", _state);
    mincg_mincginitinternal(n, diffstep, state, _state);
    mincgrestartfrom(state, x, _state);
}


/*************************************************************************
This function sets stopping conditions for CG optimization algorithm.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsG    -   >=0
                The  subroutine  finishes  its  work   if   the  condition
                |v|<EpsG is satisfied, where:
                * |.| means Euclidian norm
                * v - scaled gradient vector, v[i]=g[i]*s[i]
                * g - gradient
                * s - scaling coefficients set by MinCGSetScale()
    EpsF    -   >=0
                The  subroutine  finishes  its work if on k+1-th iteration
                the  condition  |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1}
                is satisfied.
    EpsX    -   >=0
                The subroutine finishes its work if  on  k+1-th  iteration
                the condition |v|<=EpsX is fulfilled, where:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - ste pvector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinCGSetScale()
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsG=0, EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to
automatic stopping criterion selection (small EpsX).

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetcond(mincgstate* state,
     double epsg,
     double epsf,
     double epsx,
     ae_int_t maxits,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsg, _state), "MinCGSetCond: EpsG is not finite number!", _state);
    ae_assert(ae_fp_greater_eq(epsg,(double)(0)), "MinCGSetCond: negative EpsG!", _state);
    ae_assert(ae_isfinite(epsf, _state), "MinCGSetCond: EpsF is not finite number!", _state);
    ae_assert(ae_fp_greater_eq(epsf,(double)(0)), "MinCGSetCond: negative EpsF!", _state);
    ae_assert(ae_isfinite(epsx, _state), "MinCGSetCond: EpsX is not finite number!", _state);
    ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "MinCGSetCond: negative EpsX!", _state);
    ae_assert(maxits>=0, "MinCGSetCond: negative MaxIts!", _state);
    if( ((ae_fp_eq(epsg,(double)(0))&&ae_fp_eq(epsf,(double)(0)))&&ae_fp_eq(epsx,(double)(0)))&&maxits==0 )
    {
        epsx = 1.0E-6;
    }
    state->epsg = epsg;
    state->epsf = epsf;
    state->epsx = epsx;
    state->maxits = maxits;
}


/*************************************************************************
This function sets scaling coefficients for CG optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Scaling is also used by finite difference variant of CG optimizer  -  step
along I-th axis is equal to DiffStep*S[I].

In   most   optimizers  (and  in  the  CG  too)  scaling is NOT a form  of
preconditioning. It just  affects  stopping  conditions.  You  should  set
preconditioner by separate call to one of the MinCGSetPrec...() functions.

There  is  special  preconditioning  mode, however,  which  uses   scaling
coefficients to form diagonal preconditioning matrix. You  can  turn  this
mode on, if you want.   But  you should understand that scaling is not the
same thing as preconditioning - these are two different, although  related
forms of tuning solver.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void mincgsetscale(mincgstate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(s->cnt>=state->n, "MinCGSetScale: Length(S)<N", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "MinCGSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "MinCGSetScale: S contains zero elements", _state);
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinCGOptimize().

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetxrep(mincgstate* state, ae_bool needxrep, ae_state *_state)
{


    state->xrep = needxrep;
}


/*************************************************************************
This function turns on/off line search reports.
These reports are described in more details in developer-only  comments on
MinCGState object.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedDRep-   whether line search reports are needed or not

This function is intended for private use only. Turning it on artificially
may cause program failure.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetdrep(mincgstate* state, ae_bool needdrep, ae_state *_state)
{


    state->drep = needdrep;
}


/*************************************************************************
This function sets CG algorithm.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    CGType  -   algorithm type:
                * -1    automatic selection of the best algorithm
                * 0     DY (Dai and Yuan) algorithm
                * 1     Hybrid DY-HS algorithm

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetcgtype(mincgstate* state, ae_int_t cgtype, ae_state *_state)
{


    ae_assert(cgtype>=-1&&cgtype<=1, "MinCGSetCGType: incorrect CGType!", _state);
    if( cgtype==-1 )
    {
        cgtype = 1;
    }
    state->cgtype = cgtype;
}


/*************************************************************************
This function sets maximum step length

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    StpMax  -   maximum step length, >=0. Set StpMax to 0.0,  if you don't
                want to limit step length.

Use this subroutine when you optimize target function which contains exp()
or  other  fast  growing  functions,  and optimization algorithm makes too
large  steps  which  leads  to overflow. This function allows us to reject
steps  that  are  too  large  (and  therefore  expose  us  to the possible
overflow) without actually calculating function value at the x+stp*d.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetstpmax(mincgstate* state, double stpmax, ae_state *_state)
{


    ae_assert(ae_isfinite(stpmax, _state), "MinCGSetStpMax: StpMax is not finite!", _state);
    ae_assert(ae_fp_greater_eq(stpmax,(double)(0)), "MinCGSetStpMax: StpMax<0!", _state);
    state->stpmax = stpmax;
}


/*************************************************************************
This function allows to suggest initial step length to the CG algorithm.

Suggested  step  length  is used as starting point for the line search. It
can be useful when you have  badly  scaled  problem,  i.e.  when  ||grad||
(which is used as initial estimate for the first step) is many  orders  of
magnitude different from the desired step.

Line search  may  fail  on  such problems without good estimate of initial
step length. Imagine, for example, problem with ||grad||=10^50 and desired
step equal to 0.1 Line  search function will use 10^50  as  initial  step,
then  it  will  decrease step length by 2 (up to 20 attempts) and will get
10^44, which is still too large.

This function allows us to tell than line search should  be  started  from
some moderate step length, like 1.0, so algorithm will be able  to  detect
desired step length in a several searches.

Default behavior (when no step is suggested) is to use preconditioner,  if
it is available, to generate initial estimate of step length.

This function influences only first iteration of algorithm. It  should  be
called between MinCGCreate/MinCGRestartFrom() call and MinCGOptimize call.
Suggested step is ignored if you have preconditioner.

INPUT PARAMETERS:
    State   -   structure used to store algorithm state.
    Stp     -   initial estimate of the step length.
                Can be zero (no estimate).

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsuggeststep(mincgstate* state, double stp, ae_state *_state)
{


    ae_assert(ae_isfinite(stp, _state), "MinCGSuggestStep: Stp is infinite or NAN", _state);
    ae_assert(ae_fp_greater_eq(stp,(double)(0)), "MinCGSuggestStep: Stp<0", _state);
    state->suggestedstep = stp;
}


/*************************************************************************
This developer-only function allows to retrieve  unscaled  (!)  length  of
last good step (i.e. step which resulted in sufficient decrease of  target
function).

It can be used in for solution  of  sequential  optimization  subproblems,
where MinCGSuggestStep()  is  called  with  length  of  previous  step  as
parameter.

INPUT PARAMETERS:
    State   -   structure used to store algorithm state.
    
RESULT:
    length of last good step being accepted
    
NOTE:
    result of this function is undefined if you called it before

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
double mincglastgoodstep(mincgstate* state, ae_state *_state)
{
    double result;


    result = state->lastgoodstep;
    return result;
}


/*************************************************************************
Modification of the preconditioner: preconditioning is turned off.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTE:  you  can  change  preconditioner  "on  the  fly",  during algorithm
iterations.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetprecdefault(mincgstate* state, ae_state *_state)
{


    state->prectype = 0;
    state->innerresetneeded = ae_true;
}


/*************************************************************************
Modification  of  the  preconditioner:  diagonal of approximate Hessian is
used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    D       -   diagonal of the approximate Hessian, array[0..N-1],
                (if larger, only leading N elements are used).

NOTE:  you  can  change  preconditioner  "on  the  fly",  during algorithm
iterations.

NOTE 2: D[i] should be positive. Exception will be thrown otherwise.

NOTE 3: you should pass diagonal of approximate Hessian - NOT ITS INVERSE.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetprecdiag(mincgstate* state,
     /* Real    */ const ae_vector* d,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(d->cnt>=state->n, "MinCGSetPrecDiag: D is too short", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(d->ptr.p_double[i], _state), "MinCGSetPrecDiag: D contains infinite or NAN elements", _state);
        ae_assert(ae_fp_greater(d->ptr.p_double[i],(double)(0)), "MinCGSetPrecDiag: D contains non-positive elements", _state);
    }
    mincgsetprecdiagfast(state, d, _state);
}


/*************************************************************************
Modification of the preconditioner: scale-based diagonal preconditioning.

This preconditioning mode can be useful when you  don't  have  approximate
diagonal of Hessian, but you know that your  variables  are  badly  scaled
(for  example,  one  variable is in [1,10], and another in [1000,100000]),
and most part of the ill-conditioning comes from different scales of vars.

In this case simple  scale-based  preconditioner,  with H[i] = 1/(s[i]^2),
can greatly improve convergence.

IMPRTANT: you should set scale of your variables with MinCGSetScale() call
(before or after MinCGSetPrecScale() call). Without knowledge of the scale
of your variables scale-based preconditioner will be just unit matrix.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTE:  you  can  change  preconditioner  "on  the  fly",  during algorithm
iterations.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetprecscale(mincgstate* state, ae_state *_state)
{


    state->prectype = 3;
    state->innerresetneeded = ae_true;
}


/*************************************************************************
NOTES:

1. This function has two different implementations: one which  uses  exact
   (analytical) user-supplied  gradient, and one which uses function value
   only  and  numerically  differentiates  function  in  order  to  obtain
   gradient.
   
   Depending  on  the  specific  function  used to create optimizer object
   (either MinCGCreate()  for analytical gradient  or  MinCGCreateF()  for
   numerical differentiation) you should  choose  appropriate  variant  of
   MinCGOptimize() - one which accepts function AND gradient or one  which
   accepts function ONLY.

   Be careful to choose variant of MinCGOptimize()  which  corresponds  to
   your optimization scheme! Table below lists different  combinations  of
   callback (function/gradient) passed  to  MinCGOptimize()  and  specific
   function used to create optimizer.
   

                  |         USER PASSED TO MinCGOptimize()
   CREATED WITH   |  function only   |  function and gradient
   ------------------------------------------------------------
   MinCGCreateF() |     work                FAIL
   MinCGCreate()  |     FAIL                work

   Here "FAIL" denotes inappropriate combinations  of  optimizer  creation
   function and MinCGOptimize() version. Attemps to use  such  combination
   (for  example,  to create optimizer with  MinCGCreateF()  and  to  pass
   gradient information to MinCGOptimize()) will lead to  exception  being
   thrown. Either  you  did  not  pass  gradient when it WAS needed or you
   passed gradient when it was NOT needed.

  -- ALGLIB --
     Copyright 20.04.2009 by Bochkanov Sergey
*************************************************************************/
ae_bool mincgiteration(mincgstate* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    double betak;
    double v;
    double vv;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        n = state->rstate.ia.ptr.p_int[0];
        i = state->rstate.ia.ptr.p_int[1];
        betak = state->rstate.ra.ptr.p_double[0];
        v = state->rstate.ra.ptr.p_double[1];
        vv = state->rstate.ra.ptr.p_double[2];
    }
    else
    {
        n = 359;
        i = -58;
        betak = -919.0;
        v = -909.0;
        vv = 81.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    if( state->rstate.stage==6 )
    {
        goto lbl_6;
    }
    if( state->rstate.stage==7 )
    {
        goto lbl_7;
    }
    if( state->rstate.stage==8 )
    {
        goto lbl_8;
    }
    if( state->rstate.stage==9 )
    {
        goto lbl_9;
    }
    if( state->rstate.stage==10 )
    {
        goto lbl_10;
    }
    if( state->rstate.stage==11 )
    {
        goto lbl_11;
    }
    if( state->rstate.stage==12 )
    {
        goto lbl_12;
    }
    if( state->rstate.stage==13 )
    {
        goto lbl_13;
    }
    if( state->rstate.stage==14 )
    {
        goto lbl_14;
    }
    if( state->rstate.stage==15 )
    {
        goto lbl_15;
    }
    if( state->rstate.stage==16 )
    {
        goto lbl_16;
    }
    if( state->rstate.stage==17 )
    {
        goto lbl_17;
    }
    
    /*
     * Routine body
     */
    
    /*
     * Prepare
     */
    n = state->n;
    state->terminationneeded = ae_false;
    state->userterminationneeded = ae_false;
    state->repterminationtype = 0;
    state->repiterationscount = 0;
    state->repnfev = 0;
    state->debugrestartscount = 0;
    smoothnessmonitorinit(&state->smonitor, &state->s, n, 1, state->smoothnessguardlevel>0, _state);
    rvectorsetlengthatleast(&state->invs, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->lastscaleused.ptr.p_double[i] = state->s.ptr.p_double[i];
        state->invs.ptr.p_double[i] = (double)1/state->s.ptr.p_double[i];
    }
    
    /*
     *  Check, that transferred derivative value is right
     */
    mincg_clearrequestfields(state, _state);
    if( !(ae_fp_eq(state->diffstep,(double)(0))&&ae_fp_greater(state->teststep,(double)(0))) )
    {
        goto lbl_18;
    }
lbl_20:
    if( !smoothnessmonitorcheckgradientatx0(&state->smonitor, &state->xbase, &state->s, &state->s, &state->s, ae_false, state->teststep, _state) )
    {
        goto lbl_21;
    }
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = state->smonitor.x.ptr.p_double[i];
    }
    state->needfg = ae_true;
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    state->needfg = ae_false;
    state->smonitor.fi.ptr.p_double[0] = state->f;
    for(i=0; i<=n-1; i++)
    {
        state->smonitor.j.ptr.pp_double[0][i] = state->g.ptr.p_double[i];
    }
    goto lbl_20;
lbl_21:
lbl_18:
    
    /*
     * Preparations continue:
     * * set XK
     * * calculate F/G
     * * set DK to -G
     * * powerup algo (it may change preconditioner)
     * * apply preconditioner to DK
     * * report update of X
     * * check stopping conditions for G
     */
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = state->xbase.ptr.p_double[i];
    }
    ae_v_move(&state->xk.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
    mincg_clearrequestfields(state, _state);
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_22;
    }
    state->needfg = ae_true;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->needfg = ae_false;
    goto lbl_23;
lbl_22:
    state->needf = ae_true;
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->fbase = state->f;
    i = 0;
lbl_24:
    if( i>n-1 )
    {
        goto lbl_26;
    }
    v = state->x.ptr.p_double[i];
    state->x.ptr.p_double[i] = v-state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    state->fm2 = state->f;
    state->x.ptr.p_double[i] = v-0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = v+0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    state->fp1 = state->f;
    state->x.ptr.p_double[i] = v+state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 6;
    goto lbl_rcomm;
lbl_6:
    state->fp2 = state->f;
    state->x.ptr.p_double[i] = v;
    state->g.ptr.p_double[i] = ((double)8*(state->fp1-state->fm1)-(state->fp2-state->fm2))/((double)6*state->diffstep*state->s.ptr.p_double[i]);
    i = i+1;
    goto lbl_24;
lbl_26:
    state->f = state->fbase;
    state->needf = ae_false;
lbl_23:
    if( !state->drep )
    {
        goto lbl_27;
    }
    
    /*
     * Report algorithm powerup (if needed)
     */
    mincg_clearrequestfields(state, _state);
    state->algpowerup = ae_true;
    state->rstate.stage = 7;
    goto lbl_rcomm;
lbl_7:
    state->algpowerup = ae_false;
lbl_27:
    trimprepare(state->f, &state->trimthreshold, _state);
    ae_v_moveneg(&state->dk.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    mincg_preconditionedmultiply(state, &state->dk, &state->work0, &state->work1, _state);
    if( !state->xrep )
    {
        goto lbl_29;
    }
    mincg_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 8;
    goto lbl_rcomm;
lbl_8:
    state->xupdated = ae_false;
lbl_29:
    if( state->terminationneeded||state->userterminationneeded )
    {
        
        /*
         * Combined termination point for "internal" termination by TerminationNeeded flag
         * and for "user" termination by MinCGRequestTermination() (UserTerminationNeeded flag).
         * In this location rules for both of methods are same, thus only one exit point is needed.
         */
        ae_v_move(&state->xn.ptr.p_double[0], 1, &state->xk.ptr.p_double[0], 1, ae_v_len(0,n-1));
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    v = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr(state->g.ptr.p_double[i]*state->s.ptr.p_double[i], _state);
    }
    if( ae_fp_less_eq(ae_sqrt(v, _state),state->epsg) )
    {
        ae_v_move(&state->xn.ptr.p_double[0], 1, &state->xk.ptr.p_double[0], 1, ae_v_len(0,n-1));
        state->repterminationtype = 4;
        result = ae_false;
        return result;
    }
    state->repnfev = 1;
    state->k = 0;
    state->fold = state->f;
    
    /*
     * Choose initial step.
     * Apply preconditioner, if we have something other than default.
     */
    if( state->prectype==2||state->prectype==3 )
    {
        
        /*
         * because we use preconditioner, step length must be equal
         * to the norm of DK
         */
        v = ae_v_dotproduct(&state->dk.ptr.p_double[0], 1, &state->dk.ptr.p_double[0], 1, ae_v_len(0,n-1));
        state->lastgoodstep = ae_sqrt(v, _state);
    }
    else
    {
        
        /*
         * No preconditioner is used, we try to use suggested step
         */
        if( ae_fp_greater(state->suggestedstep,(double)(0)) )
        {
            state->lastgoodstep = state->suggestedstep;
        }
        else
        {
            state->lastgoodstep = 1.0;
        }
    }
    
    /*
     * Main cycle
     */
    state->rstimer = mincg_rscountdownlen;
lbl_31:
    if( ae_false )
    {
        goto lbl_32;
    }
    
    /*
     * * clear reset flag
     * * clear termination flag
     * * store G[k] for later calculation of Y[k]
     * * prepare starting point and direction and step length for line search
     */
    state->innerresetneeded = ae_false;
    state->terminationneeded = ae_false;
    ae_v_moveneg(&state->yk.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->d.ptr.p_double[0], 1, &state->dk.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xk.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->mcstage = 0;
    state->stp = 1.0;
    linminnormalized(&state->d, &state->stp, n, _state);
    if( ae_fp_neq(state->lastgoodstep,(double)(0)) )
    {
        state->stp = state->lastgoodstep;
    }
    state->curstpmax = state->stpmax;
    
    /*
     * Report beginning of line search (if needed)
     * Terminate algorithm, if user request was detected
     */
    if( !state->drep )
    {
        goto lbl_33;
    }
    mincg_clearrequestfields(state, _state);
    state->lsstart = ae_true;
    state->rstate.stage = 9;
    goto lbl_rcomm;
lbl_9:
    state->lsstart = ae_false;
lbl_33:
    if( state->terminationneeded )
    {
        ae_v_move(&state->xn.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    
    /*
     * Minimization along D
     */
    smoothnessmonitorstartlinesearch1u(&state->smonitor, &state->s, &state->invs, &state->x, state->f, &state->g, state->repiterationscount, -1, _state);
    mcsrch(n, &state->x, &state->f, &state->g, &state->d, &state->stp, state->curstpmax, mincg_gtol, &state->mcinfo, &state->nfev, &state->work0, &state->lstate, &state->mcstage, _state);
lbl_35:
    if( state->mcstage==0 )
    {
        goto lbl_36;
    }
    
    /*
     * Calculate function/gradient using either
     * analytical gradient supplied by user
     * or finite difference approximation.
     *
     * "Trim" function in order to handle near-singularity points.
     */
    mincg_clearrequestfields(state, _state);
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_37;
    }
    state->needfg = ae_true;
    state->rstate.stage = 10;
    goto lbl_rcomm;
lbl_10:
    state->needfg = ae_false;
    goto lbl_38;
lbl_37:
    state->needf = ae_true;
    state->rstate.stage = 11;
    goto lbl_rcomm;
lbl_11:
    state->fbase = state->f;
    i = 0;
lbl_39:
    if( i>n-1 )
    {
        goto lbl_41;
    }
    v = state->x.ptr.p_double[i];
    state->x.ptr.p_double[i] = v-state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 12;
    goto lbl_rcomm;
lbl_12:
    state->fm2 = state->f;
    state->x.ptr.p_double[i] = v-0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 13;
    goto lbl_rcomm;
lbl_13:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = v+0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 14;
    goto lbl_rcomm;
lbl_14:
    state->fp1 = state->f;
    state->x.ptr.p_double[i] = v+state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 15;
    goto lbl_rcomm;
lbl_15:
    state->fp2 = state->f;
    state->x.ptr.p_double[i] = v;
    state->g.ptr.p_double[i] = ((double)8*(state->fp1-state->fm1)-(state->fp2-state->fm2))/((double)6*state->diffstep*state->s.ptr.p_double[i]);
    i = i+1;
    goto lbl_39;
lbl_41:
    state->f = state->fbase;
    state->needf = ae_false;
lbl_38:
    smoothnessmonitorenqueuepoint1u(&state->smonitor, &state->s, &state->invs, &state->d, state->stp, &state->x, state->f, &state->g, _state);
    trimfunction(&state->f, &state->g, n, state->trimthreshold, _state);
    
    /*
     * Call MCSRCH again
     */
    mcsrch(n, &state->x, &state->f, &state->g, &state->d, &state->stp, state->curstpmax, mincg_gtol, &state->mcinfo, &state->nfev, &state->work0, &state->lstate, &state->mcstage, _state);
    goto lbl_35;
lbl_36:
    smoothnessmonitorfinalizelinesearch(&state->smonitor, _state);
    
    /*
     * * terminate algorithm if "user" request for detected
     * * report end of line search
     * * store current point to XN
     * * report iteration
     * * terminate algorithm if "internal" request was detected
     */
    if( state->userterminationneeded )
    {
        ae_v_move(&state->xn.ptr.p_double[0], 1, &state->xk.ptr.p_double[0], 1, ae_v_len(0,n-1));
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    if( !state->drep )
    {
        goto lbl_42;
    }
    
    /*
     * Report end of line search (if needed)
     */
    mincg_clearrequestfields(state, _state);
    state->lsend = ae_true;
    state->rstate.stage = 16;
    goto lbl_rcomm;
lbl_16:
    state->lsend = ae_false;
lbl_42:
    ae_v_move(&state->xn.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
    if( !state->xrep )
    {
        goto lbl_44;
    }
    mincg_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 17;
    goto lbl_rcomm;
lbl_17:
    state->xupdated = ae_false;
lbl_44:
    if( state->terminationneeded )
    {
        ae_v_move(&state->xn.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    
    /*
     * Line search is finished.
     * * calculate BetaK
     * * calculate DN
     * * update timers
     * * calculate step length:
     *   * LastScaledStep is ALWAYS calculated because it is used in the stopping criteria
     *   * LastGoodStep is updated only when MCINFO is equal to 1 (Wolfe conditions hold).
     *     See below for more explanation.
     */
    if( state->mcinfo==1&&!state->innerresetneeded )
    {
        
        /*
         * Standard Wolfe conditions hold
         * Calculate Y[K] and D[K]'*Y[K]
         */
        ae_v_add(&state->yk.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
        vv = ae_v_dotproduct(&state->yk.ptr.p_double[0], 1, &state->dk.ptr.p_double[0], 1, ae_v_len(0,n-1));
        
        /*
         * Calculate BetaK according to DY formula
         */
        v = mincg_preconditionedmultiply2(state, &state->g, &state->g, &state->work0, &state->work1, _state);
        state->betady = v/vv;
        
        /*
         * Calculate BetaK according to HS formula
         */
        v = mincg_preconditionedmultiply2(state, &state->g, &state->yk, &state->work0, &state->work1, _state);
        state->betahs = v/vv;
        
        /*
         * Choose BetaK
         */
        if( state->cgtype==0 )
        {
            betak = state->betady;
        }
        if( state->cgtype==1 )
        {
            betak = ae_maxreal((double)(0), ae_minreal(state->betady, state->betahs, _state), _state);
        }
    }
    else
    {
        
        /*
         * Something is wrong (may be function is too wild or too flat)
         * or we just have to restart algo.
         *
         * We'll set BetaK=0, which will restart CG algorithm.
         * We can stop later (during normal checks) if stopping conditions are met.
         */
        betak = (double)(0);
        state->debugrestartscount = state->debugrestartscount+1;
    }
    if( state->repiterationscount>0&&state->repiterationscount%(3+n)==0 )
    {
        
        /*
         * clear Beta every N iterations
         */
        betak = (double)(0);
    }
    if( state->mcinfo==1||state->mcinfo==5 )
    {
        state->rstimer = mincg_rscountdownlen;
    }
    else
    {
        state->rstimer = state->rstimer-1;
    }
    ae_v_moveneg(&state->dn.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    mincg_preconditionedmultiply(state, &state->dn, &state->work0, &state->work1, _state);
    ae_v_addd(&state->dn.ptr.p_double[0], 1, &state->dk.ptr.p_double[0], 1, ae_v_len(0,n-1), betak);
    state->lastscaledstep = 0.0;
    for(i=0; i<=n-1; i++)
    {
        state->lastscaledstep = state->lastscaledstep+ae_sqr(state->d.ptr.p_double[i]/state->s.ptr.p_double[i], _state);
    }
    state->lastscaledstep = state->stp*ae_sqrt(state->lastscaledstep, _state);
    if( state->mcinfo==1 )
    {
        
        /*
         * Step is good (Wolfe conditions hold), update LastGoodStep.
         *
         * This check for MCINFO=1 is essential because sometimes in the
         * constrained optimization setting we may take very short steps
         * (like 1E-15) because we were very close to boundary of the
         * feasible area. Such short step does not mean that we've converged
         * to the solution - it was so short because we were close to the
         * boundary and there was a limit on step length.
         *
         * So having such short step is quite normal situation. However, we
         * should NOT start next iteration from step whose initial length is
         * estimated as 1E-15 because it may lead to the failure of the
         * linear minimizer (step is too short, function does not changes,
         * line search stagnates).
         */
        state->lastgoodstep = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            state->lastgoodstep = state->lastgoodstep+ae_sqr(state->d.ptr.p_double[i], _state);
        }
        state->lastgoodstep = state->stp*ae_sqrt(state->lastgoodstep, _state);
    }
    
    /*
     * Update information.
     * Check stopping conditions.
     */
    v = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr(state->g.ptr.p_double[i]*state->s.ptr.p_double[i], _state);
    }
    if( !ae_isfinite(v, _state)||!ae_isfinite(state->f, _state) )
    {
        
        /*
         * Abnormal termination - infinities in function/gradient
         */
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    state->repnfev = state->repnfev+state->nfev;
    state->repiterationscount = state->repiterationscount+1;
    if( state->repiterationscount>=state->maxits&&state->maxits>0 )
    {
        
        /*
         * Too many iterations
         */
        state->repterminationtype = 5;
        result = ae_false;
        return result;
    }
    if( ae_fp_less_eq(ae_sqrt(v, _state),state->epsg) )
    {
        
        /*
         * Gradient is small enough
         */
        state->repterminationtype = 4;
        result = ae_false;
        return result;
    }
    if( !state->innerresetneeded )
    {
        
        /*
         * These conditions are checked only when no inner reset was requested by user
         */
        if( ae_fp_less_eq(state->fold-state->f,state->epsf*ae_maxreal(ae_fabs(state->fold, _state), ae_maxreal(ae_fabs(state->f, _state), 1.0, _state), _state)) )
        {
            
            /*
             * F(k+1)-F(k) is small enough
             */
            state->repterminationtype = 1;
            result = ae_false;
            return result;
        }
        if( ae_fp_less_eq(state->lastscaledstep,state->epsx) )
        {
            
            /*
             * X(k+1)-X(k) is small enough
             */
            state->repterminationtype = 2;
            result = ae_false;
            return result;
        }
    }
    if( state->rstimer<=0 )
    {
        
        /*
         * Too many subsequent restarts
         */
        state->repterminationtype = 7;
        result = ae_false;
        return result;
    }
    
    /*
     * Shift Xk/Dk, update other information
     */
    ae_v_move(&state->xk.ptr.p_double[0], 1, &state->xn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->dk.ptr.p_double[0], 1, &state->dn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->fold = state->f;
    state->k = state->k+1;
    goto lbl_31;
lbl_32:
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = n;
    state->rstate.ia.ptr.p_int[1] = i;
    state->rstate.ra.ptr.p_double[0] = betak;
    state->rstate.ra.ptr.p_double[1] = v;
    state->rstate.ra.ptr.p_double[2] = vv;
    return result;
}


/*************************************************************************
This  function  activates/deactivates verification  of  the  user-supplied
analytic gradient.

Upon  activation  of  this  option  OptGuard  integrity  checker  performs
numerical differentiation of your target function  at  the  initial  point
(note: future versions may also perform check  at  the  final  point)  and
compares numerical gradient with analytic one provided by you.

If difference is too large, an error flag is set and optimization  session
continues. After optimization session is over, you can retrieve the report
which  stores  both  gradients  and  specific  components  highlighted  as
suspicious by the OptGuard.

The primary OptGuard report can be retrieved with mincgoptguardresults().

IMPORTANT: gradient check is a high-overhead option which  will  cost  you
           about 3*N additional function evaluations. In many cases it may
           cost as much as the rest of the optimization session.
           
           YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO
           CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY.

NOTE: unlike previous incarnation of the gradient checking code,  OptGuard
      does NOT interrupt optimization even if it discovers bad gradient.
      
INPUT PARAMETERS:
    State       -   structure used to store algorithm state
    TestStep    -   verification step used for numerical differentiation:
                    * TestStep=0 turns verification off
                    * TestStep>0 activates verification
                    You should carefully choose TestStep. Value  which  is
                    too large (so large that  function  behavior  is  non-
                    cubic at this scale) will lead  to  false  alarms. Too
                    short step will result in rounding  errors  dominating
                    numerical derivative.
                    
                    You may use different step for different parameters by
                    means of setting scale with mincgsetscale().

=== EXPLANATION ==========================================================                    

In order to verify gradient algorithm performs following steps:
  * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i],
    where X[i] is i-th component of the initial point and S[i] is a  scale
    of i-th parameter
  * F(X) is evaluated at these trial points
  * we perform one more evaluation in the middle point of the interval
  * we  build  cubic  model using function values and derivatives at trial
    points and we compare its prediction with actual value in  the  middle
    point

  -- ALGLIB --
     Copyright 15.06.2014 by Bochkanov Sergey
*************************************************************************/
void mincgoptguardgradient(mincgstate* state,
     double teststep,
     ae_state *_state)
{


    ae_assert(ae_isfinite(teststep, _state), "MinCGOptGuardGradient: TestStep contains NaN or INF", _state);
    ae_assert(ae_fp_greater_eq(teststep,(double)(0)), "MinCGOptGuardGradient: invalid argument TestStep(TestStep<0)", _state);
    state->teststep = teststep;
}


/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0)
b) nonsmooth     target function (non-C1)

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.
      
      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.
      
      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.
      
      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.
  
=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void mincgoptguardsmoothness(mincgstate* state,
     ae_int_t level,
     ae_state *_state)
{


    ae_assert(level==0||level==1, "MinCGOptGuardSmoothness: unexpected value of level parameter", _state);
    state->smoothnessguardlevel = level;
}


/*************************************************************************
Results of OptGuard integrity check, should be called  after  optimization
session is over.

=== PRIMARY REPORT =======================================================

OptGuard performs several checks which are intended to catch common errors
in the implementation of nonlinear function/gradient:
* incorrect analytic gradient
* discontinuous (non-C0) target functions (constraints)
* nonsmooth     (non-C1) target functions (constraints)

Each of these checks is activated with appropriate function:
* mincgoptguardgradient() for gradient verification
* mincgoptguardsmoothness() for C0/C1 checks

Following flags are set when these errors are suspected:
* rep.badgradsuspected, and additionally:
  * rep.badgradvidx for specific variable (gradient element) suspected
  * rep.badgradxbase, a point where gradient is tested
  * rep.badgraduser, user-provided gradient  (stored  as  2D  matrix  with
    single row in order to make  report  structure  compatible  with  more
    complex optimizers like MinNLC or MinLM)
  * rep.badgradnum,   reference    gradient    obtained    via   numerical
    differentiation (stored as  2D matrix with single row in order to make
    report structure compatible with more complex optimizers  like  MinNLC
    or MinLM)
* rep.nonc0suspected
* rep.nonc1suspected

=== ADDITIONAL REPORTS/LOGS ==============================================
    
Several different tests are performed to catch C0/C1 errors, you can  find
out specific test signaled error by looking to:
* rep.nonc0test0positive, for non-C0 test #0
* rep.nonc1test0positive, for non-C1 test #0
* rep.nonc1test1positive, for non-C1 test #1

Additional information (including line search logs)  can  be  obtained  by
means of:
* mincgoptguardnonc1test0results()
* mincgoptguardnonc1test1results()
which return detailed error reports, specific points where discontinuities
were found, and so on.

==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    rep     -   generic OptGuard report;  more  detailed  reports  can  be
                retrieved with other functions.

NOTE: false negatives (nonsmooth problems are not identified as  nonsmooth
      ones) are possible although unlikely.
      
      The reason  is  that  you  need  to  make several evaluations around
      nonsmoothness  in  order  to  accumulate  enough  information  about
      function curvature. Say, if you start right from the nonsmooth point,
      optimizer simply won't get enough data to understand what  is  going
      wrong before it terminates due to abrupt changes in the  derivative.
      It is also  possible  that  "unlucky"  step  will  move  us  to  the
      termination too quickly.
      
      Our current approach is to have less than 0.1%  false  negatives  in
      our test examples  (measured  with  multiple  restarts  from  random
      points), and to have exactly 0% false positives.
   
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void mincgoptguardresults(mincgstate* state,
     optguardreport* rep,
     ae_state *_state)
{

    _optguardreport_clear(rep);

    smoothnessmonitorexportreport(&state->smonitor, rep, _state);
}


/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #0

Nonsmoothness (non-C1) test #0 studies  function  values  (not  gradient!)
obtained during line searches and monitors  behavior  of  the  directional
derivative estimate.

This test is less powerful than test #1, but it does  not  depend  on  the
gradient values and thus it is more robust against artifacts introduced by
numerical differentiation.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], f[] - arrays of length CNT which store step lengths and  function
  values at these points; f[i] is evaluated in x0+stp[i]*d.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
  
==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #0 "strong" report
    lngrep  -   C1 test #0 "long" report
    
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void mincgoptguardnonc1test0results(const mincgstate* state,
     optguardnonc1test0report* strrep,
     optguardnonc1test0report* lngrep,
     ae_state *_state)
{

    _optguardnonc1test0report_clear(strrep);
    _optguardnonc1test0report_clear(lngrep);

    smoothnessmonitorexportc1test0report(&state->smonitor.nonc1test0strrep, &state->lastscaleused, strrep, _state);
    smoothnessmonitorexportc1test0report(&state->smonitor.nonc1test0lngrep, &state->lastscaleused, lngrep, _state);
}


/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #1

Nonsmoothness (non-C1)  test  #1  studies  individual  components  of  the
gradient computed during line search.

When precise analytic gradient is provided this test is more powerful than
test #0  which  works  with  function  values  and  ignores  user-provided
gradient.  However,  test  #0  becomes  more   powerful   when   numerical
differentiation is employed (in such cases test #1 detects  higher  levels
of numerical noise and becomes too conservative).

This test also tells specific components of the gradient which violate  C1
continuity, which makes it more informative than #0, which just tells that
continuity is violated.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* vidx - is an index of the variable in [0,N) with nonsmooth derivative
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], g[] - arrays of length CNT which store step lengths and  gradient
  values at these points; g[i] is evaluated in  x0+stp[i]*d  and  contains
  vidx-th component of the gradient.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
  
==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #1 "strong" report
    lngrep  -   C1 test #1 "long" report
    
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void mincgoptguardnonc1test1results(mincgstate* state,
     optguardnonc1test1report* strrep,
     optguardnonc1test1report* lngrep,
     ae_state *_state)
{

    _optguardnonc1test1report_clear(strrep);
    _optguardnonc1test1report_clear(lngrep);

    smoothnessmonitorexportc1test1report(&state->smonitor.nonc1test1strrep, &state->lastscaleused, strrep, _state);
    smoothnessmonitorexportc1test1report(&state->smonitor.nonc1test1lngrep, &state->lastscaleused, lngrep, _state);
}


/*************************************************************************
Conjugate gradient results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report:
                * Rep.TerminationType completetion code:
                    * -8    internal integrity control  detected  infinite
                            or NAN values in  function/gradient.  Abnormal
                            termination signalled.
                    * -7    gradient verification failed.
                            See MinCGSetGradientCheck() for more information.
                    *  1    relative function improvement is no more than
                            EpsF.
                    *  2    relative step is no more than EpsX.
                    *  4    gradient norm is no more than EpsG
                    *  5    MaxIts steps was taken
                    *  7    stopping conditions are too stringent,
                            further improvement is impossible,
                            we return best X found so far
                    *  8    terminated by user
                * Rep.IterationsCount contains iterations count
                * NFEV countains number of function calculations

  -- ALGLIB --
     Copyright 20.04.2009 by Bochkanov Sergey
*************************************************************************/
void mincgresults(const mincgstate* state,
     /* Real    */ ae_vector* x,
     mincgreport* rep,
     ae_state *_state)
{

    ae_vector_clear(x);
    _mincgreport_clear(rep);

    mincgresultsbuf(state, x, rep, _state);
}


/*************************************************************************
Conjugate gradient results

Buffered implementation of MinCGResults(), which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 20.04.2009 by Bochkanov Sergey
*************************************************************************/
void mincgresultsbuf(const mincgstate* state,
     /* Real    */ ae_vector* x,
     mincgreport* rep,
     ae_state *_state)
{


    if( x->cnt<state->n )
    {
        ae_vector_set_length(x, state->n, _state);
    }
    ae_v_move(&x->ptr.p_double[0], 1, &state->xn.ptr.p_double[0], 1, ae_v_len(0,state->n-1));
    rep->iterationscount = state->repiterationscount;
    rep->nfev = state->repnfev;
    rep->terminationtype = state->repterminationtype;
}


/*************************************************************************
This  subroutine  restarts  CG  algorithm from new point. All optimization
parameters are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure used to store algorithm state.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
void mincgrestartfrom(mincgstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{


    ae_assert(x->cnt>=state->n, "MinCGRestartFrom: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, state->n, _state), "MinCGCreate: X contains infinite or NaN values!", _state);
    ae_v_move(&state->xbase.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,state->n-1));
    mincgsuggeststep(state, 0.0, _state);
    ae_vector_set_length(&state->rstate.ia, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 2+1, _state);
    state->rstate.stage = -1;
    mincg_clearrequestfields(state, _state);
}


/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.
      
NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void mincgrequesttermination(mincgstate* state, ae_state *_state)
{


    state->userterminationneeded = ae_true;
}


/*************************************************************************
Faster version of MinCGSetPrecDiag(), for time-critical parts of code,
without safety checks.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetprecdiagfast(mincgstate* state,
     /* Real    */ const ae_vector* d,
     ae_state *_state)
{
    ae_int_t i;


    rvectorsetlengthatleast(&state->diagh, state->n, _state);
    rvectorsetlengthatleast(&state->diaghl2, state->n, _state);
    state->prectype = 2;
    state->vcnt = 0;
    state->innerresetneeded = ae_true;
    for(i=0; i<=state->n-1; i++)
    {
        state->diagh.ptr.p_double[i] = d->ptr.p_double[i];
        state->diaghl2.ptr.p_double[i] = 0.0;
    }
}


/*************************************************************************
This function sets low-rank preconditioner for Hessian matrix  H=D+V'*C*V,
where:
* H is a Hessian matrix, which is approximated by D/V/C
* D=D1+D2 is a diagonal matrix, which includes two positive definite terms:
  * constant term D1 (is not updated or infrequently updated)
  * variable term D2 (can be cheaply updated from iteration to iteration)
* V is a low-rank correction
* C is a diagonal factor of low-rank correction

Preconditioner P is calculated using approximate Woodburry formula:
    P  = D^(-1) - D^(-1)*V'*(C^(-1)+V*D1^(-1)*V')^(-1)*V*D^(-1)
       = D^(-1) - D^(-1)*VC'*VC*D^(-1),
where
    VC = sqrt(B)*V
    B  = (C^(-1)+V*D1^(-1)*V')^(-1)
    
Note that B is calculated using constant term (D1) only,  which  allows us
to update D2 without recalculation of B or   VC.  Such  preconditioner  is
exact when D2 is zero. When D2 is non-zero, it is only approximation,  but
very good and cheap one.

This function accepts D1, V, C.
D2 is set to zero by default.

Cost of this update is O(N*VCnt*VCnt), but D2 can be updated in just O(N)
by MinCGSetPrecVarPart.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetpreclowrankfast(mincgstate* state,
     /* Real    */ const ae_vector* d1,
     /* Real    */ const ae_vector* c,
     /* Real    */ const ae_matrix* v,
     ae_int_t vcnt,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    double t;
    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);

    if( vcnt==0 )
    {
        mincgsetprecdiagfast(state, d1, _state);
        ae_frame_leave(_state);
        return;
    }
    n = state->n;
    ae_matrix_set_length(&b, vcnt, vcnt, _state);
    rvectorsetlengthatleast(&state->diagh, n, _state);
    rvectorsetlengthatleast(&state->diaghl2, n, _state);
    rmatrixsetlengthatleast(&state->vcorr, vcnt, n, _state);
    state->prectype = 2;
    state->vcnt = vcnt;
    state->innerresetneeded = ae_true;
    for(i=0; i<=n-1; i++)
    {
        state->diagh.ptr.p_double[i] = d1->ptr.p_double[i];
        state->diaghl2.ptr.p_double[i] = 0.0;
    }
    for(i=0; i<=vcnt-1; i++)
    {
        for(j=i; j<=vcnt-1; j++)
        {
            t = (double)(0);
            for(k=0; k<=n-1; k++)
            {
                t = t+v->ptr.pp_double[i][k]*v->ptr.pp_double[j][k]/d1->ptr.p_double[k];
            }
            b.ptr.pp_double[i][j] = t;
        }
        b.ptr.pp_double[i][i] = b.ptr.pp_double[i][i]+1.0/c->ptr.p_double[i];
    }
    if( !spdmatrixcholeskyrec(&b, 0, vcnt, ae_true, &state->work0, _state) )
    {
        state->vcnt = 0;
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=vcnt-1; i++)
    {
        ae_v_move(&state->vcorr.ptr.pp_double[i][0], 1, &v->ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
        for(j=0; j<=i-1; j++)
        {
            t = b.ptr.pp_double[j][i];
            ae_v_subd(&state->vcorr.ptr.pp_double[i][0], 1, &state->vcorr.ptr.pp_double[j][0], 1, ae_v_len(0,n-1), t);
        }
        t = (double)1/b.ptr.pp_double[i][i];
        ae_v_muld(&state->vcorr.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), t);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function updates variable part (diagonal matrix D2)
of low-rank preconditioner.

This update is very cheap and takes just O(N) time.

It has no effect with default preconditioner.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void mincgsetprecvarpart(mincgstate* state,
     /* Real    */ const ae_vector* d2,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    for(i=0; i<=n-1; i++)
    {
        state->diaghl2.ptr.p_double[i] = d2->ptr.p_double[i];
    }
}


/*************************************************************************
Set V1 reverse communication protocol
*************************************************************************/
void mincgsetprotocolv1(mincgstate* state, ae_state *_state)
{


    state->protocolversion = 1;
    ae_vector_set_length(&state->rstate.ia, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 2+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Clears request fileds (to be sure that we don't forgot to clear something)
*************************************************************************/
static void mincg_clearrequestfields(mincgstate* state, ae_state *_state)
{


    state->needf = ae_false;
    state->needfg = ae_false;
    state->xupdated = ae_false;
    state->lsstart = ae_false;
    state->lsend = ae_false;
    state->algpowerup = ae_false;
}


/*************************************************************************
This function calculates preconditioned product H^(-1)*x and stores result
back into X. Work0[] and Work1[] are used as temporaries (size must be at
least N; this function doesn't allocate arrays).

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
static void mincg_preconditionedmultiply(mincgstate* state,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* work0,
     /* Real    */ ae_vector* work1,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t vcnt;
    double v;


    n = state->n;
    vcnt = state->vcnt;
    if( state->prectype==0 )
    {
        return;
    }
    if( state->prectype==3 )
    {
        for(i=0; i<=n-1; i++)
        {
            x->ptr.p_double[i] = x->ptr.p_double[i]*state->s.ptr.p_double[i]*state->s.ptr.p_double[i];
        }
        return;
    }
    ae_assert(state->prectype==2, "MinCG: internal error (unexpected PrecType)", _state);
    
    /*
     * handle part common for VCnt=0 and VCnt<>0
     */
    for(i=0; i<=n-1; i++)
    {
        x->ptr.p_double[i] = x->ptr.p_double[i]/(state->diagh.ptr.p_double[i]+state->diaghl2.ptr.p_double[i]);
    }
    
    /*
     * if VCnt>0
     */
    if( vcnt>0 )
    {
        for(i=0; i<=vcnt-1; i++)
        {
            v = ae_v_dotproduct(&state->vcorr.ptr.pp_double[i][0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
            work0->ptr.p_double[i] = v;
        }
        for(i=0; i<=n-1; i++)
        {
            work1->ptr.p_double[i] = (double)(0);
        }
        for(i=0; i<=vcnt-1; i++)
        {
            v = work0->ptr.p_double[i];
            ae_v_addd(&state->work1.ptr.p_double[0], 1, &state->vcorr.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
        }
        for(i=0; i<=n-1; i++)
        {
            x->ptr.p_double[i] = x->ptr.p_double[i]-state->work1.ptr.p_double[i]/(state->diagh.ptr.p_double[i]+state->diaghl2.ptr.p_double[i]);
        }
    }
}


/*************************************************************************
This function calculates preconditioned product x'*H^(-1)*y. Work0[] and
Work1[] are used as temporaries (size must be at least N; this function
doesn't allocate arrays).

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
static double mincg_preconditionedmultiply2(mincgstate* state,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* work0,
     /* Real    */ ae_vector* work1,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t vcnt;
    double v0;
    double v1;
    double result;


    n = state->n;
    vcnt = state->vcnt;
    
    /*
     * no preconditioning
     */
    if( state->prectype==0 )
    {
        v0 = ae_v_dotproduct(&x->ptr.p_double[0], 1, &y->ptr.p_double[0], 1, ae_v_len(0,n-1));
        result = v0;
        return result;
    }
    if( state->prectype==3 )
    {
        result = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            result = result+x->ptr.p_double[i]*state->s.ptr.p_double[i]*state->s.ptr.p_double[i]*y->ptr.p_double[i];
        }
        return result;
    }
    ae_assert(state->prectype==2, "MinCG: internal error (unexpected PrecType)", _state);
    
    /*
     * low rank preconditioning
     */
    result = 0.0;
    for(i=0; i<=n-1; i++)
    {
        result = result+x->ptr.p_double[i]*y->ptr.p_double[i]/(state->diagh.ptr.p_double[i]+state->diaghl2.ptr.p_double[i]);
    }
    if( vcnt>0 )
    {
        for(i=0; i<=n-1; i++)
        {
            work0->ptr.p_double[i] = x->ptr.p_double[i]/(state->diagh.ptr.p_double[i]+state->diaghl2.ptr.p_double[i]);
            work1->ptr.p_double[i] = y->ptr.p_double[i]/(state->diagh.ptr.p_double[i]+state->diaghl2.ptr.p_double[i]);
        }
        for(i=0; i<=vcnt-1; i++)
        {
            v0 = ae_v_dotproduct(&work0->ptr.p_double[0], 1, &state->vcorr.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            v1 = ae_v_dotproduct(&work1->ptr.p_double[0], 1, &state->vcorr.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            result = result-v0*v1;
        }
    }
    return result;
}


/*************************************************************************
Internal initialization subroutine

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
static void mincg_mincginitinternal(ae_int_t n,
     double diffstep,
     mincgstate* state,
     ae_state *_state)
{
    ae_int_t i;


    
    /*
     * Initialize
     */
    state->protocolversion = 1;
    state->teststep = (double)(0);
    state->smoothnessguardlevel = 0;
    smoothnessmonitorinit(&state->smonitor, &state->s, 0, 0, ae_false, _state);
    state->n = n;
    state->diffstep = diffstep;
    state->lastgoodstep = (double)(0);
    mincgsetcond(state, (double)(0), (double)(0), (double)(0), 0, _state);
    mincgsetxrep(state, ae_false, _state);
    mincgsetdrep(state, ae_false, _state);
    mincgsetstpmax(state, (double)(0), _state);
    mincgsetcgtype(state, -1, _state);
    mincgsetprecdefault(state, _state);
    ae_vector_set_length(&state->xk, n, _state);
    ae_vector_set_length(&state->dk, n, _state);
    ae_vector_set_length(&state->xn, n, _state);
    ae_vector_set_length(&state->dn, n, _state);
    ae_vector_set_length(&state->x, n, _state);
    ae_vector_set_length(&state->d, n, _state);
    ae_vector_set_length(&state->g, n, _state);
    ae_vector_set_length(&state->work0, n, _state);
    ae_vector_set_length(&state->work1, n, _state);
    ae_vector_set_length(&state->yk, n, _state);
    ae_vector_set_length(&state->s, n, _state);
    ae_vector_set_length(&state->invs, n, _state);
    ae_vector_set_length(&state->lastscaleused, n, _state);
    rvectorsetlengthatleast(&state->xbase, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->s.ptr.p_double[i] = 1.0;
        state->invs.ptr.p_double[i] = 1.0;
        state->lastscaleused.ptr.p_double[i] = 1.0;
    }
}


void _mincgstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    mincgstate *p = (mincgstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->diagh, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diaghl2, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->vcorr, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xk, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dk, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->yk, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xbase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    _linminstate_init(&p->lstate, _state, make_automatic);
    ae_vector_init(&p->work0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->work1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->invs, 0, DT_REAL, _state, make_automatic);
    _smoothnessmonitor_init(&p->smonitor, _state, make_automatic);
    ae_vector_init(&p->lastscaleused, 0, DT_REAL, _state, make_automatic);
}


void _mincgstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    mincgstate       *dst = (mincgstate*)_dst;
    const mincgstate *src = (const mincgstate*)_src;
    dst->n = src->n;
    dst->epsg = src->epsg;
    dst->epsf = src->epsf;
    dst->epsx = src->epsx;
    dst->maxits = src->maxits;
    dst->stpmax = src->stpmax;
    dst->suggestedstep = src->suggestedstep;
    dst->xrep = src->xrep;
    dst->drep = src->drep;
    dst->cgtype = src->cgtype;
    dst->prectype = src->prectype;
    ae_vector_init_copy(&dst->diagh, &src->diagh, _state, make_automatic);
    ae_vector_init_copy(&dst->diaghl2, &src->diaghl2, _state, make_automatic);
    ae_matrix_init_copy(&dst->vcorr, &src->vcorr, _state, make_automatic);
    dst->vcnt = src->vcnt;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    dst->diffstep = src->diffstep;
    dst->nfev = src->nfev;
    dst->mcstage = src->mcstage;
    dst->k = src->k;
    ae_vector_init_copy(&dst->xk, &src->xk, _state, make_automatic);
    ae_vector_init_copy(&dst->dk, &src->dk, _state, make_automatic);
    ae_vector_init_copy(&dst->xn, &src->xn, _state, make_automatic);
    ae_vector_init_copy(&dst->dn, &src->dn, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    dst->fold = src->fold;
    dst->stp = src->stp;
    dst->curstpmax = src->curstpmax;
    ae_vector_init_copy(&dst->yk, &src->yk, _state, make_automatic);
    dst->lastgoodstep = src->lastgoodstep;
    dst->lastscaledstep = src->lastscaledstep;
    dst->mcinfo = src->mcinfo;
    dst->innerresetneeded = src->innerresetneeded;
    dst->terminationneeded = src->terminationneeded;
    dst->trimthreshold = src->trimthreshold;
    ae_vector_init_copy(&dst->xbase, &src->xbase, _state, make_automatic);
    dst->rstimer = src->rstimer;
    dst->protocolversion = src->protocolversion;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    dst->f = src->f;
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    dst->needf = src->needf;
    dst->needfg = src->needfg;
    dst->xupdated = src->xupdated;
    dst->algpowerup = src->algpowerup;
    dst->lsstart = src->lsstart;
    dst->lsend = src->lsend;
    dst->userterminationneeded = src->userterminationneeded;
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    dst->repiterationscount = src->repiterationscount;
    dst->repnfev = src->repnfev;
    dst->repterminationtype = src->repterminationtype;
    dst->debugrestartscount = src->debugrestartscount;
    _linminstate_init_copy(&dst->lstate, &src->lstate, _state, make_automatic);
    dst->fbase = src->fbase;
    dst->fm2 = src->fm2;
    dst->fm1 = src->fm1;
    dst->fp1 = src->fp1;
    dst->fp2 = src->fp2;
    dst->betahs = src->betahs;
    dst->betady = src->betady;
    ae_vector_init_copy(&dst->work0, &src->work0, _state, make_automatic);
    ae_vector_init_copy(&dst->work1, &src->work1, _state, make_automatic);
    ae_vector_init_copy(&dst->invs, &src->invs, _state, make_automatic);
    dst->teststep = src->teststep;
    dst->smoothnessguardlevel = src->smoothnessguardlevel;
    _smoothnessmonitor_init_copy(&dst->smonitor, &src->smonitor, _state, make_automatic);
    ae_vector_init_copy(&dst->lastscaleused, &src->lastscaleused, _state, make_automatic);
}


void _mincgstate_clear(void* _p)
{
    mincgstate *p = (mincgstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->diagh);
    ae_vector_clear(&p->diaghl2);
    ae_matrix_clear(&p->vcorr);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->xk);
    ae_vector_clear(&p->dk);
    ae_vector_clear(&p->xn);
    ae_vector_clear(&p->dn);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->yk);
    ae_vector_clear(&p->xbase);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->g);
    _rcommstate_clear(&p->rstate);
    _linminstate_clear(&p->lstate);
    ae_vector_clear(&p->work0);
    ae_vector_clear(&p->work1);
    ae_vector_clear(&p->invs);
    _smoothnessmonitor_clear(&p->smonitor);
    ae_vector_clear(&p->lastscaleused);
}


void _mincgstate_destroy(void* _p)
{
    mincgstate *p = (mincgstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->diagh);
    ae_vector_destroy(&p->diaghl2);
    ae_matrix_destroy(&p->vcorr);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->xk);
    ae_vector_destroy(&p->dk);
    ae_vector_destroy(&p->xn);
    ae_vector_destroy(&p->dn);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->yk);
    ae_vector_destroy(&p->xbase);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->g);
    _rcommstate_destroy(&p->rstate);
    _linminstate_destroy(&p->lstate);
    ae_vector_destroy(&p->work0);
    ae_vector_destroy(&p->work1);
    ae_vector_destroy(&p->invs);
    _smoothnessmonitor_destroy(&p->smonitor);
    ae_vector_destroy(&p->lastscaleused);
}


void _mincgreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    mincgreport *p = (mincgreport*)_p;
    ae_touch_ptr((void*)p);
}


void _mincgreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    mincgreport       *dst = (mincgreport*)_dst;
    const mincgreport *src = (const mincgreport*)_src;
    dst->iterationscount = src->iterationscount;
    dst->nfev = src->nfev;
    dst->terminationtype = src->terminationtype;
}


void _mincgreport_clear(void* _p)
{
    mincgreport *p = (mincgreport*)_p;
    ae_touch_ptr((void*)p);
}


void _mincgreport_destroy(void* _p)
{
    mincgreport *p = (mincgreport*)_p;
    ae_touch_ptr((void*)p);
}


#endif
#if defined(AE_COMPILE_DFGENMOD) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
Initialize derivative-free general model-based solver (DFGM)

IMPORTANT: the solver does NOT support fixed variables. The caller has  to
           preprocess the problem in order to drop all fixed vars.
*************************************************************************/
void dfgminitbuf(/* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* x0,
     ae_int_t n,
     ae_int_t m,
     ae_bool isls,
     ae_int_t modeltype,
     const nlpstoppingcriteria* criteria,
     ae_int_t nnoisyrestarts,
     double rad0,
     ae_int_t maxfev,
     dfgmstate* state,
     ae_state *_state)
{
    ae_int_t i;


    
    /*
     * General initialization
     */
    ae_assert(isls||m==1, "DFGM: M<>1 for a non-least-squares problem", _state);
    state->n = n;
    state->m = m;
    state->isleastsquares = isls;
    state->cntlc = 0;
    state->cntnlc = 0;
    state->rad0 = coalesce(rad0, 0.1, _state);
    state->nnoisyrestarts = nnoisyrestarts;
    
    /*
     * Model type
     */
    ae_assert((modeltype==0||modeltype==1)||modeltype==2, "DFGM: unexpected model type, check 0316 failed", _state);
    ae_assert(modeltype!=0||isls, "DFGM: ModelType=0 is possible only with least squares problems", _state);
    ae_assert(modeltype!=1||isls, "DFGM: ModelType=1 is possible only with least squares problems", _state);
    ae_assert(modeltype!=2||!isls, "DFGM: ModelType=2 is possible only with non-least squares problems", _state);
    state->modeltype = modeltype;
    
    /*
     *
     */
    ae_assert((modeltype==0||modeltype==1)||modeltype==2, "DFGM: unexpected model type, check 256748 failed", _state);
    
    /*
     * Stopping criteria
     */
    state->epsx = critgetepswithdefault(criteria, 0.000001, _state);
    state->epsf = critgetepsf(criteria, _state);
    state->maxits = critgetmaxits(criteria, _state);
    state->maxfev = maxfev;
    state->toosmalltrustrad = coalesce(0.001*state->epsx, ae_machineepsilon, _state);
    
    /*
     * Prepare scaled problem
     */
    rallocv(n, &state->x0, _state);
    rallocv(n, &state->s, _state);
    bvectorsetlengthatleast(&state->hasbndl, n, _state);
    bvectorsetlengthatleast(&state->hasbndu, n, _state);
    rvectorsetlengthatleast(&state->scaledbndl, n, _state);
    rvectorsetlengthatleast(&state->scaledbndu, n, _state);
    rsetallocv(n, -1.0E50, &state->finitebndl, _state);
    rsetallocv(n, 1.0E50, &state->finitebndu, _state);
    for(i=0; i<=n-1; i++)
    {
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
        if( state->hasbndl.ptr.p_bool[i] )
        {
            state->scaledbndl.ptr.p_double[i] = bndl->ptr.p_double[i]/s->ptr.p_double[i];
            state->finitebndl.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i] )
        {
            state->scaledbndu.ptr.p_double[i] = bndu->ptr.p_double[i]/s->ptr.p_double[i];
            state->finitebndu.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i];
        }
        if( state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i] )
        {
            ae_assert(ae_fp_less(bndl->ptr.p_double[i],bndu->ptr.p_double[i]), "DFGM: integrity check 6007 failed, box constraints with no interior point", _state);
        }
        state->x0.ptr.p_double[i] = x0->ptr.p_double[i]/s->ptr.p_double[i];
        state->s.ptr.p_double[i] = s->ptr.p_double[i];
    }
    enforceboundaryconstraints(&state->x0, &state->scaledbndl, &state->hasbndl, &state->scaledbndu, &state->hasbndu, n, 0, _state);
    
    /*
     * Internal RNG
     */
    hqrndseed(117564, 983549, &state->rs, _state);
    
    /*
     * Report fields
     */
    state->repterminationtype = 0;
    state->repiterationscount = 0;
    state->repnfev = 0;
    state->repsubsolverits = 0;
    
    /*
     * Model-specific initialization
     */
    ae_assert((modeltype==0||modeltype==1)||modeltype==2, "DFGM: unexpected model type, check 302739 failed", _state);
    
    /*
     * Trace status
     */
    ae_assert((modeltype==0||modeltype==1)||modeltype==2, "DFGM: unexpected model type, check 0317 failed", _state);
    state->dotrace = ae_false;
    if( modeltype==0 )
    {
        state->dotrace = ae_is_trace_enabled("2PS");
    }
    if( modeltype==1 )
    {
        state->dotrace = ae_is_trace_enabled("DFOLSA");
    }
    if( modeltype==2 )
    {
        state->dotrace = ae_is_trace_enabled("ORBIT");
    }
    state->dotrace = state->dotrace||ae_is_trace_enabled("DFGM");
    
    /*
     * RComm
     */
    ae_vector_set_length(&state->rstate.ia, 4+1, _state);
    ae_vector_set_length(&state->rstate.ba, 0+1, _state);
    ae_vector_set_length(&state->rstate.ra, 7+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Sets linear constraints. Supported only for ModelType=2
*************************************************************************/
void dfgmsetlc2(dfgmstate* state,
     const sparsematrix* c,
     /* Real    */ const ae_vector* cl,
     /* Real    */ const ae_vector* cu,
     ae_int_t m,
     ae_state *_state)
{


    ae_assert(state->modeltype==2, "DFGMSetLC2: only modelType=2 is supported", _state);
    ae_assert(m>=0, "DFGMSetLC2: M<0", _state);
    ae_assert(m==0||(c->m==m&&c->n==state->n), "DFGMSetLC2: matrix size does not match problem statement", _state);
    state->cntlc = m;
    if( m==0 )
    {
        return;
    }
    sparsecopytocrsbuf(c, &state->c, _state);
    rcopyallocv(m, cl, &state->cl, _state);
    rcopyallocv(m, cu, &state->cu, _state);
    rsetallocv(state->n, 0.0, &state->tmp0, _state);
    scaleshiftsparselcinplace(&state->s, &state->tmp0, state->n, &state->c, m, &state->cl, &state->cu, _state);
    normalizesparselcinplace(&state->c, m, &state->cl, &state->cu, state->n, ae_true, &state->tmp0, ae_false, _state);
}


/*************************************************************************
Sets nonlinear constraints. Supported only for ModelType=2
*************************************************************************/
void dfgmsetnlc2(dfgmstate* state,
     /* Real    */ const ae_vector* nl,
     /* Real    */ const ae_vector* nu,
     ae_int_t m,
     ae_state *_state)
{


    ae_assert(state->modeltype==2, "DFGMSetNLC2: only modelType=2 is supported", _state);
    ae_assert(m>=0, "DFGMSetNLC2: M<0", _state);
    state->cntnlc = m;
    if( m==0 )
    {
        return;
    }
    rcopyallocv(m, nl, &state->nl, _state);
    rcopyallocv(m, nu, &state->nu, _state);
}


/*************************************************************************

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
ae_bool dfgmiteration(dfgmstate* state,
     ae_bool userterminationneeded,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t m;
    ae_int_t cntnlc;
    double predf;
    double predh;
    double fnew;
    double hnew;
    double skrellen;
    double v;
    double v1;
    double vv;
    ae_bool accepted;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        i = state->rstate.ia.ptr.p_int[0];
        j = state->rstate.ia.ptr.p_int[1];
        n = state->rstate.ia.ptr.p_int[2];
        m = state->rstate.ia.ptr.p_int[3];
        cntnlc = state->rstate.ia.ptr.p_int[4];
        accepted = state->rstate.ba.ptr.p_bool[0];
        predf = state->rstate.ra.ptr.p_double[0];
        predh = state->rstate.ra.ptr.p_double[1];
        fnew = state->rstate.ra.ptr.p_double[2];
        hnew = state->rstate.ra.ptr.p_double[3];
        skrellen = state->rstate.ra.ptr.p_double[4];
        v = state->rstate.ra.ptr.p_double[5];
        v1 = state->rstate.ra.ptr.p_double[6];
        vv = state->rstate.ra.ptr.p_double[7];
    }
    else
    {
        i = 359;
        j = -58;
        n = -919;
        m = -909;
        cntnlc = 81;
        accepted = ae_true;
        predf = 74.0;
        predh = -788.0;
        fnew = 809.0;
        hnew = 205.0;
        skrellen = -838.0;
        v = 939.0;
        v1 = -526.0;
        vv = 763.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    if( state->rstate.stage==6 )
    {
        goto lbl_6;
    }
    if( state->rstate.stage==7 )
    {
        goto lbl_7;
    }
    
    /*
     * Routine body
     */
    n = state->n;
    m = state->m;
    cntnlc = state->cntnlc;
    if( state->dotrace )
    {
        ae_trace("> derivative-free model-based solver started\n");
        if( state->modeltype==0 )
        {
            ae_trace("algorithm     = 2PS\n");
        }
        if( state->modeltype==1 )
        {
            ae_trace("algorithm     = DFO-LSA\n");
        }
    }
    
    /*
     * Initialization phase
     *
     * NOTE: TrustRadFactor is initialized by zero prior to calling UpdateTrustRegion()
     *       because this function expects some initial value to be present here.
     */
    dfgenmod_initcloud(state, state->modeltype==2, _state);
    rcopyallocv(n, &state->x0, &state->xk, _state);
    state->requesttype = 4;
    state->querysize = 1;
    rcopyallocv(n, &state->xk, &state->querydata, _state);
    rallocv(m+cntnlc, &state->replyfi, _state);
    dfgenmod_sendrequesttocloud(state, &state->querydata, state->querysize, _state);
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    if( !isfinitevector(&state->replyfi, (m+cntnlc)*state->querysize, _state) )
    {
        
        /*
         * Critical failure - the target is infinite from the very beginning
         */
        if( state->dotrace )
        {
            ae_trace("[ERROR] target at the initial point is infinite, stopping due to the critical failure\n");
        }
        state->infinitiesencountered = ae_true;
        state->recoveredfrominfinities = ae_false;
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    dfgenmod_sendreplytocloud(state, &state->replyfi, _state);
    rcopyallocv(m+cntnlc, &state->replyfi, &state->fkvec, _state);
    if( state->isleastsquares )
    {
        state->fk = 0.5*rdotv2(m, &state->fkvec, _state);
    }
    else
    {
        ae_assert(m==1, "DFGM: integrity check 458634 failed", _state);
        state->fk = state->fkvec.ptr.p_double[0];
    }
    state->hk = (double)(0);
    if( state->cntlc>0 )
    {
        rallocv(n, &state->tmp0, _state);
        rcopyv(n, &state->xk, &state->tmp0, _state);
        sparsemv(&state->c, &state->tmp0, &state->tmp1, _state);
        for(i=0; i<=state->cntlc-1; i++)
        {
            if( ae_isfinite(state->cl.ptr.p_double[i], _state) )
            {
                state->hk = state->hk+ae_maxreal(state->cl.ptr.p_double[i]-state->tmp1.ptr.p_double[i], 0.0, _state);
            }
            if( ae_isfinite(state->cu.ptr.p_double[i], _state) )
            {
                state->hk = state->hk+ae_maxreal(state->tmp1.ptr.p_double[i]-state->cu.ptr.p_double[i], 0.0, _state);
            }
        }
    }
    for(i=0; i<=cntnlc-1; i++)
    {
        if( ae_isfinite(state->nl.ptr.p_double[i], _state) )
        {
            state->hk = state->hk+ae_maxreal(state->nl.ptr.p_double[i]-state->fkvec.ptr.p_double[m+i], 0.0, _state);
        }
        if( ae_isfinite(state->nu.ptr.p_double[i], _state) )
        {
            state->hk = state->hk+ae_maxreal(state->fkvec.ptr.p_double[m+i]-state->nu.ptr.p_double[i], 0.0, _state);
        }
    }
    rallocv(n, &state->xp, _state);
    rallocv(m+cntnlc, &state->fpvec, _state);
    rallocv(n, &state->sk, _state);
    rallocv(n, &state->xn, _state);
    rallocv(m+cntnlc, &state->fnvec, _state);
    state->infinitiesencountered = ae_false;
    state->recoveredfrominfinities = ae_true;
    dfgenmod_inithistory(state, _state);
    state->trustradfactor = (double)(0);
    dfgenmod_updatetrustregion(state, state->rad0, _state);
    state->trustradbnd = state->trustradfactor;
    state->wrksetsize = 0;
    nlpfinit((double)10*ae_maxreal((double)(10), state->hk, _state), &state->filter, _state);
    state->filter.gammaf = 0.001;
    if( ae_fp_greater(state->hk,(double)(0)) )
    {
        nlpfappend(&state->filter, state->fk, state->hk, _state);
    }
    
    /*
     * Start iterations
     */
lbl_8:
    if( ae_false )
    {
        goto lbl_9;
    }
    
    /*
     * The working set is empty. It may happen during either the very first iteration of the solver,
     * or after the restart. Generate new working set from scratch, compute FkVec, Fk, Hk.
     */
    if( state->wrksetsize!=0 )
    {
        goto lbl_10;
    }
    ae_vector_set_length(&state->rstateimprove.ia, 13+1, _state);
    ae_vector_set_length(&state->rstateimprove.ba, 0+1, _state);
    ae_vector_set_length(&state->rstateimprove.ra, 12+1, _state);
    state->rstateimprove.stage = -1;
lbl_12:
    if( !dfgenmod_improveworkingsetgeometry(state, _state) )
    {
        goto lbl_13;
    }
    if( state->requesttype==4 )
    {
        state->repnfev = state->repnfev+state->querysize;
    }
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    goto lbl_12;
lbl_13:
    ae_assert(state->repterminationtype<=0, "DFGM: integrity check 4524 failed", _state);
    if( state->repterminationtype<0 )
    {
        goto lbl_9;
    }
    ae_assert(state->wrksetsize>0, "DFGM: integrity check 0227 failed", _state);
lbl_10:
    
    /*
     * Update history of changes in the trust radius and the Jacobian
     */
    dfgenmod_updatehistory(state, _state);
    
    /*
     * Report current point
     */
    state->requesttype = -1;
    rcopyallocv(n, &state->xk, &state->reportx, _state);
    state->reportf = state->fk;
    if( state->isleastsquares )
    {
        state->reportf = (double)2*state->reportf;
    }
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    
    /*
     * Check stopping criteria
     */
    if( state->maxits>0&&state->repiterationscount>=state->maxits )
    {
        if( state->dotrace )
        {
            ae_trace("> stopping condition triggered: maximum number of iterations\n");
        }
        state->repterminationtype = 5;
        goto lbl_9;
    }
    if( state->maxfev>0&&state->repnfev>=state->maxfev )
    {
        if( state->dotrace )
        {
            ae_trace("> stopping condition triggered: maximum number of function evaluations\n");
        }
        state->repterminationtype = 5;
        goto lbl_9;
    }
    if( ae_fp_less_eq(state->trustradbnd,ae_maxreal(state->epsx, state->toosmalltrustrad, _state)) )
    {
        
        /*
         * Trust radius decreased: either perform a restart or stop
         */
        if( state->restartstoperform>0 )
        {
            dfgenmod_updatetrustregion(state, state->rad0, _state);
            state->trustradbnd = state->trustradfactor;
            state->wrksetsize = 0;
            dfgenmod_clearhistoryonrestart(state, _state);
            state->restartstoperform = state->restartstoperform-1;
            if( state->dotrace )
            {
                ae_trace("> restart is performed due to a small trust region size (%0d restarts left)\n",
                    (int)(state->restartstoperform));
            }
            goto lbl_8;
        }
        if( ae_fp_less_eq(state->trustradbnd,state->epsx) )
        {
            if( state->dotrace )
            {
                ae_trace("> stopping condition triggered: step less than EpsX\n");
            }
            state->repterminationtype = 2;
        }
        else
        {
            if( state->dotrace )
            {
                ae_trace("> stopping condition triggered: step is too small, emergency termination\n");
            }
            state->repterminationtype = 7;
        }
        goto lbl_9;
    }
    v = (double)(0);
    for(i=0; i<=state->successhistorylen-1; i++)
    {
        v = v+state->successhistory.ptr.p_double[i];
    }
    if( state->successhistorylen==state->successhistorymax&&ae_fp_less_eq(ae_fabs(v, _state),state->epsf*rmaxabs2(state->fk, (double)(1), _state)) )
    {
        if( state->dotrace )
        {
            ae_trace("> stopping condition triggered: total progress over last N+1 successful its is less than EpsF\n");
        }
        state->repterminationtype = 1;
        goto lbl_9;
    }
    if( userterminationneeded )
    {
        if( state->dotrace )
        {
            ae_trace("> user requested immediate termination\n");
        }
        state->repterminationtype = 8;
        goto lbl_9;
    }
    
    /*
     * Trace output (if needed)
     */
    if( state->dotrace )
    {
        ae_trace("\n=== ITERATION %5d STARTED ========================================================================\n",
            (int)(state->repiterationscount));
        ae_trace("DeltaK        = %0.3e (current trust radius factor)\n",
            (double)(state->trustradfactor));
        ae_trace("RhoK          = %0.3e (lower bound on the trust radius)\n",
            (double)(state->trustradbnd));
        ae_trace("|x|inf        = %0.3e\n",
            (double)(rmaxabsv(n, &state->xk, _state)));
        ae_trace("> stopping criteria:\n");
        v = (double)(0);
        for(i=0; i<=state->successhistorylen-1; i++)
        {
            v = v+state->successhistory.ptr.p_double[i];
        }
        ae_trace("sum delta_F   = %10.2e (sum of function decreases over last N+1 successful its)\n",
            (double)(v));
        ae_trace("> computational budget used:\n");
        ae_trace("eval          = %7d    (target/constraints evaluations)\n",
            (int)(state->repnfev));
        ae_trace("splx.equiv    = %9.1f  (simplex gradient equivalent = evaluations/(N+1) )\n",
            (double)((double)state->repnfev/(double)(n+1)));
        rallocv(n, &state->tmp0, _state);
        rallocv(n, &state->tmp1, _state);
        dfgenmod_mostandleastdistant(&state->invtrustregion, n, &state->wrkset, state->wrksetsize, &v, &i, &vv, &j, _state);
        ae_trace("> working set geometry:\n");
        ae_trace("furthest pt   = %6.2f     (distance to furthest point divided by DeltaK)\n",
            (double)(v));
        ae_trace("nearest pt    = %6.2f     (distance to nearest point divided by DeltaK)\n",
            (double)(vv));
        if( state->nnoisyrestarts>0 )
        {
            ae_trace("> restart autodetection parameters:\n");
            if( state->historylen>=dfgenmod_minhistory )
            {
                linregline(&state->iteridxhistory, &state->jacdiffhistory, state->historylen, &v, &v1, &vv, _state);
                ae_trace("slope         = %6.2f     (slope of log(J[i+1]-J[i]) vs iteration index regression line)\n",
                    (double)(v1));
                ae_trace("correlation   = %6.2f     (corr  of log(J[i+1]-J[i]) vs iteration index regression line)\n",
                    (double)(vv));
                v = (double)(0);
                v1 = (double)(0);
                vv = (double)(0);
                for(i=0; i<=state->historylen-1; i++)
                {
                    if( ae_fp_greater(state->trustradhistory.ptr.p_double[i],(double)(0)) )
                    {
                        v = v+(double)1;
                    }
                    if( ae_fp_less(state->trustradhistory.ptr.p_double[i],(double)(0)) )
                    {
                        v1 = v1+(double)1;
                    }
                    if( ae_fp_eq(state->trustradhistory.ptr.p_double[i],(double)(0)) )
                    {
                        vv = vv+(double)1;
                    }
                }
                ae_trace("tr-increase   = %3d    (steps with increased trust radius)\n",
                    (int)(ae_round(v, _state)));
                ae_trace("tr-decrease   = %3d    (steps with decreased trust radius)\n",
                    (int)(ae_round(v1, _state)));
                ae_trace("tr-stagnate   = %3d    (steps with stagnated trust radius)\n",
                    (int)(ae_round(vv, _state)));
            }
            else
            {
                ae_trace(">> accumulating history...\n");
            }
        }
        ae_trace("\n");
    }
    
    /*
     * Generate trial request, invoke safety phase if necessary
     */
    dfgenmod_preparetrialrequest(state, &predf, &predh, _state);
    skrellen = dfgenmod_normrelativetotrustregion(state, &state->sk, _state);
    if( state->dotrace )
    {
        ae_trace("> prepared trial step with relative length |Sk|/DeltaK=%0.1e, predicted deltaF=%0.1e, deltaH=%0.1e\n",
            (double)(skrellen),
            (double)(predf-state->fk),
            (double)(predh-state->hk));
    }
    if( ae_fp_greater(skrellen*state->trustradfactor,dfgenmod_gammasafety*state->trustradbnd) )
    {
        goto lbl_14;
    }
    
    /*
     * Decrease trust region size and send step denial signal
     */
    if( state->dotrace )
    {
        ae_trace("> proposed step is too short, decreasing trust region radius\n");
    }
    dfgenmod_updatetrustregion(state, ae_maxreal(dfgenmod_omegasafety*state->trustradfactor, state->trustradbnd, _state), _state);
    if( dfgenmod_deltakatminimum(state, _state) )
    {
        v = state->trustradbnd;
        dfgenmod_updatetrustregion(state, dfgenmod_alpha2*v, _state);
        state->trustradbnd = dfgenmod_alpha1*v;
    }
    dfgenmod_sendstepdenialtomodel(state, _state);
    
    /*
     * Improve geometry of the dataset
     */
    if( state->dotrace )
    {
        ae_trace("> improving geometry of the working set\n");
    }
    ae_vector_set_length(&state->rstateimprove.ia, 13+1, _state);
    ae_vector_set_length(&state->rstateimprove.ba, 0+1, _state);
    ae_vector_set_length(&state->rstateimprove.ra, 12+1, _state);
    state->rstateimprove.stage = -1;
lbl_16:
    if( !dfgenmod_improveworkingsetgeometry(state, _state) )
    {
        goto lbl_17;
    }
    if( state->requesttype==4 )
    {
        state->repnfev = state->repnfev+state->querysize;
    }
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    goto lbl_16;
lbl_17:
    if( state->repterminationtype!=0 )
    {
        goto lbl_9;
    }
    
    /*
     * Done
     */
    inc(&state->repiterationscount, _state);
    goto lbl_8;
lbl_14:
    if( state->requesttype==4 )
    {
        state->repnfev = state->repnfev+state->querysize;
    }
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    if( !isfinitevector(&state->replyfi, (m+cntnlc)*state->querysize, _state) )
    {
        
        /*
         * Infinities in the target/constraints, decrease trust radius and retry
         */
        if( state->dotrace )
        {
            ae_trace("[WARNING] target at the trial point is infinite, decreasing trust radius and restarting iteration\n");
        }
        state->infinitiesencountered = ae_true;
        state->recoveredfrominfinities = ae_false;
        dfgenmod_updatetrustregion(state, dfgenmod_gammabad*state->trustradfactor, _state);
        state->trustradbnd = ae_minreal(state->trustradfactor, state->trustradbnd, _state);
        dfgenmod_sendstepdenialtomodel(state, _state);
        inc(&state->repiterationscount, _state);
        goto lbl_8;
    }
    dfgenmod_processtrialrequest(state, &fnew, &hnew, _state);
    if( state->dotrace )
    {
        ae_trace("> evaluating target at Xk+Sk, actual deltaF=%0.1e, deltaH=%0.1e\n",
            (double)(fnew-state->fk),
            (double)(hnew-state->hk));
    }
    
    /*
     * Accept or deny trial step
     */
    accepted = dfgenmod_testacceptancebyfilter(state, state->fk, state->hk, predf, predh, fnew, hnew, _state);
    if( !accepted )
    {
        goto lbl_18;
    }
    
    /*
     * Move to the new point
     * Update trust radius and filter
     */
    if( state->dotrace )
    {
        ae_trace("> trial step is accepted by the filter\n");
        if( !state->recoveredfrominfinities )
        {
            ae_trace("> successfully recovered from the previous encounter with infinities/NANs in the target!\n");
        }
        ae_trace("targetF:        %17.9e -> %17.9e (delta=%11.3e)\n",
            (double)(state->fk),
            (double)(fnew),
            (double)(fnew-state->fk));
        ae_trace("targetH:        %17.9e -> %17.9e (delta=%11.3e; constraints violation)\n",
            (double)(state->hk),
            (double)(hnew),
            (double)(hnew-state->hk));
    }
    if( ae_fp_less(predf,state->fk) )
    {
        
        /*
         * f-step is predicted, increase/tighten trust radius depending on the objective behavior
         */
        if( ae_fp_greater((state->fk-fnew)/(state->fk-predf),dfgenmod_eta2) )
        {
            if( state->dotrace )
            {
                ae_trace("> f-step was predicted, model quality is good, increasing trust radius\n");
            }
            dfgenmod_updatetrustregion(state, ae_minreal(dfgenmod_gammainc*state->trustradfactor, dfgenmod_gammainc2*(skrellen*state->trustradfactor), _state), _state);
        }
        else
        {
            if( state->dotrace )
            {
                ae_trace("> f-step was predicted, model quality is low, tightening trust radius\n");
            }
            v = ae_maxreal(dfgenmod_gammadec, dfgenmod_gammadec2*skrellen, _state)*state->trustradfactor;
            if( ae_fp_less(v,1.5*state->trustradbnd) )
            {
                v = state->trustradbnd;
            }
            dfgenmod_updatetrustregion(state, v, _state);
        }
    }
    else
    {
        
        /*
         * h-step is predicted, increase/tighten trust radius depending on the constraint violation behavior
         */
        if( ae_fp_less(predh,state->hk)&&ae_fp_greater((state->hk-hnew)/(state->hk-predh),dfgenmod_eta2) )
        {
            if( state->dotrace )
            {
                ae_trace("> h-step was predicted, constraint linearization quality is good, increasing trust radius\n");
            }
            dfgenmod_updatetrustregion(state, ae_minreal(dfgenmod_gammainc*state->trustradfactor, dfgenmod_gammainc2*(skrellen*state->trustradfactor), _state), _state);
        }
        else
        {
            if( state->dotrace )
            {
                ae_trace("> h-step was predicted, constraint linearization quality is low, tightening trust radius\n");
            }
            v = ae_maxreal(dfgenmod_gammadec, dfgenmod_gammadec2*skrellen, _state)*state->trustradfactor;
            if( ae_fp_less(v,1.5*state->trustradbnd) )
            {
                v = state->trustradbnd;
            }
            dfgenmod_updatetrustregion(state, v, _state);
        }
    }
    dfgenmod_sendstepacceptancetomodel(state, state->fk, state->hk, fnew, hnew, _state);
    rcopyv(n, &state->xk, &state->xp, _state);
    rcopyv(m+state->cntnlc, &state->fkvec, &state->fpvec, _state);
    state->fp = state->fk;
    state->hp = state->hk;
    rcopyv(n, &state->xn, &state->xk, _state);
    rcopyv(m+state->cntnlc, &state->fnvec, &state->fkvec, _state);
    state->fk = fnew;
    state->hk = hnew;
    state->recoveredfrominfinities = ae_true;
    
    /*
     * Perform step and update working set and model, with possible generation of RComm requests in the process
     */
    if( state->dotrace )
    {
        ae_trace("> updating working set\n");
    }
    ae_vector_set_length(&state->rstateupdate.ia, 7+1, _state);
    ae_vector_set_length(&state->rstateupdate.ra, 4+1, _state);
    state->rstateupdate.stage = -1;
lbl_20:
    if( !dfgenmod_updateworkingsetandmodel(state, &state->xp, &state->xn, &state->fnvec, fnew, hnew, ae_true, _state) )
    {
        goto lbl_21;
    }
    if( state->requesttype==4 )
    {
        state->repnfev = state->repnfev+state->querysize;
    }
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    goto lbl_20;
lbl_21:
    if( state->repterminationtype!=0 )
    {
        goto lbl_9;
    }
    goto lbl_19;
lbl_18:
    
    /*
     * Decrease trust radius, send step denial signal
     */
    if( state->dotrace )
    {
        ae_trace("> trial step is not accepted by the filter, decreasing trust radius\n");
    }
    v = ae_minreal(dfgenmod_gammadec, dfgenmod_gammadec2*skrellen, _state)*state->trustradfactor;
    if( ae_fp_less(v,1.5*state->trustradbnd) )
    {
        v = state->trustradbnd;
    }
    dfgenmod_updatetrustregion(state, v, _state);
    dfgenmod_sendstepdenialtomodel(state, _state);
    
    /*
     * Update working set and model in order to reuse new information as much as possible.
     * Move center to the best point in the working set, as suggested by DFO-LSA authors.
     */
    if( state->dotrace )
    {
        ae_trace("> updating working set\n");
    }
    ae_vector_set_length(&state->rstateupdate.ia, 7+1, _state);
    ae_vector_set_length(&state->rstateupdate.ra, 4+1, _state);
    state->rstateupdate.stage = -1;
lbl_22:
    if( !dfgenmod_updateworkingsetandmodel(state, &state->xk, &state->xn, &state->fnvec, fnew, hnew, ae_false, _state) )
    {
        goto lbl_23;
    }
    if( state->requesttype==4 )
    {
        state->repnfev = state->repnfev+state->querysize;
    }
    state->rstate.stage = 6;
    goto lbl_rcomm;
lbl_6:
    goto lbl_22;
lbl_23:
    if( state->repterminationtype!=0 )
    {
        goto lbl_9;
    }
    
    /*
     * Handle denial
     */
    if( !(dfgenmod_restartautodetected(state, _state)&&state->restartstoperform>0) )
    {
        goto lbl_24;
    }
    
    /*
     * Step not accepted, the situation suggests restart
     */
    if( state->dotrace )
    {
        ae_trace("> \n");
    }
    dfgenmod_updatetrustregion(state, state->rad0, _state);
    state->trustradbnd = state->trustradfactor;
    state->wrksetsize = 0;
    dfgenmod_clearhistoryonrestart(state, _state);
    state->restartstoperform = state->restartstoperform-1;
    if( state->dotrace )
    {
        ae_trace("> restart is performed due to an autodetection code signal (%0d restarts left)\n",
            (int)(state->restartstoperform));
    }
    goto lbl_25;
lbl_24:
    if( !dfgenmod_workingsetneedsimprovement(state, _state) )
    {
        goto lbl_26;
    }
    
    /*
     * Step not accepted, working set geometry needs improvement
     */
    if( state->dotrace )
    {
        ae_trace("> working set needs improvement\n");
    }
    ae_vector_set_length(&state->rstateimprove.ia, 13+1, _state);
    ae_vector_set_length(&state->rstateimprove.ba, 0+1, _state);
    ae_vector_set_length(&state->rstateimprove.ra, 12+1, _state);
    state->rstateimprove.stage = -1;
lbl_28:
    if( !dfgenmod_improveworkingsetgeometry(state, _state) )
    {
        goto lbl_29;
    }
    if( state->requesttype==4 )
    {
        state->repnfev = state->repnfev+state->querysize;
    }
    state->rstate.stage = 7;
    goto lbl_rcomm;
lbl_7:
    goto lbl_28;
lbl_29:
    if( state->repterminationtype!=0 )
    {
        goto lbl_9;
    }
    goto lbl_27;
lbl_26:
    
    /*
     * Unsuccessful phase: step not accepted, restart not suggested, working set geometry does not need improvement
     */
    if( dfgenmod_deltakatminimum(state, _state) )
    {
        if( state->dotrace )
        {
            ae_trace("> trust radius at the lower bound, decreasing the bound\n");
        }
        v = state->trustradbnd;
        dfgenmod_updatetrustregion(state, dfgenmod_alpha2*v, _state);
        state->trustradbnd = dfgenmod_alpha1*v;
    }
lbl_27:
lbl_25:
lbl_19:
    
    /*
     * Update counters
     */
    inc(&state->repiterationscount, _state);
    goto lbl_8;
lbl_9:
    
    /*
     * Finalize
     */
    ae_assert(state->repterminationtype!=0, "DFGM: integrity check 0808 failed", _state);
    if( state->repterminationtype<0 )
    {
        result = ae_false;
        return result;
    }
    if( state->infinitiesencountered )
    {
        if( !state->recoveredfrominfinities )
        {
            if( state->dotrace )
            {
                ae_trace("> optimizer failed to recover from infinities, declaring unsuccessful termination\n");
            }
            state->repterminationtype = -8;
            result = ae_false;
            return result;
        }
        if( state->dotrace )
        {
            ae_trace("> optimizer encountered infinities, but successfully recovered from them, adding +800 to the completion code\n");
        }
        state->repterminationtype = state->repterminationtype+800;
    }
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = i;
    state->rstate.ia.ptr.p_int[1] = j;
    state->rstate.ia.ptr.p_int[2] = n;
    state->rstate.ia.ptr.p_int[3] = m;
    state->rstate.ia.ptr.p_int[4] = cntnlc;
    state->rstate.ba.ptr.p_bool[0] = accepted;
    state->rstate.ra.ptr.p_double[0] = predf;
    state->rstate.ra.ptr.p_double[1] = predh;
    state->rstate.ra.ptr.p_double[2] = fnew;
    state->rstate.ra.ptr.p_double[3] = hnew;
    state->rstate.ra.ptr.p_double[4] = skrellen;
    state->rstate.ra.ptr.p_double[5] = v;
    state->rstate.ra.ptr.p_double[6] = v1;
    state->rstate.ra.ptr.p_double[7] = vv;
    return result;
}


void linregline(/* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* y,
     ae_int_t n,
     double* a,
     double* b,
     double* corrxy,
     ae_state *_state)
{
    ae_int_t i;
    double ss;
    double sx;
    double sxx;
    double sy;
    double stt;
    double e1;
    double e2;
    double t;
    double varx;
    double vary;
    double meanx;
    double meany;

    *a = 0.0;
    *b = 0.0;
    *corrxy = 0.0;

    ae_assert(n>=2, "DFGM: integrity check 6541 failed", _state);
    
    /*
     * Calculate S, SX, SY, SXX
     */
    ss = (double)(0);
    sx = (double)(0);
    sy = (double)(0);
    sxx = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        ss = ss+(double)1;
        sx = sx+x->ptr.p_double[i];
        sy = sy+y->ptr.p_double[i];
        sxx = sxx+ae_sqr(x->ptr.p_double[i], _state);
    }
    
    /*
     * Test for condition number
     */
    t = ae_sqrt((double)4*ae_sqr(sx, _state)+ae_sqr(ss-sxx, _state), _state);
    e1 = 0.5*(ss+sxx+t);
    e2 = 0.5*(ss+sxx-t);
    ae_assert(ae_fp_greater(ae_minreal(e1, e2, _state),(double)1000*ae_machineepsilon*ae_maxreal(e1, e2, _state)), "DFGM: integrity check 6702 failed", _state);
    
    /*
     * Calculate A, B
     */
    *a = (double)(0);
    *b = (double)(0);
    stt = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        t = x->ptr.p_double[i]-sx/ss;
        *b = *b+t*y->ptr.p_double[i];
        stt = stt+ae_sqr(t, _state);
    }
    *b = *b/stt;
    *a = (sy-sx*(*b))/ss;
    
    /*
     * Calculate other parameters
     */
    meanx = sx/(double)n;
    meany = sy/(double)n;
    varx = (double)(0);
    vary = (double)(0);
    *corrxy = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        varx = varx+(x->ptr.p_double[i]-meanx)*(x->ptr.p_double[i]-meanx);
        vary = vary+(y->ptr.p_double[i]-meany)*(y->ptr.p_double[i]-meany);
        *corrxy = *corrxy+(x->ptr.p_double[i]-meanx)*(y->ptr.p_double[i]-meany);
    }
    *corrxy = *corrxy/ae_sqrt(coalesce(varx*vary, (double)(1), _state), _state);
}


/*************************************************************************
Initialize the following histories:
* a history of changes in the trust radius and the Jacobian. If NNoisyRestarts
  is zero from the very beginning, this history is not tracked.
* a history of changes in the objective, which is used to track

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_inithistory(dfgmstate* state, ae_state *_state)
{


    state->restartstoperform = state->nnoisyrestarts;
    state->historylen = 0;
    state->historynext = 0;
    state->lasttrustrad = (double)(0);
    if( state->nnoisyrestarts>0 )
    {
        rsetallocm(state->m+state->cntnlc, state->n, 0.0, &state->lastjac, _state);
        rallocv(dfgenmod_maxhistory, &state->trustradhistory, _state);
        rallocv(dfgenmod_maxhistory, &state->jacdiffhistory, _state);
        rallocv(dfgenmod_maxhistory, &state->iteridxhistory, _state);
    }
    
    /*
     * Successful iterations history
     */
    state->successhistorymax = ae_maxint(state->n+1, 5, _state);
    state->successhistorylen = 0;
    state->successhistorynext = 0;
    rsetallocv(state->successhistorymax, ae_sqrt(ae_maxrealnumber, _state), &state->successhistory, _state);
}


/*************************************************************************
Updates history

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_updatehistory(dfgmstate* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t cntnlc;
    ae_int_t i;
    ae_bool b;
    double jacdiff2;


    n = state->n;
    m = state->m;
    cntnlc = state->cntnlc;
    if( state->nnoisyrestarts==0 )
    {
        return;
    }
    
    /*
     * Enqueue trust radius change
     */
    state->trustradhistory.ptr.p_double[state->historynext] = (double)(0);
    if( ae_fp_less(state->trustradfactor,0.999*state->lasttrustrad) )
    {
        state->trustradhistory.ptr.p_double[state->historynext] = (double)(-1);
    }
    if( ae_fp_greater(state->trustradfactor,1.001*state->lasttrustrad) )
    {
        state->trustradhistory.ptr.p_double[state->historynext] = (double)(1);
    }
    state->lasttrustrad = state->trustradfactor;
    
    /*
     * Compute difference in Jacobians, enqueue
     */
    rallocv(n, &state->tmp0, _state);
    jacdiff2 = (double)(0);
    b = ae_false;
    if( state->modeltype==0 )
    {
        for(i=0; i<=m+cntnlc-1; i++)
        {
            rcopyrv(n, &state->tpsmodel.jac, i, &state->tmp0, _state);
            raddrv(n, -1.0, &state->lastjac, i, &state->tmp0, _state);
            jacdiff2 = jacdiff2+rdotv2(n, &state->tmp0, _state);
        }
        rcopym(m+cntnlc, n, &state->tpsmodel.jac, &state->lastjac, _state);
        b = ae_true;
    }
    if( state->modeltype==1 )
    {
        for(i=0; i<=m+cntnlc-1; i++)
        {
            rcopyrv(n, &state->lsamodel.jac, i, &state->tmp0, _state);
            raddrv(n, -1.0, &state->lastjac, i, &state->tmp0, _state);
            jacdiff2 = jacdiff2+rdotv2(n, &state->tmp0, _state);
        }
        rcopym(m+cntnlc, n, &state->lsamodel.jac, &state->lastjac, _state);
        b = ae_true;
    }
    ae_assert(b, "DFGM: integrity check 5451 failed", _state);
    state->jacdiffhistory.ptr.p_double[state->historynext] = ae_log(coalesce(jacdiff2, ae_machineepsilon*ae_machineepsilon, _state), _state);
    
    /*
     * Enqueue iterations count
     */
    state->iteridxhistory.ptr.p_double[state->historynext] = (double)(state->repiterationscount);
    
    /*
     * Update history size
     */
    state->historylen = ae_minint(state->historylen+1, dfgenmod_maxhistory, _state);
    state->historynext = (state->historynext+1)%dfgenmod_maxhistory;
}


/*************************************************************************
Clear history on restart

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_clearhistoryonrestart(dfgmstate* state,
     ae_state *_state)
{


    state->historylen = 0;
    state->historynext = 0;
    state->successhistorylen = 0;
    state->successhistorynext = 0;
    rsetallocv(state->successhistorymax, ae_sqrt(ae_maxrealnumber, _state), &state->successhistory, _state);
    dfgenmod_clearcloud(state, _state);
}


/*************************************************************************
Compute per-variable trust region using trust region factor and a  current
location State.Xk

The trust region is stored to State.TrustRegion[], which is array[N], can
be automatically allocated by this function.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_updatetrustregion(dfgmstate* state,
     double trustradfactor,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    state->trustradfactor = trustradfactor;
    rallocv(n, &state->trustregion, _state);
    rallocv(n, &state->invtrustregion, _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_fp_less(state->finitebndl.ptr.p_double[i],state->finitebndu.ptr.p_double[i]), "DFGM: integrity check 4230 failed", _state);
        state->trustregion.ptr.p_double[i] = trustradfactor*ae_minreal(1.0, state->finitebndu.ptr.p_double[i]-state->finitebndl.ptr.p_double[i], _state);
        state->invtrustregion.ptr.p_double[i] = (double)1/state->trustregion.ptr.p_double[i];
    }
}


/*************************************************************************
Returns max(|x[i]|/State.TrustRegion[I])

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static double dfgenmod_normrelativetotrustregion(const dfgmstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    double result;


    n = state->n;
    result = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        result = ae_maxreal(result, ae_fabs(x->ptr.p_double[i]/state->trustregion.ptr.p_double[i], _state), _state);
    }
    return result;
}


/*************************************************************************
Prepares trial step Sk, Xn=Xk+Sk (guarded  against  box  constraints), loads
RComm V2 request to  RequestType,  QuerySize,  QueryData  and preallocates
ReplyFi.

Returns predicted value of F (target) and sum of constraints violations H

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_preparetrialrequest(dfgmstate* state,
     double* predf,
     double* predh,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t cntlc;
    ae_int_t cntnlc;
    ae_int_t nnz;
    ae_int_t ipmterminationtype;
    ae_int_t i;
    ae_int_t j;
    ae_int_t nx;

    *predf = 0.0;
    *predh = 0.0;

    n = state->n;
    m = state->m;
    cntlc = state->cntlc;
    cntnlc = state->cntnlc;
    *predf = (double)(0);
    *predh = (double)(0);
    
    /*
     * 2PS
     */
    if( state->modeltype==0 )
    {
        ae_assert(cntlc==0, "DFGM: integrity check 4429 failed", _state);
        ae_assert(cntnlc==0, "DFGM: integrity check 4430 failed", _state);
        ae_assert(state->isleastsquares, "DFGM: integrity check 4431 failed", _state);
        
        /*
         * Generate trial step
         */
        rsetallocv(n, 0.0, &state->tmp0, _state);
        vipminitdense(&state->ipmsolver, &state->trustregion, &state->tmp0, n, ae_true, _state);
        vipmsetquadraticlinear(&state->ipmsolver, &state->tpsmodel.q, &state->sparsedummy, 0, ae_true, &state->tpsmodel.g, _state);
        rallocv(n, &state->tmp1, _state);
        for(i=0; i<=n-1; i++)
        {
            state->tmp0.ptr.p_double[i] = -state->trustregion.ptr.p_double[i];
            state->tmp1.ptr.p_double[i] = state->trustregion.ptr.p_double[i];
            if( state->hasbndl.ptr.p_bool[i]&&ae_fp_greater(state->scaledbndl.ptr.p_double[i]-state->xk.ptr.p_double[i],state->tmp0.ptr.p_double[i]) )
            {
                state->tmp0.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i]-state->xk.ptr.p_double[i];
            }
            if( state->hasbndu.ptr.p_bool[i]&&ae_fp_less(state->scaledbndu.ptr.p_double[i]-state->xk.ptr.p_double[i],state->tmp1.ptr.p_double[i]) )
            {
                state->tmp1.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i]-state->xk.ptr.p_double[i];
            }
        }
        vipmsetconstraints(&state->ipmsolver, &state->tmp0, &state->tmp1, &state->sparsedummy, 0, &state->densedummy, 0, &state->tmp0, &state->tmp1, _state);
        vipmoptimize(&state->ipmsolver, ae_false, &state->tmp0, &state->tmp1, &state->tmp2, &ipmterminationtype, _state);
        ae_assert(ipmterminationtype>0, "DFGM: integrity check 7740 failed", _state);
        rcopyv(n, &state->tmp0, &state->sk, _state);
        rcopyv(n, &state->xk, &state->xn, _state);
        raddv(n, 1.0, &state->sk, &state->xn, _state);
        enforceboundaryconstraints(&state->xn, &state->scaledbndl, &state->hasbndl, &state->scaledbndu, &state->hasbndu, n, 0, _state);
        
        /*
         * Predicted value of target and constraints
         */
        ae_assert(cntlc==0, "DFGM: integrity check 9544 failed", _state);
        ae_assert(cntnlc==0, "DFGM: integrity check 9545 failed", _state);
        rallocv(n, &state->tmp0, _state);
        rgemv(n, n, 1.0, &state->tpsmodel.q, 0, &state->sk, 0.0, &state->tmp0, _state);
        *predf = state->fk+rdotv(n, &state->sk, &state->tpsmodel.g, _state)+0.5*rdotv(n, &state->sk, &state->tmp0, _state);
        *predh = (double)(0);
        
        /*
         * Issue RComm V2 request
         */
        state->requesttype = 4;
        state->querysize = 1;
        rcopyallocv(n, &state->xn, &state->querydata, _state);
        rallocv(m+cntnlc, &state->replyfi, _state);
        dfgenmod_sendrequesttocloud(state, &state->querydata, state->querysize, _state);
        
        /*
         * Done
         */
        return;
    }
    
    /*
     * DFO-LSA
     */
    if( state->modeltype==1 )
    {
        ae_assert(cntlc==0, "DFGM: integrity check 2539 failed", _state);
        ae_assert(cntnlc==0, "DFGM: integrity check 2639 failed", _state);
        ae_assert(state->isleastsquares, "DFGM: integrity check 2739 failed", _state);
        
        /*
         * Generate trial step
         */
        rsetallocv(n, 0.0, &state->tmp0, _state);
        vipminitdense(&state->ipmsolver, &state->trustregion, &state->tmp0, n, ae_true, _state);
        vipmsetquadraticlinear(&state->ipmsolver, &state->lsamodel.q, &state->sparsedummy, 0, ae_true, &state->lsamodel.g, _state);
        rallocv(n, &state->tmp1, _state);
        for(i=0; i<=n-1; i++)
        {
            state->tmp0.ptr.p_double[i] = -state->trustregion.ptr.p_double[i];
            state->tmp1.ptr.p_double[i] = state->trustregion.ptr.p_double[i];
            if( state->hasbndl.ptr.p_bool[i]&&ae_fp_greater(state->scaledbndl.ptr.p_double[i]-state->xk.ptr.p_double[i],state->tmp0.ptr.p_double[i]) )
            {
                state->tmp0.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i]-state->xk.ptr.p_double[i];
            }
            if( state->hasbndu.ptr.p_bool[i]&&ae_fp_less(state->scaledbndu.ptr.p_double[i]-state->xk.ptr.p_double[i],state->tmp1.ptr.p_double[i]) )
            {
                state->tmp1.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i]-state->xk.ptr.p_double[i];
            }
        }
        vipmsetconstraints(&state->ipmsolver, &state->tmp0, &state->tmp1, &state->sparsedummy, 0, &state->densedummy, 0, &state->tmp0, &state->tmp1, _state);
        vipmoptimize(&state->ipmsolver, ae_false, &state->tmp0, &state->tmp1, &state->tmp2, &ipmterminationtype, _state);
        ae_assert(ipmterminationtype>0, "DFGM: integrity check 7740 failed", _state);
        rcopyv(n, &state->tmp0, &state->sk, _state);
        rcopyv(n, &state->xk, &state->xn, _state);
        raddv(n, 1.0, &state->sk, &state->xn, _state);
        enforceboundaryconstraints(&state->xn, &state->scaledbndl, &state->hasbndl, &state->scaledbndu, &state->hasbndu, n, 0, _state);
        
        /*
         * Predicted value of target and constraints
         */
        ae_assert(cntlc==0, "DFGM: integrity check 9544 failed", _state);
        ae_assert(cntnlc==0, "DFGM: integrity check 9545 failed", _state);
        rallocv(n, &state->tmp0, _state);
        rgemv(n, n, 1.0, &state->lsamodel.q, 0, &state->sk, 0.0, &state->tmp0, _state);
        *predf = state->fk+rdotv(n, &state->sk, &state->lsamodel.g, _state)+0.5*rdotv(n, &state->sk, &state->tmp0, _state);
        *predh = (double)(0);
        
        /*
         * Issue RComm V2 request
         */
        state->requesttype = 4;
        state->querysize = 1;
        rcopyallocv(n, &state->xn, &state->querydata, _state);
        rallocv(m+cntnlc, &state->replyfi, _state);
        dfgenmod_sendrequesttocloud(state, &state->querydata, state->querysize, _state);
        
        /*
         * Done
         */
        return;
    }
    
    /*
     * ORBIT
     */
    if( state->modeltype==2 )
    {
        dfgenmod_rbfinitializemodelfromworkingset(state, _state);
        critinitdefault(&state->rbfmodel.crit, _state);
        critsetcondv1(&state->rbfmodel.crit, 0.0, dfgenmod_rbfsubsolverepsx, 0, _state);
        nx = n+2*cntlc+2*cntnlc;
        rallocv(nx, &state->rbfmodel.tmpbndl, _state);
        rallocv(nx, &state->rbfmodel.tmpbndu, _state);
        rallocv(nx, &state->rbfmodel.tmpx0, _state);
        rallocv(nx, &state->rbfmodel.tmps, _state);
        for(i=0; i<=n-1; i++)
        {
            state->rbfmodel.tmpbndl.ptr.p_double[i] = state->xk.ptr.p_double[i]-state->trustregion.ptr.p_double[i];
            state->rbfmodel.tmpbndu.ptr.p_double[i] = state->xk.ptr.p_double[i]+state->trustregion.ptr.p_double[i];
            if( state->hasbndl.ptr.p_bool[i]&&ae_fp_greater(state->scaledbndl.ptr.p_double[i],state->rbfmodel.tmpbndl.ptr.p_double[i]) )
            {
                state->rbfmodel.tmpbndl.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i];
            }
            if( state->hasbndu.ptr.p_bool[i]&&ae_fp_less(state->scaledbndu.ptr.p_double[i],state->rbfmodel.tmpbndu.ptr.p_double[i]) )
            {
                state->rbfmodel.tmpbndu.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i];
            }
            state->rbfmodel.tmpx0.ptr.p_double[i] = state->xk.ptr.p_double[i];
            state->rbfmodel.tmps.ptr.p_double[i] = state->trustregion.ptr.p_double[i];
        }
        for(i=n; i<=nx-1; i++)
        {
            state->rbfmodel.tmpbndl.ptr.p_double[i] = 0.0;
            state->rbfmodel.tmpbndu.ptr.p_double[i] = _state->v_posinf;
            state->rbfmodel.tmps.ptr.p_double[i] = state->trustradfactor;
            state->rbfmodel.tmpx0.ptr.p_double[i] = 0.0;
        }
        rallocv(cntlc+1, &state->rbfmodel.clx, _state);
        rallocv(cntlc+1, &state->rbfmodel.cux, _state);
        sparsecreatecrsemptybuf(nx, &state->rbfmodel.cx, _state);
        iallocv(nx, &state->tmpi, _state);
        rallocv(nx, &state->tmp0, _state);
        for(i=0; i<=cntlc-1; i++)
        {
            nnz = 0;
            for(j=state->c.ridx.ptr.p_int[i]; j<=state->c.ridx.ptr.p_int[i+1]-1; j++)
            {
                state->tmpi.ptr.p_int[nnz] = state->c.idx.ptr.p_int[j];
                state->tmp0.ptr.p_double[nnz] = state->c.vals.ptr.p_double[j];
                nnz = nnz+1;
            }
            state->rbfmodel.clx.ptr.p_double[i] = state->cl.ptr.p_double[i];
            if( ae_isfinite(state->cl.ptr.p_double[i], _state) )
            {
                state->tmpi.ptr.p_int[nnz] = n+2*i+0;
                state->tmp0.ptr.p_double[nnz] = 1.0;
                nnz = nnz+1;
            }
            state->rbfmodel.cux.ptr.p_double[i] = state->cu.ptr.p_double[i];
            if( ae_isfinite(state->cu.ptr.p_double[i], _state) )
            {
                state->tmpi.ptr.p_int[nnz] = n+2*i+1;
                state->tmp0.ptr.p_double[nnz] = -1.0;
                nnz = nnz+1;
            }
            ae_assert(nnz<=nx, "DFGM: integrity check 345240 failed", _state);
            sparseappendcompressedrow(&state->rbfmodel.cx, &state->tmpi, &state->tmp0, nnz, _state);
        }
        iallocv(cntlc, &state->tmpi, _state);
        for(i=0; i<=cntlc-1; i++)
        {
            state->tmpi.ptr.p_int[i] = i;
        }
        minfsqpinitbuf(&state->rbfmodel.tmpbndl, &state->rbfmodel.tmpbndu, &state->rbfmodel.tmps, &state->rbfmodel.tmpx0, nx, &state->rbfmodel.cx, &state->rbfmodel.clx, &state->rbfmodel.cux, &state->tmpi, cntlc, &state->nl, &state->nu, cntnlc, &state->rbfmodel.crit, ae_false, &state->rbfmodel.fsqpsolver, _state);
        smoothnessmonitorinit(&state->rbfmodel.smonitor, &state->rbfmodel.tmps, nx, 1+cntnlc, ae_false, _state);
        while(minfsqpiteration(&state->rbfmodel.fsqpsolver, &state->rbfmodel.smonitor, ae_false, _state))
        {
            if( state->rbfmodel.fsqpsolver.xupdated )
            {
                continue;
            }
            if( state->rbfmodel.fsqpsolver.needfisj )
            {
                
                /*
                 * Compute the original model in Fi[] and its gradient in tmp1[]
                 */
                rallocv(nx, &state->tmp0, _state);
                rallocv((m+cntnlc)*n, &state->tmp1, _state);
                rcopyv(nx, &state->rbfmodel.fsqpsolver.x, &state->tmp0, _state);
                rmergemulv(nx, &state->rbfmodel.tmps, &state->tmp0, _state);
                rmergemaxv(nx, &state->rbfmodel.tmpbndl, &state->tmp0, _state);
                rmergeminv(nx, &state->rbfmodel.tmpbndu, &state->tmp0, _state);
                dfgenmod_rbfcomputemodel(state, &state->tmp0, &state->rbfmodel.fsqpsolver.fi, ae_true, &state->tmp1, ae_true, _state);
                
                /*
                 * Modify the model by replacing objective with a sum of constraint violations and
                 * by adding slacks to nonlinear constraints.
                 */
                rsetallocv((m+cntnlc)*nx, 0.0, &state->tmp2, _state);
                state->rbfmodel.fsqpsolver.fi.ptr.p_double[0] = (double)(0);
                for(j=n; j<=nx-1; j++)
                {
                    state->rbfmodel.fsqpsolver.fi.ptr.p_double[0] = state->rbfmodel.fsqpsolver.fi.ptr.p_double[0]+state->tmp0.ptr.p_double[j];
                    state->tmp2.ptr.p_double[j] = 1.0;
                }
                for(i=1; i<=cntnlc; i++)
                {
                    rcopyvx(n, &state->tmp1, i*n, &state->tmp2, i*nx, _state);
                    if( ae_isfinite(state->nl.ptr.p_double[i-1], _state) )
                    {
                        j = n+2*cntlc+2*(i-1)+0;
                        state->rbfmodel.fsqpsolver.fi.ptr.p_double[i] = state->rbfmodel.fsqpsolver.fi.ptr.p_double[i]+state->tmp0.ptr.p_double[j];
                        state->tmp2.ptr.p_double[i*nx+j] = 1.0;
                    }
                    if( ae_isfinite(state->nu.ptr.p_double[i-1], _state) )
                    {
                        j = n+2*cntlc+2*(i-1)+1;
                        state->rbfmodel.fsqpsolver.fi.ptr.p_double[i] = state->rbfmodel.fsqpsolver.fi.ptr.p_double[i]-state->tmp0.ptr.p_double[j];
                        state->tmp2.ptr.p_double[i*nx+j] = -1.0;
                    }
                }
                sparsecreatecrsfromdensev(&state->tmp2, 1+cntnlc, nx, &state->rbfmodel.fsqpsolver.sj, _state);
                sparsemultiplycolsby(&state->rbfmodel.fsqpsolver.sj, &state->rbfmodel.tmps, _state);
                continue;
            }
            ae_assert(ae_false, "DFGM: integrity check 261738 failed", _state);
        }
        ae_assert(state->rbfmodel.fsqpsolver.repterminationtype>0, "DFGM: integrity check 265740 failed", _state);
        state->repsubsolverits = state->repsubsolverits+state->rbfmodel.fsqpsolver.repiterationscount;
        
        /*
         * Extract stopping point and compute predicted decrease in constraint violation
         */
        rcopyv(nx, &state->rbfmodel.fsqpsolver.stepk.x, &state->rbfmodel.tmpx0, _state);
        rmergemulv(nx, &state->rbfmodel.tmps, &state->rbfmodel.tmpx0, _state);
        rmergemaxv(nx, &state->rbfmodel.tmpbndl, &state->rbfmodel.tmpx0, _state);
        rmergeminv(nx, &state->rbfmodel.tmpbndu, &state->rbfmodel.tmpx0, _state);
        *predh = 0.0;
        for(i=n; i<=nx-1; i++)
        {
            *predh = *predh+state->rbfmodel.tmpx0.ptr.p_double[i];
        }
        if( state->dotrace )
        {
            ae_trace("$$ %0d its, best pred H = %0.3e\n",
                (int)(state->rbfmodel.fsqpsolver.repiterationscount),
                (double)(*predh));
        }
        
        /*
         * Append a constraint on maximum constraint violation to the original problem, solve it
         */
        iallocv(nx, &state->tmpi, _state);
        rallocv(nx, &state->tmp0, _state);
        nnz = 0;
        for(i=n; i<=nx-1; i++)
        {
            state->tmpi.ptr.p_int[nnz] = i;
            state->tmp0.ptr.p_double[nnz] = 1.0;
            nnz = nnz+1;
        }
        ae_assert(state->rbfmodel.clx.cnt>=cntlc+1&&state->rbfmodel.cux.cnt>=cntlc+1, "DFGM: integrity check 423305 failed", _state);
        sparseappendcompressedrow(&state->rbfmodel.cx, &state->tmpi, &state->tmp0, nnz, _state);
        state->rbfmodel.clx.ptr.p_double[cntlc] = _state->v_neginf;
        state->rbfmodel.cux.ptr.p_double[cntlc] = *predh;
        iallocv(cntlc+1, &state->tmpi, _state);
        for(i=0; i<=cntlc; i++)
        {
            state->tmpi.ptr.p_int[i] = i;
        }
        minfsqpinitbuf(&state->rbfmodel.tmpbndl, &state->rbfmodel.tmpbndu, &state->rbfmodel.tmps, &state->rbfmodel.tmpx0, nx, &state->rbfmodel.cx, &state->rbfmodel.clx, &state->rbfmodel.cux, &state->tmpi, cntlc+1, &state->nl, &state->nu, cntnlc, &state->rbfmodel.crit, ae_false, &state->rbfmodel.fsqpsolver, _state);
        smoothnessmonitorinit(&state->rbfmodel.smonitor, &state->rbfmodel.tmps, nx, 1+cntnlc, ae_false, _state);
        while(minfsqpiteration(&state->rbfmodel.fsqpsolver, &state->rbfmodel.smonitor, ae_false, _state))
        {
            if( state->rbfmodel.fsqpsolver.xupdated )
            {
                continue;
            }
            if( state->rbfmodel.fsqpsolver.needfisj )
            {
                
                /*
                 * Compute the original model in Fi[] and its gradient in tmp1[]
                 */
                rallocv(nx, &state->tmp0, _state);
                rallocv((m+cntnlc)*n, &state->tmp1, _state);
                rcopyv(nx, &state->rbfmodel.fsqpsolver.x, &state->tmp0, _state);
                rmergemulv(nx, &state->rbfmodel.tmps, &state->tmp0, _state);
                rmergemaxv(nx, &state->rbfmodel.tmpbndl, &state->tmp0, _state);
                rmergeminv(nx, &state->rbfmodel.tmpbndu, &state->tmp0, _state);
                dfgenmod_rbfcomputemodel(state, &state->tmp0, &state->rbfmodel.fsqpsolver.fi, ae_true, &state->tmp1, ae_true, _state);
                
                /*
                 * Modify the model by adding slacks to nonlinear constraints.
                 */
                rsetallocv((m+cntnlc)*nx, 0.0, &state->tmp2, _state);
                rcopyvx(n, &state->tmp1, 0, &state->tmp2, 0, _state);
                for(i=1; i<=cntnlc; i++)
                {
                    rcopyvx(n, &state->tmp1, i*n, &state->tmp2, i*nx, _state);
                    if( ae_isfinite(state->nl.ptr.p_double[i-1], _state) )
                    {
                        j = n+2*cntlc+2*(i-1)+0;
                        state->rbfmodel.fsqpsolver.fi.ptr.p_double[i] = state->rbfmodel.fsqpsolver.fi.ptr.p_double[i]+state->tmp0.ptr.p_double[j];
                        state->tmp2.ptr.p_double[i*nx+j] = 1.0;
                    }
                    if( ae_isfinite(state->nu.ptr.p_double[i-1], _state) )
                    {
                        j = n+2*cntlc+2*(i-1)+1;
                        state->rbfmodel.fsqpsolver.fi.ptr.p_double[i] = state->rbfmodel.fsqpsolver.fi.ptr.p_double[i]-state->tmp0.ptr.p_double[j];
                        state->tmp2.ptr.p_double[i*nx+j] = -1.0;
                    }
                }
                sparsecreatecrsfromdensev(&state->tmp2, 1+cntnlc, nx, &state->rbfmodel.fsqpsolver.sj, _state);
                sparsemultiplycolsby(&state->rbfmodel.fsqpsolver.sj, &state->rbfmodel.tmps, _state);
                continue;
            }
            ae_assert(ae_false, "DFGM: integrity check 261738 failed", _state);
        }
        state->repsubsolverits = state->repsubsolverits+state->rbfmodel.fsqpsolver.repiterationscount;
        rcopyv(n, &state->rbfmodel.fsqpsolver.stepk.x, &state->xn, _state);
        rmergemulv(n, &state->trustregion, &state->xn, _state);
        rmergemaxv(n, &state->finitebndl, &state->xn, _state);
        rmergeminv(n, &state->finitebndu, &state->xn, _state);
        if( state->dotrace )
        {
            ae_trace("$$ %0d its in optimization phase\n",
                (int)(state->rbfmodel.fsqpsolver.repiterationscount));
        }
        
        /*
         *
         */
        rallocv(m+cntnlc, &state->tmp1, _state);
        rcopyv(n, &state->xn, &state->sk, _state);
        raddv(n, -1.0, &state->xk, &state->sk, _state);
        dfgenmod_rbfcomputemodel(state, &state->xn, &state->tmp1, ae_true, &state->tmp0, ae_false, _state);
        *predf = state->tmp1.ptr.p_double[0];
        *predh = (double)(0);
        if( cntlc>0 )
        {
            sparsemv(&state->c, &state->xn, &state->tmp0, _state);
            for(i=0; i<=cntlc-1; i++)
            {
                if( ae_isfinite(state->cl.ptr.p_double[i], _state) )
                {
                    *predh = *predh+ae_maxreal(state->cl.ptr.p_double[i]-state->tmp0.ptr.p_double[i], 0.0, _state);
                }
                if( ae_isfinite(state->cu.ptr.p_double[i], _state) )
                {
                    *predh = *predh+ae_maxreal(state->tmp0.ptr.p_double[i]-state->cu.ptr.p_double[i], 0.0, _state);
                }
            }
        }
        for(i=0; i<=cntnlc-1; i++)
        {
            if( ae_isfinite(state->nl.ptr.p_double[i], _state) )
            {
                *predh = *predh+ae_maxreal(state->nl.ptr.p_double[i]-state->tmp1.ptr.p_double[m+i], 0.0, _state);
            }
            if( ae_isfinite(state->nu.ptr.p_double[i], _state) )
            {
                *predh = *predh+ae_maxreal(state->tmp1.ptr.p_double[m+i]-state->nu.ptr.p_double[i], 0.0, _state);
            }
        }
        
        /*
         * Issue RComm V2 request
         */
        state->requesttype = 4;
        state->querysize = 1;
        rcopyallocv(n, &state->xn, &state->querydata, _state);
        rallocv(m+cntnlc, &state->replyfi, _state);
        dfgenmod_sendrequesttocloud(state, &state->querydata, state->querysize, _state);
        
        /*
         * Done
         */
        return;
    }
    
    /*
     * Unexpected
     */
    ae_assert(ae_false, "DFGM: PrepareTrialRequest() is unable to handle unexpected algorithm", _state);
}


/*************************************************************************
Processes results of the RComm V2 request for a trial point.

Returns the value of F (target) and sum of constraints violations H

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_processtrialrequest(dfgmstate* state,
     double* f,
     double* h,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t m;
    ae_int_t cntlc;
    ae_int_t cntnlc;

    *f = 0.0;
    *h = 0.0;

    m = state->m;
    cntlc = state->cntlc;
    cntnlc = state->cntnlc;
    *f = (double)(0);
    *h = (double)(0);
    
    /*
     * 2PS, LSA
     */
    if( state->modeltype==0||state->modeltype==1 )
    {
        ae_assert(cntlc==0, "DFGM: integrity check 6222 failed", _state);
        ae_assert(cntnlc==0, "DFGM: integrity check 6323 failed", _state);
        ae_assert(state->isleastsquares, "DFGM: integrity check 6424 failed", _state);
        ae_assert(state->querysize==1, "DFGM: integrity check 3540 failed", _state);
        *f = 0.5*rdotv2(m, &state->replyfi, _state);
        *h = (double)(0);
        rcopyv(m+cntnlc, &state->replyfi, &state->fnvec, _state);
        dfgenmod_sendreplytocloud(state, &state->replyfi, _state);
        return;
    }
    
    /*
     * ORBIT
     */
    if( state->modeltype==2 )
    {
        ae_assert(!state->isleastsquares, "DFGM: integrity check 240106 failed", _state);
        ae_assert(state->querysize==1, "DFGM: integrity check 241107 failed", _state);
        *f = state->replyfi.ptr.p_double[0];
        *h = (double)(0);
        if( cntlc>0 )
        {
            sparsemv(&state->c, &state->xn, &state->tmp0, _state);
            for(i=0; i<=cntlc-1; i++)
            {
                if( ae_isfinite(state->cl.ptr.p_double[i], _state) )
                {
                    *h = *h+ae_maxreal(state->cl.ptr.p_double[i]-state->tmp0.ptr.p_double[i], 0.0, _state);
                }
                if( ae_isfinite(state->cu.ptr.p_double[i], _state) )
                {
                    *h = *h+ae_maxreal(state->tmp0.ptr.p_double[i]-state->cu.ptr.p_double[i], 0.0, _state);
                }
            }
        }
        for(i=0; i<=cntnlc-1; i++)
        {
            if( ae_isfinite(state->nl.ptr.p_double[i], _state) )
            {
                *h = *h+ae_maxreal(state->nl.ptr.p_double[i]-state->replyfi.ptr.p_double[m+i], 0.0, _state);
            }
            if( ae_isfinite(state->nu.ptr.p_double[i], _state) )
            {
                *h = *h+ae_maxreal(state->replyfi.ptr.p_double[m+i]-state->nu.ptr.p_double[i], 0.0, _state);
            }
        }
        rcopyv(m+cntnlc, &state->replyfi, &state->fnvec, _state);
        dfgenmod_sendreplytocloud(state, &state->replyfi, _state);
        return;
    }
    ae_assert(ae_false, "DFGM: ProcessTrialRequest() is unable to handle unexpected algorithm", _state);
}


/*************************************************************************
Tests step acceptance.

Input parameters:
* Fk, Hk - current target Fk and a sum of constraint violations Hk at Xk
* Fp, Hp - predicted values
* Fn, Hn - actual values at Xn=Xk+Sk

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static ae_bool dfgenmod_testacceptancebyfilter(dfgmstate* state,
     double fk,
     double hk,
     double fp,
     double hp,
     double fn,
     double hn,
     ae_state *_state)
{
    ae_bool result;


    
    /*
     * Reject, if the step does not improve either F or H
     */
    if( ae_fp_greater_eq(fn,fk)&&ae_fp_greater_eq(hn,hk) )
    {
        result = ae_false;
        return result;
    }
    
    /*
     * The step is an h-type step that reduces constraint violation, however
     * fails to reduce it to zero within the trust region.
     */
    if( (ae_fp_greater(hk,(double)(0))&&ae_fp_less(hp,hk))&&ae_fp_greater(hp,0.001*hk) )
    {
        result = ae_fp_less(hn-hk,dfgenmod_eta1*(hp-hk));
        return result;
    }
    
    /*
     * The step is an f-type step
     *
     * NOTE: it is important to have this block after the previous one, not before;
     *       having it before in its present form means that steps that reduce constraint
     *       violation but increase objective will not be accepted.
     */
    if( ae_fp_less(fp,fk)&&ae_fp_greater(fn-fk,dfgenmod_eta1*(fp-fk)) )
    {
        result = ae_false;
        return result;
    }
    result = nlpfisacceptable(&state->filter, fk, hk, fn, hn, _state);
    return result;
}


/*************************************************************************
Sets signal that the step was accepted.

Presently it does the following
* inserts current iterate into the filter. If the iterate is not
  acceptable, it is silently denied.

* if Fn<Fk, appends Fn-Fk to State.SuccessHistory

Input parameters:
* Fn, Hn - target Fk and a sum of constraint violations Hk at Xn=Xk+Sk

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_sendstepacceptancetomodel(dfgmstate* state,
     double fk,
     double hk,
     double fn,
     double hn,
     ae_state *_state)
{


    
    /*
     * Append to the filter
     */
    if( nlpfisacceptable1(&state->filter, fn, hn, _state) )
    {
        if( ae_fp_greater(hk,(double)(0)) )
        {
            nlpfappend(&state->filter, fk, hk, _state);
        }
    }
    
    /*
     * Append to the State.SuccessHistory
     */
    if( ae_fp_less(fn,fk) )
    {
        state->successhistorylen = ae_minint(state->successhistorylen+1, state->successhistorymax, _state);
        ae_assert(state->successhistorynext<=state->successhistorylen-1, "DFGM: integrity check 697019 failed", _state);
        state->successhistory.ptr.p_double[state->successhistorynext] = fn-fk;
        state->successhistorynext = (state->successhistorynext+1)%state->successhistorymax;
    }
}


/*************************************************************************
Sends information about denial of the current step.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_sendstepdenialtomodel(dfgmstate* state,
     ae_state *_state)
{


    
    /*
     * 2PS
     */
    if( state->modeltype==0 )
    {
        return;
    }
    
    /*
     * LSA
     */
    if( state->modeltype==1 )
    {
        return;
    }
    
    /*
     * ORBIT
     */
    if( state->modeltype==2 )
    {
        return;
    }
    ae_assert(ae_false, "DFGM: SendDenialToModel() is unable to handle unexpected algorithm", _state);
}


/*************************************************************************
Returns true if State.DeltaK is roughly State.RhoK

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static ae_bool dfgenmod_deltakatminimum(const dfgmstate* state,
     ae_state *_state)
{
    ae_bool result;


    result = ae_fp_less_eq(state->trustradfactor,state->trustradbnd*1.01);
    return result;
}


/*************************************************************************
Returns true if restart is recommended right now

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static ae_bool dfgenmod_restartautodetected(const dfgmstate* state,
     ae_state *_state)
{
    ae_bool result;


    result = ae_false;
    return result;
}


/*************************************************************************
Returns true if working set needs improvement

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static ae_bool dfgenmod_workingsetneedsimprovement(dfgmstate* state,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    double mostdistant;
    double leastdistant;
    ae_bool result;


    n = state->n;
    result = ae_false;
    
    /*
     * 2PS, LSA
     */
    if( state->modeltype==0||state->modeltype==1 )
    {
        ae_assert(state->wrksetsize==n+1, "DFGM: integrity check 2111 failed", _state);
        dfgenmod_mostandleastdistant(&state->invtrustregion, n, &state->wrkset, state->wrksetsize, &mostdistant, &i, &leastdistant, &j, _state);
        if( state->modeltype==0 )
        {
            result = ae_fp_greater(mostdistant,dfgenmod_twopsstephi)||ae_fp_less(leastdistant,dfgenmod_twopssteplo);
        }
        else
        {
            result = ae_fp_greater(mostdistant,dfgenmod_pointtoofar)||ae_fp_less(leastdistant,dfgenmod_pointtooclose);
        }
        return result;
    }
    
    /*
     * ORBIT
     */
    if( state->modeltype==2 )
    {
        
        /*
         * ORBIT does not check this condition
         */
        result = ae_false;
        return result;
    }
    ae_assert(ae_false, "DFGM: WorkingSetNeedsImprovement() is unable to handle unexpected algorithm", _state);
    return result;
}


/*************************************************************************
Handle successful or failed step.

This function updates working set and (for some solvers) issues additional
RComm V2 requests if working set needs immediate revaluation.

It also updates the model.

On exit it may set State.RepTerminationType to a negative  code  (failure)
if it encountered infinities in the target/constraints during  update  and
failed to recover from the problem.

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
It expects that State.Xk and State.FkVec were already updated at the moment
this function is called.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static ae_bool dfgenmod_updateworkingsetandmodel(dfgmstate* state,
     /* Real    */ const ae_vector* xk,
     /* Real    */ const ae_vector* xn,
     /* Real    */ const ae_vector* fnvec,
     double fn,
     double hn,
     ae_bool successfulstep,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t cntlc;
    ae_int_t cntnlc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t replaceidx;
    double rating;
    double bestrating;
    double v;
    double vv;
    double mx;
    ae_bool result;


    
    /*
     * 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( state->rstateupdate.stage>=0 )
    {
        n = state->rstateupdate.ia.ptr.p_int[0];
        m = state->rstateupdate.ia.ptr.p_int[1];
        cntlc = state->rstateupdate.ia.ptr.p_int[2];
        cntnlc = state->rstateupdate.ia.ptr.p_int[3];
        i = state->rstateupdate.ia.ptr.p_int[4];
        j = state->rstateupdate.ia.ptr.p_int[5];
        k = state->rstateupdate.ia.ptr.p_int[6];
        replaceidx = state->rstateupdate.ia.ptr.p_int[7];
        rating = state->rstateupdate.ra.ptr.p_double[0];
        bestrating = state->rstateupdate.ra.ptr.p_double[1];
        v = state->rstateupdate.ra.ptr.p_double[2];
        vv = state->rstateupdate.ra.ptr.p_double[3];
        mx = state->rstateupdate.ra.ptr.p_double[4];
    }
    else
    {
        n = -541;
        m = -698;
        cntlc = -900;
        cntnlc = -318;
        i = -940;
        j = 1016;
        k = -229;
        replaceidx = -536;
        rating = 487.0;
        bestrating = -115.0;
        v = 886.0;
        vv = 346.0;
        mx = -722.0;
    }
    if( state->rstateupdate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstateupdate.stage==1 )
    {
        goto lbl_1;
    }
    
    /*
     * Routine body
     */
    n = state->n;
    m = state->m;
    cntlc = state->cntlc;
    cntnlc = state->cntnlc;
    
    /*
     * 2PS
     */
    if( state->modeltype!=0 )
    {
        goto lbl_2;
    }
    
    /*
     * Do not update working set on unsuccessful steps because this solver uses fixed
     * stencil which does not allow inclusion of arbitrary points. The only way to update
     * working set is to rebuild it at a new point using the same stencil.
     */
    if( !successfulstep )
    {
        result = ae_false;
        return result;
    }
    
    /*
     * Clear working set (it will be recomputed from scratch on a new place) and call working set improvement code
     */
    state->wrksetsize = 0;
    ae_vector_set_length(&state->rstateimprove.ia, 13+1, _state);
    ae_vector_set_length(&state->rstateimprove.ba, 0+1, _state);
    ae_vector_set_length(&state->rstateimprove.ra, 12+1, _state);
    state->rstateimprove.stage = -1;
lbl_4:
    if( !dfgenmod_improveworkingsetgeometry(state, _state) )
    {
        goto lbl_5;
    }
    state->rstateupdate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    goto lbl_4;
lbl_5:
    result = ae_false;
    return result;
lbl_2:
    
    /*
     * LSA
     */
    if( state->modeltype==1 )
    {
        
        /*
         * If the step was not successful, we have to skip update if the new point
         * is too close to the center. When the step is successful, we can replace
         * the center. But it is not possible on unsuccessful steps.
         */
        if( !successfulstep )
        {
            v = (double)(0);
            for(j=0; j<=n-1; j++)
            {
                v = ae_maxreal(v, ae_fabs(xn->ptr.p_double[j]-xk->ptr.p_double[j], _state)*state->invtrustregion.ptr.p_double[j], _state);
            }
            if( ae_fp_less(v,dfgenmod_pointtooclose) )
            {
                result = ae_false;
                return result;
            }
        }
        
        /*
         * Select ReplaceIdx - index of a point to replace by the new point Xn,
         * with 0<=ReplaceIdx<N meaning replacing off-center points of the working
         * set, and ReplaceIdx=N meaning replacing (previously) central point.
         */
        rallocv(n, &state->tmp0, _state);
        rallocv(n, &state->tmp1, _state);
        replaceidx = -1;
        bestrating = (double)(0);
        for(i=0; i<=n; i++)
        {
            
            /*
             * Skip central point if the step is not successful (we remain at the old
             * place, so the central point can not be replaced)
             */
            if( i==n&&!successfulstep )
            {
                break;
            }
            
            /*
             * Compute Lagrange polynomial coefficients
             */
            if( i==n )
            {
                rsetv(n, 0.0, &state->tmp0, _state);
                for(j=0; j<=n-1; j++)
                {
                    rcopycv(n, &state->lsamodel.invw, j, &state->tmp1, _state);
                    raddv(n, -1.0, &state->tmp1, &state->tmp0, _state);
                }
            }
            else
            {
                rcopycv(n, &state->lsamodel.invw, i, &state->tmp0, _state);
            }
            
            /*
             * Compute rating as |L(Xn-Xk)|*max(|Xn-Wrk[I]|/Scales,1)^2, compare with the best
             */
            rcopyrv(n, &state->wrkset, i, &state->tmp1, _state);
            raddv(n, -1.0, xn, &state->tmp1, _state);
            rating = ae_fabs(rdotv(n, xn, &state->tmp0, _state)-rdotv(n, xk, &state->tmp0, _state)+rcase2(i==n, (double)(1), (double)(0), _state), _state)*ae_sqr(ae_maxreal(dfgenmod_normrelativetotrustregion(state, &state->tmp1, _state), (double)(1), _state), _state);
            if( replaceidx<0||ae_fp_greater(rating,bestrating) )
            {
                replaceidx = i;
                bestrating = rating;
            }
        }
        ae_assert(replaceidx>=0, "DFGM: integrity check 2850 failed", _state);
        if( state->dotrace )
        {
            if( replaceidx<n )
            {
                ae_trace(">> replacing point %0d (non-central)\n",
                    (int)(replaceidx));
            }
            else
            {
                ae_trace(">> replacing previously central point\n");
            }
        }
        
        /*
         * Modify WrkSet
         */
        if( successfulstep )
        {
            if( replaceidx<n )
            {
                
                /*
                 * Successful step, we replace off-central point by Xn.
                 *
                 * Because we always move Xn to N-th row of the working set, it means
                 * that we push the former central point from the row #N to the
                 * ReplaceIdx-th one.
                 */
                rcopyrr(n+m+cntnlc, &state->wrkset, n, &state->wrkset, replaceidx, _state);
                rcopyvr(n, xn, &state->wrkset, n, _state);
                for(i=0; i<=m+cntnlc-1; i++)
                {
                    state->wrkset.ptr.pp_double[n][n+i] = fnvec->ptr.p_double[i];
                }
            }
            else
            {
                
                /*
                 * Successful step, we replace central point by Xn.
                 */
                rcopyvr(n, xn, &state->wrkset, n, _state);
                for(i=0; i<=m+cntnlc-1; i++)
                {
                    state->wrkset.ptr.pp_double[n][n+i] = fnvec->ptr.p_double[i];
                }
            }
        }
        else
        {
            
            /*
             * Non-successful step, only off-central point can be replaced. The center
             * is not replaced or moved.
             */
            ae_assert(replaceidx<n, "DFGM: integrity check 6712 failed", _state);
            rcopyvr(n, xn, &state->wrkset, replaceidx, _state);
            for(i=0; i<=m+cntnlc-1; i++)
            {
                state->wrkset.ptr.pp_double[replaceidx][n+i] = fnvec->ptr.p_double[i];
            }
        }
        
        /*
         * Recompute matrices W and InvW
         */
        for(i=0; i<=n-1; i++)
        {
            rcopyrr(n, &state->wrkset, i, &state->lsamodel.w, i, _state);
            raddrr(n, -1.0, &state->wrkset, n, &state->lsamodel.w, i, _state);
        }
        rcopym(n, n, &state->lsamodel.w, &state->lsamodel.invw, _state);
        rmatrixinverse(&state->lsamodel.invw, n, &state->invrep, _state);
        if( state->invrep.terminationtype<=0 )
        {
            if( state->dotrace )
            {
                ae_trace(">> dataset matrix inverse failed, emergency termination with completion code +7\n");
            }
            state->repterminationtype = 7;
            result = ae_false;
            return result;
        }
        if( state->dotrace )
        {
            ae_trace(">> successfully inverted dataset matrix, cond=%0.2e\n",
                (double)((double)1/(state->invrep.r1+ae_machineepsilon)));
            rallocv(n, &state->tmp0, _state);
            rallocv(n, &state->tmp1, _state);
            v = (double)(0);
            for(i=0; i<=n; i++)
            {
                if( i==n )
                {
                    rsetv(n, 0.0, &state->tmp0, _state);
                    for(j=0; j<=n-1; j++)
                    {
                        rcopycv(n, &state->lsamodel.invw, j, &state->tmp1, _state);
                        raddv(n, -1.0, &state->tmp1, &state->tmp0, _state);
                    }
                }
                else
                {
                    rcopycv(n, &state->lsamodel.invw, i, &state->tmp0, _state);
                }
                v = ae_maxreal(v, dfgenmod_maxabslag(xn, n, &state->trustregion, &state->finitebndl, &state->finitebndu, &state->tmp0, rcase2(i==n, (double)(1), (double)(0), _state), &state->tmp1, &state->tmp2, ae_false, _state), _state);
            }
            ae_trace(">> poisedness constant is %0.2e\n",
                (double)(v));
        }
        
        /*
         * The working set is ready, rebuild a quadratic model of the target.
         *
         * Below we rely on the specific ordering of the working set
         * (the central point comes last, N additional ones come first).
         */
        ae_assert(state->cntnlc==0, "DFGM: integrity check 4811 failed", _state);
        ae_assert(state->wrksetsize==n+1, "DFGM: integrity check 0425 failed", _state);
        rallocm(n, m, &state->lsamodel.tmprhs, _state);
        rmatrixcopy(n, m, &state->wrkset, 0, n, &state->lsamodel.tmprhs, 0, 0, _state);
        rallocv(m, &state->lsamodel.tmp0, _state);
        for(i=0; i<=m-1; i++)
        {
            state->lsamodel.tmp0.ptr.p_double[i] = state->wrkset.ptr.pp_double[n][n+i];
        }
        for(i=0; i<=n-1; i++)
        {
            raddvr(m, -1.0, &state->lsamodel.tmp0, &state->lsamodel.tmprhs, i, _state);
        }
        rallocm(m, n, &state->lsamodel.jac, _state);
        rmatrixgemm(m, n, n, 1.0, &state->lsamodel.tmprhs, 0, 0, 1, &state->lsamodel.invw, 0, 0, 1, 0.0, &state->lsamodel.jac, 0, 0, _state);
        rsetallocv(n, 0.0, &state->lsamodel.g, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                state->lsamodel.g.ptr.p_double[i] = state->lsamodel.g.ptr.p_double[i]+state->wrkset.ptr.pp_double[n][n+j]*state->lsamodel.jac.ptr.pp_double[j][i];
            }
        }
        rallocm(n, n, &state->lsamodel.q, _state);
        rmatrixgemm(n, n, m, 1.0, &state->lsamodel.jac, 0, 0, 1, &state->lsamodel.jac, 0, 0, 0, 0.0, &state->lsamodel.q, 0, 0, _state);
        
        /*
         * Done
         */
        result = ae_false;
        return result;
    }
    
    /*
     * ORBIT
     */
    if( state->modeltype!=2 )
    {
        goto lbl_6;
    }
    ae_vector_set_length(&state->rstateimprove.ia, 13+1, _state);
    ae_vector_set_length(&state->rstateimprove.ba, 0+1, _state);
    ae_vector_set_length(&state->rstateimprove.ra, 12+1, _state);
    state->rstateimprove.stage = -1;
lbl_8:
    if( !dfgenmod_improveworkingsetgeometry(state, _state) )
    {
        goto lbl_9;
    }
    state->rstateupdate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    goto lbl_8;
lbl_9:
    result = ae_false;
    return result;
lbl_6:
    ae_assert(ae_false, "DFGM: UpdateWorkingSetAndModel() is unable to handle unexpected algorithm", _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstateupdate.ia.ptr.p_int[0] = n;
    state->rstateupdate.ia.ptr.p_int[1] = m;
    state->rstateupdate.ia.ptr.p_int[2] = cntlc;
    state->rstateupdate.ia.ptr.p_int[3] = cntnlc;
    state->rstateupdate.ia.ptr.p_int[4] = i;
    state->rstateupdate.ia.ptr.p_int[5] = j;
    state->rstateupdate.ia.ptr.p_int[6] = k;
    state->rstateupdate.ia.ptr.p_int[7] = replaceidx;
    state->rstateupdate.ra.ptr.p_double[0] = rating;
    state->rstateupdate.ra.ptr.p_double[1] = bestrating;
    state->rstateupdate.ra.ptr.p_double[2] = v;
    state->rstateupdate.ra.ptr.p_double[3] = vv;
    state->rstateupdate.ra.ptr.p_double[4] = mx;
    return result;
}


/*************************************************************************
For State.WrkSetSize=0 this function assumes that we actually  perform  an
initial model construction.  In  the  latter case we assume that the first
element of the working set contains some  meaningful value,  in the latter
case   the  working  set  is  built  completely  from scratch.

For State.WrkSetSize>0 this function assumes that the working set was
already initialized and we perform an update: modify working set and
(for some solvers) issues additional RComm V2 requests if working set needs
immediate revaluation.

It also updates the model:
* recomputes State.Fk and State.Hk
* updates current model by (possibly) incorporating State.Xk and (possibly)
  removing some of the previously included points.

On exit it may set State.RepTerminationType to a negative  code  (failure)
if it encountered infinities in the target/constraints during  update  and
failed to recover from the problem.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static ae_bool dfgenmod_improveworkingsetgeometry(dfgmstate* state,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t cntlc;
    ae_int_t cntnlc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double vlft;
    double vrgt;
    double v;
    double vmin;
    double vmax;
    double x0i;
    ae_int_t improveidx;
    ae_int_t improvedfar;
    ae_bool workingsetchanged;
    ae_int_t leastdistantidx;
    ae_int_t mostdistantidx;
    double leastdistant;
    double mostdistant;
    double mx;
    ae_int_t replaceidx;
    double rating;
    double bestrating;
    double vv;
    double roundradius;
    ae_int_t roundidx;
    ae_int_t chosenduringround;
    ae_bool result;


    
    /*
     * 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( state->rstateimprove.stage>=0 )
    {
        n = state->rstateimprove.ia.ptr.p_int[0];
        m = state->rstateimprove.ia.ptr.p_int[1];
        cntlc = state->rstateimprove.ia.ptr.p_int[2];
        cntnlc = state->rstateimprove.ia.ptr.p_int[3];
        i = state->rstateimprove.ia.ptr.p_int[4];
        j = state->rstateimprove.ia.ptr.p_int[5];
        k = state->rstateimprove.ia.ptr.p_int[6];
        improveidx = state->rstateimprove.ia.ptr.p_int[7];
        improvedfar = state->rstateimprove.ia.ptr.p_int[8];
        leastdistantidx = state->rstateimprove.ia.ptr.p_int[9];
        mostdistantidx = state->rstateimprove.ia.ptr.p_int[10];
        replaceidx = state->rstateimprove.ia.ptr.p_int[11];
        roundidx = state->rstateimprove.ia.ptr.p_int[12];
        chosenduringround = state->rstateimprove.ia.ptr.p_int[13];
        workingsetchanged = state->rstateimprove.ba.ptr.p_bool[0];
        vlft = state->rstateimprove.ra.ptr.p_double[0];
        vrgt = state->rstateimprove.ra.ptr.p_double[1];
        v = state->rstateimprove.ra.ptr.p_double[2];
        vmin = state->rstateimprove.ra.ptr.p_double[3];
        vmax = state->rstateimprove.ra.ptr.p_double[4];
        x0i = state->rstateimprove.ra.ptr.p_double[5];
        leastdistant = state->rstateimprove.ra.ptr.p_double[6];
        mostdistant = state->rstateimprove.ra.ptr.p_double[7];
        mx = state->rstateimprove.ra.ptr.p_double[8];
        rating = state->rstateimprove.ra.ptr.p_double[9];
        bestrating = state->rstateimprove.ra.ptr.p_double[10];
        vv = state->rstateimprove.ra.ptr.p_double[11];
        roundradius = state->rstateimprove.ra.ptr.p_double[12];
    }
    else
    {
        n = -413;
        m = -461;
        cntlc = 927;
        cntnlc = 201;
        i = 922;
        j = -154;
        k = 306;
        improveidx = -1011;
        improvedfar = 951;
        leastdistantidx = -463;
        mostdistantidx = 88;
        replaceidx = -861;
        roundidx = -678;
        chosenduringround = -731;
        workingsetchanged = ae_true;
        vlft = -763.0;
        vrgt = -233.0;
        v = -936.0;
        vmin = -279.0;
        vmax = 94.0;
        x0i = -812.0;
        leastdistant = 427.0;
        mostdistant = 178.0;
        mx = -819.0;
        rating = -826.0;
        bestrating = 667.0;
        vv = 692.0;
        roundradius = 84.0;
    }
    if( state->rstateimprove.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstateimprove.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstateimprove.stage==2 )
    {
        goto lbl_2;
    }
    
    /*
     * Routine body
     */
    n = state->n;
    m = state->m;
    cntlc = state->cntlc;
    cntnlc = state->cntnlc;
    
    /*
     * If working set is empty (initialization) or algorithm rebuilds
     * working set from scratch every time (like 2PS), initialize
     * working set using the best geometry possible (orthogonal distribution).
     *
     * After the working set was generated, the model is data structures
     * are initialized.
     *
     * NOTE: this section gets the fresh data from the user even when the algorithm
     *       relies on a previously stored cloud.
     */
    workingsetchanged = ae_false;
    if( !(state->wrksetsize==0||state->modeltype==0) )
    {
        goto lbl_3;
    }
    
    /*
     * Generate working set which satisfies box constraints
     */
    rallocv(n, &state->tmpdeltas, _state);
lbl_5:
    if( ae_false )
    {
        goto lbl_6;
    }
    
    /*
     * Initialize working set
     */
    rallocm(n+1, n+m+cntnlc, &state->wrkset, _state);
    state->wrksetsize = 0;
    for(i=0; i<=n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i] )
        {
            ae_assert(ae_fp_greater(state->scaledbndu.ptr.p_double[i],state->scaledbndl.ptr.p_double[i]), "DFGM: integrity check 7909 failed", _state);
        }
        rcopyvr(n, &state->xk, &state->wrkset, state->wrksetsize, _state);
        
        /*
         * Evaluate two candidate points: one obtained by making a step to the left, and one obtained by making a step to the right
         */
        x0i = state->xk.ptr.p_double[i];
        vlft = boundval(x0i-dfgenmod_twopsstep*state->trustregion.ptr.p_double[i], state->finitebndl.ptr.p_double[i], state->finitebndu.ptr.p_double[i], _state);
        vrgt = boundval(x0i+dfgenmod_twopsstep*state->trustregion.ptr.p_double[i], state->finitebndl.ptr.p_double[i], state->finitebndu.ptr.p_double[i], _state);
        if( ae_fp_less_eq(x0i-vlft,0.9*(vrgt-x0i)) )
        {
            
            /*
             * The left step is shorter than the right one due to box constraints, choose the right step
             */
            state->wrkset.ptr.pp_double[state->wrksetsize][i] = vrgt;
            state->tmpdeltas.ptr.p_double[i] = vrgt-x0i;
            state->wrksetsize = state->wrksetsize+1;
            continue;
        }
        if( ae_fp_less_eq(vrgt-x0i,0.9*(x0i-vlft)) )
        {
            
            /*
             * The right step is shorter than the left one due to box constraints, choose the left step
             */
            state->wrkset.ptr.pp_double[state->wrksetsize][i] = vlft;
            state->tmpdeltas.ptr.p_double[i] = vlft-x0i;
            state->wrksetsize = state->wrksetsize+1;
            continue;
        }
        
        /*
         * Both steps are equally good, choose the random one
         */
        state->wrkset.ptr.pp_double[state->wrksetsize][i] = rcase2(hqrnduniformi(&state->rs, 2, _state)==0, vlft, vrgt, _state);
        state->tmpdeltas.ptr.p_double[i] = state->wrkset.ptr.pp_double[state->wrksetsize][i]-x0i;
        state->wrksetsize = state->wrksetsize+1;
    }
    rcopyvr(n, &state->xk, &state->wrkset, state->wrksetsize, _state);
    state->wrksetsize = state->wrksetsize+1;
    ae_assert(state->wrksetsize==n+1, "DFGM: integrity check 1309 failed", _state);
    for(i=0; i<=n-1; i++)
    {
        state->tmpdeltas.ptr.p_double[i] = coalesce(state->tmpdeltas.ptr.p_double[i], 1.0, _state);
    }
    
    /*
     * Issue RComm V2 request, check that the reply does not contain infinities
     */
    state->requesttype = 4;
    state->querysize = state->wrksetsize;
    rallocv(n*state->querysize, &state->querydata, _state);
    rallocv((m+cntnlc)*state->querysize, &state->replyfi, _state);
    for(i=0; i<=state->wrksetsize-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            state->querydata.ptr.p_double[i*n+j] = state->wrkset.ptr.pp_double[i][j];
        }
    }
    dfgenmod_sendrequesttocloud(state, &state->querydata, state->querysize, _state);
    state->rstateimprove.stage = 0;
    goto lbl_rcomm;
lbl_0:
    if( !isfinitevector(&state->replyfi, (m+cntnlc)*state->querysize, _state) )
    {
        
        /*
         * Decrease trust radius and retry
         */
        if( state->dotrace )
        {
            ae_trace("[WARNING] target at the geometry-improving point is infinite, decreasing trust radius and restarting iteration\n");
        }
        state->infinitiesencountered = ae_true;
        state->recoveredfrominfinities = ae_false;
        dfgenmod_updatetrustregion(state, dfgenmod_gammabad*state->trustradfactor, _state);
        state->trustradbnd = ae_minreal(state->trustradfactor, state->trustradbnd, _state);
        if( ae_fp_less(state->trustradfactor,coalesce(state->epsx, state->toosmalltrustrad, _state)) )
        {
            state->repterminationtype = -8;
            result = ae_false;
            return result;
        }
        goto lbl_5;
    }
    
    /*
     * Accept the reply.
     * Save to the cloud, if requested by the algorith,
     */
    for(i=0; i<=state->wrksetsize-1; i++)
    {
        for(j=0; j<=m+cntnlc-1; j++)
        {
            state->wrkset.ptr.pp_double[i][n+j] = state->replyfi.ptr.p_double[i*(m+cntnlc)+j];
        }
    }
    dfgenmod_sendreplytocloud(state, &state->replyfi, _state);
    goto lbl_6;
    goto lbl_5;
lbl_6:
    
    /*
     * Initialize model data structures
     */
    ae_assert((state->modeltype==0||state->modeltype==1)||state->modeltype==2, "DFGM: integrity check 0337 failed", _state);
    if( state->modeltype==0 )
    {
        rcopyallocv(n, &state->tmpdeltas, &state->tpsmodel.deltas, _state);
    }
    if( state->modeltype==1 )
    {
        rsetallocm(n, n, 0.0, &state->lsamodel.w, _state);
        rsetallocm(n, n, 0.0, &state->lsamodel.invw, _state);
        for(i=0; i<=n-1; i++)
        {
            state->lsamodel.w.ptr.pp_double[i][i] = state->tmpdeltas.ptr.p_double[i];
            state->lsamodel.invw.ptr.pp_double[i][i] = (double)1/state->tmpdeltas.ptr.p_double[i];
        }
    }
    workingsetchanged = ae_true;
lbl_3:
    
    /*
     * 2PS.
     */
    if( state->modeltype==0 )
    {
        
        /*
         * The working set was already computed, rebuild a quadratic model of the target.
         *
         * Below we rely on the specific ordering of the working set
         * (the central point comes last, N additional ones come first).
         */
        ae_assert(state->cntnlc==0, "DFGM: integrity check 7614 failed", _state);
        ae_assert(state->wrksetsize==n+1, "DFGM: integrity check 0425 failed", _state);
        rsetallocv(n, 0.0, &state->tpsmodel.g, _state);
        rallocm(n, n, &state->tpsmodel.q, _state);
        rallocm(m, n, &state->tpsmodel.jac, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                state->tpsmodel.jac.ptr.pp_double[j][i] = (state->wrkset.ptr.pp_double[i][n+j]-state->wrkset.ptr.pp_double[n][n+j])/state->tpsmodel.deltas.ptr.p_double[i];
                state->tpsmodel.g.ptr.p_double[i] = state->tpsmodel.g.ptr.p_double[i]+state->wrkset.ptr.pp_double[n][n+j]*state->tpsmodel.jac.ptr.pp_double[j][i];
            }
        }
        rmatrixgemm(n, n, m, 1.0, &state->tpsmodel.jac, 0, 0, 1, &state->tpsmodel.jac, 0, 0, 0, 0.0, &state->tpsmodel.q, 0, 0, _state);
        
        /*
         * Done
         */
        result = ae_false;
        return result;
    }
    
    /*
     * DFO-LSA.
     */
    if( state->modeltype!=1 )
    {
        goto lbl_7;
    }
    
    /*
     * Improve geometry of the dataset, if needed
     */
    ae_assert(state->wrksetsize==n+1, "DFGM: integrity check 9726 failed", _state);
    improvedfar = 0;
    
    /*
     * Repeat improvement rounds until there is no point selected for improvement.
     */
lbl_9:
    
    /*
     * Select a point to improve:
     * * improve location of all points that are too close
     * * improve location of a limited number of points that are too far
     */
    dfgenmod_mostandleastdistant(&state->invtrustregion, n, &state->wrkset, state->wrksetsize, &mostdistant, &mostdistantidx, &leastdistant, &leastdistantidx, _state);
    improveidx = -1;
    if( leastdistantidx>=0&&ae_fp_less_eq(leastdistant,dfgenmod_pointtooclose*ae_minreal(mostdistant, 1.0, _state)) )
    {
        improveidx = leastdistantidx;
    }
    if( improveidx<0&&ae_fp_greater_eq(mostdistant,dfgenmod_pointunacceptablyfar) )
    {
        improveidx = mostdistantidx;
    }
    if( (improveidx<0&&ae_fp_less((double)(improvedfar),dfgenmod_lsaimprovementlimit))&&ae_fp_greater_eq(mostdistant,dfgenmod_pointtoofar) )
    {
        improvedfar = improvedfar+1;
        improveidx = mostdistantidx;
    }
    
    /*
     * Try to find a location for an improved point, handle possible infinities in the target
     * by decreasing the trust radius and recomputing the proposal.
     *
     * The loop below has the loop condition "ImproveIdx>=0", the intention is to execute
     * it continually (until we settle on an infinity-free trust region) when there is a
     * point to improve, but to skip it if there is no points to be improved
     */
    rallocv(n, &state->tmp0, _state);
    rallocv(n, &state->tmp1, _state);
    rallocv(n, &state->tmp2, _state);
lbl_12:
    if( improveidx<0 )
    {
        goto lbl_13;
    }
    
    /*
     * Insert new point to the location where Lagrange polynomial for ImproveIdx-th point
     * reaches its maximum absolute value.
     *
     * Lagrange polynomial for this problem is a linear function with coefficients stored
     * in the ImproveIdx-th column of State.lsaModel.InvW (the constant term is zero). We
     * solve the problem by finding min L(x) and max L(x) subject to trust radius constraints,
     * and then selecting a solution with maximum absolute value.
     *
     * NOTE: C0 parameter of MaxAbsLag() is zero because we always have ImproveIdx<N, which
     *       corresponds to a Lagrange function which is zero at the origin
     */
    ae_assert(improveidx<n, "DFGM: integrity check 9820 failed", _state);
    rcopycv(n, &state->lsamodel.invw, improveidx, &state->tmp2, _state);
    dfgenmod_maxabslag(&state->xk, n, &state->trustregion, &state->finitebndl, &state->finitebndu, &state->tmp2, 0.0, &state->tmp0, &state->tmp1, ae_true, _state);
    rcopyvr(n, &state->tmp0, &state->wrkset, improveidx, _state);
    
    /*
     * Issue RComm V2 request, check that the reply does not contain infinities
     */
    state->requesttype = 4;
    state->querysize = 1;
    rallocv(n*state->querysize, &state->querydata, _state);
    rallocv((m+cntnlc)*state->querysize, &state->replyfi, _state);
    for(j=0; j<=n-1; j++)
    {
        state->querydata.ptr.p_double[j] = state->wrkset.ptr.pp_double[improveidx][j];
    }
    dfgenmod_sendrequesttocloud(state, &state->querydata, state->querysize, _state);
    state->rstateimprove.stage = 1;
    goto lbl_rcomm;
lbl_1:
    if( !isfinitevector(&state->replyfi, (m+cntnlc)*state->querysize, _state) )
    {
        
        /*
         * Decrease trust radius and retry
         */
        if( state->dotrace )
        {
            ae_trace("[WARNING] target at the geometry-improving point is infinite, decreasing trust radius and restarting iteration\n");
        }
        state->infinitiesencountered = ae_true;
        state->recoveredfrominfinities = ae_false;
        dfgenmod_updatetrustregion(state, dfgenmod_gammabad*state->trustradfactor, _state);
        state->trustradbnd = ae_minreal(state->trustradfactor, state->trustradbnd, _state);
        if( ae_fp_less(state->trustradfactor,coalesce(state->epsx, state->toosmalltrustrad, _state)) )
        {
            state->repterminationtype = -8;
            result = ae_false;
            return result;
        }
        goto lbl_12;
    }
    
    /*
     * Accept the reply
     */
    dfgenmod_sendreplytocloud(state, &state->replyfi, _state);
    for(j=0; j<=m+cntnlc-1; j++)
    {
        state->wrkset.ptr.pp_double[improveidx][n+j] = state->replyfi.ptr.p_double[j];
    }
    workingsetchanged = ae_true;
    goto lbl_13;
    goto lbl_12;
lbl_13:
    
    /*
     * Quick exit: no changes in the working set at this point means that
     *             the working set will never change. We can exit now.
     */
    if( !workingsetchanged )
    {
        result = ae_false;
        return result;
    }
    
    /*
     * Recompute matrices W and InvW, if there was an improvement in the
     * working set (this phase is ignored if WorkingSetChanged=True,
     * but ImproveIdx<0, which happens during the initialization)
     */
    if( improveidx>=0 )
    {
        rcopyrr(n, &state->wrkset, improveidx, &state->lsamodel.w, improveidx, _state);
        raddvr(n, -1.0, &state->xk, &state->lsamodel.w, improveidx, _state);
        rcopym(n, n, &state->lsamodel.w, &state->lsamodel.invw, _state);
        rmatrixinverse(&state->lsamodel.invw, n, &state->invrep, _state);
        if( state->invrep.terminationtype<=0 )
        {
            state->repterminationtype = 7;
            result = ae_false;
            return result;
        }
    }
    if( improveidx>=0 )
    {
        goto lbl_9;
    }
    
    /*
     * The working set is ready, rebuild a quadratic model of the target.
     *
     * Below we rely on the specific ordering of the working set
     * (the central point comes last, N additional ones come first).
     */
    ae_assert(state->wrksetsize==n+1, "DFGM: integrity check 0425 failed", _state);
    rallocm(n, m, &state->lsamodel.tmprhs, _state);
    rmatrixcopy(n, m, &state->wrkset, 0, n, &state->lsamodel.tmprhs, 0, 0, _state);
    rallocv(m, &state->lsamodel.tmp0, _state);
    for(i=0; i<=m-1; i++)
    {
        state->lsamodel.tmp0.ptr.p_double[i] = state->wrkset.ptr.pp_double[n][n+i];
    }
    for(i=0; i<=n-1; i++)
    {
        raddvr(m, -1.0, &state->lsamodel.tmp0, &state->lsamodel.tmprhs, i, _state);
    }
    rallocm(m, n, &state->lsamodel.jac, _state);
    rmatrixgemm(m, n, n, 1.0, &state->lsamodel.tmprhs, 0, 0, 1, &state->lsamodel.invw, 0, 0, 1, 0.0, &state->lsamodel.jac, 0, 0, _state);
    rsetallocv(n, 0.0, &state->lsamodel.g, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            state->lsamodel.g.ptr.p_double[i] = state->lsamodel.g.ptr.p_double[i]+state->wrkset.ptr.pp_double[n][n+j]*state->lsamodel.jac.ptr.pp_double[j][i];
        }
    }
    rallocm(n, n, &state->lsamodel.q, _state);
    rmatrixgemm(n, n, m, 1.0, &state->lsamodel.jac, 0, 0, 1, &state->lsamodel.jac, 0, 0, 0, 0.0, &state->lsamodel.q, 0, 0, _state);
    
    /*
     * Done
     */
    result = ae_false;
    return result;
lbl_7:
    
    /*
     * ORBIT
     */
    if( state->modeltype!=2 )
    {
        goto lbl_14;
    }
    
    /*
     * Select subset of points from the cloud to work with.
     *
     * Initialize DistFromCenter[] and DistFromChosen[] (the latter is a copy
     * of DistFromCenter[]). Set availability flags.
     */
    ae_assert(state->savepointstocloud, "DFGM: integrity check 701531 failed", _state);
    state->rbfmodel.currentcloudsize = 0;
    for(i=0; i<=state->cloudsize-1; i++)
    {
        v = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            v = ae_maxreal(v, ae_fabs(state->clouddata.ptr.pp_double[i][j]-state->xk.ptr.p_double[j], _state)*state->invtrustregion.ptr.p_double[j], _state);
        }
        if( ae_fp_less_eq(v,dfgenmod_rbfmindist)||ae_fp_greater(v,dfgenmod_rbftheta0) )
        {
            continue;
        }
        rgrowrowsfixedcolsm(state->rbfmodel.currentcloudsize+1, n+m+cntnlc, &state->rbfmodel.currentcloud, _state);
        rcopyrr(n+m+cntnlc, &state->clouddata, i, &state->rbfmodel.currentcloud, state->rbfmodel.currentcloudsize, _state);
        rgrowappendv(state->rbfmodel.currentcloudsize+1, &state->rbfmodel.distfromcenter, v, _state);
        rgrowappendv(state->rbfmodel.currentcloudsize+1, &state->rbfmodel.distfromchosen, v, _state);
        bgrowappendv(state->rbfmodel.currentcloudsize+1, &state->rbfmodel.available, ae_true, _state);
        state->rbfmodel.currentcloudsize = state->rbfmodel.currentcloudsize+1;
    }
    
    /*
     * Try to select initial affine-independent working set of N points (plus one central)
     * from the cloud, without performing additional evaluations
     */
    ae_assert(ae_fp_greater(dfgenmod_rbfmindist,(double)(0)), "DFGM: integrity check 734622 failed", _state);
    rallocm(state->rbfmodel.currentcloudsize, n, &state->rbfmodel.residualdisplacements, _state);
    for(i=0; i<=state->rbfmodel.currentcloudsize-1; i++)
    {
        rcopyrr(n, &state->rbfmodel.currentcloud, i, &state->rbfmodel.residualdisplacements, i, _state);
        raddvr(n, -1.0, &state->xk, &state->rbfmodel.residualdisplacements, i, _state);
        rmergemulvr(n, &state->invtrustregion, &state->rbfmodel.residualdisplacements, i, _state);
    }
    state->rbfmodel.curwrksetsize = 0;
    while(state->rbfmodel.curwrksetsize<n)
    {
        
        /*
         * Select largest residual displacement.
         */
        k = -1;
        v = (double)(0);
        mx = dfgenmod_rbftheta0;
        for(i=0; i<=state->rbfmodel.currentcloudsize-1; i++)
        {
            if( !state->rbfmodel.available.ptr.p_bool[i] )
            {
                continue;
            }
            if( ae_fp_less_eq(state->rbfmodel.distfromchosen.ptr.p_double[i],dfgenmod_rbfmindist) )
            {
                continue;
            }
            if( ae_fp_greater(state->rbfmodel.distfromcenter.ptr.p_double[i],mx) )
            {
                continue;
            }
            vv = rdotrr(n, &state->rbfmodel.residualdisplacements, i, &state->rbfmodel.residualdisplacements, i, _state);
            if( ae_fp_eq(vv,(double)(0))||ae_fp_less(vv,dfgenmod_rbftheta1*dfgenmod_rbftheta1) )
            {
                continue;
            }
            if( k<0||ae_fp_greater(vv,v) )
            {
                k = i;
                v = vv;
            }
        }
        if( k<0 )
        {
            break;
        }
        i = k;
        
        /*
         * Orthogonalize residual displacements with respect to the currently chosen one
         */
        rallocv(n, &state->rbfmodel.tmpcand, _state);
        rcopyrv(n, &state->rbfmodel.residualdisplacements, i, &state->rbfmodel.tmpcand, _state);
        v = rdotv2(n, &state->rbfmodel.tmpcand, _state);
        for(j=0; j<=state->rbfmodel.currentcloudsize-1; j++)
        {
            raddvr(n, -rdotvr(n, &state->rbfmodel.tmpcand, &state->rbfmodel.residualdisplacements, j, _state)/v, &state->rbfmodel.tmpcand, &state->rbfmodel.residualdisplacements, j, _state);
        }
        rmulv(n, (double)1/ae_sqrt(v, _state), &state->rbfmodel.tmpcand, _state);
        
        /*
         * Add to the working set and Q, mark as unavailable and update DistFromChosen[]
         */
        rgrowrowsfixedcolsm(state->rbfmodel.curwrksetsize+1, n+m+cntnlc, &state->rbfmodel.curwrkset, _state);
        rcopyrr(n+m+cntnlc, &state->rbfmodel.currentcloud, i, &state->rbfmodel.curwrkset, state->rbfmodel.curwrksetsize, _state);
        rgrowrowsfixedcolsm(state->rbfmodel.curwrksetsize+1, n, &state->rbfmodel.q, _state);
        rcopyvr(n, &state->rbfmodel.tmpcand, &state->rbfmodel.q, state->rbfmodel.curwrksetsize, _state);
        state->rbfmodel.available.ptr.p_bool[i] = ae_false;
        state->rbfmodel.curwrksetsize = state->rbfmodel.curwrksetsize+1;
        for(j=0; j<=state->rbfmodel.currentcloudsize-1; j++)
        {
            v = (double)(0);
            for(k=0; k<=n-1; k++)
            {
                v = ae_maxreal(v, ae_fabs(state->rbfmodel.currentcloud.ptr.pp_double[i][k]-state->rbfmodel.currentcloud.ptr.pp_double[j][k], _state)*state->invtrustregion.ptr.p_double[k], _state);
            }
            state->rbfmodel.distfromchosen.ptr.p_double[j] = ae_minreal(state->rbfmodel.distfromchosen.ptr.p_double[j], v, _state);
        }
    }
    ae_assert(state->rbfmodel.curwrksetsize<=n, "DFGM: integrity check 779633 failed", _state);
    
    /*
     * Failed to sample N linearly independent directions from the cloud; perform additional
     * sampling by generating random points within trust region. Due to box constraints it
     * is not easy to generate a direction orthogonal to the current Q which does not violate
     * box constraints, so we generate a random feasible point and check its orthogonality
     * properties with respect to Q. Actually, we generate many such points and perform a
     * competition amongst them.
     */
lbl_16:
    if( state->rbfmodel.curwrksetsize>=n )
    {
        goto lbl_17;
    }
    
    /*
     * Run the competition
     */
    state->rbfmodel.winnerrating = (double)(0);
    rcopyallocv(n, &state->xk, &state->rbfmodel.tmpwinner, _state);
    rallocv(n, &state->rbfmodel.tmpcand, _state);
    k = 0;
    while(k<5||ae_fp_eq(state->rbfmodel.winnerrating,(double)(0)))
    {
        
        /*
         * Generate random point within a box
         */
        for(i=0; i<=n-1; i++)
        {
            v = ae_maxreal(state->xk.ptr.p_double[i]-state->trustregion.ptr.p_double[i], state->finitebndl.ptr.p_double[i], _state);
            vv = ae_minreal(state->xk.ptr.p_double[i]+state->trustregion.ptr.p_double[i], state->finitebndu.ptr.p_double[i], _state);
            state->rbfmodel.tmpcand.ptr.p_double[i] = boundval(v+(vv-v)*hqrnduniformr(&state->rs, _state), state->finitebndl.ptr.p_double[i], state->finitebndu.ptr.p_double[i], _state);
        }
        
        /*
         * Compute displacement and orthogonalize it wrt Q, compare residual with the largest one so far
         */
        rcopyallocv(n, &state->rbfmodel.tmpcand, &state->rbfmodel.tmpdisp, _state);
        raddv(n, -1.0, &state->xk, &state->rbfmodel.tmpdisp, _state);
        rmergemulv(n, &state->invtrustregion, &state->rbfmodel.tmpdisp, _state);
        rmulv(n, (double)1/ae_sqrt(ae_machineepsilon+rdotv2(n, &state->rbfmodel.tmpdisp, _state), _state), &state->rbfmodel.tmpdisp, _state);
        rowwisegramschmidt(&state->rbfmodel.q, state->rbfmodel.curwrksetsize, n, &state->rbfmodel.tmpdisp, &state->tmp0, ae_false, _state);
        v = ae_sqrt(rdotv2(n, &state->rbfmodel.tmpdisp, _state), _state);
        if( ae_fp_greater(v,(double)(0))&&ae_fp_greater(v,state->rbfmodel.winnerrating) )
        {
            state->rbfmodel.winnerrating = v;
            rcopyv(n, &state->rbfmodel.tmpcand, &state->rbfmodel.tmpwinner, _state);
        }
        
        /*
         * Next try, check for eternal loop
         */
        k = k+1;
        ae_assert(k<1000, "DFGM: integrity check 848747 failed (eternal loop in model-improving points generation)", _state);
    }
    
    /*
     * Issue RComm V2 request for the winner, check that the reply does not contain infinities
     */
    state->requesttype = 4;
    state->querysize = 1;
    rcopyallocv(n, &state->rbfmodel.tmpwinner, &state->querydata, _state);
    rallocv(m+cntnlc, &state->replyfi, _state);
    dfgenmod_sendrequesttocloud(state, &state->querydata, state->querysize, _state);
    state->rstateimprove.stage = 2;
    goto lbl_rcomm;
lbl_2:
    if( !isfinitevector(&state->replyfi, (m+cntnlc)*state->querysize, _state) )
    {
        
        /*
         * Decrease trust radius and retry
         */
        if( state->dotrace )
        {
            ae_trace("[WARNING] target at the geometry-improving point is infinite, decreasing trust radius and restarting iteration\n");
        }
        state->infinitiesencountered = ae_true;
        state->recoveredfrominfinities = ae_false;
        dfgenmod_updatetrustregion(state, dfgenmod_gammabad*state->trustradfactor, _state);
        state->trustradbnd = ae_minreal(state->trustradfactor, state->trustradbnd, _state);
        if( ae_fp_less(state->trustradfactor,coalesce(state->epsx, state->toosmalltrustrad, _state)) )
        {
            state->repterminationtype = -8;
            result = ae_false;
            return result;
        }
        goto lbl_16;
    }
    dfgenmod_sendreplytocloud(state, &state->replyfi, _state);
    rgrowrowsfixedcolsm(state->rbfmodel.curwrksetsize+1, n+m+cntnlc, &state->rbfmodel.curwrkset, _state);
    for(j=0; j<=n-1; j++)
    {
        state->rbfmodel.curwrkset.ptr.pp_double[state->rbfmodel.curwrksetsize][j] = state->rbfmodel.tmpwinner.ptr.p_double[j];
    }
    for(j=0; j<=m+cntnlc-1; j++)
    {
        state->rbfmodel.curwrkset.ptr.pp_double[state->rbfmodel.curwrksetsize][n+j] = state->replyfi.ptr.p_double[j];
    }
    rcopyallocv(n, &state->rbfmodel.tmpwinner, &state->rbfmodel.tmpdisp, _state);
    raddv(n, -1.0, &state->xk, &state->rbfmodel.tmpdisp, _state);
    rmergemulv(n, &state->invtrustregion, &state->rbfmodel.tmpdisp, _state);
    rowwisegramschmidt(&state->rbfmodel.q, state->rbfmodel.curwrksetsize, n, &state->rbfmodel.tmpdisp, &state->tmp0, ae_false, _state);
    rmulv(n, (double)1/ae_sqrt(rdotv2(n, &state->rbfmodel.tmpdisp, _state), _state), &state->rbfmodel.tmpdisp, _state);
    rcopyvr(n, &state->rbfmodel.tmpdisp, &state->rbfmodel.q, state->rbfmodel.curwrksetsize, _state);
    state->rbfmodel.curwrksetsize = state->rbfmodel.curwrksetsize+1;
    goto lbl_16;
lbl_17:
    ae_assert(state->rbfmodel.curwrksetsize==n, "DFGM: integrity check 902742 failed", _state);
    
    /*
     * Select additional points for RBF model, apply several rounds with search radius decreasing
     * by 2x each iteration. Such multilayered approach allows us to capture reuse and capture info
     * about both far away and close points.
     */
    roundradius = dfgenmod_rbftheta0;
    for(roundidx=0; roundidx<=3; roundidx++)
    {
        chosenduringround = 0;
        while(chosenduringround<n+1)
        {
            
            /*
             * Select largest distance from already selected points within distance
             * specified by rbfTheta0
             */
            k = -1;
            v = (double)(0);
            for(i=0; i<=state->rbfmodel.currentcloudsize-1; i++)
            {
                if( !state->rbfmodel.available.ptr.p_bool[i] )
                {
                    continue;
                }
                if( ae_fp_less_eq(state->rbfmodel.distfromchosen.ptr.p_double[i],dfgenmod_rbfmindist) )
                {
                    continue;
                }
                if( ae_fp_greater(state->rbfmodel.distfromcenter.ptr.p_double[i],roundradius) )
                {
                    continue;
                }
                vv = state->rbfmodel.distfromchosen.ptr.p_double[i];
                if( k<0||ae_fp_greater(vv,v) )
                {
                    k = i;
                    v = vv;
                }
            }
            if( k<0 )
            {
                break;
            }
            i = k;
            
            /*
             * Add to the working set and Q, mark as unavailable and update DistFromChosen[]
             */
            rgrowrowsfixedcolsm(state->rbfmodel.curwrksetsize+1, n+m+cntnlc, &state->rbfmodel.curwrkset, _state);
            rcopyrr(n+m+cntnlc, &state->rbfmodel.currentcloud, i, &state->rbfmodel.curwrkset, state->rbfmodel.curwrksetsize, _state);
            state->rbfmodel.available.ptr.p_bool[i] = ae_false;
            state->rbfmodel.curwrksetsize = state->rbfmodel.curwrksetsize+1;
            for(j=0; j<=state->rbfmodel.currentcloudsize-1; j++)
            {
                v = (double)(0);
                for(k=0; k<=n-1; k++)
                {
                    v = ae_maxreal(v, ae_fabs(state->rbfmodel.currentcloud.ptr.pp_double[i][k]-state->rbfmodel.currentcloud.ptr.pp_double[j][k], _state)*state->invtrustregion.ptr.p_double[k], _state);
                }
                state->rbfmodel.distfromchosen.ptr.p_double[j] = ae_minreal(state->rbfmodel.distfromchosen.ptr.p_double[j], v, _state);
            }
            chosenduringround = chosenduringround+1;
        }
        roundradius = 0.5*roundradius;
    }
    
    /*
     * Append the central point to the working set
     */
    rgrowrowsfixedcolsm(state->rbfmodel.curwrksetsize+1, n+m+cntnlc, &state->rbfmodel.curwrkset, _state);
    for(i=0; i<=n-1; i++)
    {
        state->rbfmodel.curwrkset.ptr.pp_double[state->rbfmodel.curwrksetsize][i] = state->xk.ptr.p_double[i];
    }
    for(i=0; i<=m+cntnlc-1; i++)
    {
        state->rbfmodel.curwrkset.ptr.pp_double[state->rbfmodel.curwrksetsize][n+i] = state->fkvec.ptr.p_double[i];
    }
    state->rbfmodel.curwrksetsize = state->rbfmodel.curwrksetsize+1;
    
    /*
     * Replace working set with the newly constructed one
     */
    state->wrksetsize = state->rbfmodel.curwrksetsize;
    rgrowrowsfixedcolsm(state->wrksetsize, n+m+cntnlc, &state->wrkset, _state);
    rcopym(state->wrksetsize, n+m+cntnlc, &state->rbfmodel.curwrkset, &state->wrkset, _state);
    
    /*
     * Done
     */
    result = ae_false;
    return result;
lbl_14:
    ae_assert(ae_false, "DFGM: ImproveWorkingSetGeometry() is unable to handle unexpected algorithm", _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstateimprove.ia.ptr.p_int[0] = n;
    state->rstateimprove.ia.ptr.p_int[1] = m;
    state->rstateimprove.ia.ptr.p_int[2] = cntlc;
    state->rstateimprove.ia.ptr.p_int[3] = cntnlc;
    state->rstateimprove.ia.ptr.p_int[4] = i;
    state->rstateimprove.ia.ptr.p_int[5] = j;
    state->rstateimprove.ia.ptr.p_int[6] = k;
    state->rstateimprove.ia.ptr.p_int[7] = improveidx;
    state->rstateimprove.ia.ptr.p_int[8] = improvedfar;
    state->rstateimprove.ia.ptr.p_int[9] = leastdistantidx;
    state->rstateimprove.ia.ptr.p_int[10] = mostdistantidx;
    state->rstateimprove.ia.ptr.p_int[11] = replaceidx;
    state->rstateimprove.ia.ptr.p_int[12] = roundidx;
    state->rstateimprove.ia.ptr.p_int[13] = chosenduringround;
    state->rstateimprove.ba.ptr.p_bool[0] = workingsetchanged;
    state->rstateimprove.ra.ptr.p_double[0] = vlft;
    state->rstateimprove.ra.ptr.p_double[1] = vrgt;
    state->rstateimprove.ra.ptr.p_double[2] = v;
    state->rstateimprove.ra.ptr.p_double[3] = vmin;
    state->rstateimprove.ra.ptr.p_double[4] = vmax;
    state->rstateimprove.ra.ptr.p_double[5] = x0i;
    state->rstateimprove.ra.ptr.p_double[6] = leastdistant;
    state->rstateimprove.ra.ptr.p_double[7] = mostdistant;
    state->rstateimprove.ra.ptr.p_double[8] = mx;
    state->rstateimprove.ra.ptr.p_double[9] = rating;
    state->rstateimprove.ra.ptr.p_double[10] = bestrating;
    state->rstateimprove.ra.ptr.p_double[11] = vv;
    state->rstateimprove.ra.ptr.p_double[12] = roundradius;
    return result;
}


/*************************************************************************
Compute maximum absolute value of  a  Lagrange  polynomial  given  by  its
coefficients vector C[] and value at the trust region center C0 over trust
region.

Returns maximum absolute value and, optionally, point  where  the  maximum
is obtained.

if NeedX is True, then X must be preallocated array[N], and tmpX must also
be preallocated array[N]. If NeedX is False, X and tmpX are not referenced.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static double dfgenmod_maxabslag(/* Real    */ const ae_vector* xk,
     ae_int_t n,
     /* Real    */ const ae_vector* trustregion,
     /* Real    */ const ae_vector* finitebndl,
     /* Real    */ const ae_vector* finitebndu,
     /* Real    */ const ae_vector* c,
     double c0,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* tmpx,
     ae_bool needx,
     ae_state *_state)
{
    double vmin;
    double vmax;
    double vlft;
    double vrgt;
    double xmin;
    double xmax;
    double xki;
    double ci;
    ae_int_t i;
    double result;


    ae_assert(!needx||x->cnt>=n, "DFGM: integrity check 5616 failed", _state);
    ae_assert(!needx||tmpx->cnt>=n, "DFGM: integrity check 5716 failed", _state);
    vmin = c0;
    vmax = c0;
    for(i=0; i<=n-1; i++)
    {
        xki = xk->ptr.p_double[i];
        ci = c->ptr.p_double[i];
        vlft = ae_maxreal(xki-trustregion->ptr.p_double[i], finitebndl->ptr.p_double[i], _state);
        vrgt = ae_minreal(xki+trustregion->ptr.p_double[i], finitebndu->ptr.p_double[i], _state);
        xmin = rcase2(ae_fp_greater_eq(ci,(double)(0)), vlft, vrgt, _state);
        xmax = rcase2(ae_fp_less_eq(ci,(double)(0)), vlft, vrgt, _state);
        vmin = vmin+(xmin-xki)*ci;
        vmax = vmax+(xmax-xki)*ci;
        if( needx )
        {
            x->ptr.p_double[i] = xmin;
            tmpx->ptr.p_double[i] = xmax;
        }
    }
    result = ae_maxreal(ae_fabs(vmin, _state), ae_fabs(vmax, _state), _state);
    if( needx&&ae_fp_less(ae_fabs(vmin, _state),ae_fabs(vmax, _state)) )
    {
        rcopyv(n, tmpx, x, _state);
    }
    return result;
}


/*************************************************************************
Compute distance to the most distant and the least distant neighbors in the
working set. The distance is computed using L-inf metric, per-variable
distances are divided by min(DeltaK,BndU[I]-BndL[I]).

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_mostandleastdistant(/* Real    */ const ae_vector* invtrustregion,
     ae_int_t n,
     /* Real    */ const ae_matrix* wrkset,
     ae_int_t wrksetsize,
     double* mostdistant,
     ae_int_t* mostdistantidx,
     double* leastdistant,
     ae_int_t* leastdistantidx,
     ae_state *_state)
{
    double mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t centralidx;

    *mostdistant = 0.0;
    *mostdistantidx = 0;
    *leastdistant = 0.0;
    *leastdistantidx = 0;

    
    /*
     * Find most distant and least distant neighbors, 
     */
    *mostdistant = 0.0;
    *mostdistantidx = -1;
    *leastdistant = 1.0E50;
    *leastdistantidx = -1;
    centralidx = wrksetsize-1;
    for(i=0; i<=wrksetsize-2; i++)
    {
        mx = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            mx = ae_maxreal(mx, ae_fabs(wrkset->ptr.pp_double[i][j]-wrkset->ptr.pp_double[centralidx][j], _state)*invtrustregion->ptr.p_double[j], _state);
        }
        if( *mostdistantidx<0||ae_fp_greater(mx,*mostdistant) )
        {
            *mostdistant = mx;
            *mostdistantidx = i;
        }
        if( *leastdistantidx<0||ae_fp_less(mx,*leastdistant) )
        {
            *leastdistant = mx;
            *leastdistantidx = i;
        }
    }
}


/*************************************************************************
This function initializes state of the points cloud: whether all evaluated
target values are saved to cloud or not.

  -- ALGLIB --
     Copyright 15.10.2024 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_initcloud(dfgmstate* state,
     ae_bool savetocloud,
     ae_state *_state)
{


    state->savepointstocloud = savetocloud;
    state->cloudsize = 0;
    state->cloudpendingcnt = 0;
}


/*************************************************************************
This function saves request information to the cloud; points locations are
saved in the special 'pending' queue, waiting to be augmented with request
reply (target values).

If the cloud is not used, it does nothing.

  -- ALGLIB --
     Copyright 15.10.2024 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_sendrequesttocloud(dfgmstate* state,
     /* Real    */ const ae_vector* querydata,
     ae_int_t requestsize,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    if( !state->savepointstocloud )
    {
        return;
    }
    rgrowrowsfixedcolsm(requestsize, state->n, &state->cloudpending, _state);
    for(i=0; i<=requestsize-1; i++)
    {
        for(j=0; j<=state->n-1; j++)
        {
            state->cloudpending.ptr.pp_double[i][j] = querydata->ptr.p_double[i*state->n+j];
        }
    }
    state->cloudpendingcnt = requestsize;
}


/*************************************************************************
This function sends to the cloud target values at the pending points (ones
previously posted with sendrequesttocloud). The amount of values sent now
must exactly match amount of previously posted values.

If cloud is not used, it does nothing.

  -- ALGLIB --
     Copyright 15.10.2024 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_sendreplytocloud(dfgmstate* state,
     /* Real    */ const ae_vector* replyfi,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t replyentrysize;


    if( !state->savepointstocloud )
    {
        return;
    }
    rgrowrowsfixedcolsm(state->cloudsize+state->cloudpendingcnt, state->n+state->m+state->cntnlc, &state->clouddata, _state);
    replyentrysize = state->m+state->cntnlc;
    for(i=0; i<=state->cloudpendingcnt-1; i++)
    {
        for(j=0; j<=state->n-1; j++)
        {
            state->clouddata.ptr.pp_double[state->cloudsize+i][j] = state->cloudpending.ptr.pp_double[i][j];
        }
        for(j=0; j<=replyentrysize-1; j++)
        {
            state->clouddata.ptr.pp_double[state->cloudsize+i][state->n+j] = replyfi->ptr.p_double[i*replyentrysize+j];
        }
    }
    state->cloudsize = state->cloudsize+state->cloudpendingcnt;
    state->cloudpendingcnt = 0;
}


/*************************************************************************
This function clears the points cloud.

  -- ALGLIB --
     Copyright 15.10.2024 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_clearcloud(dfgmstate* state, ae_state *_state)
{


    state->cloudsize = 0;
    state->cloudpendingcnt = 0;
}


/*************************************************************************
This function performs initial construction of an RBF model. It must be
called once at the initial working set generation.

  -- ALGLIB --
     Copyright 15.10.2024 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_rbfinitializemodelfromworkingset(dfgmstate* state,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t ws;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t fidx;
    double v;
    ae_matrix rbfsys;
    ae_vector sol;
    ae_vector rhs;
    ae_vector sol2;
    ae_matrix rrhs;
    ae_matrix ssol;

    ae_frame_make(_state, &_frame_block);
    memset(&rbfsys, 0, sizeof(rbfsys));
    memset(&sol, 0, sizeof(sol));
    memset(&rhs, 0, sizeof(rhs));
    memset(&sol2, 0, sizeof(sol2));
    memset(&rrhs, 0, sizeof(rrhs));
    memset(&ssol, 0, sizeof(ssol));
    ae_matrix_init(&rbfsys, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sol, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rhs, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sol2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rrhs, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ssol, 0, 0, DT_REAL, _state, ae_true);

    n = state->n;
    ws = state->wrksetsize;
    ae_assert(state->modeltype==2, "DFGM: integrity check 211136 failed", _state);
    ae_assert(ws>=n+1, "DFGM: integrity check 211137 failed", _state);
    ae_assert(state->m==1, "DFGM: integrity check 211138 failed", _state);
    rsetallocm(ws+n+1, ws+n+1, 0.0, &rbfsys, _state);
    for(i=0; i<=ws-1; i++)
    {
        for(j=0; j<=i; j++)
        {
            v = (double)(0);
            for(k=0; k<=n-1; k++)
            {
                v = v+ae_sqr((state->wrkset.ptr.pp_double[i][k]-state->wrkset.ptr.pp_double[j][k])*state->invtrustregion.ptr.p_double[k], _state);
            }
            v = v*ae_sqrt(v, _state);
            rbfsys.ptr.pp_double[i][j] = v;
            rbfsys.ptr.pp_double[j][i] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=ws-1; j++)
        {
            rbfsys.ptr.pp_double[ws+i][j] = (state->wrkset.ptr.pp_double[j][i]-state->wrkset.ptr.pp_double[ws-1][i])*state->invtrustregion.ptr.p_double[i];
            rbfsys.ptr.pp_double[j][ws+i] = rbfsys.ptr.pp_double[ws+i][j];
        }
    }
    for(j=0; j<=ws-1; j++)
    {
        rbfsys.ptr.pp_double[ws+n][j] = 1.0;
        rbfsys.ptr.pp_double[j][ws+n] = 1.0;
    }
    state->rbfmodel.modelbase = state->wrkset.ptr.pp_double[ws-1][n];
    v = (double)(0);
    for(i=0; i<=ws-1; i++)
    {
        v = v+ae_sqr(state->wrkset.ptr.pp_double[i][n]-state->rbfmodel.modelbase, _state);
    }
    state->rbfmodel.modelscale = ae_sqrt(coalesce(v, (double)(1), _state)/(double)ws, _state);
    state->rbfmodel.ncenters = ws;
    rgrowrowsfixedcolsm(ws, n, &state->rbfmodel.centers, _state);
    rcopym(ws, n, &state->wrkset, &state->rbfmodel.centers, _state);
    rallocv(n, &state->rbfmodel.x0, _state);
    rcopyrv(n, &state->wrkset, ws-1, &state->rbfmodel.x0, _state);
    rcopyallocv(n, &state->invtrustregion, &state->rbfmodel.multscale, _state);
    rsetallocm(1+state->cntnlc, ws+n+1, 0.0, &rrhs, _state);
    rallocm(1+state->cntnlc, ws, &state->rbfmodel.crbf, _state);
    rallocm(1+state->cntnlc, n+1, &state->rbfmodel.clinear, _state);
    for(fidx=0; fidx<=state->cntnlc; fidx++)
    {
        for(i=0; i<=ws-1; i++)
        {
            rrhs.ptr.pp_double[fidx][i] = (state->wrkset.ptr.pp_double[i][n+fidx]-state->rbfmodel.modelbase)/state->rbfmodel.modelscale;
        }
    }
    dfgenmod_rbfsolvecpdm(&rbfsys, &rrhs, ws, 1+state->cntnlc, n, 0.0, ae_true, &ssol, _state);
    rcopym(1+state->cntnlc, ws, &ssol, &state->rbfmodel.crbf, _state);
    for(fidx=0; fidx<=state->cntnlc; fidx++)
    {
        for(i=0; i<=n; i++)
        {
            state->rbfmodel.clinear.ptr.pp_double[fidx][i] = ssol.ptr.pp_double[fidx][ws+i];
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function computes RBF model at the required point. May return model
value and its gradient.

  -- ALGLIB --
     Copyright 15.10.2024 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_rbfcomputemodel(dfgmstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* f,
     ae_bool needf,
     /* Real    */ ae_vector* g,
     ae_bool needg,
     ae_state *_state)
{
    ae_int_t cntnlc;
    ae_int_t n;
    ae_int_t j;
    ae_int_t k;
    ae_int_t fidx;
    double v;
    double r;
    double vf;


    n = state->n;
    cntnlc = state->cntnlc;
    ae_assert(!needf||f->cnt>=1+state->cntnlc, "DFGM: integrity check 419111 failed", _state);
    ae_assert(!needg||g->cnt>=(1+state->cntnlc)*n, "DFGM: integrity check 419112 failed", _state);
    if( needf )
    {
        rsetv(1+cntnlc, 0.0, f, _state);
    }
    if( needg )
    {
        rsetv((1+state->cntnlc)*n, 0.0, g, _state);
    }
    for(fidx=0; fidx<=cntnlc; fidx++)
    {
        vf = (double)(0);
        for(j=0; j<=state->rbfmodel.ncenters-1; j++)
        {
            r = (double)(0);
            for(k=0; k<=n-1; k++)
            {
                v = (x->ptr.p_double[k]-state->rbfmodel.centers.ptr.pp_double[j][k])*state->rbfmodel.multscale.ptr.p_double[k];
                r = r+v*v;
            }
            r = ae_sqrt(r, _state);
            vf = vf+state->rbfmodel.crbf.ptr.pp_double[fidx][j]*(r*r*r);
            if( needg )
            {
                for(k=0; k<=n-1; k++)
                {
                    g->ptr.p_double[fidx*n+k] = g->ptr.p_double[fidx*n+k]+state->rbfmodel.crbf.ptr.pp_double[fidx][j]*(double)3*r*(x->ptr.p_double[k]-state->rbfmodel.centers.ptr.pp_double[j][k])*state->rbfmodel.multscale.ptr.p_double[k]*state->rbfmodel.multscale.ptr.p_double[k];
                }
            }
        }
        for(k=0; k<=n-1; k++)
        {
            vf = vf+state->rbfmodel.clinear.ptr.pp_double[fidx][k]*(x->ptr.p_double[k]-state->rbfmodel.x0.ptr.p_double[k])*state->rbfmodel.multscale.ptr.p_double[k];
            if( needg )
            {
                g->ptr.p_double[fidx*n+k] = g->ptr.p_double[fidx*n+k]+state->rbfmodel.clinear.ptr.pp_double[fidx][k]*state->rbfmodel.multscale.ptr.p_double[k];
            }
        }
        vf = vf+state->rbfmodel.clinear.ptr.pp_double[fidx][n];
        if( needf )
        {
            f->ptr.p_double[fidx] = vf*state->rbfmodel.modelscale+state->rbfmodel.modelbase;
        }
        if( needg )
        {
            rmulvx(n, state->rbfmodel.modelscale, g, fidx*n, _state);
        }
    }
}


/*************************************************************************
This function solves RBF  system using conditionally positive definiteness
if possible.

INPUT PARAMETERS:
    A           array[NCenters+NX,NCenters], basis function matrix and
                linear polynomial values
    B           array[NCenters], target values    
    NCenters    centers count
    NX          space dimensionality
    LambdaV     smoothing parameter, LambdaV>=0
    isCPD       whether basis is conditionally positive definite or not


  -- ALGLIB --
     Copyright 15.10.2024 by Bochkanov Sergey
*************************************************************************/
static void dfgenmod_rbfsolvecpdm(/* Real    */ const ae_matrix* a,
     /* Real    */ const ae_matrix* bb,
     ae_int_t ncenters,
     ae_int_t nrhs,
     ae_int_t nx,
     double lambdav,
     ae_bool iscpd,
     /* Real    */ ae_matrix* ssol,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t ncoeff;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double vv;
    double mx;
    double reg;
    ae_int_t ortbasissize;
    ae_matrix q;
    ae_matrix q1;
    ae_matrix r;
    ae_vector c;
    ae_vector y;
    ae_vector z;
    ae_vector ortbasismap;
    ae_vector choltmp;

    ae_frame_make(_state, &_frame_block);
    memset(&q, 0, sizeof(q));
    memset(&q1, 0, sizeof(q1));
    memset(&r, 0, sizeof(r));
    memset(&c, 0, sizeof(c));
    memset(&y, 0, sizeof(y));
    memset(&z, 0, sizeof(z));
    memset(&ortbasismap, 0, sizeof(ortbasismap));
    memset(&choltmp, 0, sizeof(choltmp));
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c, 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(&ortbasismap, 0, DT_INT, _state, ae_true);
    ae_vector_init(&choltmp, 0, DT_REAL, _state, ae_true);

    ae_assert(ae_fp_greater_eq(lambdav,(double)(0)), "DFGM: integrity check 854519 failed", _state);
    ae_assert(iscpd, "DFGM: integrity check 855520 failed", _state);
    ncoeff = ncenters+nx+1;
    reg = ae_sqrt(ae_machineepsilon, _state);
    
    /*
     * First, compute orthogonal basis of space spanned by polynomials of degree 1
     */
    rallocm(ncenters, ncenters, &r, _state);
    rallocm(nx+1, ncenters, &q1, _state);
    iallocv(nx+1, &ortbasismap, _state);
    rsetr(ncenters, (double)1/ae_sqrt((double)(ncenters), _state), &q1, 0, _state);
    r.ptr.pp_double[0][0] = ae_sqrt((double)(ncenters), _state);
    ortbasismap.ptr.p_int[0] = nx;
    ortbasissize = 1;
    rallocv(ncenters, &z, _state);
    for(k=0; k<=nx-1; k++)
    {
        for(j=0; j<=ncenters-1; j++)
        {
            z.ptr.p_double[j] = a->ptr.pp_double[ncenters+k][j];
        }
        v = ae_sqrt(rdotv2(ncenters, &z, _state), _state);
        rowwisegramschmidt(&q1, ortbasissize, ncenters, &z, &y, ae_true, _state);
        vv = ae_sqrt(rdotv2(ncenters, &z, _state), _state);
        if( ae_fp_greater(vv,ae_sqrt(ae_machineepsilon, _state)*(v+(double)1)) )
        {
            rcopymulvr(ncenters, (double)1/vv, &z, &q1, ortbasissize, _state);
            rcopyvc(ortbasissize, &y, &r, ortbasissize, _state);
            r.ptr.pp_double[ortbasissize][ortbasissize] = vv;
            ortbasismap.ptr.p_int[ortbasissize] = k;
            ortbasissize = ortbasissize+1;
        }
    }
    
    /*
     * Second, compute system matrix Q and target values for cardinal basis functions B.
     *
     * The Q is conditionally positive definite, i.e. x'*Q*x>0 for any x satisfying orthogonality conditions
     * (orthogonal with respect to basis stored in Q1).
     */
    rsetallocm(ncenters, ncenters, 0.0, &q, _state);
    for(i=0; i<=ncenters-1; i++)
    {
        rcopyrr(ncenters, a, i, &q, i, _state);
    }
    rallocm(nrhs, ncoeff, ssol, _state);
    rcopym(nrhs, ncenters, bb, ssol, _state);
    for(i=0; i<=ncenters-1; i++)
    {
        q.ptr.pp_double[i][i] = q.ptr.pp_double[i][i]+lambdav;
    }
    
    /*
     * Transform Q from conditionally positive definite to the (simply) positive definite one:
     * multiply linear system Q*x=RHS from both sides by (I-Q1'*Q1), apply additional regularization.
     *
     * NOTE: RHS is also multiplied by (I-Q1'*Q1), but from the left only.
     */
    rallocv(ncenters, &z, _state);
    for(i=0; i<=ncenters-1; i++)
    {
        rcopyrv(ncenters, &q, i, &z, _state);
        rowwisegramschmidt(&q1, ortbasissize, ncenters, &z, &y, ae_false, _state);
        rcopyvr(ncenters, &z, &q, i, _state);
    }
    for(i=0; i<=ncenters-1; i++)
    {
        rcopycv(ncenters, &q, i, &z, _state);
        rowwisegramschmidt(&q1, ortbasissize, ncenters, &z, &y, ae_false, _state);
        rcopyvc(ncenters, &z, &q, i, _state);
    }
    for(i=0; i<=nrhs-1; i++)
    {
        rcopyrv(ncenters, ssol, i, &z, _state);
        rowwisegramschmidt(&q1, ortbasissize, ncenters, &z, &y, ae_false, _state);
        rcopyvr(ncenters, &z, ssol, i, _state);
    }
    mx = 1.0;
    for(i=0; i<=ncenters-1; i++)
    {
        mx = ae_maxreal(mx, ae_fabs(q.ptr.pp_double[i][i], _state), _state);
    }
    for(i=0; i<=ncenters-1; i++)
    {
        rcopyrv(ncenters, &q, i, &z, _state);
        for(j=0; j<=ortbasissize-1; j++)
        {
            raddrv(ncenters, mx*q1.ptr.pp_double[j][i], &q1, j, &z, _state);
        }
        rcopyvr(ncenters, &z, &q, i, _state);
    }
    for(i=0; i<=ncenters-1; i++)
    {
        q.ptr.pp_double[i][i] = q.ptr.pp_double[i][i]+reg*mx;
    }
    
    /*
     * Perform Cholesky factorization, solve and obtain RBF coefficients (we still have
     * to compute polynomial term - it will be done later)
     */
    if( !spdmatrixcholeskyrec(&q, 0, ncenters, ae_false, &choltmp, _state) )
    {
        ae_assert(ae_false, "GENMOD: RBF solver failed due to extreme degeneracy", _state);
    }
    rmatrixrighttrsm(nrhs, ncenters, &q, 0, 0, ae_false, ae_false, 1, ssol, 0, 0, _state);
    rmatrixrighttrsm(nrhs, ncenters, &q, 0, 0, ae_false, ae_false, 0, ssol, 0, 0, _state);
    
    /*
     * Now, having RBF coefficients we can compute residual from fitting ACBF targets
     * with pure RBF term and fit polynomial term to this residual. In the ideal world
     * it should result in the nice and precise polynomial coefficients.
     */
    rallocv(ncenters, &z, _state);
    rallocv(ncenters, &c, _state);
    for(i=0; i<=nrhs-1; i++)
    {
        rcopyrv(ncenters, bb, i, &z, _state);
        rcopyrv(ncenters, ssol, i, &c, _state);
        rgemv(ncenters, ncenters, -1.0, a, 0, &c, 1.0, &z, _state);
        rowwisegramschmidt(&q1, ortbasissize, ncenters, &z, &y, ae_true, _state);
        rmatrixtrsv(ortbasissize, &r, 0, 0, ae_true, ae_false, 0, &y, 0, _state);
        for(j=0; j<=nx; j++)
        {
            ssol->ptr.pp_double[i][ncenters+j] = 0.0;
        }
        for(j=0; j<=ortbasissize-1; j++)
        {
            ssol->ptr.pp_double[i][ncenters+ortbasismap.ptr.p_int[j]] = y.ptr.p_double[j];
        }
    }
    ae_frame_leave(_state);
}


void _df2psmodel_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    df2psmodel *p = (df2psmodel*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->deltas, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->jac, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->q, 0, 0, DT_REAL, _state, make_automatic);
}


void _df2psmodel_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    df2psmodel       *dst = (df2psmodel*)_dst;
    const df2psmodel *src = (const df2psmodel*)_src;
    ae_vector_init_copy(&dst->deltas, &src->deltas, _state, make_automatic);
    ae_matrix_init_copy(&dst->jac, &src->jac, _state, make_automatic);
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    ae_matrix_init_copy(&dst->q, &src->q, _state, make_automatic);
}


void _df2psmodel_clear(void* _p)
{
    df2psmodel *p = (df2psmodel*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->deltas);
    ae_matrix_clear(&p->jac);
    ae_vector_clear(&p->g);
    ae_matrix_clear(&p->q);
}


void _df2psmodel_destroy(void* _p)
{
    df2psmodel *p = (df2psmodel*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->deltas);
    ae_matrix_destroy(&p->jac);
    ae_vector_destroy(&p->g);
    ae_matrix_destroy(&p->q);
}


void _dfolsamodel_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    dfolsamodel *p = (dfolsamodel*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_init(&p->w, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->invw, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->jac, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->q, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmprhs, 0, 0, DT_REAL, _state, make_automatic);
}


void _dfolsamodel_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    dfolsamodel       *dst = (dfolsamodel*)_dst;
    const dfolsamodel *src = (const dfolsamodel*)_src;
    ae_matrix_init_copy(&dst->w, &src->w, _state, make_automatic);
    ae_matrix_init_copy(&dst->invw, &src->invw, _state, make_automatic);
    ae_matrix_init_copy(&dst->jac, &src->jac, _state, make_automatic);
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    ae_matrix_init_copy(&dst->q, &src->q, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmprhs, &src->tmprhs, _state, make_automatic);
}


void _dfolsamodel_clear(void* _p)
{
    dfolsamodel *p = (dfolsamodel*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_clear(&p->w);
    ae_matrix_clear(&p->invw);
    ae_matrix_clear(&p->jac);
    ae_vector_clear(&p->g);
    ae_matrix_clear(&p->q);
    ae_vector_clear(&p->tmp0);
    ae_matrix_clear(&p->tmprhs);
}


void _dfolsamodel_destroy(void* _p)
{
    dfolsamodel *p = (dfolsamodel*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_destroy(&p->w);
    ae_matrix_destroy(&p->invw);
    ae_matrix_destroy(&p->jac);
    ae_vector_destroy(&p->g);
    ae_matrix_destroy(&p->q);
    ae_vector_destroy(&p->tmp0);
    ae_matrix_destroy(&p->tmprhs);
}


void _dforbfmodel_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    dforbfmodel *p = (dforbfmodel*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->multscale, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->centers, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->crbf, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->clinear, 0, 0, DT_REAL, _state, make_automatic);
    _minfsqpstate_init(&p->fsqpsolver, _state, make_automatic);
    ae_matrix_init(&p->currentcloud, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->distfromcenter, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->distfromchosen, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->available, 0, DT_BOOL, _state, make_automatic);
    ae_matrix_init(&p->curwrkset, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->residualdisplacements, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->q, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpcand, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpdisp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpwinner, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpx0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmps, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->cx, _state, make_automatic);
    ae_vector_init(&p->clx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cux, 0, DT_REAL, _state, make_automatic);
    _nlpstoppingcriteria_init(&p->crit, _state, make_automatic);
    _smoothnessmonitor_init(&p->smonitor, _state, make_automatic);
}


void _dforbfmodel_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    dforbfmodel       *dst = (dforbfmodel*)_dst;
    const dforbfmodel *src = (const dforbfmodel*)_src;
    dst->ncenters = src->ncenters;
    dst->modelbase = src->modelbase;
    dst->modelscale = src->modelscale;
    ae_vector_init_copy(&dst->multscale, &src->multscale, _state, make_automatic);
    ae_matrix_init_copy(&dst->centers, &src->centers, _state, make_automatic);
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_matrix_init_copy(&dst->crbf, &src->crbf, _state, make_automatic);
    ae_matrix_init_copy(&dst->clinear, &src->clinear, _state, make_automatic);
    _minfsqpstate_init_copy(&dst->fsqpsolver, &src->fsqpsolver, _state, make_automatic);
    ae_matrix_init_copy(&dst->currentcloud, &src->currentcloud, _state, make_automatic);
    dst->currentcloudsize = src->currentcloudsize;
    ae_vector_init_copy(&dst->distfromcenter, &src->distfromcenter, _state, make_automatic);
    ae_vector_init_copy(&dst->distfromchosen, &src->distfromchosen, _state, make_automatic);
    ae_vector_init_copy(&dst->available, &src->available, _state, make_automatic);
    ae_matrix_init_copy(&dst->curwrkset, &src->curwrkset, _state, make_automatic);
    dst->curwrksetsize = src->curwrksetsize;
    ae_matrix_init_copy(&dst->residualdisplacements, &src->residualdisplacements, _state, make_automatic);
    ae_matrix_init_copy(&dst->q, &src->q, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpcand, &src->tmpcand, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpdisp, &src->tmpdisp, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpwinner, &src->tmpwinner, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpbndl, &src->tmpbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpbndu, &src->tmpbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpx0, &src->tmpx0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmps, &src->tmps, _state, make_automatic);
    dst->winnerrating = src->winnerrating;
    _sparsematrix_init_copy(&dst->cx, &src->cx, _state, make_automatic);
    ae_vector_init_copy(&dst->clx, &src->clx, _state, make_automatic);
    ae_vector_init_copy(&dst->cux, &src->cux, _state, make_automatic);
    _nlpstoppingcriteria_init_copy(&dst->crit, &src->crit, _state, make_automatic);
    _smoothnessmonitor_init_copy(&dst->smonitor, &src->smonitor, _state, make_automatic);
}


void _dforbfmodel_clear(void* _p)
{
    dforbfmodel *p = (dforbfmodel*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->multscale);
    ae_matrix_clear(&p->centers);
    ae_vector_clear(&p->x0);
    ae_matrix_clear(&p->crbf);
    ae_matrix_clear(&p->clinear);
    _minfsqpstate_clear(&p->fsqpsolver);
    ae_matrix_clear(&p->currentcloud);
    ae_vector_clear(&p->distfromcenter);
    ae_vector_clear(&p->distfromchosen);
    ae_vector_clear(&p->available);
    ae_matrix_clear(&p->curwrkset);
    ae_matrix_clear(&p->residualdisplacements);
    ae_matrix_clear(&p->q);
    ae_vector_clear(&p->tmpcand);
    ae_vector_clear(&p->tmpdisp);
    ae_vector_clear(&p->tmpwinner);
    ae_vector_clear(&p->tmpbndl);
    ae_vector_clear(&p->tmpbndu);
    ae_vector_clear(&p->tmpx0);
    ae_vector_clear(&p->tmps);
    _sparsematrix_clear(&p->cx);
    ae_vector_clear(&p->clx);
    ae_vector_clear(&p->cux);
    _nlpstoppingcriteria_clear(&p->crit);
    _smoothnessmonitor_clear(&p->smonitor);
}


void _dforbfmodel_destroy(void* _p)
{
    dforbfmodel *p = (dforbfmodel*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->multscale);
    ae_matrix_destroy(&p->centers);
    ae_vector_destroy(&p->x0);
    ae_matrix_destroy(&p->crbf);
    ae_matrix_destroy(&p->clinear);
    _minfsqpstate_destroy(&p->fsqpsolver);
    ae_matrix_destroy(&p->currentcloud);
    ae_vector_destroy(&p->distfromcenter);
    ae_vector_destroy(&p->distfromchosen);
    ae_vector_destroy(&p->available);
    ae_matrix_destroy(&p->curwrkset);
    ae_matrix_destroy(&p->residualdisplacements);
    ae_matrix_destroy(&p->q);
    ae_vector_destroy(&p->tmpcand);
    ae_vector_destroy(&p->tmpdisp);
    ae_vector_destroy(&p->tmpwinner);
    ae_vector_destroy(&p->tmpbndl);
    ae_vector_destroy(&p->tmpbndu);
    ae_vector_destroy(&p->tmpx0);
    ae_vector_destroy(&p->tmps);
    _sparsematrix_destroy(&p->cx);
    ae_vector_destroy(&p->clx);
    ae_vector_destroy(&p->cux);
    _nlpstoppingcriteria_destroy(&p->crit);
    _smoothnessmonitor_destroy(&p->smonitor);
}


void _dfgmstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    dfgmstate *p = (dfgmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->scaledbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scaledbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitebndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitebndu, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->c, _state, make_automatic);
    ae_vector_init(&p->cl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->reportx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->querydata, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyfi, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    _rcommstate_init(&p->rstateimprove, _state, make_automatic);
    _rcommstate_init(&p->rstateupdate, _state, make_automatic);
    ae_vector_init(&p->trustregion, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->invtrustregion, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fpvec, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xk, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sk, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fkvec, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fnvec, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->wrkset, 0, 0, DT_REAL, _state, make_automatic);
    _hqrndstate_init(&p->rs, _state, make_automatic);
    _df2psmodel_init(&p->tpsmodel, _state, make_automatic);
    _dfolsamodel_init(&p->lsamodel, _state, make_automatic);
    _dforbfmodel_init(&p->rbfmodel, _state, make_automatic);
    _nlpfilter_init(&p->filter, _state, make_automatic);
    ae_matrix_init(&p->clouddata, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->cloudpending, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->successhistory, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->lastjac, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->trustradhistory, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->jacdiffhistory, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->iteridxhistory, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpi, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpdeltas, 0, DT_REAL, _state, make_automatic);
    _vipmstate_init(&p->ipmsolver, _state, make_automatic);
    ae_matrix_init(&p->densedummy, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparsedummy, _state, make_automatic);
    _matinvreport_init(&p->invrep, _state, make_automatic);
}


void _dfgmstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    dfgmstate       *dst = (dfgmstate*)_dst;
    const dfgmstate *src = (const dfgmstate*)_src;
    dst->n = src->n;
    dst->m = src->m;
    dst->cntlc = src->cntlc;
    dst->cntnlc = src->cntnlc;
    dst->isleastsquares = src->isleastsquares;
    dst->modeltype = src->modeltype;
    dst->rad0 = src->rad0;
    dst->nnoisyrestarts = src->nnoisyrestarts;
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledbndl, &src->scaledbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledbndu, &src->scaledbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndl, &src->finitebndl, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndu, &src->finitebndu, _state, make_automatic);
    _sparsematrix_init_copy(&dst->c, &src->c, _state, make_automatic);
    ae_vector_init_copy(&dst->cl, &src->cl, _state, make_automatic);
    ae_vector_init_copy(&dst->cu, &src->cu, _state, make_automatic);
    ae_vector_init_copy(&dst->nl, &src->nl, _state, make_automatic);
    ae_vector_init_copy(&dst->nu, &src->nu, _state, make_automatic);
    dst->epsx = src->epsx;
    dst->epsf = src->epsf;
    dst->maxits = src->maxits;
    dst->maxfev = src->maxfev;
    dst->toosmalltrustrad = src->toosmalltrustrad;
    dst->requesttype = src->requesttype;
    ae_vector_init_copy(&dst->reportx, &src->reportx, _state, make_automatic);
    dst->reportf = src->reportf;
    dst->querysize = src->querysize;
    ae_vector_init_copy(&dst->querydata, &src->querydata, _state, make_automatic);
    ae_vector_init_copy(&dst->replyfi, &src->replyfi, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstateimprove, &src->rstateimprove, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstateupdate, &src->rstateupdate, _state, make_automatic);
    dst->dotrace = src->dotrace;
    ae_vector_init_copy(&dst->trustregion, &src->trustregion, _state, make_automatic);
    ae_vector_init_copy(&dst->invtrustregion, &src->invtrustregion, _state, make_automatic);
    dst->trustradfactor = src->trustradfactor;
    dst->trustradbnd = src->trustradbnd;
    ae_vector_init_copy(&dst->xp, &src->xp, _state, make_automatic);
    ae_vector_init_copy(&dst->fpvec, &src->fpvec, _state, make_automatic);
    dst->fp = src->fp;
    dst->hp = src->hp;
    ae_vector_init_copy(&dst->xk, &src->xk, _state, make_automatic);
    ae_vector_init_copy(&dst->sk, &src->sk, _state, make_automatic);
    ae_vector_init_copy(&dst->xn, &src->xn, _state, make_automatic);
    ae_vector_init_copy(&dst->fkvec, &src->fkvec, _state, make_automatic);
    ae_vector_init_copy(&dst->fnvec, &src->fnvec, _state, make_automatic);
    dst->fk = src->fk;
    dst->hk = src->hk;
    ae_matrix_init_copy(&dst->wrkset, &src->wrkset, _state, make_automatic);
    dst->wrksetsize = src->wrksetsize;
    _hqrndstate_init_copy(&dst->rs, &src->rs, _state, make_automatic);
    _df2psmodel_init_copy(&dst->tpsmodel, &src->tpsmodel, _state, make_automatic);
    _dfolsamodel_init_copy(&dst->lsamodel, &src->lsamodel, _state, make_automatic);
    _dforbfmodel_init_copy(&dst->rbfmodel, &src->rbfmodel, _state, make_automatic);
    dst->infinitiesencountered = src->infinitiesencountered;
    dst->recoveredfrominfinities = src->recoveredfrominfinities;
    _nlpfilter_init_copy(&dst->filter, &src->filter, _state, make_automatic);
    dst->savepointstocloud = src->savepointstocloud;
    ae_matrix_init_copy(&dst->clouddata, &src->clouddata, _state, make_automatic);
    dst->cloudsize = src->cloudsize;
    ae_matrix_init_copy(&dst->cloudpending, &src->cloudpending, _state, make_automatic);
    dst->cloudpendingcnt = src->cloudpendingcnt;
    dst->successhistorylen = src->successhistorylen;
    dst->successhistorymax = src->successhistorymax;
    dst->successhistorynext = src->successhistorynext;
    ae_vector_init_copy(&dst->successhistory, &src->successhistory, _state, make_automatic);
    dst->restartstoperform = src->restartstoperform;
    dst->lasttrustrad = src->lasttrustrad;
    ae_matrix_init_copy(&dst->lastjac, &src->lastjac, _state, make_automatic);
    ae_vector_init_copy(&dst->trustradhistory, &src->trustradhistory, _state, make_automatic);
    ae_vector_init_copy(&dst->jacdiffhistory, &src->jacdiffhistory, _state, make_automatic);
    ae_vector_init_copy(&dst->iteridxhistory, &src->iteridxhistory, _state, make_automatic);
    dst->historylen = src->historylen;
    dst->historynext = src->historynext;
    ae_vector_init_copy(&dst->tmpi, &src->tmpi, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp2, &src->tmp2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpdeltas, &src->tmpdeltas, _state, make_automatic);
    _vipmstate_init_copy(&dst->ipmsolver, &src->ipmsolver, _state, make_automatic);
    ae_matrix_init_copy(&dst->densedummy, &src->densedummy, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsedummy, &src->sparsedummy, _state, make_automatic);
    _matinvreport_init_copy(&dst->invrep, &src->invrep, _state, make_automatic);
    dst->repterminationtype = src->repterminationtype;
    dst->repiterationscount = src->repiterationscount;
    dst->repnfev = src->repnfev;
    dst->repsubsolverits = src->repsubsolverits;
}


void _dfgmstate_clear(void* _p)
{
    dfgmstate *p = (dfgmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->scaledbndl);
    ae_vector_clear(&p->scaledbndu);
    ae_vector_clear(&p->finitebndl);
    ae_vector_clear(&p->finitebndu);
    _sparsematrix_clear(&p->c);
    ae_vector_clear(&p->cl);
    ae_vector_clear(&p->cu);
    ae_vector_clear(&p->nl);
    ae_vector_clear(&p->nu);
    ae_vector_clear(&p->reportx);
    ae_vector_clear(&p->querydata);
    ae_vector_clear(&p->replyfi);
    _rcommstate_clear(&p->rstate);
    _rcommstate_clear(&p->rstateimprove);
    _rcommstate_clear(&p->rstateupdate);
    ae_vector_clear(&p->trustregion);
    ae_vector_clear(&p->invtrustregion);
    ae_vector_clear(&p->xp);
    ae_vector_clear(&p->fpvec);
    ae_vector_clear(&p->xk);
    ae_vector_clear(&p->sk);
    ae_vector_clear(&p->xn);
    ae_vector_clear(&p->fkvec);
    ae_vector_clear(&p->fnvec);
    ae_matrix_clear(&p->wrkset);
    _hqrndstate_clear(&p->rs);
    _df2psmodel_clear(&p->tpsmodel);
    _dfolsamodel_clear(&p->lsamodel);
    _dforbfmodel_clear(&p->rbfmodel);
    _nlpfilter_clear(&p->filter);
    ae_matrix_clear(&p->clouddata);
    ae_matrix_clear(&p->cloudpending);
    ae_vector_clear(&p->successhistory);
    ae_matrix_clear(&p->lastjac);
    ae_vector_clear(&p->trustradhistory);
    ae_vector_clear(&p->jacdiffhistory);
    ae_vector_clear(&p->iteridxhistory);
    ae_vector_clear(&p->tmpi);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmp1);
    ae_vector_clear(&p->tmp2);
    ae_vector_clear(&p->tmpdeltas);
    _vipmstate_clear(&p->ipmsolver);
    ae_matrix_clear(&p->densedummy);
    _sparsematrix_clear(&p->sparsedummy);
    _matinvreport_clear(&p->invrep);
}


void _dfgmstate_destroy(void* _p)
{
    dfgmstate *p = (dfgmstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->scaledbndl);
    ae_vector_destroy(&p->scaledbndu);
    ae_vector_destroy(&p->finitebndl);
    ae_vector_destroy(&p->finitebndu);
    _sparsematrix_destroy(&p->c);
    ae_vector_destroy(&p->cl);
    ae_vector_destroy(&p->cu);
    ae_vector_destroy(&p->nl);
    ae_vector_destroy(&p->nu);
    ae_vector_destroy(&p->reportx);
    ae_vector_destroy(&p->querydata);
    ae_vector_destroy(&p->replyfi);
    _rcommstate_destroy(&p->rstate);
    _rcommstate_destroy(&p->rstateimprove);
    _rcommstate_destroy(&p->rstateupdate);
    ae_vector_destroy(&p->trustregion);
    ae_vector_destroy(&p->invtrustregion);
    ae_vector_destroy(&p->xp);
    ae_vector_destroy(&p->fpvec);
    ae_vector_destroy(&p->xk);
    ae_vector_destroy(&p->sk);
    ae_vector_destroy(&p->xn);
    ae_vector_destroy(&p->fkvec);
    ae_vector_destroy(&p->fnvec);
    ae_matrix_destroy(&p->wrkset);
    _hqrndstate_destroy(&p->rs);
    _df2psmodel_destroy(&p->tpsmodel);
    _dfolsamodel_destroy(&p->lsamodel);
    _dforbfmodel_destroy(&p->rbfmodel);
    _nlpfilter_destroy(&p->filter);
    ae_matrix_destroy(&p->clouddata);
    ae_matrix_destroy(&p->cloudpending);
    ae_vector_destroy(&p->successhistory);
    ae_matrix_destroy(&p->lastjac);
    ae_vector_destroy(&p->trustradhistory);
    ae_vector_destroy(&p->jacdiffhistory);
    ae_vector_destroy(&p->iteridxhistory);
    ae_vector_destroy(&p->tmpi);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmp1);
    ae_vector_destroy(&p->tmp2);
    ae_vector_destroy(&p->tmpdeltas);
    _vipmstate_destroy(&p->ipmsolver);
    ae_matrix_destroy(&p->densedummy);
    _sparsematrix_destroy(&p->sparsedummy);
    _matinvreport_destroy(&p->invrep);
}


#endif
#if defined(AE_COMPILE_NLCSQP) || !defined(AE_PARTIAL_BUILD)


void minsqpinitbuf(/* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* x0,
     ae_int_t n,
     /* Real    */ const ae_matrix* cleic,
     /* Integer */ const ae_vector* lcsrcidx,
     ae_int_t nec,
     ae_int_t nic,
     ae_int_t nlec,
     ae_int_t nlic,
     const nlpstoppingcriteria* criteria,
     ae_bool usedensebfgs,
     minsqpstate* state,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;
    double vv;
    ae_int_t nslack;


    nslack = n+2*(nec+nlec)+(nic+nlic);
    state->n = n;
    state->nec = nec;
    state->nic = nic;
    state->nlec = nlec;
    state->nlic = nlic;
    
    /*
     * Prepare RCOMM state
     */
    ae_vector_set_length(&state->rstate.ia, 9+1, _state);
    ae_vector_set_length(&state->rstate.ba, 14+1, _state);
    ae_vector_set_length(&state->rstate.ra, 23+1, _state);
    state->rstate.stage = -1;
    state->needfij = ae_false;
    state->xupdated = ae_false;
    ae_vector_set_length(&state->x, n, _state);
    ae_vector_set_length(&state->fi, 1+nlec+nlic, _state);
    ae_matrix_set_length(&state->j, 1+nlec+nlic, n, _state);
    
    /*
     * Allocate memory.
     */
    rallocv(n, &state->x0, _state);
    rallocv(n, &state->xprev, _state);
    rvectorsetlengthatleast(&state->s, n, _state);
    rvectorsetlengthatleast(&state->fscales, 1+nlec+nlic, _state);
    rvectorsetlengthatleast(&state->tracegamma, 1+nlec+nlic, _state);
    bvectorsetlengthatleast(&state->hasbndl, n, _state);
    bvectorsetlengthatleast(&state->hasbndu, n, _state);
    rvectorsetlengthatleast(&state->scaledbndl, n, _state);
    rvectorsetlengthatleast(&state->scaledbndu, n, _state);
    rmatrixsetlengthatleast(&state->scaledcleic, nec+nic, n+1, _state);
    ivectorsetlengthatleast(&state->lcsrcidx, nec+nic, _state);
    rvectorsetlengthatleast(&state->dtrial, nslack, _state);
    rvectorsetlengthatleast(&state->d0, nslack, _state);
    rvectorsetlengthatleast(&state->d1, nslack, _state);
    rvectorsetlengthatleast(&state->dmu, nslack, _state);
    rvectorsetlengthatleast(&state->lagbcmult, n, _state);
    rvectorsetlengthatleast(&state->dummylagbcmult, n, _state);
    rvectorsetlengthatleast(&state->lagxcmult, nec+nic+nlec+nlic, _state);
    rvectorsetlengthatleast(&state->dummylagxcmult, nec+nic+nlec+nlic, _state);
    
    /*
     * Prepare scaled problem
     */
    for(i=0; i<=n-1; i++)
    {
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
        if( state->hasbndl.ptr.p_bool[i] )
        {
            state->scaledbndl.ptr.p_double[i] = bndl->ptr.p_double[i]/s->ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i] )
        {
            state->scaledbndu.ptr.p_double[i] = bndu->ptr.p_double[i]/s->ptr.p_double[i];
        }
        if( state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i] )
        {
            ae_assert(ae_fp_less_eq(bndl->ptr.p_double[i],bndu->ptr.p_double[i]), "SQP: integrity check failed, box constraints are inconsistent", _state);
        }
        state->x0.ptr.p_double[i] = x0->ptr.p_double[i]/s->ptr.p_double[i];
        state->s.ptr.p_double[i] = s->ptr.p_double[i];
    }
    for(i=0; i<=nec+nic-1; i++)
    {
        
        /*
         * Permutation
         */
        state->lcsrcidx.ptr.p_int[i] = lcsrcidx->ptr.p_int[i];
        
        /*
         * Scale and normalize linear constraints
         */
        vv = 0.0;
        for(j=0; j<=n-1; j++)
        {
            v = cleic->ptr.pp_double[i][j]*s->ptr.p_double[j];
            state->scaledcleic.ptr.pp_double[i][j] = v;
            vv = vv+v*v;
        }
        vv = ae_sqrt(vv, _state);
        state->scaledcleic.ptr.pp_double[i][n] = cleic->ptr.pp_double[i][n];
        if( ae_fp_greater(vv,(double)(0)) )
        {
            for(j=0; j<=n; j++)
            {
                state->scaledcleic.ptr.pp_double[i][j] = state->scaledcleic.ptr.pp_double[i][j]/vv;
            }
        }
    }
    
    /*
     * Initial enforcement of box constraints
     */
    for(i=0; i<=n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i] )
        {
            state->x0.ptr.p_double[i] = ae_maxreal(state->x0.ptr.p_double[i], state->scaledbndl.ptr.p_double[i], _state);
        }
        if( state->hasbndu.ptr.p_bool[i] )
        {
            state->x0.ptr.p_double[i] = ae_minreal(state->x0.ptr.p_double[i], state->scaledbndu.ptr.p_double[i], _state);
        }
    }
    
    /*
     * Stopping criteria and settings
     */
    critcopy(criteria, &state->criteria, _state);
    state->bfgsresetfreq = nlcsqp_defaultbfgsresetfreq;
    state->usedensebfgs = usedensebfgs;
    
    /*
     * Report fields
     */
    state->repterminationtype = 0;
    state->repbcerr = (double)(0);
    state->repbcidx = -1;
    state->replcerr = (double)(0);
    state->replcidx = -1;
    state->repnlcerr = (double)(0);
    state->repnlcidx = -1;
    state->repiterationscount = 0;
    
    /*
     * Integrity checks
     */
    ae_assert(ae_fp_less(nlcsqp_sqpdeltadecrease,nlcsqp_sqpdeltaincrease), "MinSQP: integrity check failed", _state);
}


/*************************************************************************
This function performs actual processing for  SQP  algorithm.  It  expects
that caller redirects its reverse communication  requests NeedFiJ/XUpdated
to external user who will provide analytic derivative (or  handle  reports
about progress).

In case external user does not have analytic derivative, it is responsibility
of caller to intercept NeedFiJ request and  replace  it  with  appropriate
numerical differentiation scheme.

Results are stored:
* point - in State.StepKX

IMPORTANT: this function works with scaled problem formulation; it is
           responsibility of the caller to unscale request and scale
           Jacobian.
           
NOTE: SMonitor is expected to be correctly initialized smoothness monitor.

  -- ALGLIB --
     Copyright 05.03.2018 by Bochkanov Sergey
*************************************************************************/
ae_bool minsqpiteration(minsqpstate* state,
     smoothnessmonitor* smonitor,
     ae_bool userterminationneeded,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nslack;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t nlec;
    ae_int_t nlic;
    ae_int_t i;
    ae_int_t j;
    double v;
    double vv;
    double mx;
    double deltamax;
    double multiplyby;
    double setscaleto;
    double prevtrustrad;
    ae_int_t subiterationidx;
    ae_bool trustradstagnated;
    double relativetargetdecrease;
    ae_bool dotrace;
    ae_bool doprobingalways;
    ae_bool doprobingonfailure;
    ae_bool dodetailedtrace;
    ae_bool increasemeritmu;
    ae_bool meritfstagnated;
    double f0;
    double f0raw;
    double f1;
    double tol;
    double stepklagval;
    double stepknlagval;
    double expandedrad;
    ae_bool socperformed;
    double d2trustradratio;
    ae_int_t didx;
    double sksk;
    double ykyk;
    double skyk;
    double sumc1;
    double sumc1soc;
    double predictedchange;
    double predictedchangemodel;
    double predictedchangepenalty;
    ae_bool meritdecreasefailed;
    ae_bool stepperformed;
    ae_bool goodstep;
    ae_bool firstrescale;
    ae_bool infinitiesdetected;
    ae_bool failedtorecoverfrominfinities;
    ae_bool needsoc;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        n = state->rstate.ia.ptr.p_int[0];
        nslack = state->rstate.ia.ptr.p_int[1];
        nec = state->rstate.ia.ptr.p_int[2];
        nic = state->rstate.ia.ptr.p_int[3];
        nlec = state->rstate.ia.ptr.p_int[4];
        nlic = state->rstate.ia.ptr.p_int[5];
        i = state->rstate.ia.ptr.p_int[6];
        j = state->rstate.ia.ptr.p_int[7];
        subiterationidx = state->rstate.ia.ptr.p_int[8];
        didx = state->rstate.ia.ptr.p_int[9];
        trustradstagnated = state->rstate.ba.ptr.p_bool[0];
        dotrace = state->rstate.ba.ptr.p_bool[1];
        doprobingalways = state->rstate.ba.ptr.p_bool[2];
        doprobingonfailure = state->rstate.ba.ptr.p_bool[3];
        dodetailedtrace = state->rstate.ba.ptr.p_bool[4];
        increasemeritmu = state->rstate.ba.ptr.p_bool[5];
        meritfstagnated = state->rstate.ba.ptr.p_bool[6];
        socperformed = state->rstate.ba.ptr.p_bool[7];
        meritdecreasefailed = state->rstate.ba.ptr.p_bool[8];
        stepperformed = state->rstate.ba.ptr.p_bool[9];
        goodstep = state->rstate.ba.ptr.p_bool[10];
        firstrescale = state->rstate.ba.ptr.p_bool[11];
        infinitiesdetected = state->rstate.ba.ptr.p_bool[12];
        failedtorecoverfrominfinities = state->rstate.ba.ptr.p_bool[13];
        needsoc = state->rstate.ba.ptr.p_bool[14];
        v = state->rstate.ra.ptr.p_double[0];
        vv = state->rstate.ra.ptr.p_double[1];
        mx = state->rstate.ra.ptr.p_double[2];
        deltamax = state->rstate.ra.ptr.p_double[3];
        multiplyby = state->rstate.ra.ptr.p_double[4];
        setscaleto = state->rstate.ra.ptr.p_double[5];
        prevtrustrad = state->rstate.ra.ptr.p_double[6];
        relativetargetdecrease = state->rstate.ra.ptr.p_double[7];
        f0 = state->rstate.ra.ptr.p_double[8];
        f0raw = state->rstate.ra.ptr.p_double[9];
        f1 = state->rstate.ra.ptr.p_double[10];
        tol = state->rstate.ra.ptr.p_double[11];
        stepklagval = state->rstate.ra.ptr.p_double[12];
        stepknlagval = state->rstate.ra.ptr.p_double[13];
        expandedrad = state->rstate.ra.ptr.p_double[14];
        d2trustradratio = state->rstate.ra.ptr.p_double[15];
        sksk = state->rstate.ra.ptr.p_double[16];
        ykyk = state->rstate.ra.ptr.p_double[17];
        skyk = state->rstate.ra.ptr.p_double[18];
        sumc1 = state->rstate.ra.ptr.p_double[19];
        sumc1soc = state->rstate.ra.ptr.p_double[20];
        predictedchange = state->rstate.ra.ptr.p_double[21];
        predictedchangemodel = state->rstate.ra.ptr.p_double[22];
        predictedchangepenalty = state->rstate.ra.ptr.p_double[23];
    }
    else
    {
        n = 359;
        nslack = -58;
        nec = -919;
        nic = -909;
        nlec = 81;
        nlic = 255;
        i = 74;
        j = -788;
        subiterationidx = 809;
        didx = 205;
        trustradstagnated = ae_false;
        dotrace = ae_true;
        doprobingalways = ae_false;
        doprobingonfailure = ae_true;
        dodetailedtrace = ae_true;
        increasemeritmu = ae_false;
        meritfstagnated = ae_false;
        socperformed = ae_false;
        meritdecreasefailed = ae_false;
        stepperformed = ae_false;
        goodstep = ae_true;
        firstrescale = ae_false;
        infinitiesdetected = ae_true;
        failedtorecoverfrominfinities = ae_true;
        needsoc = ae_false;
        v = 346.0;
        vv = -722.0;
        mx = -413.0;
        deltamax = -461.0;
        multiplyby = 927.0;
        setscaleto = 201.0;
        prevtrustrad = 922.0;
        relativetargetdecrease = -154.0;
        f0 = 306.0;
        f0raw = -1011.0;
        f1 = 951.0;
        tol = -463.0;
        stepklagval = 88.0;
        stepknlagval = -861.0;
        expandedrad = -678.0;
        d2trustradratio = -731.0;
        sksk = -675.0;
        ykyk = -763.0;
        skyk = -233.0;
        sumc1 = -936.0;
        sumc1soc = -279.0;
        predictedchange = 94.0;
        predictedchangemodel = -812.0;
        predictedchangepenalty = 427.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    
    /*
     * Routine body
     */
    n = state->n;
    nec = state->nec;
    nic = state->nic;
    nlec = state->nlec;
    nlic = state->nlic;
    nslack = n+2*(nec+nlec)+(nic+nlic);
    dotrace = ae_is_trace_enabled("SQP")&&!ae_is_trace_enabled("SQP.LACONIC");
    dodetailedtrace = dotrace&&ae_is_trace_enabled("SQP.DETAILED");
    doprobingalways = dotrace&&ae_is_trace_enabled("SQP.PROBING");
    doprobingonfailure = dotrace&&ae_is_trace_enabled("SQP.PROBINGONFAILURE");
    
    /*
     * Prepare rcomm interface
     */
    state->needfij = ae_false;
    state->xupdated = ae_false;
    
    /*
     * Prepare debug timers
     */
    stimerinit(&state->timertotal, _state);
    stimerinit(&state->timerqp, _state);
    stimerinit(&state->timercallback, _state);
    stimerstart(&state->timertotal, _state);
    
    /*
     * Initialize algorithm data:
     * * Lagrangian and "Big C" estimates
     * * trust region
     * * initial function scales (vector of 1's)
     * * current approximation of the Hessian matrix H (unit matrix)
     * * initial linearized constraints
     * * initial violation of linear/nonlinear constraints
     */
    state->fstagnationcnt = 0;
    state->trustradstagnationcnt = 0;
    state->trustrad = nlcsqp_inittrustrad;
    state->trustradgrowth = nlcsqp_deftrustradgrowth;
    for(i=0; i<=nlec+nlic; i++)
    {
        state->fscales.ptr.p_double[i] = 1.0;
        state->tracegamma.ptr.p_double[i] = 0.0;
    }
    ae_obj_array_clear(&state->nonmonotonicmem);
    state->nonmonotoniccnt = 0;
    
    /*
     * Evaluate function vector and Jacobian at X0, send first location report.
     * Compute initial violation of constraints.
     */
    vfjallocdense(n, 1+nlec+nlic, &state->stepk, _state);
    vfjallocdense(n, 1+nlec+nlic, &state->cand, _state);
    vfjallocdense(n, 1+nlec+nlic, &state->corr, _state);
    vfjallocdense(n, 1+nlec+nlic, &state->probe, _state);
    vfjallocdense(n, 1+nlec+nlic, &state->currentlinearization, _state);
    rcopyv(n, &state->x0, &state->stepk.x, _state);
    nlcsqp_sqpsendx(state, &state->stepk.x, _state);
    state->needfij = ae_true;
    if( dotrace )
    {
        stimerstart(&state->timercallback, _state);
    }
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    if( dotrace )
    {
        stimerstop(&state->timercallback, _state);
    }
    state->needfij = ae_false;
    if( !nlcsqp_sqpretrievefij(state, &state->stepk, _state) )
    {
        
        /*
         * Failed to retrieve function/Jacobian, infinities detected!
         */
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    nlcsqp_sqpsendx(state, &state->stepk.x, _state);
    state->f = state->stepk.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0];
    state->xupdated = ae_true;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->xupdated = ae_false;
    checklcviolation(&state->scaledcleic, &state->lcsrcidx, nec, nic, &state->stepk.x, n, &state->replcerr, &state->replcidx, _state);
    unscaleandchecknlcviolation(&state->stepk.fi, &state->fscales, nlec, nlic, &state->repnlcerr, &state->repnlcidx, _state);
    
    /*
     * Trace output (if needed)
     */
    if( dotrace )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("//  SQP SOLVER STARTED                                                                            //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
    }
    
    /*
     * Perform outer (NLC) iterations
     */
    firstrescale = ae_true;
    infinitiesdetected = ae_false;
    failedtorecoverfrominfinities = ae_false;
    relativetargetdecrease = 1.0E50;
    state->meritmu = (double)(500);
    if( state->usedensebfgs )
    {
        hessianinitbfgs(&state->hess, n, state->bfgsresetfreq, (double)10*ae_maxreal(critgeteps(&state->criteria, _state), ae_sqrt(ae_machineepsilon, _state), _state), _state);
    }
    else
    {
        hessianinitlowrank(&state->hess, n, ae_minint(n, nlcsqp_xbfgsmemlen, _state), (double)10*ae_maxreal(critgeteps(&state->criteria, _state), ae_sqrt(ae_machineepsilon, _state), _state), nlcsqp_xbfgsmaxhess, _state);
    }
    nlcsqp_initqpsubsolver(state, &state->subsolver, _state);
lbl_6:
    if( ae_false )
    {
        goto lbl_7;
    }
    
    /*
     * Before beginning new outer iteration:
     * * check stopping criteria
     * * renormalize target function and/or constraints, if some of them have too large magnitudes
     * * save initial point for the outer iteration
     */
    if( ae_fp_less_eq(relativetargetdecrease,critgetepsf(&state->criteria, _state)) )
    {
        state->repterminationtype = 1;
        if( dotrace )
        {
            ae_trace("> stopping condition met: relative change in target is less than %0.3e\n",
                (double)(critgetepsf(&state->criteria, _state)));
        }
        goto lbl_7;
    }
    if( ae_fp_less_eq(state->trustrad,critgeteps(&state->criteria, _state)) )
    {
        state->repterminationtype = 2;
        if( dotrace )
        {
            ae_trace("> stopping condition met: trust radius is smaller than %0.3e\n",
                (double)(critgeteps(&state->criteria, _state)));
        }
        goto lbl_7;
    }
    v = ae_sqrt(ae_machineepsilon, _state);
    if( ae_fp_less_eq(state->trustrad,v) )
    {
        state->repterminationtype = 7;
        if( dotrace )
        {
            ae_trace("> stopping condition met: trust radius is smaller than the absolute minimum %0.3e\n",
                (double)(v));
        }
        goto lbl_7;
    }
    if( critgetmaxits(&state->criteria, _state)>0&&state->repiterationscount>=critgetmaxits(&state->criteria, _state) )
    {
        state->repterminationtype = 5;
        if( dotrace )
        {
            ae_trace("> stopping condition met: %0d iterations performed\n",
                (int)(state->repiterationscount));
        }
        goto lbl_7;
    }
    if( state->fstagnationcnt>=nlcsqp_fstagnationlimit )
    {
        state->repterminationtype = 7;
        if( dotrace )
        {
            ae_trace("> stopping criteria are too stringent: F stagnated for %0d its, stopping\n",
                (int)(state->fstagnationcnt));
        }
        goto lbl_7;
    }
    for(i=0; i<=nlec+nlic; i++)
    {
        
        /*
         * Determine (a) multiplicative coefficient applied to function value
         * and Jacobian row, and (b) new value of the function scale.
         */
        mx = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            mx = ae_maxreal(mx, ae_fabs(state->stepk.jac.ptr.pp_double[i][j], _state), _state);
        }
        multiplyby = 1.0;
        setscaleto = state->fscales.ptr.p_double[i];
        if( ae_fp_greater_eq(mx,nlcsqp_sqpbigscale) )
        {
            v = mx;
            if( !firstrescale )
            {
                v = ae_minreal(v, nlcsqp_sqpmaxrescale, _state);
            }
            multiplyby = (double)1/v;
            setscaleto = state->fscales.ptr.p_double[i]*v;
        }
        if( ae_fp_less_eq(mx,nlcsqp_sqpsmallscale)&&ae_fp_greater(state->fscales.ptr.p_double[i],1.0) )
        {
            v = mx;
            if( !firstrescale )
            {
                v = ae_maxreal(v, nlcsqp_sqpminrescale, _state);
            }
            if( ae_fp_greater(state->fscales.ptr.p_double[i]*v,(double)(1)) )
            {
                multiplyby = (double)1/v;
                setscaleto = state->fscales.ptr.p_double[i]*v;
            }
            else
            {
                multiplyby = state->fscales.ptr.p_double[i];
                setscaleto = 1.0;
            }
        }
        if( ae_fp_neq(multiplyby,1.0) )
        {
            
            /*
             * Function #I needs renormalization:
             * * update function vector element and Jacobian matrix row
             * * update FScales[] and TraceGamma[] arrays
             */
            state->stepk.fi.ptr.p_double[i] = state->stepk.fi.ptr.p_double[i]*multiplyby;
            for(j=0; j<=n-1; j++)
            {
                state->stepk.jac.ptr.pp_double[i][j] = state->stepk.jac.ptr.pp_double[i][j]*multiplyby;
            }
            state->fscales.ptr.p_double[i] = setscaleto;
            state->tracegamma.ptr.p_double[i] = state->tracegamma.ptr.p_double[i]*multiplyby;
            nlcsqp_nonmonotonicmultiplyby(state, i, multiplyby, _state);
        }
    }
    firstrescale = ae_false;
    
    /*
     * Trace output (if needed)
     */
    if( dotrace )
    {
        ae_trace("\n=== OUTER ITERATION %5d STARTED ==================================================================\n",
            (int)(state->repiterationscount));
        if( dodetailedtrace )
        {
            ae_trace("> printing raw data (prior to applying variable and function scales)\n");
            ae_trace("X (raw)       = ");
            tracevectorunscaledunshiftedautoprec(&state->stepk.x, n, &state->s, ae_true, &state->s, ae_false, _state);
            ae_trace("\n");
            ae_trace("> printing scaled data (after applying variable and function scales)\n");
            ae_trace("X (scaled)    = ");
            tracevectorautoprec(&state->stepk.x, 0, n, _state);
            ae_trace("\n");
            ae_trace("FScales       = ");
            tracevectorautoprec(&state->fscales, 0, 1+nlec+nlic, _state);
            ae_trace("\n");
            ae_trace("GammaScl      = ");
            tracevectorautoprec(&state->tracegamma, 0, 1+nlec+nlic, _state);
            ae_trace("\n");
            ae_trace("Fi (scaled)   = ");
            tracevectorautoprec(&state->stepk.fi, 0, 1+nlec+nlic, _state);
            ae_trace("\n");
            ae_trace("|Ji| (scaled) = ");
            tracerownrm1autoprec(&state->stepk.jac, 0, 1+nlec+nlic, 0, n, _state);
            ae_trace("\n");
        }
        mx = (double)(0);
        for(i=1; i<=nlec; i++)
        {
            mx = ae_maxreal(mx, ae_fabs(state->stepk.fi.ptr.p_double[i], _state), _state);
        }
        for(i=nlec+1; i<=nlec+nlic; i++)
        {
            mx = ae_maxreal(mx, state->stepk.fi.ptr.p_double[i], _state);
        }
        ae_trace("trustRad      = %0.3e\n",
            (double)(state->trustrad));
        ae_trace("meritMu       = %0.3e    (penalty for violation of constraints)\n",
            (double)(state->meritmu));
        ae_trace("lin.violation = %0.3e    (scaled violation of linear constraints)\n",
            (double)(state->replcerr));
        ae_trace("nlc.violation = %0.3e    (scaled violation of nonlinear constraints)\n",
            (double)(mx));
        ae_trace("gamma0        = %0.3e    (Hessian 2-norm estimate for target)\n",
            (double)(state->tracegamma.ptr.p_double[0]));
        j = 0;
        for(i=0; i<=nlec+nlic; i++)
        {
            if( ae_fp_greater(state->tracegamma.ptr.p_double[i],state->tracegamma.ptr.p_double[j]) )
            {
                j = i;
            }
        }
        ae_trace("gammaMax      = %0.3e    (maximum over Hessian 2-norm estimates for target/constraints)\n",
            (double)(state->tracegamma.ptr.p_double[j]));
        ae_trace("arg(gammaMax) = %0d             (function index; 0 for target, >0 for nonlinear constraints)\n",
            (int)(j));
        ae_trace("|x|inf        = %0.3e\n",
            (double)(rmaxabsv(n, &state->stepk.x, _state)));
    }
    
    /*
     * Start of the SQP iteration
     */
    rcopyv(n, &state->stepk.x, &state->xprev, _state);
    rsetv(nslack, 0.0, &state->d0, _state);
    rsetv(nslack, 0.0, &state->d1, _state);
    if( dotrace )
    {
        ae_trace("\n--- quadratic step ---------------------------------------------------------------------------------\n");
    }
    
    /*
     * Default values of the flag variables
     */
    increasemeritmu = ae_false;
    meritfstagnated = ae_false;
    meritdecreasefailed = ae_false;
    stepperformed = ae_true;
    goodstep = ae_true;
    relativetargetdecrease = 1.0E50;
    
    /*
     * Determine step direction using initial quadratic model.
     */
    socperformed = ae_false;
    if( !nlcsqp_qpsubproblemsolve(state, &state->subsolver, &state->stepk.x, &state->stepk.fi, &state->stepk.jac, &state->hess, 1.0, 1.0, &state->d0, &state->lagbcmult, &state->lagxcmult, dotrace, &j, &predictedchangemodel, &predictedchangepenalty, &sumc1, &d2trustradratio, _state) )
    {
        if( dotrace )
        {
            ae_trace("> [WARNING] QP subproblem failed with TerminationType=%0d\n> decreasing trust radius\n",
                (int)(j));
        }
        state->trustrad = 0.1*state->trustrad;
        inc(&state->repiterationscount, _state);
        goto lbl_6;
    }
    if( dotrace )
    {
        ae_trace("> QP subproblem solved with TerminationType=%0d, max|lagBoxMult|=%0.2e, max|lagNonBoxMult|=%0.2e\n",
            (int)(j),
            (double)(rmaxabsv(n, &state->lagbcmult, _state)),
            (double)(rmaxabsv(nec+nic+nlec+nlic, &state->lagxcmult, _state)));
    }
    rcopyv(nslack, &state->d0, &state->dtrial, _state);
    vfjcopy(&state->stepk, &state->currentlinearization, _state);
    
    /*
     * Perform merit function line search.
     *
     * First, we try unit step. If it does not decrease merit function,
     * a second-order correction is tried (helps to combat Maratos effect).
     */
    rallocv(n, &state->tmplaggrad, _state);
    nlcsqp_lagrangianfg(state, &state->stepk.x, state->trustrad, &state->stepk.fi, &state->stepk.jac, &state->lagbcmult, &state->lagxcmult, ae_true, &stepklagval, &state->tmplaggrad, _state);
    f0raw = nlcsqp_meritfunction(state, &state->stepk, state->meritmu, _state);
    f0 = nlcsqp_nonmonotonicadjustment(state, f0raw, _state);
    rcopyv(n, &state->stepk.x, &state->cand.x, _state);
    raddv(n, 1.0, &state->dtrial, &state->cand.x, _state);
    nlcsqp_sqpsendx(state, &state->cand.x, _state);
    state->needfij = ae_true;
    if( dotrace )
    {
        stimerstart(&state->timercallback, _state);
    }
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    if( dotrace )
    {
        stimerstop(&state->timercallback, _state);
    }
    state->needfij = ae_false;
    if( !nlcsqp_sqpretrievefij(state, &state->cand, _state) )
    {
        
        /*
         * Failed to retrieve func/Jac, infinities detected
         */
        if( dotrace )
        {
            ae_trace("[WARNING] infinities in target/constraints are detected, forcing trust radius decrease and restarting iteration\n");
        }
        state->trustrad = state->trustrad*nlcsqp_maxtrustraddecay;
        trustradresetmomentum(&state->trustradgrowth, nlcsqp_deftrustradgrowth, _state);
        infinitiesdetected = ae_true;
        failedtorecoverfrominfinities = ae_true;
        inc(&state->repiterationscount, _state);
        goto lbl_6;
    }
    f1 = nlcsqp_meritfunction(state, &state->cand, state->meritmu, _state);
    predictedchange = predictedchangemodel+state->meritmu*predictedchangepenalty;
    goodstep = ae_fp_less(f1-f0raw,nlcsqp_sufficientdecreasesigma*predictedchange);
    rallocv(n, &state->tmpcandlaggrad, _state);
    nlcsqp_lagrangianfg(state, &state->cand.x, state->trustrad, &state->cand.fi, &state->cand.jac, &state->lagbcmult, &state->lagxcmult, ae_true, &stepknlagval, &state->tmpcandlaggrad, _state);
    hessianupdatev2(&state->hess, &state->stepk.x, &state->tmplaggrad, &state->cand.x, &state->tmpcandlaggrad, 2, ae_false, dotrace, 1, _state);
    if( dotrace )
    {
        ae_trace("> proposed step with relative len %0.6f, analyzing change in the merit function: predicted=%0.2e, actual=%0.2e (ignoring nonmonotonic adjustment), ratio=%5.2f\n",
            (double)(d2trustradratio),
            (double)(predictedchange),
            (double)(f1-f0raw),
            (double)((f1-f0raw)/predictedchange));
    }
    needsoc = ae_false;
    if( ae_fp_greater_eq(predictedchange,(double)(0))||ae_fp_greater_eq(f1-f0,nlcsqp_sufficientdecreasesigma*predictedchange) )
    {
        needsoc = ae_true;
        if( dotrace )
        {
            ae_trace("> step without correction does not provide sufficient decrease in the merit function, preparing second-order correction\n");
        }
    }
    if( !needsoc )
    {
        goto lbl_8;
    }
    
    /*
     * Full step increases merit function. Let's compute second order
     * correction to the constraint model and recompute trial step D:
     * * use original model of the target
     * * extrapolate model of nonlinear constraints at StepKX+D back to origin
     *
     */
    socperformed = ae_true;
    rcopyv(n, &state->stepk.x, &state->corr.x, _state);
    state->corr.fi.ptr.p_double[0] = state->stepk.fi.ptr.p_double[0];
    for(j=0; j<=n-1; j++)
    {
        state->corr.jac.ptr.pp_double[0][j] = state->stepk.jac.ptr.pp_double[0][j];
    }
    for(i=1; i<=nlec+nlic; i++)
    {
        v = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            v = v+state->d0.ptr.p_double[j]*state->stepk.jac.ptr.pp_double[i][j];
            state->corr.jac.ptr.pp_double[i][j] = state->stepk.jac.ptr.pp_double[i][j];
        }
        state->corr.fi.ptr.p_double[i] = state->cand.fi.ptr.p_double[i]-v;
    }
    if( !nlcsqp_qpsubproblemsolve(state, &state->subsolver, &state->corr.x, &state->corr.fi, &state->corr.jac, &state->hess, 1.0, 1.0, &state->d1, &state->dummylagbcmult, &state->dummylagxcmult, dotrace, &j, &predictedchangemodel, &predictedchangepenalty, &sumc1soc, &d2trustradratio, _state) )
    {
        if( dotrace )
        {
            ae_trace("> [WARNING] second-order QP subproblem failed\n> decreasing trust radius\n");
        }
        state->trustrad = 0.1*state->trustrad;
        inc(&state->repiterationscount, _state);
        goto lbl_6;
    }
    if( dotrace )
    {
        ae_trace("> QP subproblem solved with TerminationType=%0d, max|lagBoxMult|=%0.2e, max|lagNonBoxMult|=%0.2e\n",
            (int)(j),
            (double)(rmaxabsv(n, &state->dummylagbcmult, _state)),
            (double)(rmaxabsv(nec+nic+nlec+nlic, &state->dummylagxcmult, _state)));
    }
    rcopyv(n, &state->d1, &state->dtrial, _state);
    vfjcopy(&state->corr, &state->currentlinearization, _state);
    
    /*
     * Perform line search, we again try full step (maybe it will work after SOC)
     */
    smoothnessmonitorstartlinesearch(smonitor, &state->stepk.x, &state->stepk.fi, &state->stepk.jac, state->repiterationscount, -1, _state);
    rcopyv(n, &state->stepk.x, &state->cand.x, _state);
    raddv(n, 1.0, &state->dtrial, &state->cand.x, _state);
    nlcsqp_sqpsendx(state, &state->cand.x, _state);
    state->needfij = ae_true;
    if( dotrace )
    {
        stimerstart(&state->timercallback, _state);
    }
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    if( dotrace )
    {
        stimerstop(&state->timercallback, _state);
    }
    state->needfij = ae_false;
    if( !nlcsqp_sqpretrievefij(state, &state->cand, _state) )
    {
        
        /*
         * Failed to retrieve func/Jac, infinities detected
         */
        if( dotrace )
        {
            ae_trace("[WARNING] infinities in target/constraints are detected, forcing trust radius decrease and restarting iteration\n");
        }
        state->trustrad = state->trustrad*nlcsqp_maxtrustraddecay;
        trustradresetmomentum(&state->trustradgrowth, nlcsqp_deftrustradgrowth, _state);
        inc(&state->repiterationscount, _state);
        infinitiesdetected = ae_true;
        failedtorecoverfrominfinities = ae_true;
        goto lbl_6;
    }
    smoothnessmonitorenqueuepoint(smonitor, &state->dtrial, 1.0, &state->cand.x, &state->cand.fi, &state->cand.jac, _state);
    f1 = nlcsqp_meritfunction(state, &state->cand, state->meritmu, _state);
    predictedchange = predictedchangemodel+state->meritmu*predictedchangepenalty;
    goodstep = ae_fp_less(f1-f0raw,nlcsqp_sufficientdecreasesigma*predictedchange);
    rallocv(n, &state->tmpcandlaggrad, _state);
    nlcsqp_lagrangianfg(state, &state->cand.x, state->trustrad, &state->cand.fi, &state->cand.jac, &state->lagbcmult, &state->lagxcmult, ae_true, &stepknlagval, &state->tmpcandlaggrad, _state);
    hessianpoplatestifpossible(&state->hess, _state);
    hessianupdatev2(&state->hess, &state->stepk.x, &state->tmplaggrad, &state->cand.x, &state->tmpcandlaggrad, 2, ae_true, dotrace, 1, _state);
    if( dotrace )
    {
        ae_trace("> proposed step with relative len %0.6f, analyzing change in the merit function: predicted=%0.2e, actual=%0.2e (ignoring nonmonotonic adjustment), ratio=%5.2f\n",
            (double)(d2trustradratio),
            (double)(predictedchange),
            (double)(f1-f0raw),
            (double)((f1-f0raw)/predictedchange));
    }
    if( ae_fp_greater_eq(predictedchange,(double)(0))||ae_fp_greater_eq(f1-f0,nlcsqp_sufficientdecreasesigma*predictedchange) )
    {
        
        /*
         * Mark step as failed
         */
        stepperformed = ae_false;
        goodstep = ae_false;
        meritdecreasefailed = ae_true;
        f1 = f0;
    }
    smoothnessmonitorfinalizelinesearch(smonitor, _state);
lbl_8:
    enforceboundaryconstraints(&state->cand.x, &state->scaledbndl, &state->hasbndl, &state->scaledbndu, &state->hasbndu, n, 0, _state);
    if( userterminationneeded )
    {
        
        /*
         * User requested termination, break before we move to new point
         */
        state->repterminationtype = icase2(failedtorecoverfrominfinities, -8, 8, _state);
        if( dotrace )
        {
            ae_trace("> user requested termination\n");
        }
        goto lbl_7;
    }
    
    /*
     * Analyze merit F for stagnation
     */
    meritfstagnated = stepperformed&&ae_fp_less_eq(ae_fabs(f1-f0, _state),nlcsqp_stagnationepsf*ae_fabs(f0, _state));
    v = ae_fabs(f1-f0, _state)/rmaxabs3(f0, f1, (double)(1), _state);
    vv = ae_fabs(state->cand.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0]-state->stepk.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0], _state)/rmaxabs3(state->cand.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0], state->stepk.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0], (double)(1), _state);
    relativetargetdecrease = rcase2(stepperformed, ae_maxreal(v, vv, _state), 1.0E50, _state);
    
    /*
     * Analyze linearized model - did we enforce zero violations of the constraint linearizations?
     * If we did not, we may need to increase MeritMu penalty coefficients.
     */
    if( nlcsqp_penaltiesneedincrease(state, &state->currentlinearization, &state->dtrial, predictedchangemodel, predictedchangepenalty, dotrace, _state) )
    {
        stepperformed = ae_false;
        goodstep = ae_false;
        increasemeritmu = ae_true;
    }
    
    /*
     * Trace
     */
    if( !dotrace )
    {
        goto lbl_10;
    }
    
    /*
     * Compute Lagrangian
     */
    rallocv(n, &state->tmplaggrad, _state);
    rallocv(n, &state->tmpcandlaggrad, _state);
    nlcsqp_lagrangianfg(state, &state->stepk.x, state->trustrad, &state->stepk.fi, &state->stepk.jac, &state->lagbcmult, &state->lagxcmult, ae_true, &stepklagval, &state->tmplaggrad, _state);
    nlcsqp_lagrangianfg(state, &state->cand.x, state->trustrad, &state->cand.fi, &state->cand.jac, &state->lagbcmult, &state->lagxcmult, ae_true, &stepknlagval, &state->tmpcandlaggrad, _state);
    
    /*
     * Update debug curvature information. Let
     *
     *     Sk = X(k+1)-X(k), Yk = G(k+1)-G(k)
     *
     * for a function Fi and store maximum over curvatures
     *
     *     gamma = (Yk,Yk)/|(Sk,Yk)|
     *
     * to TraceGamma[] array. Set MaxNewGamma to maximum of new values, set GammaIncreased
     * flag if at least one of TraceGamma[] entries was increased.
     */
    sksk = (double)(0);
    for(j=0; j<=n-1; j++)
    {
        v = state->cand.x.ptr.p_double[j]-state->stepk.x.ptr.p_double[j];
        sksk = sksk+v*v;
    }
    if( ae_fp_greater(sksk,(double)(0)) )
    {
        for(i=0; i<=nlec+nlic; i++)
        {
            ykyk = (double)(0);
            skyk = (double)(0);
            for(j=0; j<=n-1; j++)
            {
                v = state->cand.x.ptr.p_double[j]-state->stepk.x.ptr.p_double[j];
                vv = state->cand.jac.ptr.pp_double[i][j]-state->stepk.jac.ptr.pp_double[i][j];
                skyk = skyk+v*vv;
                ykyk = ykyk+vv*vv;
            }
            v = skyk/(sksk+ae_machineepsilon*ae_machineepsilon);
            state->tracegamma.ptr.p_double[i] = ae_maxreal(state->tracegamma.ptr.p_double[i], v, _state);
        }
    }
    
    /*
     * Perform agressive probing of the search direction - additional function evaluations
     * which help us to determine possible discontinuity and nonsmoothness of the problem
     */
    if( !(doprobingalways||(doprobingonfailure&&meritdecreasefailed)) )
    {
        goto lbl_12;
    }
    didx = 0;
lbl_14:
    if( didx>1 )
    {
        goto lbl_16;
    }
    if( didx==1&&!socperformed )
    {
        goto lbl_16;
    }
    if( didx==0 )
    {
        smoothnessmonitorstartlagrangianprobing(smonitor, &state->stepk.x, &state->d0, 1.0, state->repiterationscount, -1, _state);
    }
    else
    {
        smoothnessmonitorstartlagrangianprobing(smonitor, &state->stepk.x, &state->d1, 1.0, state->repiterationscount, -1, _state);
    }
lbl_17:
    if( !smoothnessmonitorprobelagrangian(smonitor, _state) )
    {
        goto lbl_18;
    }
    for(j=0; j<=n-1; j++)
    {
        state->probe.x.ptr.p_double[j] = smonitor->lagprobx.ptr.p_double[j];
        if( state->hasbndl.ptr.p_bool[j] )
        {
            state->probe.x.ptr.p_double[j] = ae_maxreal(state->probe.x.ptr.p_double[j], state->scaledbndl.ptr.p_double[j], _state);
        }
        if( state->hasbndu.ptr.p_bool[j] )
        {
            state->probe.x.ptr.p_double[j] = ae_minreal(state->probe.x.ptr.p_double[j], state->scaledbndu.ptr.p_double[j], _state);
        }
    }
    nlcsqp_sqpsendx(state, &state->probe.x, _state);
    state->needfij = ae_true;
    if( dotrace )
    {
        stimerstart(&state->timercallback, _state);
    }
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    if( dotrace )
    {
        stimerstop(&state->timercallback, _state);
    }
    state->needfij = ae_false;
    if( !nlcsqp_sqpretrievefij(state, &state->probe, _state) )
    {
        goto lbl_18;
    }
    rcopyallocv(1+nlec+nlic, &state->probe.fi, &smonitor->lagprobfi, _state);
    rcopyallocm(1+nlec+nlic, n, &state->probe.jac, &smonitor->lagprobj, _state);
    smonitor->lagprobrawlag = nlcsqp_rawlagrangian(state, &state->probe.x, &state->probe.fi, &state->lagbcmult, &state->lagxcmult, state->meritmu, _state);
    goto lbl_17;
lbl_18:
    ae_trace("*** ------------------------------------------------------------\n");
    ae_trace("*** |                 probing search direction                 |\n");
    if( didx==0 )
    {
        ae_trace("*** |          suggested by first-order QP subproblem          |\n");
    }
    if( didx==1 )
    {
        ae_trace("*** |          suggested by second-order QP subproblem         |\n");
    }
    ae_trace("*** ------------------------------------------------------------\n");
    ae_trace("*** |  Step  | Lagrangian (unaugmentd)|    Target  function    |\n");
    ae_trace("*** |along  D|     must be smooth     |     must be smooth     |\n");
    ae_trace("*** |        | function   |    slope  | function   |    slope  |\n");
    smoothnessmonitortracelagrangianprobingresults(smonitor, _state);
    didx = didx+1;
    goto lbl_14;
lbl_16:
lbl_12:
    
    /*
     * Output other information
     */
    mx = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        mx = ae_maxreal(mx, ae_fabs(state->dtrial.ptr.p_double[i], _state)/nlcsqp_gettrustregionk(&state->stepk.x, i, state->trustrad, _state), _state);
    }
    if( stepperformed&&goodstep )
    {
        ae_trace("> sufficient decrease step was performed\n");
    }
    else
    {
        if( stepperformed )
        {
            ae_trace("> nonmonotonic step was performed\n");
        }
        else
        {
            ae_trace("> no step was performed\n");
        }
    }
    ae_trace("max(|Di|)/TrustRad = %0.6f\n",
        (double)(mx));
    if( dodetailedtrace )
    {
        ae_trace("LagBoxMlt   = ");
        tracevectorautoprec(&state->lagbcmult, 0, n, _state);
        ae_trace("\n");
        ae_trace("LagNonBoxMlt= ");
        tracevectorautoprec(&state->lagxcmult, 0, nec+nic+nlec+nlic, _state);
        ae_trace("\n");
    }
    if( dodetailedtrace )
    {
        ae_trace("X0 (scaled) = ");
        tracevectorautoprec(&state->stepk.x, 0, n, _state);
        ae_trace("\n");
        ae_trace("D  (scaled) = ");
        tracevectorautoprec(&state->dtrial, 0, n, _state);
        ae_trace("\n");
        ae_trace("X1 (scaled) = ");
        tracevectorautoprec(&state->cand.x, 0, n, _state);
        ae_trace("\n");
        ae_trace("\n");
        ae_trace("grad F(X0)  = ");
        tracerowautoprec(&state->stepk.jac, 0, 0, n, _state);
        ae_trace("\n");
        ae_trace("grad F(X1)  = ");
        tracerowautoprec(&state->cand.jac, 0, 0, n, _state);
        ae_trace("\n");
        ae_trace("\n");
        ae_trace("grad L(X0)  = ");
        tracevectorautoprec(&state->tmplaggrad, 0, n, _state);
        ae_trace("\n");
        ae_trace("grad L(X1)  = ");
        tracevectorautoprec(&state->tmpcandlaggrad, 0, n, _state);
        ae_trace("\n");
    }
    ae_trace("targetF:        %17.9e -> %17.9e (delta=%11.3e)\n",
        (double)(state->fscales.ptr.p_double[0]*state->stepk.fi.ptr.p_double[0]),
        (double)(state->fscales.ptr.p_double[0]*state->cand.fi.ptr.p_double[0]),
        (double)(state->fscales.ptr.p_double[0]*(state->cand.fi.ptr.p_double[0]-state->stepk.fi.ptr.p_double[0])));
    ae_trace("scaled-meritF:  %17.9e -> %17.9e (delta=%11.3e)\n",
        (double)(f0),
        (double)(f1),
        (double)(f1-f0));
    ae_trace("scaled-targetF: %17.9e -> %17.9e (delta=%11.3e)\n",
        (double)(state->stepk.fi.ptr.p_double[0]),
        (double)(state->cand.fi.ptr.p_double[0]),
        (double)(state->cand.fi.ptr.p_double[0]-state->stepk.fi.ptr.p_double[0]));
    ae_trace("max|lag-grad|:  %17.9e -> %17.9e\n",
        (double)(rmaxabsv(n, &state->tmplaggrad, _state)),
        (double)(rmaxabsv(n, &state->tmpcandlaggrad, _state)));
    ae_trace("nrm2|lag-grad|: %17.9e -> %17.9e (ratio=%0.6f)\n",
        (double)(ae_sqrt(rdotv2(n, &state->tmplaggrad, _state), _state)),
        (double)(ae_sqrt(rdotv2(n, &state->tmpcandlaggrad, _state), _state)),
        (double)(ae_sqrt(rdotv2(n, &state->tmpcandlaggrad, _state), _state)/ae_sqrt(rdotv2(n, &state->tmplaggrad, _state), _state)));
    hessiangetdiagonal(&state->hess, &state->tmphdiag, _state);
    v = state->tmphdiag.ptr.p_double[0];
    for(i=0; i<=n-1; i++)
    {
        v = ae_minreal(v, state->tmphdiag.ptr.p_double[i], _state);
    }
    ae_trace("mindiag(Bk) = %0.3e\n",
        (double)(v));
    v = state->tmphdiag.ptr.p_double[0];
    for(i=0; i<=n-1; i++)
    {
        v = ae_maxreal(v, state->tmphdiag.ptr.p_double[i], _state);
    }
    ae_trace("maxdiag(Bk) = %0.3e\n",
        (double)(v));
lbl_10:
    
    /*
     * Move to new point
     */
    if( !stepperformed )
    {
        goto lbl_19;
    }
    
    /*
     * Save current point to the nonmonotonic memory buffer
     */
    nlcsqp_nonmonotonicsave(state, &state->cand, _state);
    
    /*
     * Update current state
     */
    vfjcopy(&state->cand, &state->stepk, _state);
    failedtorecoverfrominfinities = ae_false;
    
    /*
     * Report one more inner iteration
     */
    nlcsqp_sqpsendx(state, &state->stepk.x, _state);
    state->f = state->stepk.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0];
    state->xupdated = ae_true;
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    state->xupdated = ae_false;
    
    /*
     * Update constraint violations
     */
    checklcviolation(&state->scaledcleic, &state->lcsrcidx, nec, nic, &state->stepk.x, n, &state->replcerr, &state->replcidx, _state);
    unscaleandchecknlcviolation(&state->stepk.fi, &state->fscales, nlec, nlic, &state->repnlcerr, &state->repnlcidx, _state);
lbl_19:
    
    /*
     * Update MeritMu
     */
    if( increasemeritmu )
    {
        state->meritmu = ae_minreal((double)2*state->meritmu, nlcsqp_maxmeritmu, _state);
    }
    
    /*
     * Update trust region.
     *
     * NOTE: when trust region radius remains fixed for a long time it may mean that we 
     *       stagnated in eternal loop. In such cases we decrease it slightly in order
     *       to break possible loop. If such decrease was unnecessary, it may be easily
     *       fixed within few iterations.
     */
    trustradstagnated = ae_false;
    prevtrustrad = state->trustrad;
    deltamax = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        deltamax = ae_maxreal(deltamax, ae_fabs(state->xprev.ptr.p_double[i]-state->stepk.x.ptr.p_double[i], _state)/nlcsqp_gettrustregionk(&state->xprev, i, state->trustrad, _state), _state);
    }
    inc(&state->trustradstagnationcnt, _state);
    if( ae_fp_less_eq(deltamax,nlcsqp_sqpdeltadecrease) )
    {
        state->trustrad = state->trustrad*ae_maxreal(deltamax/nlcsqp_sqpdeltadecrease, nlcsqp_maxtrustraddecay, _state);
    }
    if( ae_fp_greater_eq(deltamax,nlcsqp_sqpdeltaincrease)&&goodstep )
    {
        state->trustrad = state->trustrad*state->trustradgrowth;
        trustradincreasemomentum(&state->trustradgrowth, nlcsqp_momentumgrowth, nlcsqp_maxtrustradgrowth, _state);
    }
    else
    {
        trustradresetmomentum(&state->trustradgrowth, nlcsqp_deftrustradgrowth, _state);
    }
    if( ae_fp_less(state->trustrad,0.99*prevtrustrad)||ae_fp_greater(state->trustrad,1.01*prevtrustrad) )
    {
        state->trustradstagnationcnt = 0;
    }
    if( state->trustradstagnationcnt>=nlcsqp_trustradstagnationlimit )
    {
        state->trustrad = 0.5*state->trustrad;
        state->trustradstagnationcnt = 0;
        trustradstagnated = ae_true;
    }
    
    /*
     * Trace
     */
    if( dotrace )
    {
        ae_trace("\n--- outer iteration ends ---------------------------------------------------------------------------\n");
        ae_trace("deltaMax    = %0.3f (ratio of step length to trust radius)\n",
            (double)(deltamax));
        ae_trace("newTrustRad = %0.3e",
            (double)(state->trustrad));
        if( !trustradstagnated )
        {
            if( ae_fp_greater(state->trustrad,prevtrustrad) )
            {
                ae_trace(", trust radius increased");
            }
            if( ae_fp_less(state->trustrad,prevtrustrad) )
            {
                ae_trace(", trust radius decreased");
            }
        }
        else
        {
            ae_trace(", trust radius forcibly decreased due to stagnation for %0d iterations",
                (int)(nlcsqp_trustradstagnationlimit));
        }
        ae_trace("\n");
        if( increasemeritmu )
        {
            ae_trace("meritMu     = %0.3e (increasing)\n",
                (double)(state->meritmu));
        }
    }
    
    /*
     * Advance outer iteration counter
     */
    inc(&state->repiterationscount, _state);
    state->fstagnationcnt = icase2(meritfstagnated, state->fstagnationcnt+1, 0, _state);
    goto lbl_6;
lbl_7:
    
    /*
     * Almost done
     */
    if( infinitiesdetected )
    {
        state->repterminationtype = icase2(failedtorecoverfrominfinities, -8, state->repterminationtype+800, _state);
    }
    if( dotrace )
    {
        stimerstop(&state->timertotal, _state);
        ae_trace("\n=== STOPPED ========================================================================================\n");
        if( infinitiesdetected&&failedtorecoverfrominfinities )
        {
            ae_trace("> infinities were detected, but we failed to recover by decreasing trust radius; declaring failure with code -8\n");
        }
        ae_trace("raw target:     %20.12e\n",
            (double)(state->stepk.fi.ptr.p_double[0]*state->fscales.ptr.p_double[0]));
        ae_trace("total time:     %10.1f ms\n",
            (double)(stimergetms(&state->timertotal, _state)));
        ae_trace("* incl cbck:    %10.1f ms\n",
            (double)(stimergetms(&state->timercallback, _state)));
        ae_trace("* incl QP:      %10.1f ms\n",
            (double)(stimergetms(&state->timerqp, _state)));
    }
    smoothnessmonitortracestatus(smonitor, dotrace, _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = n;
    state->rstate.ia.ptr.p_int[1] = nslack;
    state->rstate.ia.ptr.p_int[2] = nec;
    state->rstate.ia.ptr.p_int[3] = nic;
    state->rstate.ia.ptr.p_int[4] = nlec;
    state->rstate.ia.ptr.p_int[5] = nlic;
    state->rstate.ia.ptr.p_int[6] = i;
    state->rstate.ia.ptr.p_int[7] = j;
    state->rstate.ia.ptr.p_int[8] = subiterationidx;
    state->rstate.ia.ptr.p_int[9] = didx;
    state->rstate.ba.ptr.p_bool[0] = trustradstagnated;
    state->rstate.ba.ptr.p_bool[1] = dotrace;
    state->rstate.ba.ptr.p_bool[2] = doprobingalways;
    state->rstate.ba.ptr.p_bool[3] = doprobingonfailure;
    state->rstate.ba.ptr.p_bool[4] = dodetailedtrace;
    state->rstate.ba.ptr.p_bool[5] = increasemeritmu;
    state->rstate.ba.ptr.p_bool[6] = meritfstagnated;
    state->rstate.ba.ptr.p_bool[7] = socperformed;
    state->rstate.ba.ptr.p_bool[8] = meritdecreasefailed;
    state->rstate.ba.ptr.p_bool[9] = stepperformed;
    state->rstate.ba.ptr.p_bool[10] = goodstep;
    state->rstate.ba.ptr.p_bool[11] = firstrescale;
    state->rstate.ba.ptr.p_bool[12] = infinitiesdetected;
    state->rstate.ba.ptr.p_bool[13] = failedtorecoverfrominfinities;
    state->rstate.ba.ptr.p_bool[14] = needsoc;
    state->rstate.ra.ptr.p_double[0] = v;
    state->rstate.ra.ptr.p_double[1] = vv;
    state->rstate.ra.ptr.p_double[2] = mx;
    state->rstate.ra.ptr.p_double[3] = deltamax;
    state->rstate.ra.ptr.p_double[4] = multiplyby;
    state->rstate.ra.ptr.p_double[5] = setscaleto;
    state->rstate.ra.ptr.p_double[6] = prevtrustrad;
    state->rstate.ra.ptr.p_double[7] = relativetargetdecrease;
    state->rstate.ra.ptr.p_double[8] = f0;
    state->rstate.ra.ptr.p_double[9] = f0raw;
    state->rstate.ra.ptr.p_double[10] = f1;
    state->rstate.ra.ptr.p_double[11] = tol;
    state->rstate.ra.ptr.p_double[12] = stepklagval;
    state->rstate.ra.ptr.p_double[13] = stepknlagval;
    state->rstate.ra.ptr.p_double[14] = expandedrad;
    state->rstate.ra.ptr.p_double[15] = d2trustradratio;
    state->rstate.ra.ptr.p_double[16] = sksk;
    state->rstate.ra.ptr.p_double[17] = ykyk;
    state->rstate.ra.ptr.p_double[18] = skyk;
    state->rstate.ra.ptr.p_double[19] = sumc1;
    state->rstate.ra.ptr.p_double[20] = sumc1soc;
    state->rstate.ra.ptr.p_double[21] = predictedchange;
    state->rstate.ra.ptr.p_double[22] = predictedchangemodel;
    state->rstate.ra.ptr.p_double[23] = predictedchangepenalty;
    return result;
}


/*************************************************************************
This function initializes SQP subproblem.
Should be called once in the beginning of the optimization.

INPUT PARAMETERS:
    SState          -   solver state
    Subsolver       -   SQP subproblem to initialize
                        
                        
RETURN VALUE:
    True on success
    False on failure of the QP solver (unexpected... but possible due to numerical errors)
                        

  -- ALGLIB --
     Copyright 05.03.2018 by Bochkanov Sergey
*************************************************************************/
static void nlcsqp_initqpsubsolver(const minsqpstate* sstate,
     minsqpsubsolver* subsolver,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nslack;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t nlec;
    ae_int_t nlic;
    ae_int_t lccnt;
    ae_int_t nnz;
    ae_int_t offs;
    ae_int_t i;
    ae_int_t j;


    n = sstate->n;
    nec = sstate->nec;
    nic = sstate->nic;
    nlec = sstate->nlec;
    nlic = sstate->nlic;
    nslack = n+2*(nec+nlec)+(nic+nlic);
    lccnt = nec+nic+nlec+nlic;
    
    /*
     * Allocate temporaries
     */
    rvectorsetlengthatleast(&subsolver->cural, lccnt, _state);
    rvectorsetlengthatleast(&subsolver->curau, lccnt, _state);
    rvectorsetlengthatleast(&subsolver->curbndl, nslack, _state);
    rvectorsetlengthatleast(&subsolver->curbndu, nslack, _state);
    rvectorsetlengthatleast(&subsolver->curb, nslack, _state);
    
    /*
     * Linear constraints do not change across subiterations, that's
     * why we allocate storage for them at the start of the program.
     *
     * A full set of "raw" constraints is stored; later we will filter
     * out inequality ones which are inactive anywhere in the current
     * trust region.
     *
     * NOTE: because sparserawlc object stores only linear constraint
     *       (linearizations of nonlinear ones are not stored) we
     *       allocate only minimum necessary space.
     */
    nnz = 0;
    for(i=0; i<=nec+nic-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( sstate->scaledcleic.ptr.pp_double[i][j]!=0.0 )
            {
                nnz = nnz+1;
            }
        }
    }
    ivectorsetlengthatleast(&subsolver->sparserawlc.ridx, nec+nic+1, _state);
    rvectorsetlengthatleast(&subsolver->sparserawlc.vals, nnz, _state);
    ivectorsetlengthatleast(&subsolver->sparserawlc.idx, nnz, _state);
    ivectorsetlengthatleast(&subsolver->sparserawlc.didx, nec+nic, _state);
    ivectorsetlengthatleast(&subsolver->sparserawlc.uidx, nec+nic, _state);
    offs = 0;
    subsolver->sparserawlc.ridx.ptr.p_int[0] = 0;
    for(i=0; i<=nec+nic-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( sstate->scaledcleic.ptr.pp_double[i][j]!=0.0 )
            {
                
                /*
                 * Primary part of the matrix
                 */
                subsolver->sparserawlc.vals.ptr.p_double[offs] = sstate->scaledcleic.ptr.pp_double[i][j];
                subsolver->sparserawlc.idx.ptr.p_int[offs] = j;
                offs = offs+1;
            }
        }
        subsolver->sparserawlc.ridx.ptr.p_int[i+1] = offs;
    }
    subsolver->sparserawlc.matrixtype = 1;
    subsolver->sparserawlc.ninitialized = subsolver->sparserawlc.ridx.ptr.p_int[nec+nic];
    subsolver->sparserawlc.m = nec+nic;
    subsolver->sparserawlc.n = n;
    sparseinitduidx(&subsolver->sparserawlc, _state);
}


/*************************************************************************
This function solves QP subproblem given by initial point X, function vector Fi
and Jacobian Jac, and returns:
* search direction D[]
* estimates of Lagrangian multipliers
* predicted change in the quadratic model of the target, and in the L1-penalty
  added to the model

Additionally returns SumC1 - a sum of absolute errors AT THE BEGINNING of
the step (this quantity is used to choose appropriate penalty parameter;
it corresponds to |Ck|_1 at Nocedal and Wright, 'Numerical optimization' 2006,
chapter 18, formula 18.33).

The QP subproblem linear and quadratic terms are multiplied by the scalar
multipliers AlphaG and AlphaH, which can be:
* (1,1) when we want to compute a step direction
* (0,0) when we want to compute best possible improvement in the linearized
   constraints over the trust region
* (1,0) when we want to compute Lagrange multipliers using linearized model
  of the target instead of the quadratic one

  -- ALGLIB --
     Copyright 05.03.2018 by Bochkanov Sergey
*************************************************************************/
static ae_bool nlcsqp_qpsubproblemsolve(minsqpstate* state,
     minsqpsubsolver* subsolver,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     /* Real    */ const ae_matrix* jac,
     xbfgshessian* hess,
     double alphag,
     double alphah,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_vector* lagbcmult,
     /* Real    */ ae_vector* lagxcmult,
     ae_bool dotrace,
     ae_int_t* terminationtype,
     double* predictedchangemodel,
     double* predictedchangepenalty,
     double* sumc1,
     double* d2trustradratio,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nslack;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t nlec;
    ae_int_t nlic;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double vv;
    double vright;
    double vmax;
    ae_int_t lccnt;
    ae_int_t offsslackec;
    ae_int_t offsslacknlec;
    ae_int_t offsslackic;
    ae_int_t offsslacknlic;
    ae_int_t offs;
    ae_int_t nnz;
    ae_int_t j0;
    ae_int_t j1;
    double rescaleby;
    ae_bool result;

    *terminationtype = 0;
    *predictedchangemodel = 0.0;
    *predictedchangepenalty = 0.0;
    *sumc1 = 0.0;
    *d2trustradratio = 0.0;

    ae_assert(ae_fp_eq(alphag,(double)(0))||ae_fp_eq(alphag,(double)(1)), "QPSubproblemSolve: AlphaG is neither 0 nor 1", _state);
    ae_assert(ae_fp_eq(alphah,(double)(0))||ae_fp_eq(alphah,(double)(1)), "QPSubproblemSolve: AlphaH is neither 0 nor 1", _state);
    n = state->n;
    nec = state->nec;
    nic = state->nic;
    nlec = state->nlec;
    nlic = state->nlic;
    nslack = n+2*(nec+nlec)+(nic+nlic);
    lccnt = nec+nic+nlec+nlic;
    
    /*
     * Locations of slack variables
     */
    offsslackec = n;
    offsslacknlec = n+2*nec;
    offsslackic = n+2*nec+2*nlec;
    offsslacknlic = n+2*(nec+nlec)+nic;
    
    /*
     * Prepare temporary structures
     */
    rvectorgrowto(&subsolver->cural, lccnt, _state);
    rvectorgrowto(&subsolver->curau, lccnt, _state);
    rvectorsetlengthatleast(&subsolver->d0, nslack, _state);
    rallocv(lccnt, &subsolver->rescalelag, _state);
    
    /*
     * Prepare default solution: all zeros
     */
    result = ae_true;
    *terminationtype = 0;
    *sumc1 = (double)(0);
    *predictedchangemodel = (double)(0);
    *predictedchangepenalty = (double)(0);
    *d2trustradratio = 0.0;
    for(i=0; i<=nslack-1; i++)
    {
        d->ptr.p_double[i] = 0.0;
        subsolver->d0.ptr.p_double[i] = (double)(0);
    }
    rsetv(n, 0.0, lagbcmult, _state);
    rsetv(lccnt, 0.0, lagxcmult, _state);
    
    /*
     * Linear term B
     *
     * NOTE: elements [N,NSlack) are equal to MeritMu + perturbation to improve numeric properties of QP problem
     */
    for(i=0; i<=n-1; i++)
    {
        subsolver->curb.ptr.p_double[i] = alphag*jac->ptr.pp_double[0][i];
    }
    for(i=n; i<=nslack-1; i++)
    {
        subsolver->curb.ptr.p_double[i] = state->meritmu;
    }
    
    /*
     * Trust radius constraints for primary variables
     */
    bsetallocv(n, ae_false, &subsolver->retainnegativebclm, _state);
    bsetallocv(n, ae_false, &subsolver->retainpositivebclm, _state);
    for(i=0; i<=n-1; i++)
    {
        subsolver->curbndl.ptr.p_double[i] = -nlcsqp_gettrustregionk(x, i, state->trustrad, _state);
        subsolver->curbndu.ptr.p_double[i] = nlcsqp_gettrustregionk(x, i, state->trustrad, _state);
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_greater(state->scaledbndl.ptr.p_double[i]-x->ptr.p_double[i],subsolver->curbndl.ptr.p_double[i]) )
        {
            subsolver->curbndl.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i]-x->ptr.p_double[i];
            subsolver->retainnegativebclm.ptr.p_bool[i] = ae_true;
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_less(state->scaledbndu.ptr.p_double[i]-x->ptr.p_double[i],subsolver->curbndu.ptr.p_double[i]) )
        {
            subsolver->curbndu.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i]-x->ptr.p_double[i];
            subsolver->retainpositivebclm.ptr.p_bool[i] = ae_true;
        }
    }
    
    /*
     * Prepare storage for "effective" constraining matrix
     */
    nnz = subsolver->sparserawlc.ridx.ptr.p_int[nec+nic];
    for(i=0; i<=nlec+nlic-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( jac->ptr.pp_double[1+i][j]!=0.0 )
            {
                nnz = nnz+1;
            }
        }
    }
    nnz = nnz+2*nec+nic;
    nnz = nnz+2*nlec+nlic;
    ivectorgrowto(&subsolver->sparseefflc.ridx, lccnt+1, _state);
    rvectorgrowto(&subsolver->sparseefflc.vals, nnz, _state);
    ivectorgrowto(&subsolver->sparseefflc.idx, nnz, _state);
    ivectorsetlengthatleast(&subsolver->sparseefflc.didx, lccnt, _state);
    ivectorsetlengthatleast(&subsolver->sparseefflc.uidx, lccnt, _state);
    subsolver->sparseefflc.m = 0;
    subsolver->sparseefflc.n = nslack;
    subsolver->sparseefflc.matrixtype = 1;
    
    /*
     * Append linear equality/inequality constraints
     *
     * Scan sparsified linear constraints stored in sparserawlc[], skip ones
     * which are inactive anywhere in the trust region.
     */
    rvectorsetlengthatleast(&subsolver->tmp0, nslack, _state);
    for(i=0; i<=n-1; i++)
    {
        subsolver->tmp0.ptr.p_double[i] = x->ptr.p_double[i];
    }
    for(i=n; i<=nslack-1; i++)
    {
        subsolver->tmp0.ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=nec+nic-1; i++)
    {
        
        /*
         * Calculate:
         * * VRight - product of X[] (extended with zeros up to NSlack elements)
         *            and AR[i] - Ith row of sparserawlc matrix.
         * * VMax   - maximum value of X*ARi computed over trust region
         */
        vright = (double)(0);
        vmax = (double)(0);
        j0 = subsolver->sparserawlc.ridx.ptr.p_int[i];
        j1 = subsolver->sparserawlc.ridx.ptr.p_int[i+1]-1;
        for(k=j0; k<=j1; k++)
        {
            j = subsolver->sparserawlc.idx.ptr.p_int[k];
            v = x->ptr.p_double[j];
            vv = subsolver->sparserawlc.vals.ptr.p_double[k];
            vright = vright+vv*v;
            if( vv>=(double)0 )
            {
                vmax = vmax+vv*(v+subsolver->curbndu.ptr.p_double[j]);
            }
            else
            {
                vmax = vmax+vv*(v+subsolver->curbndl.ptr.p_double[j]);
            }
        }
        
        /*
         * Linear constraints are passed 'as is', without additional rescaling
         */
        subsolver->rescalelag.ptr.p_double[i] = 1.0;
        
        /*
         * If constraint is an inequality one and guaranteed to be inactive
         * within trust region, it is skipped (the row itself is retained but
         * filled by zeros).
         */
        if( i>=nec&&vmax<=state->scaledcleic.ptr.pp_double[i][n] )
        {
            offs = subsolver->sparseefflc.ridx.ptr.p_int[i];
            subsolver->sparseefflc.vals.ptr.p_double[offs] = (double)(-1);
            subsolver->sparseefflc.idx.ptr.p_int[offs] = offsslackic+(i-nec);
            subsolver->sparseefflc.ridx.ptr.p_int[i+1] = offs+1;
            subsolver->cural.ptr.p_double[i] = 0.0;
            subsolver->curau.ptr.p_double[i] = 0.0;
            subsolver->curbndl.ptr.p_double[offsslackic+(i-nec)] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslackic+(i-nec)] = (double)(0);
            subsolver->rescalelag.ptr.p_double[i] = 0.0;
            continue;
        }
        
        /*
         * Start working on row I
         */
        offs = subsolver->sparseefflc.ridx.ptr.p_int[i];
        
        /*
         * Copy constraint from sparserawlc[] to sparseefflc[]
         */
        j0 = subsolver->sparserawlc.ridx.ptr.p_int[i];
        j1 = subsolver->sparserawlc.ridx.ptr.p_int[i+1]-1;
        for(k=j0; k<=j1; k++)
        {
            subsolver->sparseefflc.idx.ptr.p_int[offs] = subsolver->sparserawlc.idx.ptr.p_int[k];
            subsolver->sparseefflc.vals.ptr.p_double[offs] = subsolver->sparserawlc.vals.ptr.p_double[k];
            offs = offs+1;
        }
        
        /*
         * Set up slack variables
         */
        if( i<nec )
        {
            subsolver->sparseefflc.vals.ptr.p_double[offs+0] = (double)(-1);
            subsolver->sparseefflc.vals.ptr.p_double[offs+1] = (double)(1);
            subsolver->sparseefflc.idx.ptr.p_int[offs+0] = offsslackec+2*i+0;
            subsolver->sparseefflc.idx.ptr.p_int[offs+1] = offsslackec+2*i+1;
            offs = offs+2;
        }
        else
        {
            
            /*
             * Slack variables for inequality constraints
             */
            subsolver->sparseefflc.vals.ptr.p_double[offs] = (double)(-1);
            subsolver->sparseefflc.idx.ptr.p_int[offs] = offsslackic+(i-nec);
            offs = offs+1;
        }
        
        /*
         * Finalize row
         */
        subsolver->sparseefflc.ridx.ptr.p_int[i+1] = offs;
        
        /*
         * Set up bounds and slack part of D0.
         *
         * NOTE: bounds for equality and inequality constraints are
         *       handled differently
         */
        v = vright-state->scaledcleic.ptr.pp_double[i][n];
        if( i<nec )
        {
            *sumc1 = *sumc1+ae_fabs(v, _state);
            subsolver->cural.ptr.p_double[i] = -v;
            subsolver->curau.ptr.p_double[i] = -v;
            subsolver->curbndl.ptr.p_double[offsslackec+2*i+0] = (double)(0);
            subsolver->curbndl.ptr.p_double[offsslackec+2*i+1] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslackec+2*i+0] = ae_fabs(v, _state);
            subsolver->curbndu.ptr.p_double[offsslackec+2*i+1] = ae_fabs(v, _state);
            if( ae_fp_greater_eq(v,(double)(0)) )
            {
                subsolver->d0.ptr.p_double[offsslackec+2*i+0] = ae_fabs(v, _state);
                subsolver->d0.ptr.p_double[offsslackec+2*i+1] = (double)(0);
            }
            else
            {
                subsolver->d0.ptr.p_double[offsslackec+2*i+0] = (double)(0);
                subsolver->d0.ptr.p_double[offsslackec+2*i+1] = ae_fabs(v, _state);
            }
        }
        else
        {
            *sumc1 = *sumc1+ae_maxreal(v, 0.0, _state);
            subsolver->cural.ptr.p_double[i] = _state->v_neginf;
            subsolver->curau.ptr.p_double[i] = -v;
            subsolver->curbndl.ptr.p_double[offsslackic+(i-nec)] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslackic+(i-nec)] = ae_maxreal(v, (double)(0), _state);
            subsolver->d0.ptr.p_double[offsslackic+(i-nec)] = ae_maxreal(v, (double)(0), _state);
        }
    }
    subsolver->sparseefflc.m = subsolver->sparseefflc.m+(nec+nic);
    
    /*
     * Append nonlinear equality/inequality constraints
     */
    for(i=0; i<=nlec+nlic-1; i++)
    {
        
        /*
         * Calculate:
         * * rescale coefficient used to normalize constraints
         * * VMax - maximum of constraint value over trust region
         */
        vmax = fi->ptr.p_double[1+i];
        vv = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            v = jac->ptr.pp_double[1+i][j];
            vv = vv+v*v;
            if( v>=(double)0 )
            {
                vmax = vmax+v*subsolver->curbndu.ptr.p_double[j];
            }
            else
            {
                vmax = vmax+v*subsolver->curbndl.ptr.p_double[j];
            }
        }
        rescaleby = (double)1/coalesce(ae_sqrt(vv, _state), (double)(1), _state);
        
        /*
         * Nonlinear constraints are passed with normalization
         */
        subsolver->rescalelag.ptr.p_double[nec+nic+i] = rescaleby;
        
        /*
         * If constraint is an inequality one and guaranteed to be inactive
         * within trust region, it is skipped (row itself is retained but
         * filled by zeros).
         */
        if( i>=nlec&&vmax<=0.0 )
        {
            offs = subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m+i];
            subsolver->sparseefflc.vals.ptr.p_double[offs] = (double)(-1);
            subsolver->sparseefflc.idx.ptr.p_int[offs] = offsslacknlic+(i-nlec);
            subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m+i+1] = offs+1;
            subsolver->cural.ptr.p_double[subsolver->sparseefflc.m+i] = 0.0;
            subsolver->curau.ptr.p_double[subsolver->sparseefflc.m+i] = 0.0;
            subsolver->curbndl.ptr.p_double[offsslacknlic+(i-nlec)] = 0.0;
            subsolver->curbndu.ptr.p_double[offsslacknlic+(i-nlec)] = 0.0;
            subsolver->d0.ptr.p_double[offsslacknlic+(i-nlec)] = 0.0;
            subsolver->rescalelag.ptr.p_double[nec+nic+i] = 0.0;
            continue;
        }
        
        /*
         * Copy scaled row
         */
        offs = subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m+i];
        for(j=0; j<=n-1; j++)
        {
            if( jac->ptr.pp_double[1+i][j]!=0.0 )
            {
                subsolver->sparseefflc.vals.ptr.p_double[offs] = rescaleby*jac->ptr.pp_double[1+i][j];
                subsolver->sparseefflc.idx.ptr.p_int[offs] = j;
                offs = offs+1;
            }
        }
        if( i<nlec )
        {
            
            /*
             * Add slack terms for equality constraints
             */
            subsolver->sparseefflc.vals.ptr.p_double[offs+0] = (double)(-1);
            subsolver->sparseefflc.vals.ptr.p_double[offs+1] = (double)(1);
            subsolver->sparseefflc.idx.ptr.p_int[offs+0] = offsslacknlec+2*i+0;
            subsolver->sparseefflc.idx.ptr.p_int[offs+1] = offsslacknlec+2*i+1;
            offs = offs+2;
        }
        else
        {
            
            /*
             * Add slack terms for inequality constraints
             */
            subsolver->sparseefflc.vals.ptr.p_double[offs] = (double)(-1);
            subsolver->sparseefflc.idx.ptr.p_int[offs] = offsslacknlic+(i-nlec);
            offs = offs+1;
        }
        subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m+i+1] = offs;
        
        /*
         * Set box constraints on slack variables and bounds on linear equality/inequality constraints
         */
        v = rescaleby*fi->ptr.p_double[1+i];
        if( i<nlec )
        {
            
            /*
             * Equality constraint
             */
            *sumc1 = *sumc1+ae_fabs(fi->ptr.p_double[1+i], _state);
            subsolver->cural.ptr.p_double[subsolver->sparseefflc.m+i] = -v;
            subsolver->curau.ptr.p_double[subsolver->sparseefflc.m+i] = -v;
            subsolver->curbndl.ptr.p_double[offsslacknlec+2*i+0] = (double)(0);
            subsolver->curbndl.ptr.p_double[offsslacknlec+2*i+1] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslacknlec+2*i+0] = ae_fabs(v, _state);
            subsolver->curbndu.ptr.p_double[offsslacknlec+2*i+1] = ae_fabs(v, _state);
            if( ae_fp_greater_eq(v,(double)(0)) )
            {
                subsolver->d0.ptr.p_double[offsslacknlec+2*i+0] = ae_fabs(v, _state);
                subsolver->d0.ptr.p_double[offsslacknlec+2*i+1] = (double)(0);
            }
            else
            {
                subsolver->d0.ptr.p_double[offsslacknlec+2*i+0] = (double)(0);
                subsolver->d0.ptr.p_double[offsslacknlec+2*i+1] = ae_fabs(v, _state);
            }
        }
        else
        {
            
            /*
             * Inequality constraint
             */
            *sumc1 = *sumc1+ae_maxreal(fi->ptr.p_double[1+i], 0.0, _state);
            subsolver->cural.ptr.p_double[subsolver->sparseefflc.m+i] = _state->v_neginf;
            subsolver->curau.ptr.p_double[subsolver->sparseefflc.m+i] = -v;
            subsolver->curbndl.ptr.p_double[offsslacknlic+(i-nlec)] = (double)(0);
            subsolver->curbndu.ptr.p_double[offsslacknlic+(i-nlec)] = ae_maxreal(v, (double)(0), _state);
            subsolver->d0.ptr.p_double[offsslacknlic+(i-nlec)] = ae_maxreal(v, (double)(0), _state);
        }
    }
    subsolver->sparseefflc.m = subsolver->sparseefflc.m+(nlec+nlic);
    
    /*
     * Finalize sparse matrix structure
     */
    ae_assert(subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m]<=subsolver->sparseefflc.idx.cnt, "QPSubproblemSolve: critical integrity check failed", _state);
    ae_assert(subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m]<=subsolver->sparseefflc.vals.cnt, "QPSubproblemSolve: critical integrity check failed", _state);
    subsolver->sparseefflc.ninitialized = subsolver->sparseefflc.ridx.ptr.p_int[subsolver->sparseefflc.m];
    sparseinitduidx(&subsolver->sparseefflc, _state);
    
    /*
     * Solve quadratic program with IPM.
     *
     * We always treat its result as a valid solution, even for TerminationType<=0.
     * In case anything is wrong with solution vector, we will detect it during line
     * search phase (merit function does not increase).
     *
     * NOTE: because we cleaned up constraints that are DEFINITELY inactive within
     *       trust region, we do not have to worry about StopOnExcessiveBounds option.
     */
    rsetallocv(nslack, state->trustrad, &subsolver->tmps, _state);
    for(i=0; i<=n-1; i++)
    {
        subsolver->tmps.ptr.p_double[i] = nlcsqp_gettrustregionk(x, i, state->trustrad, _state);
    }
    rsetallocv(nslack, 0.0, &subsolver->tmporigin, _state);
    if( dotrace )
    {
        stimerstart(&state->timerqp, _state);
    }
    if( state->usedensebfgs )
    {
        
        /*
         * Dense BFGS is used to generate a quasi-Newton matrix,
         * dense IPM is used to solve QP subproblems.
         */
        hessiangetmatrix(hess, ae_true, &subsolver->denseh, _state);
        for(i=0; i<=n-1; i++)
        {
            rmulr(n, alphah, &subsolver->denseh, i, _state);
        }
        vipminitdensewithslacks(&subsolver->ipmsolver, &subsolver->tmps, &subsolver->tmporigin, n, nslack, ae_true, _state);
        vipmsetquadraticlinear(&subsolver->ipmsolver, &subsolver->denseh, &subsolver->sparsedummy, 0, ae_true, &subsolver->curb, _state);
        vipmsetconstraints(&subsolver->ipmsolver, &subsolver->curbndl, &subsolver->curbndu, &subsolver->sparseefflc, subsolver->sparseefflc.m, &subsolver->densedummy, 0, &subsolver->cural, &subsolver->curau, _state);
        vipmoptimize(&subsolver->ipmsolver, ae_false, &subsolver->tmp0, &subsolver->tmp1, &subsolver->tmp2, terminationtype, _state);
    }
    else
    {
        
        /*
         * Low-rank quasi-Newton matrix with positive-only corrections is used,
         * sparse IPM with low-rank updates is used to solve QP subproblems.
         */
        hessiangetlowrankstabilized(hess, &subsolver->tmpdiag, &subsolver->tmpcorrc, &subsolver->hesscorrd, &subsolver->hessrank, _state);
        ae_assert(ae_fp_eq(alphah,(double)(0))||ae_fp_eq(alphah,(double)(1)), "QPSubproblemSolve: AlphaH is neither 0 nor 1", _state);
        subsolver->hesssparsediag.matrixtype = 1;
        subsolver->hesssparsediag.m = nslack;
        subsolver->hesssparsediag.n = nslack;
        iallocv(nslack+1, &subsolver->hesssparsediag.ridx, _state);
        iallocv(nslack, &subsolver->hesssparsediag.idx, _state);
        rallocv(nslack, &subsolver->hesssparsediag.vals, _state);
        subsolver->hesssparsediag.ridx.ptr.p_int[0] = 0;
        for(i=0; i<=nslack-1; i++)
        {
            subsolver->hesssparsediag.idx.ptr.p_int[i] = i;
            subsolver->hesssparsediag.vals.ptr.p_double[i] = 0.0;
            subsolver->hesssparsediag.ridx.ptr.p_int[i+1] = i+1;
        }
        raddv(n, alphah, &subsolver->tmpdiag, &subsolver->hesssparsediag.vals, _state);
        sparsecreatecrsinplace(&subsolver->hesssparsediag, _state);
        if( subsolver->hessrank>0 )
        {
            rsetallocm(subsolver->hessrank, nslack, 0.0, &subsolver->hesscorrc, _state);
            if( ae_fp_neq(alphah,(double)(0)) )
            {
                rcopym(subsolver->hessrank, n, &subsolver->tmpcorrc, &subsolver->hesscorrc, _state);
            }
        }
        ipm2init(&subsolver->ipm2, &subsolver->tmps, &subsolver->tmporigin, nslack, &subsolver->densedummy, &subsolver->hesssparsediag, 1, ae_true, &subsolver->hesscorrc, &subsolver->hesscorrd, subsolver->hessrank, &subsolver->curb, 0.0, &subsolver->curbndl, &subsolver->curbndu, &subsolver->sparseefflc, subsolver->sparseefflc.m, &subsolver->densedummy, 0, &subsolver->cural, &subsolver->curau, ae_false, ae_false, _state);
        ipm2optimize(&subsolver->ipm2, ae_false, &subsolver->tmp0, &subsolver->tmp1, &subsolver->tmp2, terminationtype, _state);
    }
    if( dotrace )
    {
        stimerstop(&state->timerqp, _state);
    }
    
    /*
     * Unpack results
     */
    rcopyv(nslack, &subsolver->tmp0, d, _state);
    for(i=0; i<=n-1; i++)
    {
        lagbcmult->ptr.p_double[i] = (double)(0);
        if( subsolver->retainpositivebclm.ptr.p_bool[i]&&ae_fp_greater(subsolver->tmp1.ptr.p_double[i],(double)(0)) )
        {
            lagbcmult->ptr.p_double[i] = subsolver->tmp1.ptr.p_double[i];
        }
        if( subsolver->retainnegativebclm.ptr.p_bool[i]&&ae_fp_less(subsolver->tmp1.ptr.p_double[i],(double)(0)) )
        {
            lagbcmult->ptr.p_double[i] = subsolver->tmp1.ptr.p_double[i];
        }
    }
    rcopyv(lccnt, &subsolver->tmp2, lagxcmult, _state);
    rmergemulv(lccnt, &subsolver->rescalelag, lagxcmult, _state);
    
    /*
     * Compute predicted decrease in an L1-penalized quadratic model
     */
    *predictedchangemodel = rdotv(n, d, &subsolver->curb, _state)+0.5*hessianvmv(hess, d, _state);
    *predictedchangepenalty = (double)(0);
    for(i=0; i<=nec+nic-1; i++)
    {
        v = rdotvr(n, x, &state->scaledcleic, i, _state)-state->scaledcleic.ptr.pp_double[i][n];
        vv = v+rdotvr(n, d, &state->scaledcleic, i, _state);
        *predictedchangepenalty = *predictedchangepenalty+rcase2(i<nec, ae_fabs(vv, _state)-ae_fabs(v, _state), ae_maxreal(vv, (double)(0), _state)-ae_maxreal(v, (double)(0), _state), _state);
    }
    for(i=0; i<=nlec+nlic-1; i++)
    {
        v = fi->ptr.p_double[1+i];
        vv = v+rdotvr(n, d, jac, 1+i, _state);
        *predictedchangepenalty = *predictedchangepenalty+rcase2(i<nlec, ae_fabs(vv, _state)-ae_fabs(v, _state), ae_maxreal(vv, (double)(0), _state)-ae_maxreal(v, (double)(0), _state), _state);
    }
    
    /*
     * Analyze length of D
     */
    ae_assert(ae_fp_eq(*d2trustradratio,0.0), "SQP: integrity check 0157 failed", _state);
    for(i=0; i<=n-1; i++)
    {
        *d2trustradratio = ae_maxreal(*d2trustradratio, ae_fabs(d->ptr.p_double[i]/nlcsqp_gettrustregionk(x, i, state->trustrad, _state), _state), _state);
    }
    return result;
}


/*************************************************************************
Copies X to State.X
*************************************************************************/
static void nlcsqp_sqpsendx(minsqpstate* state,
     /* Real    */ const ae_vector* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    for(i=0; i<=n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i]&&xs->ptr.p_double[i]<=state->scaledbndl.ptr.p_double[i] )
        {
            state->x.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i];
            continue;
        }
        if( state->hasbndu.ptr.p_bool[i]&&xs->ptr.p_double[i]>=state->scaledbndu.ptr.p_double[i] )
        {
            state->x.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i];
            continue;
        }
        state->x.ptr.p_double[i] = xs->ptr.p_double[i];
    }
}


/*************************************************************************
Retrieves F-vector and scaled Jacobian, copies them to FiS and JS.

Returns True on success, False on failure (when F or J are not finite numbers).
*************************************************************************/
static ae_bool nlcsqp_sqpretrievefij(const minsqpstate* state,
     varsfuncjac* vfj,
     ae_state *_state)
{
    ae_int_t nlec;
    ae_int_t nlic;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    double v;
    double vv;
    ae_bool result;


    n = state->n;
    nlec = state->nlec;
    nlic = state->nlic;
    v = (double)(0);
    for(i=0; i<=nlec+nlic; i++)
    {
        vv = (double)1/state->fscales.ptr.p_double[i];
        vfj->fi.ptr.p_double[i] = vv*state->fi.ptr.p_double[i];
        v = v+vfj->fi.ptr.p_double[i];
        for(j=0; j<=n-1; j++)
        {
            vfj->jac.ptr.pp_double[i][j] = vv*state->j.ptr.pp_double[i][j];
            v = v+vfj->jac.ptr.pp_double[i][j];
        }
    }
    result = ae_isfinite(v, _state);
    return result;
}


/*************************************************************************
This function calculates Lagrangian of the problem (in scaled variables):
its value and gradient.

Additionally it also estimates violation of linear constraints at the point
as well as index of the most violated constraint
*************************************************************************/
static void nlcsqp_lagrangianfg(minsqpstate* state,
     /* Real    */ const ae_vector* x,
     double trustrad,
     /* Real    */ const ae_vector* fi,
     /* Real    */ const ae_matrix* j,
     /* Real    */ const ae_vector* lagbcmult,
     /* Real    */ const ae_vector* lagxcmult,
     ae_bool uselagrangeterms,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t nlec;
    ae_int_t nlic;
    double v;
    double vlag;
    double vact;
    double vd;
    ae_bool usesparsegemv;
    double lagalpha;

    *f = 0.0;

    n = state->n;
    nec = state->nec;
    nic = state->nic;
    nlec = state->nlec;
    nlic = state->nlic;
    lagalpha = rcase2(uselagrangeterms, (double)(1), (double)(0), _state);
    
    /*
     * Target function
     */
    *f = fi->ptr.p_double[0];
    for(i=0; i<=n-1; i++)
    {
        g->ptr.p_double[i] = j->ptr.pp_double[0][i];
    }
    
    /*
     * Lagrangian terms for box constraints
     */
    for(i=0; i<=n-1; i++)
    {
        
        /*
         * Lagrangian terms
         */
        *f = *f+lagalpha*lagbcmult->ptr.p_double[i]*x->ptr.p_double[i];
        g->ptr.p_double[i] = g->ptr.p_double[i]+lagalpha*lagbcmult->ptr.p_double[i];
        
        /*
         * Penalty term
         */
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less(x->ptr.p_double[i],state->scaledbndl.ptr.p_double[i]) )
        {
            *f = *f+nlcsqp_augmentationfactor*(x->ptr.p_double[i]-state->scaledbndl.ptr.p_double[i])*(x->ptr.p_double[i]-state->scaledbndl.ptr.p_double[i]);
            g->ptr.p_double[i] = g->ptr.p_double[i]+(double)2*nlcsqp_augmentationfactor*(x->ptr.p_double[i]-state->scaledbndl.ptr.p_double[i]);
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater(x->ptr.p_double[i],state->scaledbndu.ptr.p_double[i]) )
        {
            *f = *f+nlcsqp_augmentationfactor*(x->ptr.p_double[i]-state->scaledbndu.ptr.p_double[i])*(x->ptr.p_double[i]-state->scaledbndu.ptr.p_double[i]);
            g->ptr.p_double[i] = g->ptr.p_double[i]+(double)2*nlcsqp_augmentationfactor*(x->ptr.p_double[i]-state->scaledbndu.ptr.p_double[i]);
        }
    }
    
    /*
     * Lagrangian terms for linear constraints, constraint violations
     */
    if( nec+nic>0 )
    {
        usesparsegemv = (double)state->subsolver.sparserawlc.ridx.ptr.p_int[nec+nic]<sparselevel2density(_state)*(double)n*(double)(nec+nic);
        rvectorsetlengthatleast(&state->sclagtmp0, ae_maxint(nec+nic, n, _state), _state);
        rvectorsetlengthatleast(&state->sclagtmp1, ae_maxint(nec+nic, n, _state), _state);
        if( usesparsegemv )
        {
            sparsemv(&state->subsolver.sparserawlc, x, &state->sclagtmp0, _state);
        }
        else
        {
            rmatrixgemv(nec+nic, n, 1.0, &state->scaledcleic, 0, 0, 0, x, 0, 0.0, &state->sclagtmp0, 0, _state);
        }
        for(i=0; i<=nec+nic-1; i++)
        {
            
            /*
             * Prepare
             */
            v = state->sclagtmp0.ptr.p_double[i]-state->scaledcleic.ptr.pp_double[i][n];
            vlag = lagalpha*lagxcmult->ptr.p_double[i];
            state->sclagtmp1.ptr.p_double[i] = (double)(0);
            
            /*
             * Primary Lagrangian term
             */
            vact = v;
            vd = (double)(1);
            *f = *f+vlag*vact;
            state->sclagtmp1.ptr.p_double[i] = state->sclagtmp1.ptr.p_double[i]+vlag*vd;
            
            /*
             * Quadratic augmentation term
             */
            if( i<nec||v>(double)0 )
            {
                vact = v;
            }
            else
            {
                vact = (double)(0);
            }
            *f = *f+0.5*nlcsqp_augmentationfactor*vact*vact;
            state->sclagtmp1.ptr.p_double[i] = state->sclagtmp1.ptr.p_double[i]+nlcsqp_augmentationfactor*vact;
        }
        if( usesparsegemv )
        {
            sparsemtv(&state->subsolver.sparserawlc, &state->sclagtmp1, &state->sclagtmp0, _state);
            for(i=0; i<=n-1; i++)
            {
                g->ptr.p_double[i] = g->ptr.p_double[i]+state->sclagtmp0.ptr.p_double[i];
            }
        }
        else
        {
            rmatrixgemv(n, nec+nic, 1.0, &state->scaledcleic, 0, 0, 1, &state->sclagtmp1, 0, 1.0, g, 0, _state);
        }
    }
    
    /*
     * Lagrangian terms for nonlinear constraints
     */
    rvectorsetlengthatleast(&state->sclagtmp1, nlec+nlic, _state);
    for(i=0; i<=nlec+nlic-1; i++)
    {
        v = fi->ptr.p_double[1+i];
        vlag = lagalpha*lagxcmult->ptr.p_double[nec+nic+i];
        state->sclagtmp1.ptr.p_double[i] = (double)(0);
        
        /*
         * Lagrangian term
         */
        vact = v;
        vd = (double)(1);
        *f = *f+vlag*vact;
        state->sclagtmp1.ptr.p_double[i] = state->sclagtmp1.ptr.p_double[i]+vlag*vd;
        
        /*
         * Augmentation term
         */
        if( i<nlec||v>(double)0 )
        {
            vact = v;
        }
        else
        {
            vact = (double)(0);
        }
        *f = *f+0.5*nlcsqp_augmentationfactor*vact*vact;
        state->sclagtmp1.ptr.p_double[i] = state->sclagtmp1.ptr.p_double[i]+nlcsqp_augmentationfactor*vact;
    }
    rmatrixgemv(n, nlec+nlic, 1.0, j, 1, 0, 1, &state->sclagtmp1, 0, 1.0, g, 0, _state);
}


/*************************************************************************
This function calculates L1-penalized merit function
*************************************************************************/
static double nlcsqp_meritfunction(minsqpstate* state,
     const varsfuncjac* vfj,
     double meritmu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t nlec;
    ae_int_t nlic;
    double v;
    double result;


    ae_assert(vfj->isdense, "SQP: integrity check 1057 failed", _state);
    n = state->n;
    nec = state->nec;
    nic = state->nic;
    nlec = state->nlec;
    nlic = state->nlic;
    
    /*
     * Merit function and Lagrangian: primary term
     */
    result = vfj->fi.ptr.p_double[0];
    
    /*
     * Merit function: augmentation and penalty for linear constraints
     */
    rvectorsetlengthatleast(&state->mftmp0, nec+nic, _state);
    rmatrixgemv(nec+nic, n, 1.0, &state->scaledcleic, 0, 0, 0, &vfj->x, 0, 0.0, &state->mftmp0, 0, _state);
    for(i=0; i<=nec+nic-1; i++)
    {
        v = state->mftmp0.ptr.p_double[i]-state->scaledcleic.ptr.pp_double[i][n];
        if( i<nec )
        {
            
            /*
             * Merit function: augmentation term + L1 penalty term
             */
            result = result+meritmu*ae_fabs(v, _state);
        }
        else
        {
            
            /*
             * Merit function: augmentation term + L1 penalty term
             */
            result = result+meritmu*ae_maxreal(v, (double)(0), _state);
        }
    }
    
    /*
     * Merit function: augmentation and penalty for nonlinear constraints
     */
    for(i=0; i<=nlec+nlic-1; i++)
    {
        v = vfj->fi.ptr.p_double[1+i];
        if( i<nlec )
        {
            
            /*
             * Merit function: augmentation term + L1 penalty term
             */
            result = result+meritmu*ae_fabs(v, _state);
        }
        else
        {
            
            /*
             * Merit function: augmentation term + L1 penalty term
             */
            result = result+meritmu*ae_maxreal(v, (double)(0), _state);
        }
    }
    return result;
}


/*************************************************************************
This function calculates raw (unaugmented and smooth) Lagrangian
*************************************************************************/
static double nlcsqp_rawlagrangian(minsqpstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     /* Real    */ const ae_vector* lagbcmult,
     /* Real    */ const ae_vector* lagxcmult,
     double meritmu,
     ae_state *_state)
{
    double tmp0;
    double tmp1;
    double result;


    nlcsqp_meritfunctionandrawlagrangian(state, x, fi, lagbcmult, lagxcmult, meritmu, &tmp0, &tmp1, _state);
    result = tmp1;
    return result;
}


/*************************************************************************
This function calculates L1-penalized merit function and raw  (smooth  and
un-augmented) Lagrangian
*************************************************************************/
static void nlcsqp_meritfunctionandrawlagrangian(minsqpstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     /* Real    */ const ae_vector* lagbcmult,
     /* Real    */ const ae_vector* lagxcmult,
     double meritmu,
     double* meritf,
     double* rawlag,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t nlec;
    ae_int_t nlic;
    double v;

    *meritf = 0.0;
    *rawlag = 0.0;

    n = state->n;
    nec = state->nec;
    nic = state->nic;
    nlec = state->nlec;
    nlic = state->nlic;
    
    /*
     * Merit function and Lagrangian: primary term
     */
    *meritf = fi->ptr.p_double[0];
    *rawlag = fi->ptr.p_double[0];
    
    /*
     * Lagrangian: handle box constraints
     *
     * NOTE: we do not add box constrained term to the merit function because
     *       box constraints are handled exactly and we do not violate them.
     */
    for(i=0; i<=n-1; i++)
    {
        *rawlag = *rawlag+lagbcmult->ptr.p_double[i]*x->ptr.p_double[i];
    }
    
    /*
     * Merit function: augmentation and penalty for linear constraints
     */
    rvectorsetlengthatleast(&state->mftmp0, nec+nic, _state);
    rmatrixgemv(nec+nic, n, 1.0, &state->scaledcleic, 0, 0, 0, x, 0, 0.0, &state->mftmp0, 0, _state);
    for(i=0; i<=nec+nic-1; i++)
    {
        v = state->mftmp0.ptr.p_double[i]-state->scaledcleic.ptr.pp_double[i][n];
        if( i<nec )
        {
            
            /*
             * Merit function: augmentation term + L1 penalty term
             */
            *meritf = *meritf+meritmu*ae_fabs(v, _state);
            
            /*
             * Raw Lagrangian
             */
            *rawlag = *rawlag+lagxcmult->ptr.p_double[i]*v;
        }
        else
        {
            
            /*
             * Merit function: augmentation term + L1 penalty term
             */
            *meritf = *meritf+meritmu*ae_maxreal(v, (double)(0), _state);
            
            /*
             * Raw Lagrangian
             */
            *rawlag = *rawlag+lagxcmult->ptr.p_double[i]*v;
        }
    }
    
    /*
     * Merit function: augmentation and penalty for nonlinear constraints
     */
    for(i=0; i<=nlec+nlic-1; i++)
    {
        v = fi->ptr.p_double[1+i];
        if( i<nlec )
        {
            
            /*
             * Merit function: augmentation term + L1 penalty term
             */
            *meritf = *meritf+meritmu*ae_fabs(v, _state);
            
            /*
             * Raw Lagrangian
             */
            *rawlag = *rawlag+lagxcmult->ptr.p_double[nec+nic+i]*v;
        }
        else
        {
            
            /*
             * Merit function: augmentation term + L1 penalty term
             */
            *meritf = *meritf+meritmu*ae_maxreal(v, (double)(0), _state);
            
            /*
             * Raw Lagrangian
             */
            *rawlag = *rawlag+lagxcmult->ptr.p_double[nec+nic+i]*v;
        }
    }
}


/*************************************************************************
This function analyzes constraint linearizations at the proposed candidate
point. Depending on outcome, proposes to increase MeritMu or not.

If no update is necessary (the best outcome), False will be returned.

  -- ALGLIB --
     Copyright 13.03.2023 by Bochkanov Sergey
*************************************************************************/
static ae_bool nlcsqp_penaltiesneedincrease(minsqpstate* state,
     const varsfuncjac* currentlinearization,
     /* Real    */ const ae_vector* dtrial,
     double predictedchangemodel,
     double predictedchangepenalty,
     ae_bool dotrace,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t nlec;
    ae_int_t nlic;
    ae_int_t nslack;
    ae_int_t i;
    ae_int_t j;
    ae_int_t tt;
    double tol;
    double tol2;
    double constrval;
    double allowederr;
    ae_bool failedtoenforce;
    ae_bool failedtoenforceinf;
    ae_bool infeasiblewithinbox;
    double dummy0;
    double dummy1;
    double dummy2;
    double dummy3;
    double penaltyatx;
    double penaltyatd;
    double penaltyatdinf;
    double meritdecrease;
    double penaltydecrease;
    double mxd;
    double expandedrad;
    double cx;
    double cxd;
    double sumabs;
    ae_bool result;


    n = state->n;
    nec = state->nec;
    nic = state->nic;
    nlec = state->nlec;
    nlic = state->nlic;
    nslack = n+2*(nec+nlec)+(nic+nlic);
    result = ae_false;
    tol = 1.0E-6*state->trustrad*((double)1+rmaxabsv(n, &currentlinearization->x, _state));
    
    /*
     * Already too big, skip analysis
     */
    if( ae_fp_greater_eq(state->meritmu,0.9*nlcsqp_maxmeritmu) )
    {
        return result;
    }
    
    /*
     * Allocate storage
     */
    rallocv(nslack, &state->tmppend, _state);
    rallocv(n, &state->dummylagbcmult, _state);
    rallocv(nec+nic+nlec+nlic, &state->dummylagxcmult, _state);
    
    /*
     * Compute the following quantities:
     * * penalty at X
     * * penalty at X+D
     * * whether we failed to enforce constraints at X+D or not
     * * whether any single constraint can be made feasible within max(|D|)-sized box (ignoring other constraints) or not
     */
    penaltyatx = (double)(0);
    penaltyatd = (double)(0);
    failedtoenforce = ae_false;
    infeasiblewithinbox = ae_false;
    mxd = rmaxabsv(n, dtrial, _state);
    expandedrad = 1.1*mxd;
    tol2 = ae_maxreal(ae_sqrt(ae_machineepsilon, _state)*state->trustrad, (double)1000*ae_machineepsilon, _state);
    for(i=0; i<=nec+nic-1; i++)
    {
        cx = rdotvr(n, &currentlinearization->x, &state->scaledcleic, i, _state)-state->scaledcleic.ptr.pp_double[i][n];
        cxd = cx+rdotvr(n, dtrial, &state->scaledcleic, i, _state);
        cx = rcase2(i<nec, cx, ae_maxreal(cx, 0.0, _state), _state);
        cxd = rcase2(i<nec, cxd, ae_maxreal(cxd, 0.0, _state), _state);
        sumabs = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            sumabs = sumabs+ae_fabs(state->scaledcleic.ptr.pp_double[i][j], _state);
        }
        infeasiblewithinbox = infeasiblewithinbox||ae_fp_greater(ae_fabs(cx, _state),sumabs*expandedrad+tol2);
        penaltyatx = penaltyatx+ae_fabs(cx, _state);
        penaltyatd = penaltyatd+ae_fabs(cxd, _state);
        failedtoenforce = failedtoenforce||ae_fp_greater(ae_fabs(cxd, _state),sumabs*tol);
    }
    for(i=1; i<=nlec+nlic; i++)
    {
        cx = currentlinearization->fi.ptr.p_double[i];
        cxd = cx+rdotvr(n, dtrial, &currentlinearization->jac, i, _state);
        cx = rcase2(i<1+nlec, cx, ae_maxreal(cx, 0.0, _state), _state);
        cxd = rcase2(i<1+nlec, cxd, ae_maxreal(cxd, 0.0, _state), _state);
        sumabs = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            sumabs = sumabs+ae_fabs(currentlinearization->jac.ptr.pp_double[i][j], _state);
        }
        infeasiblewithinbox = infeasiblewithinbox||ae_fp_greater(ae_fabs(cx, _state),sumabs*expandedrad+tol2);
        penaltyatx = penaltyatx+ae_fabs(cx, _state);
        penaltyatd = penaltyatd+ae_fabs(cxd, _state);
        failedtoenforce = failedtoenforce||ae_fp_greater(ae_fabs(cxd, _state),sumabs*tol);
    }
    if( infeasiblewithinbox&&ae_fp_less(mxd,0.9*state->trustrad) )
    {
        
        /*
         * MeritMu must be increased if a short DTrial was proposed, but there are some
         * constraints that are infeasible within max|DTrial|-sized box
         */
        result = ae_true;
        if( dotrace )
        {
            ae_trace("> a short DTrial was proposed, but some constraints are infeasible within trust region; MeritMu increased, iteration skipped\n");
        }
    }
    
    /*
     * We may need to increase MeritMu
     */
    if( failedtoenforce )
    {
        
        /*
         * We failed to drive constraints linearizations to zero. But what is the best
         * theoretically possible reduction (ignoring quadratic model)?
         */
        if( !nlcsqp_qpsubproblemsolve(state, &state->subsolver, &currentlinearization->x, &currentlinearization->fi, &currentlinearization->jac, &state->hess, 0.0, 0.0, &state->tmppend, &state->dummylagbcmult, &state->dummylagxcmult, dotrace, &tt, &dummy0, &dummy1, &dummy2, &dummy3, _state) )
        {
            if( dotrace )
            {
                ae_trace("> [WARNING] QP subproblem failed with TerminationType=%0d when updating MeritMu\n",
                    (int)(tt));
            }
            return result;
        }
        penaltyatdinf = (double)(0);
        failedtoenforceinf = ae_false;
        for(i=0; i<=nec+nic-1; i++)
        {
            constrval = -state->scaledcleic.ptr.pp_double[i][n];
            allowederr = (double)(0);
            for(j=0; j<=n-1; j++)
            {
                constrval = constrval+state->scaledcleic.ptr.pp_double[i][j]*(currentlinearization->x.ptr.p_double[j]+state->tmppend.ptr.p_double[j]);
                allowederr = allowederr+ae_fabs(state->scaledcleic.ptr.pp_double[i][j]*tol, _state);
            }
            if( i>=nec )
            {
                constrval = ae_maxreal(constrval, 0.0, _state);
            }
            penaltyatdinf = penaltyatdinf+ae_fabs(constrval, _state);
            failedtoenforceinf = failedtoenforceinf||ae_fp_greater(ae_fabs(constrval, _state),allowederr);
        }
        for(i=0; i<=nlec+nlic-1; i++)
        {
            constrval = currentlinearization->fi.ptr.p_double[1+i];
            allowederr = (double)(0);
            for(j=0; j<=n-1; j++)
            {
                constrval = constrval+currentlinearization->jac.ptr.pp_double[1+i][j]*state->tmppend.ptr.p_double[j];
                allowederr = allowederr+ae_fabs(currentlinearization->jac.ptr.pp_double[1+i][j]*tol, _state);
            }
            if( i>=nlec )
            {
                constrval = ae_maxreal(constrval, 0.0, _state);
            }
            penaltyatdinf = penaltyatdinf+ae_fabs(constrval, _state);
            failedtoenforceinf = failedtoenforceinf||ae_fp_greater(ae_fabs(constrval, _state),allowederr);
        }
        
        /*
         * Branch on the constraint status within trust region
         */
        if( failedtoenforceinf )
        {
            
            /*
             * Constraints are infeasible within the trust region, check that the actual penalty decrease
             * is at least some fraction of the best possible decrease.
             */
            if( ae_fp_less(penaltyatx-penaltyatd,nlcsqp_meritmueps*ae_maxreal(penaltyatx-penaltyatdinf, 0.0, _state))&&ae_fp_less(penaltyatdinf,penaltyatx*((double)1-nlcsqp_constraintsstagnationeps)) )
            {
                
                /*
                 * Merit function does not decrease fast enough, we must increase MeritMu
                 */
                result = ae_true;
                if( dotrace )
                {
                    ae_trace("> constraints infeasibility does not decrease fast enough, MeritMu increased, iteration skipped\n");
                }
            }
        }
        else
        {
            
            /*
             * Constraints are feasible within trust region, we must increase MeritMu
             */
            result = ae_true;
            if( dotrace )
            {
                ae_trace("> MeritMu penalty does not enforce constraints feasibility, MeritMu increased, iteration skipped\n");
            }
        }
    }
    
    /*
     * Now we check that decrease in the quadratic model + penalty is a significant fraction of
     * the decrease in the penalty.
     */
    meritdecrease = -(predictedchangemodel+state->meritmu*predictedchangepenalty);
    penaltydecrease = -state->meritmu*predictedchangepenalty;
    if( ae_fp_less(meritdecrease,nlcsqp_meritmueps*penaltydecrease) )
    {
        result = ae_true;
        if( dotrace )
        {
            ae_trace("> sufficient negativity condition does not hold, MeritMu increased, iteration skipped\n");
        }
    }
    return result;
}


/*************************************************************************
Returns k-th component of the trust region

  -- ALGLIB --
     Copyright 25.09.2023 by Bochkanov Sergey
*************************************************************************/
static double nlcsqp_gettrustregionk(/* Real    */ const ae_vector* xcur,
     ae_int_t k,
     double trustrad,
     ae_state *_state)
{
    double result;


    result = trustrad*ae_maxreal(ae_fabs(xcur->ptr.p_double[k], _state), (double)(1), _state);
    return result;
}


/*************************************************************************
Adjusts merit function value using non-monotonic correction  (sets  it  to
maximum among several last values, returns adjusted value)

  -- ALGLIB --
     Copyright 25.09.2023 by Bochkanov Sergey
*************************************************************************/
static double nlcsqp_nonmonotonicadjustment(minsqpstate* state,
     double fraw,
     ae_state *_state)
{
    ae_frame _frame_block;
    varsfuncjac *vfj;
    ae_smart_ptr _vfj;
    ae_int_t i;
    double result;

    ae_frame_make(_state, &_frame_block);
    memset(&_vfj, 0, sizeof(_vfj));
    ae_smart_ptr_init(&_vfj, (void**)&vfj, ae_false, _state, ae_true);

    result = fraw;
    for(i=0; i<=ae_minint(state->nonmonotoniccnt, nlcsqp_nonmonotoniclen, _state)-1; i++)
    {
        ae_obj_array_get(&state->nonmonotonicmem, i, &_vfj, _state);
        result = ae_maxreal(result, nlcsqp_meritfunction(state, vfj, state->meritmu, _state), _state);
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Saves current point to the nonmonotonic memory.

  -- ALGLIB --
     Copyright 25.09.2023 by Bochkanov Sergey
*************************************************************************/
static void nlcsqp_nonmonotonicsave(minsqpstate* state,
     const varsfuncjac* cur,
     ae_state *_state)
{
    ae_frame _frame_block;
    varsfuncjac *vfj;
    ae_smart_ptr _vfj;

    ae_frame_make(_state, &_frame_block);
    memset(&_vfj, 0, sizeof(_vfj));
    ae_smart_ptr_init(&_vfj, (void**)&vfj, ae_false, _state, ae_true);

    if( nlcsqp_nonmonotoniclen==0 )
    {
        ae_frame_leave(_state);
        return;
    }
    if( state->nonmonotoniccnt<nlcsqp_nonmonotoniclen )
    {
        
        /*
         * Append point to the buffer
         */
        ae_assert(ae_obj_array_get_length(&state->nonmonotonicmem)==state->nonmonotoniccnt, "SQP: integrity check 3710 failed", _state);
        vfj = (varsfuncjac*)ae_malloc(sizeof(varsfuncjac), _state); /* note: using vfj as a temporary prior to assigning its value to _vfj */
        memset(vfj, 0, sizeof(varsfuncjac));
        _varsfuncjac_init(vfj, _state, ae_false);
        ae_smart_ptr_assign(&_vfj, vfj, ae_true, ae_true, (ae_int_t)sizeof(varsfuncjac), _varsfuncjac_init_copy, _varsfuncjac_destroy);
        vfjcopy(cur, vfj, _state);
        ae_obj_array_append_transfer(&state->nonmonotonicmem, &_vfj, _state);
    }
    else
    {
        
        /*
         * Rewrite one of points in the buffer
         */
        ae_assert(ae_obj_array_get_length(&state->nonmonotonicmem)==nlcsqp_nonmonotoniclen, "SQP: integrity check 3711 failed", _state);
        ae_obj_array_get(&state->nonmonotonicmem, state->nonmonotoniccnt%nlcsqp_nonmonotoniclen, &_vfj, _state);
        vfjcopy(cur, vfj, _state);
    }
    state->nonmonotoniccnt = state->nonmonotoniccnt+1;
    ae_frame_leave(_state);
}


/*************************************************************************
Rescales target/constraints stored in the nonmonotonic memory.

  -- ALGLIB --
     Copyright 25.09.2023 by Bochkanov Sergey
*************************************************************************/
static void nlcsqp_nonmonotonicmultiplyby(minsqpstate* state,
     ae_int_t jacrow,
     double v,
     ae_state *_state)
{
    ae_frame _frame_block;
    varsfuncjac *vfj;
    ae_smart_ptr _vfj;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&_vfj, 0, sizeof(_vfj));
    ae_smart_ptr_init(&_vfj, (void**)&vfj, ae_false, _state, ae_true);

    for(i=0; i<=ae_minint(state->nonmonotoniccnt, nlcsqp_nonmonotoniclen, _state)-1; i++)
    {
        ae_obj_array_get(&state->nonmonotonicmem, i, &_vfj, _state);
        ae_assert(vfj->isdense, "SQP: integrity check 7600 failed", _state);
        vfj->fi.ptr.p_double[jacrow] = vfj->fi.ptr.p_double[jacrow]*v;
        rmulr(vfj->n, v, &vfj->jac, jacrow, _state);
    }
    ae_frame_leave(_state);
}


void _minsqpsubsolver_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minsqpsubsolver *p = (minsqpsubsolver*)_p;
    ae_touch_ptr((void*)p);
    _vipmstate_init(&p->ipmsolver, _state, make_automatic);
    _ipm2state_init(&p->ipm2, _state, make_automatic);
    ae_vector_init(&p->curb, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->curbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->curbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cural, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->curau, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparserawlc, _state, make_automatic);
    _sparsematrix_init(&p->sparseefflc, _state, make_automatic);
    ae_vector_init(&p->d0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->denseh, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dummy1, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->densedummy, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparsedummy, _state, make_automatic);
    ae_vector_init(&p->tmps, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmporigin, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->retainnegativebclm, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->retainpositivebclm, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->rescalelag, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpcorrc, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpdiag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hessdiag, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->hesscorrc, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hesscorrd, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->hesssparsediag, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasal, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasau, 0, DT_BOOL, _state, make_automatic);
}


void _minsqpsubsolver_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minsqpsubsolver       *dst = (minsqpsubsolver*)_dst;
    const minsqpsubsolver *src = (const minsqpsubsolver*)_src;
    _vipmstate_init_copy(&dst->ipmsolver, &src->ipmsolver, _state, make_automatic);
    _ipm2state_init_copy(&dst->ipm2, &src->ipm2, _state, make_automatic);
    ae_vector_init_copy(&dst->curb, &src->curb, _state, make_automatic);
    ae_vector_init_copy(&dst->curbndl, &src->curbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->curbndu, &src->curbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->cural, &src->cural, _state, make_automatic);
    ae_vector_init_copy(&dst->curau, &src->curau, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparserawlc, &src->sparserawlc, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparseefflc, &src->sparseefflc, _state, make_automatic);
    ae_vector_init_copy(&dst->d0, &src->d0, _state, make_automatic);
    ae_matrix_init_copy(&dst->denseh, &src->denseh, _state, make_automatic);
    ae_vector_init_copy(&dst->dummy1, &src->dummy1, _state, make_automatic);
    ae_matrix_init_copy(&dst->densedummy, &src->densedummy, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsedummy, &src->sparsedummy, _state, make_automatic);
    ae_vector_init_copy(&dst->tmps, &src->tmps, _state, make_automatic);
    ae_vector_init_copy(&dst->tmporigin, &src->tmporigin, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp2, &src->tmp2, _state, make_automatic);
    ae_vector_init_copy(&dst->retainnegativebclm, &src->retainnegativebclm, _state, make_automatic);
    ae_vector_init_copy(&dst->retainpositivebclm, &src->retainpositivebclm, _state, make_automatic);
    ae_vector_init_copy(&dst->rescalelag, &src->rescalelag, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpcorrc, &src->tmpcorrc, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpdiag, &src->tmpdiag, _state, make_automatic);
    ae_vector_init_copy(&dst->hessdiag, &src->hessdiag, _state, make_automatic);
    ae_matrix_init_copy(&dst->hesscorrc, &src->hesscorrc, _state, make_automatic);
    ae_vector_init_copy(&dst->hesscorrd, &src->hesscorrd, _state, make_automatic);
    dst->hessrank = src->hessrank;
    _sparsematrix_init_copy(&dst->hesssparsediag, &src->hesssparsediag, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->hasal, &src->hasal, _state, make_automatic);
    ae_vector_init_copy(&dst->hasau, &src->hasau, _state, make_automatic);
}


void _minsqpsubsolver_clear(void* _p)
{
    minsqpsubsolver *p = (minsqpsubsolver*)_p;
    ae_touch_ptr((void*)p);
    _vipmstate_clear(&p->ipmsolver);
    _ipm2state_clear(&p->ipm2);
    ae_vector_clear(&p->curb);
    ae_vector_clear(&p->curbndl);
    ae_vector_clear(&p->curbndu);
    ae_vector_clear(&p->cural);
    ae_vector_clear(&p->curau);
    _sparsematrix_clear(&p->sparserawlc);
    _sparsematrix_clear(&p->sparseefflc);
    ae_vector_clear(&p->d0);
    ae_matrix_clear(&p->denseh);
    ae_vector_clear(&p->dummy1);
    ae_matrix_clear(&p->densedummy);
    _sparsematrix_clear(&p->sparsedummy);
    ae_vector_clear(&p->tmps);
    ae_vector_clear(&p->tmporigin);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmp1);
    ae_vector_clear(&p->tmp2);
    ae_vector_clear(&p->retainnegativebclm);
    ae_vector_clear(&p->retainpositivebclm);
    ae_vector_clear(&p->rescalelag);
    ae_matrix_clear(&p->tmpcorrc);
    ae_vector_clear(&p->tmpdiag);
    ae_vector_clear(&p->hessdiag);
    ae_matrix_clear(&p->hesscorrc);
    ae_vector_clear(&p->hesscorrd);
    _sparsematrix_clear(&p->hesssparsediag);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->hasal);
    ae_vector_clear(&p->hasau);
}


void _minsqpsubsolver_destroy(void* _p)
{
    minsqpsubsolver *p = (minsqpsubsolver*)_p;
    ae_touch_ptr((void*)p);
    _vipmstate_destroy(&p->ipmsolver);
    _ipm2state_destroy(&p->ipm2);
    ae_vector_destroy(&p->curb);
    ae_vector_destroy(&p->curbndl);
    ae_vector_destroy(&p->curbndu);
    ae_vector_destroy(&p->cural);
    ae_vector_destroy(&p->curau);
    _sparsematrix_destroy(&p->sparserawlc);
    _sparsematrix_destroy(&p->sparseefflc);
    ae_vector_destroy(&p->d0);
    ae_matrix_destroy(&p->denseh);
    ae_vector_destroy(&p->dummy1);
    ae_matrix_destroy(&p->densedummy);
    _sparsematrix_destroy(&p->sparsedummy);
    ae_vector_destroy(&p->tmps);
    ae_vector_destroy(&p->tmporigin);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmp1);
    ae_vector_destroy(&p->tmp2);
    ae_vector_destroy(&p->retainnegativebclm);
    ae_vector_destroy(&p->retainpositivebclm);
    ae_vector_destroy(&p->rescalelag);
    ae_matrix_destroy(&p->tmpcorrc);
    ae_vector_destroy(&p->tmpdiag);
    ae_vector_destroy(&p->hessdiag);
    ae_matrix_destroy(&p->hesscorrc);
    ae_vector_destroy(&p->hesscorrd);
    _sparsematrix_destroy(&p->hesssparsediag);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->hasal);
    ae_vector_destroy(&p->hasau);
}


void _minsqpstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minsqpstate *p = (minsqpstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->scaledcleic, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lcsrcidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->scaledbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scaledbndu, 0, DT_REAL, _state, make_automatic);
    _nlpstoppingcriteria_init(&p->criteria, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->j, 0, 0, DT_REAL, _state, make_automatic);
    _varsfuncjac_init(&p->stepk, _state, make_automatic);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xprev, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fscales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tracegamma, 0, DT_REAL, _state, make_automatic);
    _minsqpsubsolver_init(&p->subsolver, _state, make_automatic);
    _xbfgshessian_init(&p->hess, _state, make_automatic);
    ae_obj_array_init(&p->nonmonotonicmem, _state, make_automatic);
    ae_vector_init(&p->lagbcmult, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagxcmult, 0, DT_REAL, _state, make_automatic);
    _varsfuncjac_init(&p->cand, _state, make_automatic);
    _varsfuncjac_init(&p->corr, _state, make_automatic);
    _varsfuncjac_init(&p->probe, _state, make_automatic);
    _varsfuncjac_init(&p->currentlinearization, _state, make_automatic);
    ae_vector_init(&p->dtrial, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dmu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmppend, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmphd, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dummylagbcmult, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dummylagxcmult, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmplaggrad, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpcandlaggrad, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmphdiag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sclagtmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sclagtmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->mftmp0, 0, DT_REAL, _state, make_automatic);
    _stimer_init(&p->timertotal, _state, make_automatic);
    _stimer_init(&p->timerqp, _state, make_automatic);
    _stimer_init(&p->timercallback, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
}


void _minsqpstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minsqpstate       *dst = (minsqpstate*)_dst;
    const minsqpstate *src = (const minsqpstate*)_src;
    dst->n = src->n;
    dst->nec = src->nec;
    dst->nic = src->nic;
    dst->nlec = src->nlec;
    dst->nlic = src->nlic;
    dst->usedensebfgs = src->usedensebfgs;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_matrix_init_copy(&dst->scaledcleic, &src->scaledcleic, _state, make_automatic);
    ae_vector_init_copy(&dst->lcsrcidx, &src->lcsrcidx, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledbndl, &src->scaledbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledbndu, &src->scaledbndu, _state, make_automatic);
    _nlpstoppingcriteria_init_copy(&dst->criteria, &src->criteria, _state, make_automatic);
    dst->bfgsresetfreq = src->bfgsresetfreq;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    ae_matrix_init_copy(&dst->j, &src->j, _state, make_automatic);
    dst->f = src->f;
    dst->needfij = src->needfij;
    dst->xupdated = src->xupdated;
    dst->meritmu = src->meritmu;
    dst->trustrad = src->trustrad;
    dst->trustradgrowth = src->trustradgrowth;
    dst->trustradstagnationcnt = src->trustradstagnationcnt;
    dst->fstagnationcnt = src->fstagnationcnt;
    _varsfuncjac_init_copy(&dst->stepk, &src->stepk, _state, make_automatic);
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->xprev, &src->xprev, _state, make_automatic);
    ae_vector_init_copy(&dst->fscales, &src->fscales, _state, make_automatic);
    ae_vector_init_copy(&dst->tracegamma, &src->tracegamma, _state, make_automatic);
    _minsqpsubsolver_init_copy(&dst->subsolver, &src->subsolver, _state, make_automatic);
    _xbfgshessian_init_copy(&dst->hess, &src->hess, _state, make_automatic);
    ae_obj_array_init_copy(&dst->nonmonotonicmem, &src->nonmonotonicmem, _state, make_automatic);
    dst->nonmonotoniccnt = src->nonmonotoniccnt;
    ae_vector_init_copy(&dst->lagbcmult, &src->lagbcmult, _state, make_automatic);
    ae_vector_init_copy(&dst->lagxcmult, &src->lagxcmult, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->cand, &src->cand, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->corr, &src->corr, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->probe, &src->probe, _state, make_automatic);
    _varsfuncjac_init_copy(&dst->currentlinearization, &src->currentlinearization, _state, make_automatic);
    ae_vector_init_copy(&dst->dtrial, &src->dtrial, _state, make_automatic);
    ae_vector_init_copy(&dst->d0, &src->d0, _state, make_automatic);
    ae_vector_init_copy(&dst->d1, &src->d1, _state, make_automatic);
    ae_vector_init_copy(&dst->dmu, &src->dmu, _state, make_automatic);
    ae_vector_init_copy(&dst->tmppend, &src->tmppend, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphd, &src->tmphd, _state, make_automatic);
    ae_vector_init_copy(&dst->dummylagbcmult, &src->dummylagbcmult, _state, make_automatic);
    ae_vector_init_copy(&dst->dummylagxcmult, &src->dummylagxcmult, _state, make_automatic);
    ae_vector_init_copy(&dst->tmplaggrad, &src->tmplaggrad, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpcandlaggrad, &src->tmpcandlaggrad, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphdiag, &src->tmphdiag, _state, make_automatic);
    ae_vector_init_copy(&dst->sclagtmp0, &src->sclagtmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->sclagtmp1, &src->sclagtmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->mftmp0, &src->mftmp0, _state, make_automatic);
    dst->repiterationscount = src->repiterationscount;
    dst->repterminationtype = src->repterminationtype;
    dst->repbcerr = src->repbcerr;
    dst->repbcidx = src->repbcidx;
    dst->replcerr = src->replcerr;
    dst->replcidx = src->replcidx;
    dst->repnlcerr = src->repnlcerr;
    dst->repnlcidx = src->repnlcidx;
    _stimer_init_copy(&dst->timertotal, &src->timertotal, _state, make_automatic);
    _stimer_init_copy(&dst->timerqp, &src->timerqp, _state, make_automatic);
    _stimer_init_copy(&dst->timercallback, &src->timercallback, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
}


void _minsqpstate_clear(void* _p)
{
    minsqpstate *p = (minsqpstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->s);
    ae_matrix_clear(&p->scaledcleic);
    ae_vector_clear(&p->lcsrcidx);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->scaledbndl);
    ae_vector_clear(&p->scaledbndu);
    _nlpstoppingcriteria_clear(&p->criteria);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->fi);
    ae_matrix_clear(&p->j);
    _varsfuncjac_clear(&p->stepk);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->xprev);
    ae_vector_clear(&p->fscales);
    ae_vector_clear(&p->tracegamma);
    _minsqpsubsolver_clear(&p->subsolver);
    _xbfgshessian_clear(&p->hess);
    ae_obj_array_clear(&p->nonmonotonicmem);
    ae_vector_clear(&p->lagbcmult);
    ae_vector_clear(&p->lagxcmult);
    _varsfuncjac_clear(&p->cand);
    _varsfuncjac_clear(&p->corr);
    _varsfuncjac_clear(&p->probe);
    _varsfuncjac_clear(&p->currentlinearization);
    ae_vector_clear(&p->dtrial);
    ae_vector_clear(&p->d0);
    ae_vector_clear(&p->d1);
    ae_vector_clear(&p->dmu);
    ae_vector_clear(&p->tmppend);
    ae_vector_clear(&p->tmphd);
    ae_vector_clear(&p->dummylagbcmult);
    ae_vector_clear(&p->dummylagxcmult);
    ae_vector_clear(&p->tmplaggrad);
    ae_vector_clear(&p->tmpcandlaggrad);
    ae_vector_clear(&p->tmphdiag);
    ae_vector_clear(&p->sclagtmp0);
    ae_vector_clear(&p->sclagtmp1);
    ae_vector_clear(&p->mftmp0);
    _stimer_clear(&p->timertotal);
    _stimer_clear(&p->timerqp);
    _stimer_clear(&p->timercallback);
    _rcommstate_clear(&p->rstate);
}


void _minsqpstate_destroy(void* _p)
{
    minsqpstate *p = (minsqpstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->s);
    ae_matrix_destroy(&p->scaledcleic);
    ae_vector_destroy(&p->lcsrcidx);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->scaledbndl);
    ae_vector_destroy(&p->scaledbndu);
    _nlpstoppingcriteria_destroy(&p->criteria);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->fi);
    ae_matrix_destroy(&p->j);
    _varsfuncjac_destroy(&p->stepk);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->xprev);
    ae_vector_destroy(&p->fscales);
    ae_vector_destroy(&p->tracegamma);
    _minsqpsubsolver_destroy(&p->subsolver);
    _xbfgshessian_destroy(&p->hess);
    ae_obj_array_destroy(&p->nonmonotonicmem);
    ae_vector_destroy(&p->lagbcmult);
    ae_vector_destroy(&p->lagxcmult);
    _varsfuncjac_destroy(&p->cand);
    _varsfuncjac_destroy(&p->corr);
    _varsfuncjac_destroy(&p->probe);
    _varsfuncjac_destroy(&p->currentlinearization);
    ae_vector_destroy(&p->dtrial);
    ae_vector_destroy(&p->d0);
    ae_vector_destroy(&p->d1);
    ae_vector_destroy(&p->dmu);
    ae_vector_destroy(&p->tmppend);
    ae_vector_destroy(&p->tmphd);
    ae_vector_destroy(&p->dummylagbcmult);
    ae_vector_destroy(&p->dummylagxcmult);
    ae_vector_destroy(&p->tmplaggrad);
    ae_vector_destroy(&p->tmpcandlaggrad);
    ae_vector_destroy(&p->tmphdiag);
    ae_vector_destroy(&p->sclagtmp0);
    ae_vector_destroy(&p->sclagtmp1);
    ae_vector_destroy(&p->mftmp0);
    _stimer_destroy(&p->timertotal);
    _stimer_destroy(&p->timerqp);
    _stimer_destroy(&p->timercallback);
    _rcommstate_destroy(&p->rstate);
}


#endif
#if defined(AE_COMPILE_SSGD) || !defined(AE_PARTIAL_BUILD)


void ssgdinitbuf(/* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* x0,
     ae_int_t n,
     /* Real    */ const ae_matrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t cntlc,
     /* Real    */ const ae_vector* nl,
     /* Real    */ const ae_vector* nu,
     ae_int_t cntnlc,
     double rad0,
     double rad1,
     ae_int_t outerits,
     double rate0,
     double rate1,
     double momentum,
     ae_int_t maxits,
     double rho,
     ssgdstate* state,
     ae_state *_state)
{
    ae_int_t i;


    
    /*
     * Integrity checks
     */
    ae_assert(ae_isfinite(rate0, _state), "SSGD: Rate0 is not a finite number", _state);
    ae_assert(ae_isfinite(rate1, _state), "SSGD: Rate1 is not a finite number", _state);
    ae_assert(ae_isfinite(momentum, _state), "SSGD: Momentum is not a finite number", _state);
    ae_assert(ae_isfinite(rad0, _state), "SSGD: Rad0 is not a finite number", _state);
    ae_assert(ae_isfinite(rad1, _state), "SSGD: Rad1 is not a finite number", _state);
    ae_assert(ae_isfinite(rho, _state), "SSGD: Rho is not a finite number", _state);
    ae_assert(ae_fp_greater(rate0,(double)(0)), "SSGD: Rate0<=0", _state);
    ae_assert(ae_fp_greater(rate1,(double)(0)), "SSGD: Rate1<=0", _state);
    ae_assert(ae_fp_greater_eq(momentum,(double)(0)), "SSGD: Momentum<0", _state);
    ae_assert(ae_fp_less(momentum,(double)(1)), "SSGD: Momentum>=1", _state);
    ae_assert(ae_fp_greater(rad0,(double)(0)), "SSGD: Rad0<=0", _state);
    ae_assert(ae_fp_greater(rad1,(double)(0)), "SSGD: Rad1<=0", _state);
    ae_assert(ae_fp_greater_eq(rate0,rate1), "SSGD: Rate0<Rate1", _state);
    ae_assert(ae_fp_greater_eq(rad0,rad1), "SSGD: Rad0<Rad1", _state);
    ae_assert(maxits>0, "SSGD: MaxIts<=0", _state);
    ae_assert(outerits>0, "SSGD: OuterIts<=0", _state);
    ae_assert(ae_fp_greater_eq(rho,(double)(0)), "SSGD: Rho<0", _state);
    
    /*
     * Initialization
     */
    state->n = n;
    state->cntlc = cntlc;
    state->cntnlc = cntnlc;
    state->rate0 = rate0;
    state->rate1 = rate1;
    state->blur0 = rad0;
    state->blur1 = rad1;
    state->outerits = outerits;
    state->maxits = maxits;
    state->rho = coalesce(rho, ssgd_defaultpenalty, _state);
    
    /*
     * Prepare RCOMM state
     */
    ae_vector_set_length(&state->rstate.ia, 6+1, _state);
    ae_vector_set_length(&state->rstate.ba, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 7+1, _state);
    state->rstate.stage = -1;
    state->needfi = ae_false;
    state->xupdated = ae_false;
    ae_vector_set_length(&state->x, n, _state);
    ae_vector_set_length(&state->fi, 1+cntnlc, _state);
    
    /*
     * Allocate memory.
     */
    rallocv(n, &state->x0, _state);
    rallocv(n, &state->s, _state);
    bvectorsetlengthatleast(&state->hasbndl, n, _state);
    bvectorsetlengthatleast(&state->hasbndu, n, _state);
    rsetallocv(n, -ae_maxrealnumber, &state->finitebndl, _state);
    rsetallocv(n, ae_maxrealnumber, &state->finitebndu, _state);
    
    /*
     * Prepare scaled problem
     */
    for(i=0; i<=n-1; i++)
    {
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
        if( state->hasbndl.ptr.p_bool[i] )
        {
            state->finitebndl.ptr.p_double[i] = bndl->ptr.p_double[i]/s->ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i] )
        {
            state->finitebndu.ptr.p_double[i] = bndu->ptr.p_double[i]/s->ptr.p_double[i];
        }
        if( state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i] )
        {
            ae_assert(ae_fp_less_eq(bndl->ptr.p_double[i],bndu->ptr.p_double[i]), "SSGD: integrity check failed, box constraints are inconsistent", _state);
        }
        state->x0.ptr.p_double[i] = x0->ptr.p_double[i]/s->ptr.p_double[i];
        state->s.ptr.p_double[i] = s->ptr.p_double[i];
    }
    if( cntlc>0 )
    {
        rsetallocv(n, 0.0, &state->tmpzero, _state);
        rcopyallocm(cntlc, n, a, &state->densea, _state);
        rcopyallocv(cntlc, al, &state->al, _state);
        rcopyallocv(cntlc, au, &state->au, _state);
        scaleshiftmixedlcinplace(s, &state->tmpzero, n, &state->dummysparsea, 0, &state->densea, cntlc, &state->al, &state->au, _state);
        normalizedenselcinplace(&state->densea, cntlc, &state->al, &state->au, n, ae_true, &state->ascales, ae_true, _state);
        ballocv(cntlc, &state->hasal, _state);
        ballocv(cntlc, &state->hasau, _state);
        for(i=0; i<=cntlc-1; i++)
        {
            state->hasal.ptr.p_bool[i] = ae_isfinite(state->al.ptr.p_double[i], _state);
            state->hasau.ptr.p_bool[i] = ae_isfinite(state->au.ptr.p_double[i], _state);
        }
    }
    if( cntnlc>0 )
    {
        rcopyallocv(cntnlc, nl, &state->rawnl, _state);
        rcopyallocv(cntnlc, nu, &state->rawnu, _state);
        ballocv(cntnlc, &state->hasnl, _state);
        ballocv(cntnlc, &state->hasnu, _state);
        for(i=0; i<=cntnlc-1; i++)
        {
            state->hasnl.ptr.p_bool[i] = ae_isfinite(state->rawnl.ptr.p_double[i], _state);
            state->hasnu.ptr.p_bool[i] = ae_isfinite(state->rawnu.ptr.p_double[i], _state);
        }
    }
}


/*************************************************************************
This function performs actual processing for SSGD  algorithm.  It  expects
that the caller redirects its reverse communication  requests  NeedFiJ  or
XUpdated to the external user,  possibly  wrapping  them  in  a  numerical
differentiation code.

  -- ALGLIB --
     Copyright 26.07.2023 by Bochkanov Sergey
*************************************************************************/
ae_bool ssgditeration(ssgdstate* state,
     ae_bool userterminationneeded,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t cntlc;
    ae_int_t cntnlc;
    ae_bool dotrace;
    ae_bool dodetailedtrace;
    ae_int_t outeridx;
    ae_int_t iteridx;
    ae_int_t i;
    ae_int_t j;
    double f0;
    double fbest;
    double fleft;
    double fright;
    double avgsqg;
    double finalscale;
    double t;
    double v;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        n = state->rstate.ia.ptr.p_int[0];
        cntlc = state->rstate.ia.ptr.p_int[1];
        cntnlc = state->rstate.ia.ptr.p_int[2];
        outeridx = state->rstate.ia.ptr.p_int[3];
        iteridx = state->rstate.ia.ptr.p_int[4];
        i = state->rstate.ia.ptr.p_int[5];
        j = state->rstate.ia.ptr.p_int[6];
        dotrace = state->rstate.ba.ptr.p_bool[0];
        dodetailedtrace = state->rstate.ba.ptr.p_bool[1];
        f0 = state->rstate.ra.ptr.p_double[0];
        fbest = state->rstate.ra.ptr.p_double[1];
        fleft = state->rstate.ra.ptr.p_double[2];
        fright = state->rstate.ra.ptr.p_double[3];
        avgsqg = state->rstate.ra.ptr.p_double[4];
        finalscale = state->rstate.ra.ptr.p_double[5];
        t = state->rstate.ra.ptr.p_double[6];
        v = state->rstate.ra.ptr.p_double[7];
    }
    else
    {
        n = 359;
        cntlc = -58;
        cntnlc = -919;
        outeridx = -909;
        iteridx = 81;
        i = 255;
        j = 74;
        dotrace = ae_false;
        dodetailedtrace = ae_true;
        f0 = 205.0;
        fbest = -838.0;
        fleft = 939.0;
        fright = -526.0;
        avgsqg = 763.0;
        finalscale = -541.0;
        t = -698.0;
        v = -900.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    
    /*
     * Routine body
     */
    n = state->n;
    cntlc = state->cntlc;
    cntnlc = state->cntnlc;
    dotrace = ae_is_trace_enabled("SSGD");
    dodetailedtrace = dotrace&&ae_is_trace_enabled("SSGD.DETAILED");
    
    /*
     * Prepare rcomm interface
     */
    ssgd_clearrequestfields(state, _state);
    
    /*
     * Initialize algorithm state.
     */
    hqrndseed(3366544, 86335763, &state->rs, _state);
    rallocv(n, &state->xoffs0, _state);
    rallocv(n, &state->xoffs1, _state);
    rallocv(n, &state->gcur, _state);
    rcopyallocv(n, &state->x0, &state->xcur, _state);
    rmergemaxv(n, &state->finitebndl, &state->xcur, _state);
    rmergeminv(n, &state->finitebndu, &state->xcur, _state);
    rsetallocv(n, 0.0, &state->dcur, _state);
    state->repiterationscount = 0;
    state->repnfev = 0;
    avgsqg = ae_machineepsilon;
    rsetallocv(1+cntnlc, ae_machineepsilon, &state->avgsqj, _state);
    rsetallocv(1+cntnlc, 1.0, &state->fscales, _state);
    finalscale = 1.0;
    rcopyallocv(n, &state->xcur, &state->xbest, _state);
    rcopyv(n, &state->xbest, &state->x, _state);
    state->needfi = ae_true;
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    state->needfi = ae_false;
    rcopyallocv(1+cntnlc, &state->fi, &state->fibest, _state);
    rallocv(n, &state->xleft, _state);
    rallocv(n, &state->xright, _state);
    rallocv(1+cntnlc, &state->fileft, _state);
    rallocv(1+cntnlc, &state->firight, _state);
    
    /*
     * Trace output (if needed)
     */
    if( dotrace )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("//  Smoothed SGD SOLVER STARTED                                                                   //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("N             = %6d\n",
            (int)(n));
        ae_trace("cntLC         = %6d\n",
            (int)(cntlc));
        ae_trace("cntNLC        = %6d\n",
            (int)(cntnlc));
    }
    
    /*
     * Perform SGD iterations
     */
    outeridx = 0;
lbl_4:
    if( outeridx>state->outerits-1 )
    {
        goto lbl_6;
    }
    
    /*
     * Choose initial point for an outer iteration as the best point found so far
     */
    rcopyv(n, &state->xbest, &state->xcur, _state);
    
    /*
     * Inner iteration: perform MaxIts SGD steps
     */
    iteridx = 0;
lbl_7:
    if( iteridx>state->maxits-1 )
    {
        goto lbl_9;
    }
    
    /*
     * Determine iteration parameters
     */
    t = (double)iteridx/(double)ae_maxint(state->maxits-1, 1, _state);
    state->currate = state->rate0*ae_exp(ae_log(state->rate1/state->rate0, _state)*ae_maxreal((double)2*t-(double)1, (double)(0), _state), _state);
    state->curblur = state->blur0*ae_exp(ae_log(state->blur1/state->blur0, _state)*(double)outeridx/(double)ae_maxint(state->outerits-1, 1, _state), _state);
    state->zcursecantstep = ssgd_secanttoblurratio*state->curblur;
    if( dotrace )
    {
        ae_trace("\n=== INNER ITERATION %5d, OUTER ITERATION %5d ===================================================\n",
            (int)(iteridx),
            (int)(outeridx));
        ae_trace("learn.Rate    = %0.3e\n",
            (double)(state->currate));
        ae_trace("smooth.Rad    = %0.3e\n",
            (double)(state->curblur));
        ae_trace("secantStep    = %0.3e\n",
            (double)(state->zcursecantstep));
    }
    
    /*
     * Compute target value at the current point, update best value so far
     */
    rcopyv(n, &state->xcur, &state->x, _state);
    state->needfi = ae_true;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->needfi = ae_false;
    f0 = ssgd_penalizedtarget(state, &state->x, &state->fi, _state);
    fbest = ssgd_penalizedtarget(state, &state->xbest, &state->fibest, _state);
    if( dodetailedtrace )
    {
        ae_trace("> printing current location and status:\n");
        ae_trace("X (raw)       = ");
        tracevectorunscaledunshiftedautoprec(&state->xcur, n, &state->s, ae_true, &state->s, ae_false, _state);
        ae_trace("\n");
        ae_trace("X (scaled)    = ");
        tracevectorautoprec(&state->xcur, 0, n, _state);
        ae_trace("\n");
    }
    if( ae_fp_less(f0,fbest) )
    {
        rcopyv(n, &state->xcur, &state->xbest, _state);
        rcopyallocv(1+cntnlc, &state->fi, &state->fibest, _state);
    }
    if( dotrace )
    {
        ae_trace("\n--- current target ---------------------------------------------------------------------------------\n");
        ae_trace("F             = %0.12e\n",
            (double)(state->fi.ptr.p_double[0]));
        ae_trace("F-penalized   = %0.12e\n",
            (double)(f0));
        ae_trace("lin.violation = %0.3e\n",
            (double)(ssgd_lcviolation(state, &state->xcur, _state)));
        ae_trace("\n--- best point so far ------------------------------------------------------------------------------\n");
        ae_trace("F             = %0.12e\n",
            (double)(state->fibest.ptr.p_double[0]));
        ae_trace("F-penalized   = %0.12e\n",
            (double)(fbest));
        ae_trace("lin.violation = %0.3e\n",
            (double)(ssgd_lcviolation(state, &state->xbest, _state)));
    }
    
    /*
     * Compute gradients (target, linear penalties, nonlinear penalties) using secant update
     */
    for(i=0; i<=n-1; i++)
    {
        state->xoffs0.ptr.p_double[i] = hqrnduniformr(&state->rs, _state)-0.5;
        state->xoffs1.ptr.p_double[i] = state->xoffs0.ptr.p_double[i];
    }
    rsetallocv(1+cntnlc, 0.0, &state->sqj, _state);
    i = 0;
lbl_10:
    if( i>n-1 )
    {
        goto lbl_12;
    }
    
    /*
     * Compute f(x-SecantStep) and f(x+SecantStep)
     */
    rcopyv(n, &state->xcur, &state->x, _state);
    raddv(n, state->curblur, &state->xoffs0, &state->x, _state);
    state->x.ptr.p_double[i] = state->x.ptr.p_double[i]-state->zcursecantstep;
    rmergemaxv(n, &state->finitebndl, &state->x, _state);
    rmergeminv(n, &state->finitebndu, &state->x, _state);
    state->needfi = ae_true;
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->needfi = ae_false;
    rcopyv(n, &state->x, &state->xleft, _state);
    rcopyv(1+cntnlc, &state->fi, &state->fileft, _state);
    rcopyv(n, &state->xcur, &state->x, _state);
    raddv(n, state->curblur, &state->xoffs1, &state->x, _state);
    state->x.ptr.p_double[i] = state->x.ptr.p_double[i]+state->zcursecantstep;
    rmergemaxv(n, &state->finitebndl, &state->x, _state);
    rmergeminv(n, &state->finitebndu, &state->x, _state);
    state->needfi = ae_true;
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    state->needfi = ae_false;
    rcopyv(n, &state->x, &state->xright, _state);
    rcopyv(1+cntnlc, &state->fi, &state->firight, _state);
    
    /*
     * Compute squared norms of target/constraint gradients
     */
    for(j=0; j<=cntnlc; j++)
    {
        v = (state->firight.ptr.p_double[j]-state->fileft.ptr.p_double[j])/((double)2*state->zcursecantstep);
        state->sqj.ptr.p_double[j] = state->sqj.ptr.p_double[j]+v*v;
    }
    
    /*
     * Compute gradient using current target/constraint scales
     */
    state->gcur.ptr.p_double[i] = (state->firight.ptr.p_double[0]-state->fileft.ptr.p_double[0])/((double)2*state->fscales.ptr.p_double[0]*state->zcursecantstep);
    for(j=0; j<=cntnlc-1; j++)
    {
        state->gcur.ptr.p_double[i] = state->gcur.ptr.p_double[i]+(ssgd_scalednlcpenalty(state, &state->firight, j, state->fscales.ptr.p_double[1+j], _state)-ssgd_scalednlcpenalty(state, &state->fileft, j, state->fscales.ptr.p_double[1+j], _state))/((double)2*state->zcursecantstep);
    }
    state->gcur.ptr.p_double[i] = state->gcur.ptr.p_double[i]+(ssgd_lcpenalty(state, &state->xright, _state)-ssgd_lcpenalty(state, &state->xleft, _state))/((double)2*state->zcursecantstep);
    i = i+1;
    goto lbl_10;
lbl_12:
    
    /*
     * Recompute scales using data collected during gradient calculation
     */
    for(i=0; i<=cntnlc; i++)
    {
        state->avgsqj.ptr.p_double[i] = state->avgsqj.ptr.p_double[i]*((double)1-ssgd_sqjmomentum)+ssgd_sqjmomentum*ae_sqrt(state->sqj.ptr.p_double[j], _state);
        state->fscales.ptr.p_double[i] = ae_maxreal(state->avgsqj.ptr.p_double[i], 1.0, _state);
    }
    if( dotrace )
    {
        ae_trace("scale(F)      = %0.3e\n",
            (double)(state->fscales.ptr.p_double[0]));
    }
    v = ae_sqrt(rdotv2(n, &state->gcur, _state), _state);
    avgsqg = avgsqg*((double)1-ssgd_sqjmomentum)+ssgd_sqjmomentum*v;
    finalscale = rmax3(avgsqg, v, 1.0, _state);
    rmulv(n, (double)1/finalscale, &state->gcur, _state);
    
    /*
     * Perform step
     */
    rmulv(n, state->momentum, &state->dcur, _state);
    raddv(n, (double)1-state->momentum, &state->gcur, &state->dcur, _state);
    raddv(n, -state->currate, &state->dcur, &state->xcur, _state);
    rmergemaxv(n, &state->finitebndl, &state->xcur, _state);
    rmergeminv(n, &state->finitebndu, &state->xcur, _state);
    state->repiterationscount = state->repiterationscount+1;
    iteridx = iteridx+1;
    goto lbl_7;
lbl_9:
    outeridx = outeridx+1;
    goto lbl_4;
lbl_6:
    if( dotrace )
    {
        ae_trace("> reached iterations limit, stopping\n");
    }
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = n;
    state->rstate.ia.ptr.p_int[1] = cntlc;
    state->rstate.ia.ptr.p_int[2] = cntnlc;
    state->rstate.ia.ptr.p_int[3] = outeridx;
    state->rstate.ia.ptr.p_int[4] = iteridx;
    state->rstate.ia.ptr.p_int[5] = i;
    state->rstate.ia.ptr.p_int[6] = j;
    state->rstate.ba.ptr.p_bool[0] = dotrace;
    state->rstate.ba.ptr.p_bool[1] = dodetailedtrace;
    state->rstate.ra.ptr.p_double[0] = f0;
    state->rstate.ra.ptr.p_double[1] = fbest;
    state->rstate.ra.ptr.p_double[2] = fleft;
    state->rstate.ra.ptr.p_double[3] = fright;
    state->rstate.ra.ptr.p_double[4] = avgsqg;
    state->rstate.ra.ptr.p_double[5] = finalscale;
    state->rstate.ra.ptr.p_double[6] = t;
    state->rstate.ra.ptr.p_double[7] = v;
    return result;
}


/*************************************************************************
Clears request fileds (to be sure that we didn't forget to clear something)
*************************************************************************/
static void ssgd_clearrequestfields(ssgdstate* state, ae_state *_state)
{


    state->needfi = ae_false;
    state->xupdated = ae_false;
}


/*************************************************************************
Compute penalized target
*************************************************************************/
static double ssgd_penalizedtarget(ssgdstate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* fi,
     ae_state *_state)
{
    ae_int_t i;
    double result;


    result = fi->ptr.p_double[0]/state->fscales.ptr.p_double[0];
    for(i=0; i<=state->cntnlc-1; i++)
    {
        result = result+ssgd_scalednlcpenalty(state, fi, i, state->fscales.ptr.p_double[1+i], _state);
    }
    result = result+ssgd_lcpenalty(state, x, _state);
    return result;
}


/*************************************************************************
Compute penalty for linear constraints
*************************************************************************/
static double ssgd_lcpenalty(ssgdstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t i;
    double v;
    double result;


    result = 0.0;
    for(i=0; i<=state->cntlc-1; i++)
    {
        v = rdotvr(state->n, x, &state->densea, i, _state);
        if( state->hasal.ptr.p_bool[i]&&v<state->al.ptr.p_double[i] )
        {
            result = result+(double)0*(state->al.ptr.p_double[i]-v)+state->rho*(state->al.ptr.p_double[i]-v)*(state->al.ptr.p_double[i]-v);
        }
        if( state->hasau.ptr.p_bool[i]&&v>state->au.ptr.p_double[i] )
        {
            result = result+(double)0*(v-state->au.ptr.p_double[i])+state->rho*(v-state->au.ptr.p_double[i])*(v-state->au.ptr.p_double[i]);
        }
    }
    return result;
}


/*************************************************************************
Compute penalty for a single nonlinear constraint
*************************************************************************/
static double ssgd_scalednlcpenalty(ssgdstate* state,
     /* Real    */ const ae_vector* _fi,
     ae_int_t idx,
     double fscale,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector fi;
    double v;
    double result;

    ae_frame_make(_state, &_frame_block);
    memset(&fi, 0, sizeof(fi));
    ae_vector_init_copy(&fi, _fi, _state, ae_true);

    result = 0.0;
    if( state->hasnl.ptr.p_bool[idx]&&fi.ptr.p_double[1+idx]<state->rawnl.ptr.p_double[idx] )
    {
        v = (state->rawnl.ptr.p_double[idx]-fi.ptr.p_double[1+idx])/fscale;
        result = result+(double)0*v+state->rho*v*v;
    }
    if( state->hasnu.ptr.p_bool[idx]&&fi.ptr.p_double[1+idx]>state->rawnu.ptr.p_double[idx] )
    {
        v = (fi.ptr.p_double[1+idx]-state->rawnu.ptr.p_double[idx])/fscale;
        result = result+(double)0*v+state->rho*v*v;
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Compute violation of linear constraints
*************************************************************************/
static double ssgd_lcviolation(ssgdstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t i;
    double v;
    double result;


    result = 0.0;
    for(i=0; i<=state->cntlc-1; i++)
    {
        v = rdotvr(state->n, x, &state->densea, i, _state);
        if( state->hasal.ptr.p_bool[i] )
        {
            result = ae_maxreal(result, state->al.ptr.p_double[i]-v, _state);
        }
        if( state->hasau.ptr.p_bool[i] )
        {
            result = ae_maxreal(result, v-state->au.ptr.p_double[i], _state);
        }
    }
    return result;
}


void _ssgdstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    ssgdstate *p = (ssgdstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->finitebndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitebndu, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->densea, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->al, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->au, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasal, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasau, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->rawnl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawnu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasnl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasnu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->ascales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->avgsqj, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fscales, 0, DT_REAL, _state, make_automatic);
    _hqrndstate_init(&p->rs, _state, make_automatic);
    ae_vector_init(&p->xbest, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fibest, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xcur, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gcur, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dcur, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    ae_vector_init(&p->sqj, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xleft, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xright, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fileft, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->firight, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xoffs0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xoffs1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpzero, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->dummysparsea, _state, make_automatic);
}


void _ssgdstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    ssgdstate       *dst = (ssgdstate*)_dst;
    const ssgdstate *src = (const ssgdstate*)_src;
    dst->n = src->n;
    dst->cntlc = src->cntlc;
    dst->cntnlc = src->cntnlc;
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndl, &src->finitebndl, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndu, &src->finitebndu, _state, make_automatic);
    ae_matrix_init_copy(&dst->densea, &src->densea, _state, make_automatic);
    ae_vector_init_copy(&dst->al, &src->al, _state, make_automatic);
    ae_vector_init_copy(&dst->au, &src->au, _state, make_automatic);
    ae_vector_init_copy(&dst->hasal, &src->hasal, _state, make_automatic);
    ae_vector_init_copy(&dst->hasau, &src->hasau, _state, make_automatic);
    ae_vector_init_copy(&dst->rawnl, &src->rawnl, _state, make_automatic);
    ae_vector_init_copy(&dst->rawnu, &src->rawnu, _state, make_automatic);
    ae_vector_init_copy(&dst->hasnl, &src->hasnl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasnu, &src->hasnu, _state, make_automatic);
    ae_vector_init_copy(&dst->ascales, &src->ascales, _state, make_automatic);
    dst->maxits = src->maxits;
    dst->rate0 = src->rate0;
    dst->rate1 = src->rate1;
    dst->momentum = src->momentum;
    dst->blur0 = src->blur0;
    dst->blur1 = src->blur1;
    dst->outerits = src->outerits;
    dst->rho = src->rho;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    dst->f = src->f;
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    dst->needfi = src->needfi;
    dst->xupdated = src->xupdated;
    ae_vector_init_copy(&dst->avgsqj, &src->avgsqj, _state, make_automatic);
    ae_vector_init_copy(&dst->fscales, &src->fscales, _state, make_automatic);
    _hqrndstate_init_copy(&dst->rs, &src->rs, _state, make_automatic);
    ae_vector_init_copy(&dst->xbest, &src->xbest, _state, make_automatic);
    ae_vector_init_copy(&dst->fibest, &src->fibest, _state, make_automatic);
    ae_vector_init_copy(&dst->xcur, &src->xcur, _state, make_automatic);
    ae_vector_init_copy(&dst->gcur, &src->gcur, _state, make_automatic);
    ae_vector_init_copy(&dst->dcur, &src->dcur, _state, make_automatic);
    dst->currate = src->currate;
    dst->curblur = src->curblur;
    dst->zcursecantstep = src->zcursecantstep;
    dst->repiterationscount = src->repiterationscount;
    dst->repnfev = src->repnfev;
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    ae_vector_init_copy(&dst->sqj, &src->sqj, _state, make_automatic);
    ae_vector_init_copy(&dst->xleft, &src->xleft, _state, make_automatic);
    ae_vector_init_copy(&dst->xright, &src->xright, _state, make_automatic);
    ae_vector_init_copy(&dst->fileft, &src->fileft, _state, make_automatic);
    ae_vector_init_copy(&dst->firight, &src->firight, _state, make_automatic);
    ae_vector_init_copy(&dst->xoffs0, &src->xoffs0, _state, make_automatic);
    ae_vector_init_copy(&dst->xoffs1, &src->xoffs1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpzero, &src->tmpzero, _state, make_automatic);
    _sparsematrix_init_copy(&dst->dummysparsea, &src->dummysparsea, _state, make_automatic);
}


void _ssgdstate_clear(void* _p)
{
    ssgdstate *p = (ssgdstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->finitebndl);
    ae_vector_clear(&p->finitebndu);
    ae_matrix_clear(&p->densea);
    ae_vector_clear(&p->al);
    ae_vector_clear(&p->au);
    ae_vector_clear(&p->hasal);
    ae_vector_clear(&p->hasau);
    ae_vector_clear(&p->rawnl);
    ae_vector_clear(&p->rawnu);
    ae_vector_clear(&p->hasnl);
    ae_vector_clear(&p->hasnu);
    ae_vector_clear(&p->ascales);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->fi);
    ae_vector_clear(&p->avgsqj);
    ae_vector_clear(&p->fscales);
    _hqrndstate_clear(&p->rs);
    ae_vector_clear(&p->xbest);
    ae_vector_clear(&p->fibest);
    ae_vector_clear(&p->xcur);
    ae_vector_clear(&p->gcur);
    ae_vector_clear(&p->dcur);
    _rcommstate_clear(&p->rstate);
    ae_vector_clear(&p->sqj);
    ae_vector_clear(&p->xleft);
    ae_vector_clear(&p->xright);
    ae_vector_clear(&p->fileft);
    ae_vector_clear(&p->firight);
    ae_vector_clear(&p->xoffs0);
    ae_vector_clear(&p->xoffs1);
    ae_vector_clear(&p->tmpzero);
    _sparsematrix_clear(&p->dummysparsea);
}


void _ssgdstate_destroy(void* _p)
{
    ssgdstate *p = (ssgdstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->finitebndl);
    ae_vector_destroy(&p->finitebndu);
    ae_matrix_destroy(&p->densea);
    ae_vector_destroy(&p->al);
    ae_vector_destroy(&p->au);
    ae_vector_destroy(&p->hasal);
    ae_vector_destroy(&p->hasau);
    ae_vector_destroy(&p->rawnl);
    ae_vector_destroy(&p->rawnu);
    ae_vector_destroy(&p->hasnl);
    ae_vector_destroy(&p->hasnu);
    ae_vector_destroy(&p->ascales);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->fi);
    ae_vector_destroy(&p->avgsqj);
    ae_vector_destroy(&p->fscales);
    _hqrndstate_destroy(&p->rs);
    ae_vector_destroy(&p->xbest);
    ae_vector_destroy(&p->fibest);
    ae_vector_destroy(&p->xcur);
    ae_vector_destroy(&p->gcur);
    ae_vector_destroy(&p->dcur);
    _rcommstate_destroy(&p->rstate);
    ae_vector_destroy(&p->sqj);
    ae_vector_destroy(&p->xleft);
    ae_vector_destroy(&p->xright);
    ae_vector_destroy(&p->fileft);
    ae_vector_destroy(&p->firight);
    ae_vector_destroy(&p->xoffs0);
    ae_vector_destroy(&p->xoffs1);
    ae_vector_destroy(&p->tmpzero);
    _sparsematrix_destroy(&p->dummysparsea);
}


#endif
#if defined(AE_COMPILE_DIFFEVO) || !defined(AE_PARTIAL_BUILD)


void gdemoinitbuf(/* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* s,
     ae_int_t n,
     ae_int_t m,
     /* Real    */ const ae_matrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t cntlc,
     /* Real    */ const ae_vector* nl,
     /* Real    */ const ae_vector* nu,
     ae_int_t cntnlc,
     ae_int_t popsize,
     ae_int_t epochscnt,
     ae_int_t seed,
     gdemostate* state,
     ae_state *_state)
{
    ae_int_t i;


    
    /*
     * Integrity checks
     */
    ae_assert(m==1, "GDEMO: M<>1", _state);
    ae_assert(popsize>=0, "GDEMO: PopSize<=0", _state);
    ae_assert(epochscnt>0, "GDEMO: EpochsCnt<=0", _state);
    
    /*
     * Initialization
     */
    popsize = ae_maxint(coalescei(popsize, 10*n, _state), 10, _state);
    state->n = n;
    state->m = m;
    state->cntlc = cntlc;
    state->cntnlc = cntnlc;
    state->popsize = popsize;
    state->epochscnt = epochscnt;
    state->constrmode = 0;
    state->rho1 = diffevo_defaultpenalty;
    state->rho2 = diffevo_defaultpenalty;
    state->eps = 0.0;
    state->nsample = (double)(1);
    state->fixedparams = ae_false;
    state->hasx0 = ae_false;
    state->stoponsmallf = ae_false;
    state->condfxepsf = 0.0;
    state->condfxepsx = 0.0;
    if( seed<=0 )
    {
        seed = ae_randominteger(30000, _state);
    }
    hqrndseed(seed, seed+1172, &state->rs, _state);
    rsetallocm(m, n, 0.0, &state->xbest, _state);
    rsetallocv(m, _state->v_nan, &state->fitbest, _state);
    rsetallocv(m, _state->v_nan, &state->rawfbest, _state);
    gdemosetprofile(state, 0, _state);
    
    /*
     * Prepare RCOMM state
     */
    ae_vector_set_length(&state->rstate.ia, 10+1, _state);
    ae_vector_set_length(&state->rstate.ba, 2+1, _state);
    ae_vector_set_length(&state->rstate.ra, 3+1, _state);
    state->rstate.stage = -1;
    
    /*
     * Prepare scaled problem
     */
    rallocv(n, &state->s, _state);
    bvectorsetlengthatleast(&state->hasbndl, n, _state);
    bvectorsetlengthatleast(&state->hasbndu, n, _state);
    rsetallocv(n, -ae_maxrealnumber, &state->finitebndl, _state);
    rsetallocv(n, ae_maxrealnumber, &state->finitebndu, _state);
    for(i=0; i<=n-1; i++)
    {
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
        if( state->hasbndl.ptr.p_bool[i] )
        {
            state->finitebndl.ptr.p_double[i] = bndl->ptr.p_double[i]/s->ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i] )
        {
            state->finitebndu.ptr.p_double[i] = bndu->ptr.p_double[i]/s->ptr.p_double[i];
        }
        if( state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i] )
        {
            ae_assert(ae_fp_less_eq(bndl->ptr.p_double[i],bndu->ptr.p_double[i]), "GDEMO: integrity check failed, box constraints are inconsistent", _state);
        }
        state->s.ptr.p_double[i] = s->ptr.p_double[i];
    }
    if( cntlc>0 )
    {
        rsetallocv(n, 0.0, &state->tmpzero, _state);
        rcopyallocm(cntlc, n, a, &state->densea, _state);
        rcopyallocv(cntlc, al, &state->al, _state);
        rcopyallocv(cntlc, au, &state->au, _state);
        scaleshiftmixedlcinplace(s, &state->tmpzero, n, &state->dummysparsea, 0, &state->densea, cntlc, &state->al, &state->au, _state);
        normalizedenselcinplace(&state->densea, cntlc, &state->al, &state->au, n, ae_true, &state->ascales, ae_true, _state);
        ballocv(cntlc, &state->hasal, _state);
        ballocv(cntlc, &state->hasau, _state);
        for(i=0; i<=cntlc-1; i++)
        {
            state->hasal.ptr.p_bool[i] = ae_isfinite(state->al.ptr.p_double[i], _state);
            state->hasau.ptr.p_bool[i] = ae_isfinite(state->au.ptr.p_double[i], _state);
        }
    }
    if( cntnlc>0 )
    {
        rcopyallocv(cntnlc, nl, &state->rawnl, _state);
        rcopyallocv(cntnlc, nu, &state->rawnu, _state);
        ballocv(cntnlc, &state->hasnl, _state);
        ballocv(cntnlc, &state->hasnu, _state);
        for(i=0; i<=cntnlc-1; i++)
        {
            state->hasnl.ptr.p_bool[i] = ae_isfinite(state->rawnl.ptr.p_double[i], _state);
            state->hasnu.ptr.p_bool[i] = ae_isfinite(state->rawnu.ptr.p_double[i], _state);
        }
    }
}


/*************************************************************************
Sets performance profile:
* 0 for ROBUST
* 1 for QUICK

  -- ALGLIB --
     Copyright 07.12.2023 by Bochkanov Sergey
*************************************************************************/
void gdemosetprofile(gdemostate* state,
     ae_int_t profile,
     ae_state *_state)
{


    ae_assert(profile==0||profile==1, "GDEMOSetProfile: unexpected profile", _state);
    state->profile = profile;
}


/*************************************************************************
Set fixed algorithm params (the default is to use adaptive algorithm)

  -- ALGLIB --
     Copyright 07.12.2023 by Bochkanov Sergey
*************************************************************************/
void gdemosetfixedparams(gdemostate* state,
     ae_int_t strategy,
     double crossoverprob,
     double differentialweight,
     ae_state *_state)
{


    state->fixedparams = ae_true;
    state->fixedstrategy = strategy;
    state->fixedcrossoverprob = crossoverprob;
    state->fixeddifferentialweight = differentialweight;
}


/*************************************************************************
Sets small F, the solver stops when the best value decreases below this level

  -- ALGLIB --
     Copyright 07.12.2023 by Bochkanov Sergey
*************************************************************************/
void gdemosetsmallf(gdemostate* state, double f, ae_state *_state)
{


    state->stoponsmallf = ae_true;
    state->smallf = f;
}


/*************************************************************************
This function sets a  combined   stopping  condition:  stopping  when  two
criteria are met simultaneously:
* function values has converged within epsF
* variable values has converged within epsX

Ignored for multi-objective setting.

  -- ALGLIB --
     Copyright 23.04.2024 by Bochkanov Sergey
*************************************************************************/
void gdemosetcondfx(gdemostate* state,
     double epsf,
     double epsx,
     ae_state *_state)
{


    state->condfxepsf = epsf;
    state->condfxepsx = epsx;
}


/*************************************************************************
Sets constraint handling mode to L1/L2 penalty

  -- ALGLIB --
     Copyright 07.12.2023 by Bochkanov Sergey
*************************************************************************/
void gdemosetmodepenalty(gdemostate* state,
     double rhol1,
     double rhol2,
     ae_state *_state)
{


    state->constrmode = 0;
    state->rho1 = rhol1;
    state->rho2 = rhol2;
}


/*************************************************************************
Sets initial point

  -- ALGLIB --
     Copyright 07.12.2023 by Bochkanov Sergey
*************************************************************************/
void gdemosetx0(gdemostate* state,
     /* Real    */ const ae_vector* x0,
     ae_state *_state)
{


    state->hasx0 = ae_true;
    rcopyallocv(state->n, x0, &state->x0, _state);
}


/*************************************************************************
This function performs actual processing for GDEMO  algorithm.

  -- ALGLIB --
     Copyright 07.12.2023 by Bochkanov Sergey
*************************************************************************/
ae_bool gdemoiteration(gdemostate* state,
     ae_bool userterminationneeded,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t cntlc;
    ae_int_t cntnlc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t k1;
    double v;
    double vv;
    double vleft;
    double vright;
    ae_bool dotrace;
    ae_bool dodetailedtrace;
    ae_int_t popsize;
    ae_int_t residualsize;
    ae_int_t iteridx;
    ae_bool bad;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        n = state->rstate.ia.ptr.p_int[0];
        m = state->rstate.ia.ptr.p_int[1];
        cntlc = state->rstate.ia.ptr.p_int[2];
        cntnlc = state->rstate.ia.ptr.p_int[3];
        i = state->rstate.ia.ptr.p_int[4];
        j = state->rstate.ia.ptr.p_int[5];
        k = state->rstate.ia.ptr.p_int[6];
        k1 = state->rstate.ia.ptr.p_int[7];
        popsize = state->rstate.ia.ptr.p_int[8];
        residualsize = state->rstate.ia.ptr.p_int[9];
        iteridx = state->rstate.ia.ptr.p_int[10];
        dotrace = state->rstate.ba.ptr.p_bool[0];
        dodetailedtrace = state->rstate.ba.ptr.p_bool[1];
        bad = state->rstate.ba.ptr.p_bool[2];
        v = state->rstate.ra.ptr.p_double[0];
        vv = state->rstate.ra.ptr.p_double[1];
        vleft = state->rstate.ra.ptr.p_double[2];
        vright = state->rstate.ra.ptr.p_double[3];
    }
    else
    {
        n = 359;
        m = -58;
        cntlc = -919;
        cntnlc = -909;
        i = 81;
        j = 255;
        k = 74;
        k1 = -788;
        popsize = 809;
        residualsize = 205;
        iteridx = -838;
        dotrace = ae_true;
        dodetailedtrace = ae_false;
        bad = ae_true;
        v = -541.0;
        vv = -698.0;
        vleft = -900.0;
        vright = -318.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    
    /*
     * Routine body
     */
    n = state->n;
    m = state->m;
    cntlc = state->cntlc;
    cntnlc = state->cntnlc;
    popsize = state->popsize;
    dotrace = ae_is_trace_enabled("GDEMO");
    dodetailedtrace = dotrace&&ae_is_trace_enabled("GDEMO.DETAILED");
    ae_assert(m==1, "GDEMO: integrity check 0732 failed", _state);
    
    /*
     * Init
     */
    state->repiterationscount = 0;
    state->repterminationtype = 5;
    state->replcerr = 0.0;
    state->replcidx = -1;
    state->repnlcerr = 0.0;
    state->repnlcidx = -1;
    rallocv(n, &state->reportx, _state);
    if( dotrace )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("//  GDEMO SOLVER HAS STARTED                                                                      //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("N             = %6d\n",
            (int)(n));
        ae_trace("M             = %6d\n",
            (int)(m));
        ae_trace("cntLC         = %6d\n",
            (int)(cntlc));
        ae_trace("cntNLC        = %6d\n",
            (int)(cntnlc));
        ae_trace("parameters    = ");
        if( state->fixedparams )
        {
            ae_trace("fixed\n");
        }
        else
        {
            ae_trace("adaptive\n");
        }
    }
    
    /*
     * Initial state of memory, pools, etc
     */
    state->mucr = 0.5;
    state->muf = 0.5;
    
    /*
     * Generate initial population, perform several iterations dropping invalid candidates
     * (ones with infinite target/constraint values) until the entire population is generated:
     * * generate initial population using INIT heuristic, compute empirical bounds (per-variable
     *   max and min of population elements)
     * * repeatedly query targets, keep good ones, drop bad ones, tighten empirical bounds
     *   approaching them closer to points that resulted in good values
     */
    state->population.cnt = popsize;
    rallocm(popsize, n, &state->population.x, _state);
    rallocm(popsize, m+cntnlc, &state->population.rawreplies, _state);
    rallocm(popsize, m, &state->population.fitness, _state);
    rsetallocm(popsize, diffevo_paramscnt, 0.0, &state->population.deparams, _state);
    rallocv(n, &state->empiricalbndl, _state);
    rallocv(n, &state->empiricalbndu, _state);
    for(i=0; i<=popsize-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( state->hasx0&&i==0 )
            {
                
                /*
                 * Add user-provided initial point to the population
                 */
                state->population.x.ptr.pp_double[i][j] = boundval(state->x0.ptr.p_double[j], state->finitebndl.ptr.p_double[j], state->finitebndu.ptr.p_double[j], _state);
            }
            else
            {
                
                /*
                 * Generate population individual using hints from box constraints
                 */
                if( state->hasbndl.ptr.p_bool[j]&&state->hasbndu.ptr.p_bool[j] )
                {
                    state->population.x.ptr.pp_double[i][j] = state->finitebndl.ptr.p_double[j]+(state->finitebndu.ptr.p_double[j]-state->finitebndl.ptr.p_double[j])*hqrnduniformr(&state->rs, _state);
                    continue;
                }
                if( state->hasbndl.ptr.p_bool[j]&&!state->hasbndu.ptr.p_bool[j] )
                {
                    state->population.x.ptr.pp_double[i][j] = state->finitebndl.ptr.p_double[j]+ae_pow((double)(2), hqrndnormal(&state->rs, _state), _state);
                    continue;
                }
                if( state->hasbndu.ptr.p_bool[j]&&!state->hasbndl.ptr.p_bool[j] )
                {
                    state->population.x.ptr.pp_double[i][j] = state->finitebndu.ptr.p_double[j]-ae_pow((double)(2), hqrndnormal(&state->rs, _state), _state);
                    continue;
                }
                state->population.x.ptr.pp_double[i][j] = hqrndnormal(&state->rs, _state);
            }
        }
        if( i==0 )
        {
            rcopyrv(n, &state->population.x, i, &state->empiricalbndl, _state);
            rcopyrv(n, &state->population.x, i, &state->empiricalbndu, _state);
        }
        else
        {
            rmergeminrv(n, &state->population.x, i, &state->empiricalbndl, _state);
            rmergemaxrv(n, &state->population.x, i, &state->empiricalbndu, _state);
        }
    }
    rsetallocv(n, ae_maxrealnumber, &state->goodbndl, _state);
    rsetallocv(n, -ae_maxrealnumber, &state->goodbndu, _state);
    residualsize = popsize;
    iteridx = 0;
lbl_4:
    if( iteridx>ae_maxint(diffevo_minpowerupits, ae_round(0.25*(double)state->epochscnt, _state), _state)-1 )
    {
        goto lbl_6;
    }
    
    /*
     * query target values
     */
    state->requesttype = 4;
    state->querysize = residualsize;
    rallocv(n*residualsize, &state->querydata, _state);
    for(i=0; i<=residualsize-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            state->querydata.ptr.p_double[i*n+j] = state->population.x.ptr.pp_double[i][j];
        }
    }
    rallocv(residualsize*(m+cntnlc), &state->replyfi, _state);
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    for(i=0; i<=residualsize-1; i++)
    {
        for(j=0; j<=m+cntnlc-1; j++)
        {
            state->population.rawreplies.ptr.pp_double[i][j] = state->replyfi.ptr.p_double[i*(m+cntnlc)+j];
        }
    }
    
    /*
     * Move good candidates (ones with finite targets/constraints) to the end, decrease residual size
     */
    i = residualsize-1;
    while(i>=0)
    {
        bad = ae_false;
        for(j=0; j<=m+cntnlc-1; j++)
        {
            bad = bad||!ae_isfinite(state->population.rawreplies.ptr.pp_double[i][j], _state);
        }
        if( !bad )
        {
            
            /*
             * Move candidate to the end of the current subpopulation, update box containing good values, decrease residual size
             */
            if( i!=residualsize-1 )
            {
                rcopyrr(n, &state->population.x, i, &state->population.x, residualsize-1, _state);
                rcopyrr(m+cntnlc, &state->population.rawreplies, i, &state->population.rawreplies, residualsize-1, _state);
            }
            rmergeminrv(n, &state->population.x, residualsize-1, &state->goodbndl, _state);
            rmergemaxrv(n, &state->population.x, residualsize-1, &state->goodbndu, _state);
            residualsize = residualsize-1;
        }
        i = i-1;
    }
    if( residualsize==0 )
    {
        goto lbl_6;
    }
    
    /*
     * Tighten empirical bounds used to generate new candidates. 
     */
    if( residualsize<=popsize-diffevo_minpopsizefortightening )
    {
        ae_assert(diffevo_minpopsizefortightening>=2, "GDEMO: integrity check 0618 failed", _state);
        for(j=0; j<=n-1; j++)
        {
            state->empiricalbndl.ptr.p_double[j] = diffevo_tighteningmu*state->empiricalbndl.ptr.p_double[j]+((double)1-diffevo_tighteningmu)*state->goodbndl.ptr.p_double[j];
            state->empiricalbndu.ptr.p_double[j] = diffevo_tighteningmu*state->empiricalbndu.ptr.p_double[j]+((double)1-diffevo_tighteningmu)*state->goodbndu.ptr.p_double[j];
        }
    }
    
    /*
     * Generate new candidates in the empirical box
     */
    for(i=0; i<=residualsize-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            state->population.x.ptr.pp_double[i][j] = state->empiricalbndl.ptr.p_double[j]+(state->empiricalbndu.ptr.p_double[j]-state->empiricalbndl.ptr.p_double[j])*hqrnduniformr(&state->rs, _state);
        }
    }
    iteridx = iteridx+1;
    goto lbl_4;
lbl_6:
    if( residualsize>0 )
    {
        
        /*
         * We failed to generate complete population, return with error
         */
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    diffevo_computefitness(state, &state->population, ae_false, &state->population, _state);
    diffevo_updatebest(state, _state);
    ae_assert(m==1, "GDEMO: integrity check 566226 failed", _state);
    state->requesttype = -1;
    rallocv(n, &state->reportx, _state);
    rcopyrv(n, &state->xbest, 0, &state->reportx, _state);
    state->reportf = state->rawfbest.ptr.p_double[0];
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    
    /*
     * Run iterations
     */
    if( dotrace )
    {
        ae_trace("> ALGLIB GDEMO solver started, %0d variables, %0d linear constraints, %0d general constraints\n",
            (int)(n),
            (int)(cntlc),
            (int)(cntnlc));
        ae_trace("   ITS                 MERIT FUNC                  OBJECTIVE      muCR       muF\n");
    }
    iteridx = 0;
lbl_7:
    if( iteridx>state->epochscnt-1 )
    {
        goto lbl_9;
    }
    if( dotrace )
    {
        i = 0;
        if( state->repiterationscount<=10 )
        {
            i = 1;
        }
        if( state->repiterationscount<=100&&state->repiterationscount%5==0 )
        {
            i = 1;
        }
        if( state->repiterationscount%50==0 )
        {
            i = 1;
        }
        if( i>0 )
        {
            ae_trace("%6d    %23.15e    %23.15e    %16.14f    %16.14f\n",
                (int)(state->repiterationscount),
                (double)(state->fitbest.ptr.p_double[0]),
                (double)(state->rawfbest.ptr.p_double[0]),
                (double)(state->mucr),
                (double)(state->muf));
        }
    }
    
    /*
     * Generate candidates
     */
    diffevo_generatetrialpoints(state, &state->population, &state->offspring, _state);
    
    /*
     * RComm request and fitness computation
     */
    state->requesttype = 4;
    state->querysize = state->offspring.cnt;
    rallocv(n*state->offspring.cnt, &state->querydata, _state);
    for(i=0; i<=state->offspring.cnt-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            state->querydata.ptr.p_double[i*n+j] = state->offspring.x.ptr.pp_double[i][j];
        }
    }
    rallocv(state->offspring.cnt*(m+cntnlc), &state->replyfi, _state);
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    for(i=0; i<=state->offspring.cnt-1; i++)
    {
        for(j=0; j<=m+cntnlc-1; j++)
        {
            state->offspring.rawreplies.ptr.pp_double[i][j] = state->replyfi.ptr.p_double[i*(m+cntnlc)+j];
        }
    }
    diffevo_computefitness(state, &state->population, ae_true, &state->offspring, _state);
    
    /*
     * Update population with candidates
     */
    diffevo_updatepopulation(state, &state->offspring, _state);
    
    /*
     * Update best value so far and other counters, check stopping criteria
     */
    diffevo_updatebest(state, _state);
    ae_assert(m==1, "GDEMO: integrity check 566226 failed", _state);
    state->requesttype = -1;
    rallocv(n, &state->reportx, _state);
    rcopyrv(n, &state->xbest, 0, &state->reportx, _state);
    state->reportf = state->rawfbest.ptr.p_double[0];
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    state->repiterationscount = state->repiterationscount+1;
    if( (state->stoponsmallf&&m==1)&&ae_fp_less_eq(state->rawfbest.ptr.p_double[0],state->smallf) )
    {
        state->repterminationtype = 6;
        goto lbl_9;
    }
    if( (ae_fp_greater(state->condfxepsf,(double)(0))||ae_fp_greater(state->condfxepsx,(double)(0)))&&m==1 )
    {
        ae_assert(!ae_isnan(state->fitbest.ptr.p_double[0], _state), "GDEMO: integrity check 2618 failed", _state);
        
        /*
         * Count agents within EpsF near the optimal one
         */
        k = 0;
        k1 = 0;
        v = state->fitbest.ptr.p_double[0]+state->condfxepsf*rmaxabs2(state->fitbest.ptr.p_double[0], 1.0, _state);
        for(i=0; i<=popsize-1; i++)
        {
            
            /*
             * Try to filter out individuals based on:
             * * target value (if epsF>0); tried first because it is the cheapest condition
             * * distance towards the best one
             */
            if( state->condfxepsf>0.0&&state->population.fitness.ptr.pp_double[i][0]>v )
            {
                continue;
            }
            k = k+1;
            if( state->condfxepsx>0.0 )
            {
                vv = 0.0;
                for(j=0; j<=n-1; j++)
                {
                    vv = vv+(state->population.x.ptr.pp_double[i][j]-state->xbest.ptr.pp_double[0][j])*(state->population.x.ptr.pp_double[i][j]-state->xbest.ptr.pp_double[0][j]);
                }
                if( vv/(double)n>state->condfxepsx*state->condfxepsx )
                {
                    continue;
                }
            }
            k1 = k1+1;
        }
        if( ae_fp_greater_eq((double)(k),ae_minreal((double)(2*n+1), (double)(popsize), _state))&&ae_fp_greater_eq((double)(k1),ae_minreal((double)(2*n+1), (double)(popsize), _state)) )
        {
            state->repterminationtype = 1;
            goto lbl_9;
        }
    }
    iteridx = iteridx+1;
    goto lbl_7;
lbl_9:
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = n;
    state->rstate.ia.ptr.p_int[1] = m;
    state->rstate.ia.ptr.p_int[2] = cntlc;
    state->rstate.ia.ptr.p_int[3] = cntnlc;
    state->rstate.ia.ptr.p_int[4] = i;
    state->rstate.ia.ptr.p_int[5] = j;
    state->rstate.ia.ptr.p_int[6] = k;
    state->rstate.ia.ptr.p_int[7] = k1;
    state->rstate.ia.ptr.p_int[8] = popsize;
    state->rstate.ia.ptr.p_int[9] = residualsize;
    state->rstate.ia.ptr.p_int[10] = iteridx;
    state->rstate.ba.ptr.p_bool[0] = dotrace;
    state->rstate.ba.ptr.p_bool[1] = dodetailedtrace;
    state->rstate.ba.ptr.p_bool[2] = bad;
    state->rstate.ra.ptr.p_double[0] = v;
    state->rstate.ra.ptr.p_double[1] = vv;
    state->rstate.ra.ptr.p_double[2] = vleft;
    state->rstate.ra.ptr.p_double[3] = vright;
    return result;
}


/*************************************************************************
Compute fitness from raw replies

INPUT PARAMETERS:
    State                   -   solver
    Population2RawReplies   -   present population, used by fitness computation
                                methods which analyze distribution of constraint
                                violations in the current population
    PopSize                 -   population size, or zero, if this
                                function is called at the initialization
                                phase when we compute fitness for the initial
                                population
    RawReplies              -   raw replies to process
    BatchSize               -   size of the set being processed
    Fitness                 -   preallocated output array
    

  -- ALGLIB --
     Copyright 07.12.2023 by Bochkanov Sergey
*************************************************************************/
static void diffevo_computefitness(const gdemostate* state,
     const gdemopopulation* refpopulation,
     ae_bool hasreference,
     gdemopopulation* trial,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t cntlc;
    ae_int_t cntnlc;
    ae_int_t i;
    ae_int_t j;
    double p;
    double v;
    double vv;
    ae_bool bad;


    n = state->n;
    m = state->m;
    cntlc = state->cntlc;
    cntnlc = state->cntnlc;
    ae_assert(trial->fitness.rows>=trial->cnt&&trial->fitness.cols>=m, "GDEMO: integrity check 5831 failed", _state);
    ae_assert(ae_fp_eq(state->nsample,(double)(1)), "GDEMO: integrity check 5632 failed", _state);
    
    /*
     * Quick exit
     */
    if( cntlc+cntnlc==0 )
    {
        for(i=0; i<=trial->cnt-1; i++)
        {
            bad = ae_false;
            for(j=0; j<=m-1; j++)
            {
                v = trial->rawreplies.ptr.pp_double[i][j];
                trial->fitness.ptr.pp_double[i][j] = v;
                bad = bad||!ae_isfinite(v, _state);
            }
            if( bad )
            {
                for(j=0; j<=m-1; j++)
                {
                    trial->fitness.ptr.pp_double[i][j] = ae_maxrealnumber;
                }
            }
        }
        return;
    }
    
    /*
     * Default constraint handling strategy: L1 penalty
     */
    if( state->constrmode==0 )
    {
        for(i=0; i<=trial->cnt-1; i++)
        {
            
            /*
             * Handle bad targets/constraints
             */
            bad = ae_false;
            for(j=0; j<=m+cntnlc-1; j++)
            {
                bad = bad||!ae_isfinite(trial->rawreplies.ptr.pp_double[i][j], _state);
            }
            if( bad )
            {
                for(j=0; j<=m-1; j++)
                {
                    trial->fitness.ptr.pp_double[i][j] = ae_maxrealnumber;
                }
                continue;
            }
            
            /*
             * No infinities, normal processing
             */
            p = (double)(0);
            for(j=0; j<=cntlc-1; j++)
            {
                v = rdotrr(n, &trial->x, i, &state->densea, j, _state);
                if( state->hasal.ptr.p_bool[j] )
                {
                    vv = ae_maxreal(state->al.ptr.p_double[j]-v, 0.0, _state);
                    p = p+state->rho1*vv+state->rho2*vv*vv;
                }
                if( state->hasau.ptr.p_bool[j] )
                {
                    vv = ae_maxreal(v-state->au.ptr.p_double[j], 0.0, _state);
                    p = p+state->rho1*vv+state->rho2*vv*vv;
                }
            }
            for(j=0; j<=cntnlc-1; j++)
            {
                v = trial->rawreplies.ptr.pp_double[i][m+j];
                if( state->hasnl.ptr.p_bool[j] )
                {
                    vv = ae_maxreal(state->rawnl.ptr.p_double[j]-v, 0.0, _state);
                    p = p+state->rho1*vv+state->rho2*vv*vv;
                }
                if( state->hasnu.ptr.p_bool[j] )
                {
                    vv = ae_maxreal(v-state->rawnu.ptr.p_double[j], 0.0, _state);
                    p = p+state->rho1*vv+state->rho2*vv*vv;
                }
            }
            for(j=0; j<=m-1; j++)
            {
                trial->fitness.ptr.pp_double[i][j] = trial->rawreplies.ptr.pp_double[i][j]+p;
            }
        }
        return;
    }
    
    /*
     * Unknown constrmode
     */
    ae_assert(ae_false, "GDEMO: integrity check 2202 failed", _state);
}


/*************************************************************************
Updates best value so far. For multiobjective models - fails.

  -- ALGLIB --
     Copyright 07.12.2023 by Bochkanov Sergey
*************************************************************************/
static void diffevo_updatebest(gdemostate* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t cntlc;
    ae_int_t cntnlc;
    ae_int_t i;
    ae_int_t j;
    double v;
    double vv;


    n = state->n;
    m = state->m;
    cntlc = state->cntlc;
    cntnlc = state->cntnlc;
    ae_assert(ae_fp_eq(state->nsample,(double)(1)), "GDEMO: integrity check 9242 failed", _state);
    ae_assert(m==1, "GDEMO: integrity check 9243 failed", _state);
    for(i=0; i<=state->population.cnt-1; i++)
    {
        if( ae_isnan(state->fitbest.ptr.p_double[0], _state)||ae_fp_less(state->population.fitness.ptr.pp_double[i][0],state->fitbest.ptr.p_double[0]) )
        {
            
            /*
             * Save best value
             */
            rcopyrr(n, &state->population.x, i, &state->xbest, 0, _state);
            state->fitbest.ptr.p_double[0] = state->population.fitness.ptr.pp_double[i][0];
            state->rawfbest.ptr.p_double[0] = state->population.rawreplies.ptr.pp_double[i][0];
            
            /*
             * Update errors
             */
            state->replcerr = 0.0;
            state->replcidx = -1;
            for(j=0; j<=cntlc-1; j++)
            {
                v = rdotrr(n, &state->xbest, 0, &state->densea, j, _state);
                if( state->hasal.ptr.p_bool[j] )
                {
                    vv = state->ascales.ptr.p_double[j]*ae_maxreal(state->al.ptr.p_double[j]-v, 0.0, _state);
                    if( ae_fp_greater(vv,state->replcerr) )
                    {
                        state->replcerr = vv;
                        state->replcidx = j;
                    }
                }
                if( state->hasau.ptr.p_bool[j] )
                {
                    vv = state->ascales.ptr.p_double[j]*ae_maxreal(v-state->au.ptr.p_double[j], 0.0, _state);
                    if( ae_fp_greater(vv,state->replcerr) )
                    {
                        state->replcerr = vv;
                        state->replcidx = j;
                    }
                }
            }
            state->repnlcerr = 0.0;
            state->repnlcidx = -1;
            for(j=0; j<=cntnlc-1; j++)
            {
                v = state->population.rawreplies.ptr.pp_double[i][m+j];
                if( state->hasnl.ptr.p_bool[j] )
                {
                    vv = ae_maxreal(state->rawnl.ptr.p_double[j]-v, 0.0, _state);
                    if( ae_fp_greater(vv,state->repnlcerr) )
                    {
                        state->repnlcerr = vv;
                        state->repnlcidx = j;
                    }
                }
                if( state->hasnu.ptr.p_bool[j] )
                {
                    vv = ae_maxreal(v-state->rawnu.ptr.p_double[j], 0.0, _state);
                    if( ae_fp_greater(vv,state->repnlcerr) )
                    {
                        state->repnlcerr = vv;
                        state->repnlcidx = j;
                    }
                }
            }
        }
    }
}


/*************************************************************************
This function generates candidates

  -- ALGLIB --
     Copyright 07.12.2023 by Bochkanov Sergey
*************************************************************************/
static void diffevo_generatetrialpoints(gdemostate* state,
     const gdemopopulation* population,
     gdemopopulation* trial,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t rngmax;
    double invmax;
    ae_int_t i;
    ae_int_t popsize;
    double v0;
    double v1;


    n = state->n;
    m = state->m;
    popsize = state->popsize;
    ae_assert(m==1, "GDEMO: integrity check 4959 failed", _state);
    rngmax = hqrndgetmax(_state);
    invmax = (double)1/(double)rngmax;
    
    /*
     * Generate DE parameters for candidates and allocate space
     */
    ae_assert(popsize>=10, "GDEMO: integrity check 7156 failed", _state);
    rgrowrowsfixedcolsm(popsize, n, &trial->x, _state);
    rgrowrowsfixedcolsm(popsize, m+state->cntnlc, &trial->rawreplies, _state);
    rgrowrowsfixedcolsm(popsize, m, &trial->fitness, _state);
    rgrowrowsfixedcolsm(popsize, diffevo_paramscnt, &trial->deparams, _state);
    trial->cnt = 0;
    for(i=0; i<=popsize-1; i++)
    {
        ae_assert(diffevo_paramscnt==4, "GDEMO: integrity check 6759 failed", _state);
        if( state->fixedparams )
        {
            trial->deparams.ptr.pp_double[trial->cnt][0] = (double)(state->fixedstrategy);
            trial->deparams.ptr.pp_double[trial->cnt][1] = state->fixedcrossoverprob;
            trial->deparams.ptr.pp_double[trial->cnt][2] = state->fixeddifferentialweight;
            trial->deparams.ptr.pp_double[trial->cnt][3] = state->fixeddifferentialweight;
        }
        else
        {
            
            /*
             * Generate a pair of normal numbers V0 and V1, with V1 being guarded away from zero.
             *
             * Use V0 to compute perturbed CR, and use V1 (or ratio V0/V1) to compute perturbed F.
             * Because V0 is sometimes used to perturb both CR and F, there is some inter-dependence
             * between two perturbations. However, numerical experiments show that it does not
             * impact algorithm convergence.
             */
            trial->deparams.ptr.pp_double[trial->cnt][0] = (double)(diffevo_stcurrenttoordbest1);
            do
            {
                hqrndnormal2(&state->rs, &v0, &v1, _state);
            }
            while(ae_fp_less_eq(ae_fabs(v1, _state),1.0E-9));
            trial->deparams.ptr.pp_double[trial->cnt][1] = boundval(state->mucr+0.1*v0, 0.01, (double)(1), _state);
            if( state->profile==0 )
            {
                
                /*
                 * Use 'ROBUST' profile for DE weight
                 */
                trial->deparams.ptr.pp_double[trial->cnt][2] = boundval(state->muf+0.1*(v0/v1), 0.01, (double)(2), _state);
            }
            else
            {
                
                /*
                 * Use 'QUICK' profile for DE weight
                 */
                trial->deparams.ptr.pp_double[trial->cnt][2] = boundval(state->muf+0.1*(2.0*((double)hqrndintegerbase(&state->rs, _state)*invmax)-(double)1), 0.01, (double)(1), _state);
            }
            trial->deparams.ptr.pp_double[trial->cnt][3] = trial->deparams.ptr.pp_double[trial->cnt][2];
        }
        trial->cnt = trial->cnt+1;
    }
    ae_assert(trial->cnt==popsize, "GDEMO: integrity check 0719 failed", _state);
    
    /*
     * Generate candidates
     */
    diffevo_gencand(&trial->deparams, &population->x, &population->fitness, n, m, popsize, &state->xbest, &state->finitebndl, &state->finitebndu, &state->rs, &trial->x, _state);
}


/*************************************************************************
This is an internal function that generates candidates given DE params

  -- ALGLIB --
     Copyright 07.12.2023 by Bochkanov Sergey
*************************************************************************/
static void diffevo_gencand(/* Real    */ const ae_matrix* deparams,
     /* Real    */ const ae_matrix* populationx,
     /* Real    */ const ae_matrix* populationfitness,
     ae_int_t n,
     ae_int_t m,
     ae_int_t popsize,
     /* Real    */ const ae_matrix* xbest,
     /* Real    */ const ae_vector* finitebndl,
     /* Real    */ const ae_vector* finitebndu,
     hqrndstate* rs,
     /* Real    */ ae_matrix* trialx,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t rngmax;
    double invmax;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t k2;
    ae_int_t k3;
    ae_int_t r;
    ae_bool processed;
    ae_int_t strategytype;
    double crossoverprob;
    double differentialweight;
    double differentialweight2;
    ae_vector donor;
    ae_vector donorbase;

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

    
    /*
     * Try fast kernel
     */
    if( diffevo_gdemogencandkernel(deparams, populationx, populationfitness, n, m, popsize, xbest, finitebndl, finitebndu, diffevo_bndlimit, hqrndintegerbase(rs, _state), trialx, _state) )
    {
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Use generic code
     */
    ae_assert(m==1, "GDEMO: integrity check 4959 failed", _state);
    ae_assert(popsize>=10, "GDEMO: integrity check 7156 failed", _state);
    ae_assert(trialx->rows>=popsize&&trialx->cols>=n, "GDEMO: integrity check 7530 failed", _state);
    rngmax = hqrndgetmax(_state);
    invmax = (double)1/(double)rngmax;
    rallocv(n, &donor, _state);
    rallocv(n, &donorbase, _state);
    for(i=0; i<=popsize-1; i++)
    {
        
        /*
         * Load candidate parameters
         */
        strategytype = ae_round(deparams->ptr.pp_double[i][0], _state);
        crossoverprob = deparams->ptr.pp_double[i][1];
        differentialweight = deparams->ptr.pp_double[i][2];
        differentialweight2 = deparams->ptr.pp_double[i][3];
        
        /*
         * Generate a set of 4 indexes, possibly nondistinct (enforcing distinctness makes the code slower,
         * whislt not providing significant improvement in quality)
         */
        k0 = hqrndintegerbase(rs, _state)%popsize;
        k1 = hqrndintegerbase(rs, _state)%popsize;
        k2 = hqrndintegerbase(rs, _state)%popsize;
        k3 = hqrndintegerbase(rs, _state)%popsize;
        
        /*
         * Generate donor vector
         */
        processed = ae_false;
        if( strategytype==0 )
        {
            
            /*
             * DE/rand/1
             */
            rcopyrv(n, populationx, k0, &donor, _state);
            raddrv(n, -1.0, populationx, k1, &donor, _state);
            rmulv(n, differentialweight, &donor, _state);
            rcopyrv(n, populationx, k2, &donorbase, _state);
            processed = ae_true;
        }
        if( strategytype==1 )
        {
            
            /*
             * DE/best/2
             */
            rsetv(n, 0.0, &donor, _state);
            raddrv(n, 1.0, populationx, k0, &donor, _state);
            raddrv(n, -1.0, populationx, k1, &donor, _state);
            raddrv(n, 1.0, populationx, k2, &donor, _state);
            raddrv(n, -1.0, populationx, k3, &donor, _state);
            rmulv(n, differentialweight, &donor, _state);
            rcopyrv(n, xbest, hqrndintegerbase(rs, _state)%m, &donorbase, _state);
            processed = ae_true;
        }
        if( strategytype==diffevo_stcurrenttobest1 )
        {
            
            /*
             * DE/current-to-best/1
             */
            rsetv(n, 0.0, &donor, _state);
            raddrv(n, differentialweight, populationx, k0, &donor, _state);
            raddrv(n, -differentialweight, populationx, k1, &donor, _state);
            raddrv(n, differentialweight2, xbest, hqrndintegerbase(rs, _state)%m, &donor, _state);
            raddrv(n, -differentialweight2, populationx, i, &donor, _state);
            rcopyrv(n, populationx, i, &donorbase, _state);
            processed = ae_true;
        }
        if( strategytype==diffevo_stcurrenttoordbest1 )
        {
            
            /*
             * DE/current-to-ord_best/1
             */
            if( m!=1 )
            {
                ae_assert(ae_false, "GDEMO: integrity check 091405 failed", _state);
            }
            rsetv(n, 0.0, &donor, _state);
            if( ae_fp_less(populationfitness->ptr.pp_double[k0][0],populationfitness->ptr.pp_double[k1][0]) )
            {
                raddrv(n, differentialweight, populationx, k0, &donor, _state);
                raddrv(n, -differentialweight, populationx, k1, &donor, _state);
            }
            else
            {
                raddrv(n, differentialweight, populationx, k1, &donor, _state);
                raddrv(n, -differentialweight, populationx, k0, &donor, _state);
            }
            raddrv(n, differentialweight2, xbest, hqrndintegerbase(rs, _state)%m, &donor, _state);
            raddrv(n, -differentialweight2, populationx, i, &donor, _state);
            rcopyrv(n, populationx, i, &donorbase, _state);
            processed = ae_true;
        }
        ae_assert(processed, "GDEMO: integrity check 7131 failed", _state);
        raddv(n, 1.0, &donorbase, &donor, _state);
        for(j=0; j<=n-1; j++)
        {
            donor.ptr.p_double[j] = ae_maxreal(donor.ptr.p_double[j], diffevo_bndlimit*donorbase.ptr.p_double[j]+((double)1-diffevo_bndlimit)*finitebndl->ptr.p_double[j], _state);
            donor.ptr.p_double[j] = ae_minreal(donor.ptr.p_double[j], diffevo_bndlimit*donorbase.ptr.p_double[j]+((double)1-diffevo_bndlimit)*finitebndu->ptr.p_double[j], _state);
        }
        
        /*
         * Perform crossover with the target, apply box constraints to the donor in the process
         */
        r = hqrndintegerbase(rs, _state)%n;
        rcopyrr(n, populationx, i, trialx, i, _state);
        for(j=0; j<=n-1; j++)
        {
            if( (double)hqrndintegerbase(rs, _state)*invmax<=crossoverprob||j==r )
            {
                trialx->ptr.pp_double[i][j] = donor.ptr.p_double[j];
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This is an internal function that generates candidates given DE params
using SIMD kernels

  -- ALGLIB --
     Copyright 07.12.2023 by Bochkanov Sergey
*************************************************************************/
static ae_bool diffevo_gdemogencandkernel(/* Real    */ const ae_matrix* deparams,
     /* Real    */ const ae_matrix* populationx,
     /* Real    */ const ae_matrix* populationfitness,
     ae_int_t n,
     ae_int_t m,
     ae_int_t popsize,
     /* Real    */ const ae_matrix* xbest,
     /* Real    */ const ae_vector* finitebndl,
     /* Real    */ const ae_vector* finitebndu,
     double bndsmooth,
     ae_int_t seedval,
     /* Real    */ ae_matrix* trialx,
     ae_state *_state)
{
    ae_bool result;


    result = ae_false;
    return result;
}


/*************************************************************************
Promote candidates

  -- ALGLIB --
     Copyright 07.12.2023 by Bochkanov Sergey
*************************************************************************/
static void diffevo_updatepopulation(gdemostate* state,
     const gdemopopulation* children,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t cntnlc;
    ae_int_t i;
    double avgcr;
    double sumf;
    double sumf2;
    double df;
    double sumdf;
    ae_int_t avgcnt;


    n = state->n;
    m = state->m;
    cntnlc = state->cntnlc;
    
    /*
     * Perform competition.
     *
     * NOTE: it is important to compare fitness as follows: "if CandFitness<=PopFitness",
     *       using <= instead of <, because it promotes explorational behavior in areas
     *       with vanishing gradient.
     */
    ae_assert(ae_fp_eq(state->nsample,(double)(1)), "GDEMO: integrity check 2649 failed", _state);
    ae_assert(m==1, "GDEMO: integrity check 2650 failed", _state);
    ae_assert(state->popsize==children->cnt, "GDEMO: integrity check 9300 failed", _state);
    avgcr = 0.0;
    sumf = 0.0;
    sumf2 = 0.0;
    sumdf = 0.0;
    avgcnt = 0;
    for(i=0; i<=state->popsize-1; i++)
    {
        if( ae_fp_less_eq(children->fitness.ptr.pp_double[i][0],state->population.fitness.ptr.pp_double[i][0]) )
        {
            df = state->population.fitness.ptr.pp_double[i][0]-children->fitness.ptr.pp_double[i][0];
            
            /*
             * The candidate wins! Copy the candidate data
             */
            rcopyrr(n, &children->x, i, &state->population.x, i, _state);
            rcopyrr(m+cntnlc, &children->rawreplies, i, &state->population.rawreplies, i, _state);
            rcopyrr(m, &children->fitness, i, &state->population.fitness, i, _state);
            
            /*
             * Update average params
             */
            ae_assert(diffevo_paramscnt==4, "GDEMO: integrity check 207213 failed", _state);
            if( df>(double)0 )
            {
                avgcr = avgcr+df*children->deparams.ptr.pp_double[i][1];
                sumf = sumf+df*children->deparams.ptr.pp_double[i][2];
                sumf2 = sumf2+df*ae_sqr(children->deparams.ptr.pp_double[i][2], _state);
                sumdf = sumdf+df;
                avgcnt = avgcnt+1;
            }
        }
    }
    if( avgcnt>0 )
    {
        state->mucr = ((double)1-diffevo_mulearningrate)*state->mucr+diffevo_mulearningrate*(avgcr/sumdf);
        state->muf = ((double)1-diffevo_mulearningrate)*state->muf+diffevo_mulearningrate*(sumf2/sumdf/(sumf/sumdf));
    }
}


void _gdemopopulation_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gdemopopulation *p = (gdemopopulation*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_init(&p->x, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->rawreplies, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->fitness, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->deparams, 0, 0, DT_REAL, _state, make_automatic);
}


void _gdemopopulation_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gdemopopulation       *dst = (gdemopopulation*)_dst;
    const gdemopopulation *src = (const gdemopopulation*)_src;
    dst->cnt = src->cnt;
    ae_matrix_init_copy(&dst->x, &src->x, _state, make_automatic);
    ae_matrix_init_copy(&dst->rawreplies, &src->rawreplies, _state, make_automatic);
    ae_matrix_init_copy(&dst->fitness, &src->fitness, _state, make_automatic);
    ae_matrix_init_copy(&dst->deparams, &src->deparams, _state, make_automatic);
}


void _gdemopopulation_clear(void* _p)
{
    gdemopopulation *p = (gdemopopulation*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_clear(&p->x);
    ae_matrix_clear(&p->rawreplies);
    ae_matrix_clear(&p->fitness);
    ae_matrix_clear(&p->deparams);
}


void _gdemopopulation_destroy(void* _p)
{
    gdemopopulation *p = (gdemopopulation*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_destroy(&p->x);
    ae_matrix_destroy(&p->rawreplies);
    ae_matrix_destroy(&p->fitness);
    ae_matrix_destroy(&p->deparams);
}


void _gdemostate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gdemostate *p = (gdemostate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->finitebndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitebndu, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->densea, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->al, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->au, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasal, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasau, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->rawnl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawnu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasnl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasnu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->ascales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->reportx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->querydata, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyfi, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    _hqrndstate_init(&p->rs, _state, make_automatic);
    _gdemopopulation_init(&p->population, _state, make_automatic);
    _gdemopopulation_init(&p->offspring, _state, make_automatic);
    ae_matrix_init(&p->xbest, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fitbest, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawfbest, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpzero, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->dummysparsea, _state, make_automatic);
    ae_vector_init(&p->goodbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->goodbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->empiricalbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->empiricalbndu, 0, DT_REAL, _state, make_automatic);
}


void _gdemostate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gdemostate       *dst = (gdemostate*)_dst;
    const gdemostate *src = (const gdemostate*)_src;
    dst->n = src->n;
    dst->m = src->m;
    dst->cntlc = src->cntlc;
    dst->cntnlc = src->cntnlc;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    dst->hasx0 = src->hasx0;
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndl, &src->finitebndl, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndu, &src->finitebndu, _state, make_automatic);
    ae_matrix_init_copy(&dst->densea, &src->densea, _state, make_automatic);
    ae_vector_init_copy(&dst->al, &src->al, _state, make_automatic);
    ae_vector_init_copy(&dst->au, &src->au, _state, make_automatic);
    ae_vector_init_copy(&dst->hasal, &src->hasal, _state, make_automatic);
    ae_vector_init_copy(&dst->hasau, &src->hasau, _state, make_automatic);
    ae_vector_init_copy(&dst->rawnl, &src->rawnl, _state, make_automatic);
    ae_vector_init_copy(&dst->rawnu, &src->rawnu, _state, make_automatic);
    ae_vector_init_copy(&dst->hasnl, &src->hasnl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasnu, &src->hasnu, _state, make_automatic);
    ae_vector_init_copy(&dst->ascales, &src->ascales, _state, make_automatic);
    dst->popsize = src->popsize;
    dst->epochscnt = src->epochscnt;
    dst->constrmode = src->constrmode;
    dst->rho1 = src->rho1;
    dst->rho2 = src->rho2;
    dst->eps = src->eps;
    dst->nsample = src->nsample;
    dst->profile = src->profile;
    dst->fixedparams = src->fixedparams;
    dst->fixedstrategy = src->fixedstrategy;
    dst->fixedcrossoverprob = src->fixedcrossoverprob;
    dst->fixeddifferentialweight = src->fixeddifferentialweight;
    dst->stoponsmallf = src->stoponsmallf;
    dst->smallf = src->smallf;
    dst->condfxepsf = src->condfxepsf;
    dst->condfxepsx = src->condfxepsx;
    dst->mucr = src->mucr;
    dst->muf = src->muf;
    dst->requesttype = src->requesttype;
    ae_vector_init_copy(&dst->reportx, &src->reportx, _state, make_automatic);
    dst->reportf = src->reportf;
    dst->querysize = src->querysize;
    ae_vector_init_copy(&dst->querydata, &src->querydata, _state, make_automatic);
    ae_vector_init_copy(&dst->replyfi, &src->replyfi, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    _hqrndstate_init_copy(&dst->rs, &src->rs, _state, make_automatic);
    _gdemopopulation_init_copy(&dst->population, &src->population, _state, make_automatic);
    _gdemopopulation_init_copy(&dst->offspring, &src->offspring, _state, make_automatic);
    ae_matrix_init_copy(&dst->xbest, &src->xbest, _state, make_automatic);
    ae_vector_init_copy(&dst->fitbest, &src->fitbest, _state, make_automatic);
    ae_vector_init_copy(&dst->rawfbest, &src->rawfbest, _state, make_automatic);
    dst->repterminationtype = src->repterminationtype;
    dst->repiterationscount = src->repiterationscount;
    dst->replcerr = src->replcerr;
    dst->replcidx = src->replcidx;
    dst->repnlcerr = src->repnlcerr;
    dst->repnlcidx = src->repnlcidx;
    ae_vector_init_copy(&dst->tmpzero, &src->tmpzero, _state, make_automatic);
    _sparsematrix_init_copy(&dst->dummysparsea, &src->dummysparsea, _state, make_automatic);
    ae_vector_init_copy(&dst->goodbndl, &src->goodbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->goodbndu, &src->goodbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->empiricalbndl, &src->empiricalbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->empiricalbndu, &src->empiricalbndu, _state, make_automatic);
}


void _gdemostate_clear(void* _p)
{
    gdemostate *p = (gdemostate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->finitebndl);
    ae_vector_clear(&p->finitebndu);
    ae_matrix_clear(&p->densea);
    ae_vector_clear(&p->al);
    ae_vector_clear(&p->au);
    ae_vector_clear(&p->hasal);
    ae_vector_clear(&p->hasau);
    ae_vector_clear(&p->rawnl);
    ae_vector_clear(&p->rawnu);
    ae_vector_clear(&p->hasnl);
    ae_vector_clear(&p->hasnu);
    ae_vector_clear(&p->ascales);
    ae_vector_clear(&p->reportx);
    ae_vector_clear(&p->querydata);
    ae_vector_clear(&p->replyfi);
    _rcommstate_clear(&p->rstate);
    _hqrndstate_clear(&p->rs);
    _gdemopopulation_clear(&p->population);
    _gdemopopulation_clear(&p->offspring);
    ae_matrix_clear(&p->xbest);
    ae_vector_clear(&p->fitbest);
    ae_vector_clear(&p->rawfbest);
    ae_vector_clear(&p->tmpzero);
    _sparsematrix_clear(&p->dummysparsea);
    ae_vector_clear(&p->goodbndl);
    ae_vector_clear(&p->goodbndu);
    ae_vector_clear(&p->empiricalbndl);
    ae_vector_clear(&p->empiricalbndu);
}


void _gdemostate_destroy(void* _p)
{
    gdemostate *p = (gdemostate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->finitebndl);
    ae_vector_destroy(&p->finitebndu);
    ae_matrix_destroy(&p->densea);
    ae_vector_destroy(&p->al);
    ae_vector_destroy(&p->au);
    ae_vector_destroy(&p->hasal);
    ae_vector_destroy(&p->hasau);
    ae_vector_destroy(&p->rawnl);
    ae_vector_destroy(&p->rawnu);
    ae_vector_destroy(&p->hasnl);
    ae_vector_destroy(&p->hasnu);
    ae_vector_destroy(&p->ascales);
    ae_vector_destroy(&p->reportx);
    ae_vector_destroy(&p->querydata);
    ae_vector_destroy(&p->replyfi);
    _rcommstate_destroy(&p->rstate);
    _hqrndstate_destroy(&p->rs);
    _gdemopopulation_destroy(&p->population);
    _gdemopopulation_destroy(&p->offspring);
    ae_matrix_destroy(&p->xbest);
    ae_vector_destroy(&p->fitbest);
    ae_vector_destroy(&p->rawfbest);
    ae_vector_destroy(&p->tmpzero);
    _sparsematrix_destroy(&p->dummysparsea);
    ae_vector_destroy(&p->goodbndl);
    ae_vector_destroy(&p->goodbndu);
    ae_vector_destroy(&p->empiricalbndl);
    ae_vector_destroy(&p->empiricalbndu);
}


#endif
#if defined(AE_COMPILE_MINDF) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
                          GLOBAL OPTIMIZATION
               SUBJECT TO BOX/LINEAR/NONLINEAR CONSTRAINTS

The  subroutine  minimizes  function   F(x)  of N arguments subject to any
combination of:
* bound constraints
* linear inequality constraints
* linear equality constraints
* nonlinear generalized inequality constraints Li<=Ci(x)<=Ui, with one  of
  Li/Ui possibly being infinite

REQUIREMENTS:
* F() and C()  do  NOT  have  to be differentiable, locally Lipschitz   or
  continuous. Most solvers in this subpackage can deal with  nonsmoothness
  or minor discontinuities, although obviously smoother problems  are  the
  most easy ones.
* generally, F() and C() must be computable at any point which is feasible
  subject to box constraints

USAGE:

1. User  initializes  algorithm  state   with   mindfcreate()   call   and
   chooses specific solver to be used. There is some solver which is  used
   by default, with default settings, but  you  should  NOT  rely  on  the
   default choice. It may change in the future releases of ALGLIB  without
   notice, and no one can guarantee that the new solver will be  able   to
   solve your problem with default settings.

2. User adds boundary and/or linear and/or nonlinear constraints by  means
   of calling one of the following functions:
   a) mindfsetbc() for boundary constraints
   b) mindfsetlc2dense() for linear constraints
   c) mindfsetnlc2() for nonlinear constraints
   You may combine (a), (b) and (c) in one optimization problem.

3. User sets scale of the variables with mindfsetscale() function.  It  is
   VERY important to set  variable  scales  because  many  derivative-free
   algorithms refuse to work when variables are badly scaled.
   Scaling  helps  to seed initial  population,  control  convergence  and
   enforce penalties for constraint violation.

4. Finally, user calls mindfoptimize()  function which takes algorithm
   state and pointer (delegate, etc) to callback function which calculates
   F and G.

5. User calls mindfresults() to get a solution


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from  size  of  X
    X       -   starting point, array[N]. Some solvers can utilize  a good
                initial point to seed computations.
                
                As of ALGLIB 4.04, the initial point is:
                * used by GDEMO
                
                If the chosen solver does not need initial point, one  can
                supply zeros.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state


IMPORTANT: the  MINDF  optimizer   supports   parallel   model  evaluation
           ('callback  parallelism'). This feature, which  is  present  in
           commercial ALGLIB editions, greatly accelerates algorithms like
           differential evolution which usually issue  batch  requests  to
           user callbacks which can be efficiently parallelized.
           
           Callback parallelism  is  usually  beneficial  when  the  batch
           evalution requires more than several milliseconds.
           
           See ALGLIB Reference Manual, 'Working with commercial  version'
           section,  and  comments  on  mindfoptimize()  function for more
           information.
           
  -- ALGLIB --
     Copyright 24.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfcreate(ae_int_t n,
     /* Real    */ const ae_vector* x,
     mindfstate* state,
     ae_state *_state)
{

    _mindfstate_clear(state);

    ae_assert(n>=1, "MinDFCreate: N<1", _state);
    ae_assert(x->cnt>=n, "MinDFCreate: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinDFCreate: X contains infinite or NaN values", _state);
    
    /*
     * Default settings and state
     */
    state->dotimers = ae_false;
    state->protocolversion = 2;
    state->n = n;
    rsetallocv(n, _state->v_neginf, &state->bndl, _state);
    rsetallocv(n, _state->v_posinf, &state->bndu, _state);
    rsetallocv(n, -ae_maxrealnumber, &state->finitebndl, _state);
    rsetallocv(n, ae_maxrealnumber, &state->finitebndu, _state);
    bsetallocv(n, ae_false, &state->hasbndl, _state);
    bsetallocv(n, ae_false, &state->hasbndu, _state);
    state->m = 0;
    state->nnlc = 0;
    rsetallocv(n, 1.0, &state->s, _state);
    state->hasx0 = ae_true;
    rcopyallocv(n, x, &state->x0, _state);
    rcopyallocv(n, x, &state->xf, _state);
    state->xrep = ae_false;
    state->ssgdmomentum = mindf_defaultmomentum;
    state->rngseed = 0;
    state->gdemofixedparams = ae_false;
    state->gdemoprofile = 0;
    state->gdemorho1 = (double)(0);
    state->gdemorho2 = (double)(0);
    state->stoponsmallf = ae_false;
    state->condfxepsf = 0.0;
    state->condfxepsx = 0.0;
    mindfsetalgogdemo(state, 100, 0, _state);
    
    /*
     * communication with user
     */
    state->userterminationneeded = ae_false;
    ae_vector_set_length(&state->rstate.ia, 2+1, _state);
    ae_vector_set_length(&state->rstate.ba, 0+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
This function sets box constraints.

Box constraints are inactive by default (after initial creation).

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF.

  -- ALGLIB --
     Copyright 24.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetbc(mindfstate* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_assert(bndl->cnt>=n, "MinDFSetBC: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=n, "MinDFSetBC: Length(BndU)<N", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "MinDFSetBC: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "MinDFSetBC: BndL contains NAN or -INF", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
        state->finitebndl.ptr.p_double[i] = rcase2(state->hasbndl.ptr.p_bool[i], state->bndl.ptr.p_double[i], -ae_maxrealnumber, _state);
        state->finitebndu.ptr.p_double[i] = rcase2(state->hasbndu.ptr.p_bool[i], state->bndu.ptr.p_double[i], ae_maxrealnumber, _state);
    }
}


/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU with dense
constraint matrix A.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinDFcreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetlc2dense(mindfstate* state,
     /* Real    */ const ae_matrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t k,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;


    n = state->n;
    
    /*
     * Check input arguments
     */
    ae_assert(k>=0, "MinDFSetLC2Dense: K<0", _state);
    ae_assert(k==0||a->cols>=n, "MinDFSetLC2Dense: Cols(A)<N", _state);
    ae_assert(k==0||a->rows>=k, "MinDFSetLC2Dense: Rows(A)<K", _state);
    ae_assert(apservisfinitematrix(a, k, n, _state), "MinDFSetLC2Dense: A contains infinite or NaN values!", _state);
    ae_assert(al->cnt>=k, "MinDFSetLC2Dense: Length(AL)<K", _state);
    ae_assert(au->cnt>=k, "MinDFSetLC2Dense: Length(AU)<K", _state);
    for(i=0; i<=k-1; i++)
    {
        ae_assert(ae_isfinite(al->ptr.p_double[i], _state)||ae_isneginf(al->ptr.p_double[i], _state), "MinDFSetLC2Dense: AL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(au->ptr.p_double[i], _state)||ae_isposinf(au->ptr.p_double[i], _state), "MinDFSetLC2Dense: AU contains NAN or -INF", _state);
    }
    
    /*
     * Quick exit if needed
     */
    if( k==0 )
    {
        state->m = 0;
        return;
    }
    
    /*
     * Copy
     */
    rcopyallocv(k, al, &state->al, _state);
    rcopyallocv(k, au, &state->au, _state);
    rcopyallocm(k, n, a, &state->densea, _state);
    state->m = k;
}


/*************************************************************************
This function sets two-sided nonlinear constraints.

In fact, this function sets only  NUMBER  of  the  nonlinear  constraints.
Constraints  themselves  (constraint  functions)   are   passed   to   the
MinDFOptimize() method.

This method accepts user-defined vector function F[] where:
* first component of F[] corresponds to the objective
* subsequent NNLC components of F[] correspond to the two-sided  nonlinear
  constraints NL<=C(x)<=NU, where
  * NL[i]=NU[i] => I-th row is an equality constraint Ci(x)=NL
  * NL[i]<NU[i] => I-th tow is a  two-sided constraint NL[i]<=Ci(x)<=NU[i]
  * NL[i]=-INF  => I-th row is an one-sided constraint Ci(x)<=NU[i]
  * NU[i]=+INF  => I-th row is an one-sided constraint NL[i]<=Ci(x)
  * NL[i]=-INF, NU[i]=+INF => constraint is ignored

NOTE: you may combine nonlinear constraints with linear/boundary ones.  If
      your problem has mixed constraints, you  may explicitly specify some
      of them as linear or box ones.
      It helps optimizer to handle them more efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinDFCreate call.
    NL      -   array[NNLC], lower bounds, can contain -INF
    NU      -   array[NNLC], lower bounds, can contain +INF
    NNLC    -   constraints count, NNLC>=0

NOTE 1: nonlinear constraints are satisfied only  approximately!   It   is
        possible   that  algorithm  will  evaluate  function  outside   of
        feasible area!
        
NOTE 2: the algorithm scales variables according to the scale specified by
        MinDFSetScale() function, so it can handle problems with badly
        scaled variables (as long as we KNOW their scales).
           
        However,  there  is  no  way  to  automatically  scale   nonlinear
        constraints. Inappropriate scaling  of nonlinear  constraints  may
        ruin convergence. Solving problem with  constraint  "1000*G0(x)=0"
        is NOT the same as solving it with constraint "0.001*G0(x)=0".
           
        It means that YOU are  the  one who is responsible for the correct
        scaling of the nonlinear constraints. We recommend  you  to  scale
        nonlinear constraints in such  a  way  that  the  derivatives  (if
        constraints are differentiable) have approximately unit  magnitude
        (for problems  with  unit  variable  scales)  or  have  magnitudes
        approximately equal to 1/S[i] (where S is a variable scale set  by
        MinDFSetScale() function).

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetnlc2(mindfstate* state,
     /* Real    */ const ae_vector* nl,
     /* Real    */ const ae_vector* nu,
     ae_int_t nnlc,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(nnlc>=0, "MinDFSetNLC2: NNLC<0", _state);
    ae_assert(nl->cnt>=nnlc, "MinDFSetNLC2: Length(NL)<NNLC", _state);
    ae_assert(nu->cnt>=nnlc, "MinDFSetNLC2: Length(NU)<NNLC", _state);
    state->nnlc = nnlc;
    rallocv(nnlc, &state->nl, _state);
    rallocv(nnlc, &state->nu, _state);
    for(i=0; i<=nnlc-1; i++)
    {
        ae_assert(ae_isfinite(nl->ptr.p_double[i], _state)||ae_isneginf(nl->ptr.p_double[i], _state), "MinDFSetNLC2: NL[i] is +INF or NAN", _state);
        ae_assert(ae_isfinite(nu->ptr.p_double[i], _state)||ae_isposinf(nu->ptr.p_double[i], _state), "MinDFSetNLC2: NU[i] is -INF or NAN", _state);
        state->nl.ptr.p_double[i] = nl->ptr.p_double[i];
        state->nu.ptr.p_double[i] = nu->ptr.p_double[i];
    }
}


/*************************************************************************
This function sets stopping conditions for the optimizer.

This function sets a  combined   stopping  condition:  stopping  when  two
criteria are met simultaneously:
* function   values   has  converged  to  a  neighborhood  whose  size  is
  proportional to epsF
* variable   values   has  converged  to  a  neighborhood  whose  size  is
  proportional to epsX
It is possible to use only one condition by setting another EPS to zero.

Most derivarive-free solvers are heuristics, so the code used to implement
this stopping condition is an heuristic too. Usually 'proportional to EPS'
means that we are somewhere between Eps/10...Eps*10 away from the solution.
However, there are no warranties that the solver  has  actually  converged
to something, although in practice it works well.

The  specific  meaning  of  'converging'  is  algorithm-dependent.  It  is
possible that some future ALGLIB optimizers will  ignore  this  condition,
see comments on specific solvers for more info. This  condition  does  not
work for multi-objective problems.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsF    -   >=0:
                * zero value means no condition for F
                * EpsF>0 means stopping when the solver converged with
                  an error estimate less than EpsF*max(|F|,1)
    EpsX    -   >=0:
                * zero value means no condition for X
                * EpsX>0 means stopping when the solver converged with
                  error in I-th variable less than EpsX*S[i], where S[i]
                  is a variable scale

  -- ALGLIB --
     Copyright 23.04.2024 by Bochkanov Sergey
*************************************************************************/
void mindfsetcondfx(mindfstate* state,
     double epsf,
     double epsx,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsf, _state), "MinDFSetCondFX: EpsF is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsf,(double)(0)), "MinDFSetCondFX: negative EpsF", _state);
    ae_assert(ae_isfinite(epsx, _state), "MinDFSetCondFX: EpsX is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "MinDFSetCondFX: negative EpsX", _state);
    state->condfxepsf = epsf;
    state->condfxepsx = epsx;
}


/*************************************************************************
This function activates/deactivates internal timers  used  to  track  time
spent in various parts of the solver (mostly, callbacks vs solver itself).

When activated with this function, the following timings are stored in the
mindfreport structure fields:
* total time spend in the optimization
* time spent in the callback
* time spent in the solver itself

See comments on mindfreport structure for more  information  about  timers
and their accuracy.

Timers are an essential part of reports that helps to find out  where  the
most time is spent and how to  optimize  the  code.  E.g.,  noticing  that
significant amount of time is spent  in  numerical  differentiation  makes
obvious that ALGLIB-provided parallel numerical differentiation is needed.

However, time measurements add noticeable  overhead,  about  50-100ns  per
function call. In some applications it results in a significant  slowdown,
that's why this option is inactive  by  default  and  should  be  manually
activated.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    UseTimers-  true or false
    
NOTE: when tracing is turned on with alglib::trace_file(), some derivative
      free  solvers   may  also  perform  internal,  more  detailed   time
      measurements, which are printed to the log file.

  -- ALGLIB --
     Copyright 23.04.2024 by Bochkanov Sergey
*************************************************************************/
void mindfusetimers(mindfstate* state,
     ae_bool usetimers,
     ae_state *_state)
{


    state->dotimers = usetimers;
}


/*************************************************************************
This function sets stopping condition for the optimizer: function   values
has converged to a neighborhood whose size is proportional to epsF.

Most derivarive-free solvers are heuristics, so the code used to implement
this stopping condition is an heuristic too. Usually 'proportional to EPS'
means that we are somewhere between Eps/10...Eps*10 away from the solution.
However, there are no warranties that the solver  has  actually  converged
to something, although in practice it works well.

The  specific  meaning  of  'converging'  is  algorithm-dependent.  It  is
possible that some future ALGLIB optimizers will  ignore  this  condition,
see comments on specific solvers for more info. This  condition  does  not
work for multi-objective problems.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsF    -   >=0:
                * zero value means no condition for F
                * EpsF>0 means stopping when the solver converged with
                  an error estimate less than EpsF*max(|F|,1)

  -- ALGLIB --
     Copyright 23.04.2024 by Bochkanov Sergey
*************************************************************************/
void mindfsetcondf(mindfstate* state, double epsf, ae_state *_state)
{


    ae_assert(ae_isfinite(epsf, _state), "MinDFSetCondF: EpsF is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsf,(double)(0)), "MinDFSetCondF: negative EpsF", _state);
    state->condfxepsf = epsf;
    state->condfxepsx = (double)(0);
}


/*************************************************************************
This function sets variable scales.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison  with  tolerances)  and  to
guide algorithm steps.

The scale of a variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetscale(mindfstate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(s->cnt>=state->n, "MinDFSetScale: Length(S)<N", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "MinDFSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "MinDFSetScale: S contains zero elements", _state);
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinDFOptimize().

NOTE: algorithm passes two parameters to rep() callback  - the best  point
      so far and a function value at the point. For unconstrained problems
      the function value is  non-increasing (the most recent best point is
      always at least not worse than the previous best one).  However,  it
      can increase between iterations when  solving  constrained  problems
      (a  better  point  may  have  higher  objective  value  but  smaller
      constraint violation).

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void mindfsetxrep(mindfstate* state, ae_bool needxrep, ae_state *_state)
{


    state->xrep = needxrep;
}


/*************************************************************************
This subroutine submits request for termination of a running optimizer. It
should be called from a user-supplied callback when user decides  that  it
is time to "smoothly" terminate optimization process.  As  a  result,  the
optimizer  stops  at  the  point  which  was  "current  accepted" when the
termination request was submitted and returns  error  code  8  (successful
termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on an optimizer which is NOT running will have
      no effect.

NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfrequesttermination(mindfstate* state, ae_state *_state)
{


    state->userterminationneeded = ae_true;
}


/*************************************************************************
This function sets seed used by internal RNG.

By default, a random seed is used, i.e. every time you run the solver,  we
seed its generator with a new value obtained  from  the  system-wide  RNG.
Thus, the solver returns non-deterministic results. You  can  change  such
a behavior by specifying a fixed positive seed value.

INPUT PARAMETERS:
    S           -   optimizer structure
    SeedVal     -   seed value:
                    * positive values are used for  seeding  RNG  with  a
                      fixed  seed,  i.e.  subsequent  runs  on  the  same
                      objective will return the same results
                    * non-positive seed means that a random  seed is used
                      for every run, i.e. subsequent  runs  on  the  same
                      objective will return slightly different results

  -- ALGLIB --
     Copyright 26.04.2024 by Bochkanov Sergey
*************************************************************************/
void mindfsetseed(mindfstate* s, ae_int_t seedval, ae_state *_state)
{


    s->rngseed = seedval;
}


/*************************************************************************
This subroutine sets optimization algorithm to the differential  evolution
solver GDEMO (Generalized Differential Evolution Multiobjective)  with  an
automatic parameters selection.

NOTE: a version with manually tuned parameters can be activated by calling 
      the mindfsetalgogdemofixed() function.

The primary stopping condition for the solver is to stop after the specified
number of iterations. You can also specify  additional  criteria  to  stop
early:
* stop when subpopulation target values (2N+1 best individuals) are within
  EPS from the best one so far (function values seem to converge)
* stop when both subpopulation target values  AND  subpopulation  variable 
  values are within EPS from the best one so far

The first condition is specified with mindfsetcondf(), the second  one  is
activated with mindfsetcondfx().

Both conditions are heuristics which  may fail. Being 'within EPS from the
best value so far'  in  practice  means  that  we  are  somewhere   within
[0.1EPS,10EPS] from the true  solution;  however,  on  difficult  problems
this condition may fire too early.

Imposing an additional requirement that variable values have clustered  too
may prevent us from premature  stopping. However, on multi-extremal and/or
noisy problems too many individuals may be trapped away from the  optimum,
preventing this condition from activation.

ALGORITHM PROPERTIES:

* the solver uses a variant of  the  adaptive  parameter  tuning  strategy
  called 'Success-History  Based  Parameter  Adaptation  for  Differential
  Evolution Ensemble' (SHADE) by Ryoji Tanabe and Alex  Fukunaga.  You  do
  not have to specify crossover probability and differential weight,   the
  solver will automatically choose the most appropriate strategy.
  
* the solver can handle box, linear,  nonlinear  constraints.  Linear  and
  nonlinear constraints are  handled  by  means  of an L1/L2  penalty. The
  solver does not violate box constraints at any point,  but  may  violate
  linear and nonlinear ones. Penalty coefficient can be changed with   the
  mindfsetgdemopenalty() function.
  
* the solver heavily depends on variable scales being available (specified
  by means of mindfsetscale() call) and on box constraints with both lower
  and upper bounds being available which are used to determine the  search
  region. It will work without box constraints  and  without  scales,  but
  results are likely to be suboptimal.
  
* the solver is SIMD-optimized  and  parallelized  (in  commercial  ALGLIB
  editions), with nearly linear scalability of parallel processing.
  
* this solver is intended for finding solutions  with up to several digits
  of precision at best. Its primary purpose  is  to  find  at  least  some
  solution to an otherwise intractable problem.

IMPORTANT: derivative-free optimization is inherently less robust than the
           smooth nonlinear programming, especially when nonsmoothness and
           discontinuities are present.
           
           Derivative-free  algorithms  have  less  convergence guarantees
           than their smooth  counterparts.  It  is  considered  a  normal
           (although, obviously, undesirable) situation when a derivative-
           -free algorithm fails to converge with desired precision. Having
           2 digits of accurasy is already a  good  result,  on  difficult
           problems (high numerical noise, discontinuities) you  may  have
           even less than that.

INPUT PARAMETERS:
    State       -   solver
    EpochsCnt   -   iterations count, >0.  Usually   the  algorithm  needs
                    hundreds of iterations to converge.
    PopSize     -   population  size,  >=0.  Zero  value  means  that  the
                    default value (which is 10*N in the  current  version)
                    will be chosen. Good values are  in  5*N...20*N,  with
                    the smaller values being recommended for easy problems
                    and the larger  values  for  difficult  multi-extremal
                    and/or noisy tasks.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetalgogdemo(mindfstate* state,
     ae_int_t epochscnt,
     ae_int_t popsize,
     ae_state *_state)
{


    ae_assert(popsize>=0, "MinDFSetAlgoGDEMO: PopSize<0", _state);
    ae_assert(epochscnt>0, "MinDFSetAlgoGDEMO: EpochsCnt<=0", _state);
    state->gdemoepochscnt = epochscnt;
    state->gdemopopsize = popsize;
    state->gdemofixedparams = ae_false;
    state->solvertype = 0;
}


/*************************************************************************
This subroutine tells GDEMO differential  evolution  optimizer  to  use  a
ROBUST profile (the default option).

The ROBUST profile is intended  to  facilitate  explorative  behavior  and
robust convergence even on difficult multi-extremal problems. It comes  at
the expense of increased running time  even  on  easy  problems. The QUICK
profile can be chosen if your problem is relatively easy to handle and you
prefer speed over robustness. In most cases, the QUICK profile  is  ~2x-3x
faster than the robust one.

This function has effect only on adaptive GDEMO with automatic  parameters
selection.  It  has  no  effect  on  fixed-parameters  GDEMO  or any other
solvers.

IMPORTANT: this function does NOT change the  optimization  algorithm.  If
           want to activate differential evolution solver, you still  have
           to call a proper mindfsetalgo???() function.

INPUT PARAMETERS:
    State       -   solver

  -- ALGLIB --
     Copyright 25.04.2024 by Bochkanov Sergey
*************************************************************************/
void mindfsetgdemoprofilerobust(mindfstate* state, ae_state *_state)
{


    state->gdemoprofile = 0;
}


/*************************************************************************
This subroutine tells GDEMO differential  evolution  optimizer  to  use  a
QUICK profile.

The QUICK profile is intended  to  facilitate accelerated  convergence  on
medium-complexity problems at the cost  of  (sometimes)  having  premature
convergence  on  difficult  and/or  multi-extremal  problems.  The  ROBUST
profile can be selected if you favor convergence  warranties  over  speed.
In most cases, the ROBUST profile is ~2x-3x slower than the QUICK one.

This function has effect only on adaptive GDEMO with automatic  parameters
selection.  It  has  no  effect  on  fixed-parameters  GDEMO  or any other
solvers.

IMPORTANT: this function does NOT change the  optimization  algorithm.  If
           you want to activate differential evolution solver,  you  still
           have to call a proper mindfsetalgo???() function.

INPUT PARAMETERS:
    State       -   solver

  -- ALGLIB --
     Copyright 25.04.2024 by Bochkanov Sergey
*************************************************************************/
void mindfsetgdemoprofilequick(mindfstate* state, ae_state *_state)
{


    state->gdemoprofile = 1;
}


/*************************************************************************
This subroutine tells GDEMO differential  evolution  optimizer  to  handle
linear/nonlinear constraints by an L1/L2 penalty function.

IMPORTANT: this function does NOT change the  optimization  algorithm.  If
           want to activate differential evolution solver, you still  have
           to call a proper mindfsetalgo???() function.

INPUT PARAMETERS:
    State       -   solver
    Rho1, Rho2  -   penalty parameters for constraint violations:
                    * Rho1 is a multiplier for L1 penalty
                    * Rho2 is a multiplier for L2 penalty
                    * Rho1,Rho2>=0
                    * having both of them at zero means that some  default
                      value will be chosen.
                    Ignored for problems with box-only constraints.
                    
                    L1 penalty is usually better at enforcing constraints,
                    but leads to slower convergence than L2 penalty. It is
                    possible to combine both kinds of penalties together.
                    
                    There is a compromise between constraint  satisfaction
                    and  optimality:  high  values  of   Rho   mean   that
                    constraints are satisfied with high accuracy  but that
                    the target may  be  underoptimized  due  to  numerical
                    difficulties.  Small  values  of  Rho  mean  that  the
                    solution may  grossly  violate  constraints.  Choosing
                    good Rho is usually a matter of trial and error.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetgdemopenalty(mindfstate* state,
     double rho1,
     double rho2,
     ae_state *_state)
{


    ae_assert(ae_isfinite(rho1, _state), "MinDFSetGDEMOPenalty: Rho1 is not a finite number", _state);
    ae_assert(ae_isfinite(rho2, _state), "MinDFSetGDEMOPenalty: Rho2 is not a finite number", _state);
    ae_assert(ae_fp_greater_eq(rho1,(double)(0)), "MinDFSetGDEMOPenalty: Rho1<0", _state);
    ae_assert(ae_fp_greater_eq(rho2,(double)(0)), "MinDFSetGDEMOPenalty: Rho2<0", _state);
    state->gdemorho1 = rho1;
    state->gdemorho2 = rho2;
}


/*************************************************************************
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetsmallf(mindfstate* state, double f, ae_state *_state)
{


    ae_assert(ae_isfinite(f, _state), "MinDFSetSmallF: F is not a finite number", _state);
    state->stoponsmallf = ae_true;
    state->smallf = f;
}


/*************************************************************************
This subroutine sets optimization algorithm to the differential  evolution
solver GDEMO (Generalized Differential Evolution Multiobjective)  with the
manual parameters selection.

Unlike DE with an automatic parameters selection  this  function  requires
user to manually specify  algorithm  parameters. In the general  case  the
full-auto GDEMO is better. However, it has to spend some time finding  out
properties of a problem being solved; furthermore, it is  not  allowed  to
try potentially dangerous values of  parameters  that  lead  to  premature
stopping. Manually tuning the solver to the specific problem at  hand  can
get 2x-3x better running time.

Aside from that, the algorithm is fully equivalent to automatic GDEMO, and
we   recommend  you  reading  comments  on  mindfsetalgogdemo()  for  more
information about algorithm properties and stopping criteria.

INPUT PARAMETERS:
    State       -   solver
    EpochsCnt   -   iterations count, >0.  Usually   the  algorithm  needs
                    hundreds of iterations to converge.
    Strategy    -   specific DE strategy to use:
                    * 0 for DE/rand/1
                    * 1 for DE/best/2
                    * 2 for DE/current-to-best/1
    CrossoverProb-  crossover probability, 0<CrossoverProb<1
    DifferentialWeight- weight, 0<DifferentialWeight<2
    PopSize     -   population  size,  >=0.  Zero  value  means  that  the
                    default value (which is 10*N in the  current  version)
                    will be chosen. Good values are  in  5*N...20*N,  with
                    the smaller values being recommended for easy problems
                    and the larger  values  for  difficult  multi-extremal
                    and/or noisy tasks.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfsetalgogdemofixed(mindfstate* state,
     ae_int_t epochscnt,
     ae_int_t strategy,
     double crossoverprob,
     double differentialweight,
     ae_int_t popsize,
     ae_state *_state)
{


    ae_assert(strategy>=0&&strategy<=2, "MinDFSetAlgoGDEMOFixed: incorrect Strategy", _state);
    ae_assert((ae_isfinite(crossoverprob, _state)&&ae_fp_greater(crossoverprob,(double)(0)))&&ae_fp_less(crossoverprob,(double)(1)), "MinDFSetAlgoGDEMOFixed: CrossoverProb is infinite number or outside of (0,1)", _state);
    ae_assert((ae_isfinite(differentialweight, _state)&&ae_fp_greater(differentialweight,(double)(0)))&&ae_fp_less(differentialweight,(double)(2)), "MinDFSetAlgoGDEMOFixed: DifferentialWeight is infinite number or outside of (0,2)", _state);
    ae_assert(popsize>=0, "MinDFSetAlgoGDEMOFixed: PopSize<0", _state);
    ae_assert(epochscnt>0, "MinDFSetAlgoGDEMOFixed: EpochsCnt<=0", _state);
    state->gdemoepochscnt = epochscnt;
    state->gdemopopsize = popsize;
    state->gdemofixedparams = ae_true;
    state->gdemostrategy = strategy;
    state->gdemocrossoverprob = crossoverprob;
    state->gdemodifferentialweight = differentialweight;
    state->solvertype = 0;
}


/*************************************************************************

CALLBACK PARALLELISM

The  MINDF  optimizer  supports   parallel   model  evaluation  ('callback
parallelism').  This  feature,  which  is  present  in  commercial  ALGLIB
editions, greatly accelerates  optimization  when  using  a  solver  which
issues batch requests, i.e. multiple requests  for  target  values,  which
can be computed independently by different threads.

Callback parallelism is  usually   beneficial  when   processing  a  batch
request requires more than several  milliseconds.  It  also  requires  the
solver which issues requests in convenient batches, e.g. the  differential
evolution solver.

See ALGLIB Reference Manual, 'Working with commercial version' section for
more information.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
ae_bool mindfiteration(mindfstate* state, ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_bool b;
    ae_int_t originalrequest;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        i = state->rstate.ia.ptr.p_int[0];
        n = state->rstate.ia.ptr.p_int[1];
        originalrequest = state->rstate.ia.ptr.p_int[2];
        b = state->rstate.ba.ptr.p_bool[0];
    }
    else
    {
        i = 359;
        n = -58;
        originalrequest = -919;
        b = ae_true;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    
    /*
     * Routine body
     */
    n = state->n;
    
    /*
     * Init
     */
    state->repf = ae_maxrealnumber;
    state->replcerr = 0.0;
    state->repnlcerr = 0.0;
    state->repterminationtype = 0;
    state->repiterationscount = 0;
    state->repnfunc = 0;
    state->repnrequests = 0;
    state->reptimetotal = (double)(0);
    state->reptimecallback = (double)(0);
    state->userterminationneeded = ae_false;
    stimerinit(&state->timertotal, _state);
    stimerinit(&state->timercallback, _state);
    if( state->dotimers )
    {
        stimerstart(&state->timertotal, _state);
    }
    
    /*
     * Allocate temporaries, as mandated by the V2 protocol
     */
    ae_assert(state->protocolversion==2, "MINDF: integrity check 5124 failed", _state);
    rallocv(n, &state->reportx, _state);
    rallocv(n, &state->tmpx1, _state);
    rallocv(1+state->nnlc, &state->tmpf1, _state);
    
    /*
     * Various algorithms supported by the optimizer
     */
    if( state->solvertype!=0 )
    {
        goto lbl_4;
    }
    
    /*
     * Initialize the solver
     */
    b = ae_false;
    if( state->solvertype==0 )
    {
        gdemoinitbuf(&state->bndl, &state->bndu, &state->s, n, 1, &state->densea, &state->al, &state->au, state->m, &state->nl, &state->nu, state->nnlc, state->gdemopopsize, state->gdemoepochscnt, state->rngseed, &state->gdemosolver, _state);
        gdemosetprofile(&state->gdemosolver, state->gdemoprofile, _state);
        if( state->hasx0 )
        {
            gdemosetx0(&state->gdemosolver, &state->x0, _state);
        }
        if( state->gdemofixedparams )
        {
            gdemosetfixedparams(&state->gdemosolver, state->gdemostrategy, state->gdemocrossoverprob, state->gdemodifferentialweight, _state);
        }
        if( state->stoponsmallf )
        {
            gdemosetsmallf(&state->gdemosolver, state->smallf, _state);
        }
        if( ae_fp_greater(state->condfxepsf,(double)(0))||ae_fp_greater(state->condfxepsx,(double)(0)) )
        {
            gdemosetcondfx(&state->gdemosolver, state->condfxepsf, state->condfxepsx, _state);
        }
        b = ae_fp_neq(state->gdemorho1,(double)(0))||ae_fp_neq(state->gdemorho2,(double)(0));
        gdemosetmodepenalty(&state->gdemosolver, rcase2(b, state->gdemorho1, 50.0, _state), rcase2(b, state->gdemorho2, 50.0, _state), _state);
        b = ae_true;
    }
    ae_assert(b, "MINDF: integrity check 9536 failed", _state);
    
    /*
     * Perform iterations
     */
lbl_6:
    if( ae_false )
    {
        goto lbl_7;
    }
    
    /*
     * Run the iteration function and load request
     */
    state->requesttype = 0;
    originalrequest = 0;
    b = ae_false;
    if( state->solvertype==0 )
    {
        if( !gdemoiteration(&state->gdemosolver, state->userterminationneeded, _state) )
        {
            goto lbl_7;
        }
        if( state->gdemosolver.requesttype==4 )
        {
            rallocv(n*state->gdemosolver.querysize, &state->unscaled, _state);
            state->batchsize = state->gdemosolver.querysize;
            unscalexbatchfinitebnd(&state->gdemosolver.querydata, state->gdemosolver.querysize, &state->s, n, &state->gdemosolver.finitebndl, &state->gdemosolver.finitebndu, &state->finitebndl, &state->finitebndu, &state->unscaled, _state);
            originalrequest = 4;
            b = ae_true;
        }
        if( state->gdemosolver.requesttype==-1 )
        {
            unscalexbatchfinitebnd(&state->gdemosolver.reportx, 1, &state->s, n, &state->gdemosolver.finitebndl, &state->gdemosolver.finitebndu, &state->finitebndl, &state->finitebndu, &state->reportx, _state);
            state->reportf = state->gdemosolver.reportf;
            state->repf = state->gdemosolver.reportf;
            originalrequest = -1;
            b = ae_true;
        }
    }
    ae_assert(b, "DFGM: integrity check 3141 failed", _state);
    
    /*
     * Forward request to user
     */
    b = ae_false;
    if( originalrequest!=-1 )
    {
        goto lbl_8;
    }
    
    /*
     * Report
     */
    ae_assert(state->protocolversion==2, "MINDF: integrity check 4642 failed", _state);
    state->requesttype = -1;
    if( !state->xrep )
    {
        goto lbl_10;
    }
    if( state->dotimers )
    {
        stimerstart(&state->timercallback, _state);
    }
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    if( state->dotimers )
    {
        stimerstop(&state->timercallback, _state);
    }
lbl_10:
    b = ae_true;
lbl_8:
    if( originalrequest!=4 )
    {
        goto lbl_12;
    }
    if( state->dotimers )
    {
        stimerstart(&state->timercallback, _state);
    }
    state->repnfunc = state->repnfunc+state->batchsize;
    state->repnrequests = state->repnrequests+1;
    rallocv(n, &state->querydata, _state);
    rallocv(1+state->nnlc, &state->replyfi, _state);
    rallocv((1+state->nnlc)*state->batchsize, &state->combined, _state);
    i = 0;
lbl_14:
    if( i>state->batchsize-1 )
    {
        goto lbl_16;
    }
    state->requesttype = 4;
    state->queryfuncs = 1+state->nnlc;
    state->queryvars = n;
    state->querydim = 0;
    state->querysize = 1;
    rcopyvx(n, &state->unscaled, i*n, &state->querydata, 0, _state);
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    rcopyvx(1+state->nnlc, &state->replyfi, 0, &state->combined, i*(1+state->nnlc), _state);
    i = i+1;
    goto lbl_14;
lbl_16:
    if( state->dotimers )
    {
        stimerstop(&state->timercallback, _state);
    }
    b = ae_true;
lbl_12:
    ae_assert(b, "MINDF: integrity check 8545 failed", _state);
    
    /*
     * Send results back to the optimizer
     */
    ae_assert(originalrequest==-1||originalrequest==4, "MINDF: integrity check 9046 failed", _state);
    b = ae_false;
    if( originalrequest==-1 )
    {
        
        /*
         * Do nothing
         */
        b = ae_true;
    }
    if( originalrequest==4&&state->solvertype==0 )
    {
        
        /*
         * 2PS solver
         */
        ae_assert(state->protocolversion==2, "MINDF: integrity check 0446 failed", _state);
        rcopyv((1+state->nnlc)*state->batchsize, &state->combined, &state->gdemosolver.replyfi, _state);
        b = ae_true;
    }
    ae_assert(b, "MINDF: integrity check 1247 failed", _state);
    goto lbl_6;
lbl_7:
    
    /*
     * Results
     */
    if( state->dotimers )
    {
        stimerstop(&state->timertotal, _state);
        state->reptimetotal = 0.001*stimergetms(&state->timertotal, _state);
        state->reptimecallback = 0.001*stimergetms(&state->timercallback, _state);
    }
    b = ae_false;
    if( state->solvertype==0 )
    {
        state->repterminationtype = state->gdemosolver.repterminationtype;
        state->repiterationscount = state->gdemosolver.repiterationscount;
        state->repbcerr = (double)(0);
        state->repbcidx = -1;
        state->replcerr = state->gdemosolver.replcerr;
        state->replcidx = state->gdemosolver.replcidx;
        state->repnlcerr = state->gdemosolver.repnlcerr;
        state->repnlcidx = state->gdemosolver.repnlcidx;
        rallocv(n, &state->tmp0, _state);
        rcopyrv(n, &state->gdemosolver.xbest, 0, &state->tmp0, _state);
        unscalexbatchfinitebnd(&state->tmp0, 1, &state->s, n, &state->gdemosolver.finitebndl, &state->gdemosolver.finitebndu, &state->finitebndl, &state->finitebndu, &state->xf, _state);
        b = ae_true;
    }
    ae_assert(b, "MINDF: integrity check 435259 failed", _state);
    result = ae_false;
    return result;
lbl_4:
    
    /*
     * SSGD solver
     */
    if( state->solvertype!=-1 )
    {
        goto lbl_17;
    }
    ssgdinitbuf(&state->bndl, &state->bndu, &state->s, &state->x0, n, &state->densea, &state->al, &state->au, state->m, &state->nl, &state->nu, state->nnlc, state->ssgdblur0, state->ssgdblur1, state->ssgdouterits, state->ssgdrate0, state->ssgdrate1, state->ssgdmomentum, state->ssgdmaxits, state->ssgdrho, &state->ssgdsolver, _state);
lbl_19:
    if( !ssgditeration(&state->ssgdsolver, state->userterminationneeded, _state) )
    {
        goto lbl_20;
    }
    if( !state->ssgdsolver.needfi )
    {
        goto lbl_21;
    }
    state->requesttype = 4;
    state->querysize = 1;
    state->queryfuncs = 1+state->nnlc;
    state->queryvars = n;
    state->querydim = 0;
    rallocv(state->querysize*state->queryfuncs, &state->replyfi, _state);
    rallocv(n, &state->querydata, _state);
    mindf_unscale(state, &state->ssgdsolver.x, &state->querydata, _state);
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    rcopyv(1+state->nnlc, &state->replyfi, &state->ssgdsolver.fi, _state);
    goto lbl_19;
lbl_21:
    if( !state->ssgdsolver.xupdated )
    {
        goto lbl_23;
    }
    
    /*
     * Report current point
     */
    state->repf = state->ssgdsolver.f;
    if( !state->xrep )
    {
        goto lbl_25;
    }
    state->requesttype = -1;
    mindf_unscale(state, &state->ssgdsolver.x, &state->reportx, _state);
    state->reportf = state->ssgdsolver.f;
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
lbl_25:
    goto lbl_19;
lbl_23:
    ae_assert(ae_false, "MinDF: SSGD solver issued unexpected request", _state);
    goto lbl_19;
lbl_20:
    state->repterminationtype = 1;
    state->repiterationscount = state->ssgdsolver.repiterationscount;
    state->repbcerr = (double)(0);
    state->repbcidx = 0;
    state->replcerr = (double)(0);
    state->replcidx = 0;
    state->repnlcerr = (double)(0);
    state->repnlcidx = 0;
    mindf_unscale(state, &state->ssgdsolver.xcur, &state->xf, _state);
    result = ae_false;
    return result;
lbl_17:
    ae_assert(ae_false, "MinDF: unexpected solver type", _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = i;
    state->rstate.ia.ptr.p_int[1] = n;
    state->rstate.ia.ptr.p_int[2] = originalrequest;
    state->rstate.ba.ptr.p_bool[0] = b;
    return result;
}


/*************************************************************************
MinDF results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report:
    
                rep.f stores objective value at the solution
    
                You  should     check  rep.terminationtype  in   order  to
                distinguish successful termination from unsuccessful one:
                * -8   internal integrity control  detected  infinite  or
                       NAN   values   in   function/gradient.    Abnormal
                       termination signalled.
                * -3   box constraints are inconsistent
                * -1   inconsistent parameters were passed:
                       * penalty parameter is zero, but we have nonlinear
                         constraints set by MinDFsetnlc2()
                *  1   successful termination according to a solver-specific
                       set of conditions
                *  8   User requested termination via minnsrequesttermination()
                
                If you activated timers  with  mindfusetimers(),  you  can
                also find out how much time was spent in various code parts:
                * rep.timetotal    - for a total time in seconds
                * rep.timesolver   - for a time spent in the solver itself
                * rep.timecallback - for a time spent in user callbacks
                See comments on mindfreport structure for more information
                about timers and their accuracy.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfresults(const mindfstate* state,
     /* Real    */ ae_vector* x,
     mindfreport* rep,
     ae_state *_state)
{

    ae_vector_clear(x);
    _mindfreport_clear(rep);

    mindfresultsbuf(state, x, rep, _state);
}


/*************************************************************************

Buffered implementation of MinDFresults() which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 25.07.2023 by Bochkanov Sergey
*************************************************************************/
void mindfresultsbuf(const mindfstate* state,
     /* Real    */ ae_vector* x,
     mindfreport* rep,
     ae_state *_state)
{


    rep->f = state->repf;
    rep->iterationscount = state->repiterationscount;
    rep->nfev = state->repnfunc;
    rep->terminationtype = state->repterminationtype;
    rep->bcerr = state->repbcerr;
    rep->lcerr = state->replcerr;
    rep->nlcerr = state->repnlcerr;
    rep->timetotal = state->reptimetotal;
    rep->timecallback = ae_minreal(state->reptimetotal, state->reptimecallback, _state);
    rep->timesolver = rep->timetotal-rep->timecallback;
    if( state->repterminationtype>0 )
    {
        rcopyallocv(state->n, &state->xf, x, _state);
    }
    else
    {
        rsetallocv(state->n, _state->v_nan, x, _state);
    }
}


/*************************************************************************
Set V1 reverse communication protocol
*************************************************************************/
void mindfsetprotocolv2(mindfstate* state, ae_state *_state)
{


    state->protocolversion = 2;
    ae_vector_set_length(&state->rstate.ia, 2+1, _state);
    ae_vector_set_length(&state->rstate.ba, 0+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Unscales X (converts from scaled variables to original ones)
*************************************************************************/
static void mindf_unscale(const mindfstate* state,
     /* Real    */ const ae_vector* xs,
     /* Real    */ ae_vector* xu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    for(i=0; i<=n-1; i++)
    {
        xu->ptr.p_double[i] = xs->ptr.p_double[i]*state->s.ptr.p_double[i];
    }
}


void _mindfstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    mindfstate *p = (mindfstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitebndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitebndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_matrix_init(&p->densea, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->al, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->au, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->reportx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->querydata, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyfi, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replydj, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->replysj, _state, make_automatic);
    ae_vector_init(&p->tmpx1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpc1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpf1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpg1, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpj1, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->tmps1, _state, make_automatic);
    _gdemostate_init(&p->gdemosolver, _state, make_automatic);
    _ssgdstate_init(&p->ssgdsolver, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    _stimer_init(&p->timertotal, _state, make_automatic);
    _stimer_init(&p->timercallback, _state, make_automatic);
    ae_vector_init(&p->unscaled, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->combined, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
}


void _mindfstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    mindfstate       *dst = (mindfstate*)_dst;
    const mindfstate *src = (const mindfstate*)_src;
    dst->solvertype = src->solvertype;
    dst->n = src->n;
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndl, &src->finitebndl, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndu, &src->finitebndu, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_matrix_init_copy(&dst->densea, &src->densea, _state, make_automatic);
    ae_vector_init_copy(&dst->al, &src->al, _state, make_automatic);
    ae_vector_init_copy(&dst->au, &src->au, _state, make_automatic);
    dst->m = src->m;
    ae_vector_init_copy(&dst->nl, &src->nl, _state, make_automatic);
    ae_vector_init_copy(&dst->nu, &src->nu, _state, make_automatic);
    dst->nnlc = src->nnlc;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    dst->hasx0 = src->hasx0;
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    dst->condfxepsf = src->condfxepsf;
    dst->condfxepsx = src->condfxepsx;
    dst->stoponsmallf = src->stoponsmallf;
    dst->dotimers = src->dotimers;
    dst->smallf = src->smallf;
    dst->rngseed = src->rngseed;
    dst->xrep = src->xrep;
    ae_vector_init_copy(&dst->xf, &src->xf, _state, make_automatic);
    dst->protocolversion = src->protocolversion;
    dst->requesttype = src->requesttype;
    ae_vector_init_copy(&dst->reportx, &src->reportx, _state, make_automatic);
    dst->reportf = src->reportf;
    dst->querysize = src->querysize;
    dst->queryfuncs = src->queryfuncs;
    dst->queryvars = src->queryvars;
    dst->querydim = src->querydim;
    dst->queryformulasize = src->queryformulasize;
    ae_vector_init_copy(&dst->querydata, &src->querydata, _state, make_automatic);
    ae_vector_init_copy(&dst->replyfi, &src->replyfi, _state, make_automatic);
    ae_vector_init_copy(&dst->replydj, &src->replydj, _state, make_automatic);
    _sparsematrix_init_copy(&dst->replysj, &src->replysj, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpx1, &src->tmpx1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpc1, &src->tmpc1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpf1, &src->tmpf1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpg1, &src->tmpg1, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpj1, &src->tmpj1, _state, make_automatic);
    _sparsematrix_init_copy(&dst->tmps1, &src->tmps1, _state, make_automatic);
    dst->userterminationneeded = src->userterminationneeded;
    dst->gdemoprofile = src->gdemoprofile;
    dst->gdemoepochscnt = src->gdemoepochscnt;
    dst->gdemopopsize = src->gdemopopsize;
    dst->gdemorho1 = src->gdemorho1;
    dst->gdemorho2 = src->gdemorho2;
    _gdemostate_init_copy(&dst->gdemosolver, &src->gdemosolver, _state, make_automatic);
    dst->gdemofixedparams = src->gdemofixedparams;
    dst->gdemostrategy = src->gdemostrategy;
    dst->gdemocrossoverprob = src->gdemocrossoverprob;
    dst->gdemodifferentialweight = src->gdemodifferentialweight;
    dst->ssgdmaxits = src->ssgdmaxits;
    dst->ssgdrate0 = src->ssgdrate0;
    dst->ssgdrate1 = src->ssgdrate1;
    dst->ssgdblur0 = src->ssgdblur0;
    dst->ssgdblur1 = src->ssgdblur1;
    dst->ssgdouterits = src->ssgdouterits;
    dst->ssgdmomentum = src->ssgdmomentum;
    dst->ssgdrho = src->ssgdrho;
    _ssgdstate_init_copy(&dst->ssgdsolver, &src->ssgdsolver, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    dst->repf = src->repf;
    dst->repterminationtype = src->repterminationtype;
    dst->repiterationscount = src->repiterationscount;
    dst->repnfunc = src->repnfunc;
    dst->repnrequests = src->repnrequests;
    dst->repbcerr = src->repbcerr;
    dst->repbcidx = src->repbcidx;
    dst->replcerr = src->replcerr;
    dst->replcidx = src->replcidx;
    dst->repnlcerr = src->repnlcerr;
    dst->repnlcidx = src->repnlcidx;
    dst->reptimetotal = src->reptimetotal;
    dst->reptimecallback = src->reptimecallback;
    _stimer_init_copy(&dst->timertotal, &src->timertotal, _state, make_automatic);
    _stimer_init_copy(&dst->timercallback, &src->timercallback, _state, make_automatic);
    ae_vector_init_copy(&dst->unscaled, &src->unscaled, _state, make_automatic);
    ae_vector_init_copy(&dst->combined, &src->combined, _state, make_automatic);
    dst->batchsize = src->batchsize;
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
}


void _mindfstate_clear(void* _p)
{
    mindfstate *p = (mindfstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->finitebndl);
    ae_vector_clear(&p->finitebndu);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_matrix_clear(&p->densea);
    ae_vector_clear(&p->al);
    ae_vector_clear(&p->au);
    ae_vector_clear(&p->nl);
    ae_vector_clear(&p->nu);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->xf);
    ae_vector_clear(&p->reportx);
    ae_vector_clear(&p->querydata);
    ae_vector_clear(&p->replyfi);
    ae_vector_clear(&p->replydj);
    _sparsematrix_clear(&p->replysj);
    ae_vector_clear(&p->tmpx1);
    ae_vector_clear(&p->tmpc1);
    ae_vector_clear(&p->tmpf1);
    ae_vector_clear(&p->tmpg1);
    ae_matrix_clear(&p->tmpj1);
    _sparsematrix_clear(&p->tmps1);
    _gdemostate_clear(&p->gdemosolver);
    _ssgdstate_clear(&p->ssgdsolver);
    _rcommstate_clear(&p->rstate);
    _stimer_clear(&p->timertotal);
    _stimer_clear(&p->timercallback);
    ae_vector_clear(&p->unscaled);
    ae_vector_clear(&p->combined);
    ae_vector_clear(&p->tmp0);
}


void _mindfstate_destroy(void* _p)
{
    mindfstate *p = (mindfstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->finitebndl);
    ae_vector_destroy(&p->finitebndu);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_matrix_destroy(&p->densea);
    ae_vector_destroy(&p->al);
    ae_vector_destroy(&p->au);
    ae_vector_destroy(&p->nl);
    ae_vector_destroy(&p->nu);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->xf);
    ae_vector_destroy(&p->reportx);
    ae_vector_destroy(&p->querydata);
    ae_vector_destroy(&p->replyfi);
    ae_vector_destroy(&p->replydj);
    _sparsematrix_destroy(&p->replysj);
    ae_vector_destroy(&p->tmpx1);
    ae_vector_destroy(&p->tmpc1);
    ae_vector_destroy(&p->tmpf1);
    ae_vector_destroy(&p->tmpg1);
    ae_matrix_destroy(&p->tmpj1);
    _sparsematrix_destroy(&p->tmps1);
    _gdemostate_destroy(&p->gdemosolver);
    _ssgdstate_destroy(&p->ssgdsolver);
    _rcommstate_destroy(&p->rstate);
    _stimer_destroy(&p->timertotal);
    _stimer_destroy(&p->timercallback);
    ae_vector_destroy(&p->unscaled);
    ae_vector_destroy(&p->combined);
    ae_vector_destroy(&p->tmp0);
}


void _mindfreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    mindfreport *p = (mindfreport*)_p;
    ae_touch_ptr((void*)p);
}


void _mindfreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    mindfreport       *dst = (mindfreport*)_dst;
    const mindfreport *src = (const mindfreport*)_src;
    dst->f = src->f;
    dst->iterationscount = src->iterationscount;
    dst->nfev = src->nfev;
    dst->bcerr = src->bcerr;
    dst->lcerr = src->lcerr;
    dst->nlcerr = src->nlcerr;
    dst->terminationtype = src->terminationtype;
    dst->timetotal = src->timetotal;
    dst->timesolver = src->timesolver;
    dst->timecallback = src->timecallback;
}


void _mindfreport_clear(void* _p)
{
    mindfreport *p = (mindfreport*)_p;
    ae_touch_ptr((void*)p);
}


void _mindfreport_destroy(void* _p)
{
    mindfreport *p = (mindfreport*)_p;
    ae_touch_ptr((void*)p);
}


#endif
#if defined(AE_COMPILE_REVISEDDUALSIMPLEX) || !defined(AE_PARTIAL_BUILD)


void dsssettingsinit(dualsimplexsettings* settings, ae_state *_state)
{


    settings->xtolabs = 1.0E-6;
    settings->dtolabs = 1.0E-6;
    settings->xtolrelabs = 0.01;
    settings->pivottol = (double)10*ae_sqrt(ae_machineepsilon, _state);
    settings->perturbmag = (double)10*settings->pivottol;
    settings->maxtrfage = reviseddualsimplex_defaultmaxtrfage;
    settings->trftype = 3;
    settings->ratiotest = 1;
    settings->pricing = 1;
    settings->shifting = 2;
}


/*************************************************************************
This function initializes DSS structure. Previously  allocated  memory  is
reused as much as possible.

Default state of the problem is zero cost vector, all variables are  fixed
at zero.

  -- ALGLIB --
     Copyright 01.07.2018 by Bochkanov Sergey
*************************************************************************/
void dssinit(ae_int_t n, dualsimplexstate* s, ae_state *_state)
{
    ae_int_t i;


    ae_assert(n>0, "DSSInit: N<=0", _state);
    s->ns = n;
    s->m = 0;
    rvectorgrowto(&s->rawbndl, n, _state);
    rvectorgrowto(&s->rawbndu, n, _state);
    for(i=0; i<=n-1; i++)
    {
        s->rawbndl.ptr.p_double[i] = 0.0;
        s->rawbndu.ptr.p_double[i] = 0.0;
    }
    reviseddualsimplex_subprobleminit(n, &s->primary, _state);
    reviseddualsimplex_basisinit(n, 0, &s->basis, _state);
    rvectorgrowto(&s->repx, n, _state);
    rvectorgrowto(&s->replagbc, n, _state);
    ivectorgrowto(&s->repstats, n, _state);
    for(i=0; i<=n-1; i++)
    {
        s->repx.ptr.p_double[i] = 0.0;
        s->repstats.ptr.p_int[i] = 1;
    }
    s->dotrace = ae_false;
    s->dodetailedtrace = ae_false;
    s->dotimers = ae_false;
}


/*************************************************************************
This function specifies LP problem

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    
    BndL    -   lower bounds, array[N].
    BndU    -   upper bounds, array[N].
    
    DenseA  -   dense array[K,N], dense linear constraints (not supported
                in present version)
    SparseA -   sparse linear constraints, sparsematrix[K,N] in CRS format
    AKind   -   type of A: 0 for dense, 1 for sparse
    AL, AU  -   lower and upper bounds, array[K]
    K       -   number of equality/inequality constraints, K>=0.
    
    ProposedBasis- basis to import from (if BasisType=2)
    BasisInitType-  what to do with basis:
                * 0 - set new basis to all-logicals
                * 1 - try to reuse previous basis as much as possible
                * 2 - try to import basis from ProposedBasis
    Settings-   algorithm settings

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void dsssetproblem(dualsimplexstate* state,
     /* Real    */ const ae_vector* c,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_matrix* densea,
     const sparsematrix* sparsea,
     ae_int_t akind,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t k,
     const dualsimplexbasis* proposedbasis,
     ae_int_t basisinittype,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t offs;
    ae_int_t ns;
    ae_int_t j0;
    ae_int_t j1;
    ae_bool processed;
    ae_int_t oldm;
    ae_bool basisinitialized;
    double v;


    ns = state->primary.ns;
    oldm = state->primary.m;
    
    /*
     * Integrity checks
     */
    ae_assert(bndl->cnt>=ns, "DSSSetProblem: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=ns, "DSSSetProblem: Length(BndU)<N", _state);
    ae_assert(c->cnt>=ns, "SubproblemSetCost: Length(C)<N", _state);
    ae_assert(isfinitevector(c, ns, _state), "SubproblemSetCost: C contains infinite or NaN elements", _state);
    ae_assert(akind==0||akind==1, "DSSSetProblem: incorrect AKind", _state);
    ae_assert((basisinittype==0||basisinittype==1)||basisinittype==2, "DSSSetProblem: incorrect BasisInitType", _state);
    ae_assert(k>=0, "DSSSetProblem: K<0", _state);
    if( k>0&&akind==1 )
    {
        ae_assert(sparsea->m==k, "DSSSetProblem: rows(A)<>K", _state);
        ae_assert(sparsea->n==ns, "DSSSetProblem: cols(A)<>N", _state);
    }
    
    /*
     * Downgrade state
     */
    reviseddualsimplex_downgradestate(&state->primary, reviseddualsimplex_ssinvalid, _state);
    
    /*
     * Reallocate storage
     */
    rvectorgrowto(&state->primary.bndl, ns+k, _state);
    rvectorgrowto(&state->primary.bndu, ns+k, _state);
    ivectorgrowto(&state->primary.bndt, ns+k, _state);
    rvectorgrowto(&state->primary.rawc, ns+k, _state);
    rvectorgrowto(&state->primary.effc, ns+k, _state);
    rvectorgrowto(&state->primary.xa, ns+k, _state);
    rvectorgrowto(&state->primary.d, ns+k, _state);
    rvectorgrowto(&state->primary.xb, k, _state);
    rvectorgrowto(&state->primary.bndlb, k, _state);
    rvectorgrowto(&state->primary.bndub, k, _state);
    ivectorgrowto(&state->primary.bndtb, k, _state);
    rvectorgrowto(&state->primary.bndtollb, k, _state);
    rvectorgrowto(&state->primary.bndtolub, k, _state);
    
    /*
     * Save original problem formulation
     */
    state->ns = ns;
    state->m = k;
    for(i=0; i<=ns-1; i++)
    {
        state->rawbndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->rawbndu.ptr.p_double[i] = bndu->ptr.p_double[i];
    }
    
    /*
     * Setup cost, scale and box constraints
     */
    rsetv(ns+k, 0.0, &state->primary.rawc, _state);
    rsetv(ns+k, 0.0, &state->primary.effc, _state);
    for(i=0; i<=ns-1; i++)
    {
        state->primary.rawc.ptr.p_double[i] = c->ptr.p_double[i];
        state->primary.effc.ptr.p_double[i] = c->ptr.p_double[i];
    }
    for(i=0; i<=ns-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "DSSSetProblem: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "DSSSetProblem: BndU contains NAN or -INF", _state);
        state->primary.bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->primary.bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        
        /*
         * Set bound type
         */
        if( ae_isfinite(bndl->ptr.p_double[i], _state)&&ae_isfinite(bndu->ptr.p_double[i], _state) )
        {
            if( ae_fp_greater(bndl->ptr.p_double[i],bndu->ptr.p_double[i]) )
            {
                state->primary.bndt.ptr.p_int[i] = reviseddualsimplex_ccinfeasible;
            }
            if( ae_fp_less(bndl->ptr.p_double[i],bndu->ptr.p_double[i]) )
            {
                state->primary.bndt.ptr.p_int[i] = reviseddualsimplex_ccrange;
            }
            if( ae_fp_eq(bndl->ptr.p_double[i],bndu->ptr.p_double[i]) )
            {
                state->primary.bndt.ptr.p_int[i] = reviseddualsimplex_ccfixed;
            }
            continue;
        }
        if( ae_isfinite(bndl->ptr.p_double[i], _state)&&!ae_isfinite(bndu->ptr.p_double[i], _state) )
        {
            state->primary.bndt.ptr.p_int[i] = reviseddualsimplex_cclower;
            continue;
        }
        if( !ae_isfinite(bndl->ptr.p_double[i], _state)&&ae_isfinite(bndu->ptr.p_double[i], _state) )
        {
            state->primary.bndt.ptr.p_int[i] = reviseddualsimplex_ccupper;
            continue;
        }
        ae_assert(ae_isneginf(bndl->ptr.p_double[i], _state)&&ae_isposinf(bndu->ptr.p_double[i], _state), "DSSSetProblem: integrity check failed", _state);
        state->primary.bndt.ptr.p_int[i] = reviseddualsimplex_ccfree;
    }
    
    /*
     * Quick exit if no linear constraints is present
     */
    if( k==0 )
    {
        state->primary.m = 0;
        reviseddualsimplex_basisinit(state->primary.ns, state->primary.m, &state->basis, _state);
        return;
    }
    
    /*
     * Extend A with structural terms and transpose it:
     * * allocate place for A^T extended with logical part.
     * * copy with transposition
     * * perform integrity check for array sizes
     * * manually append new items
     * * update DIdx/UIdx
     */
    processed = ae_false;
    state->primary.m = k;
    if( akind==0 )
    {
        ae_assert(ae_false, "DSSSetProblem: does not support dense inputs yet", _state);
    }
    if( akind==1 )
    {
        
        /*
         * Transpose constraints matrix, apply column and row scaling.
         * Extend it with identity submatrix.
         *
         * NOTE: in order to improve stability of LU factorization we
         *       normalize rows using 2-norm, not INF-norm. Having rows
         *       normalized with 2-norm makes every element less than
         *       1.0 in magnitude, which allows us later to move logical
         *       columns to the beginning of LU factors without loosing
         *       stability.
         */
        rvectorsetlengthatleast(&state->at.vals, sparsea->ridx.ptr.p_int[k]+k, _state);
        ivectorsetlengthatleast(&state->at.idx, sparsea->ridx.ptr.p_int[k]+k, _state);
        ivectorsetlengthatleast(&state->at.ridx, ns+k+1, _state);
        ivectorsetlengthatleast(&state->at.didx, ns+k, _state);
        ivectorsetlengthatleast(&state->at.uidx, ns+k, _state);
        sparsecopytransposecrsbuf(sparsea, &state->at, _state);
        rvectorsetlengthatleast(&state->rowscales, k, _state);
        for(i=0; i<=k-1; i++)
        {
            state->rowscales.ptr.p_double[i] = (double)(0);
        }
        for(i=0; i<=ns-1; i++)
        {
            j0 = state->at.ridx.ptr.p_int[i];
            j1 = state->at.ridx.ptr.p_int[i+1]-1;
            for(j=j0; j<=j1; j++)
            {
                v = state->at.vals.ptr.p_double[j];
                jj = state->at.idx.ptr.p_int[j];
                state->at.vals.ptr.p_double[j] = v;
                state->rowscales.ptr.p_double[jj] = state->rowscales.ptr.p_double[jj]+v*v;
            }
        }
        rvectorsetlengthatleast(&state->tmp0, k, _state);
        for(i=0; i<=k-1; i++)
        {
            state->rowscales.ptr.p_double[i] = coalesce(ae_sqrt(state->rowscales.ptr.p_double[i], _state), (double)(1), _state);
            state->tmp0.ptr.p_double[i] = (double)1/state->rowscales.ptr.p_double[i];
        }
        for(i=0; i<=ns-1; i++)
        {
            j0 = state->at.ridx.ptr.p_int[i];
            j1 = state->at.ridx.ptr.p_int[i+1]-1;
            for(j=j0; j<=j1; j++)
            {
                state->at.vals.ptr.p_double[j] = state->at.vals.ptr.p_double[j]*state->tmp0.ptr.p_double[state->at.idx.ptr.p_int[j]];
            }
        }
        ae_assert(state->at.vals.cnt>=sparsea->ridx.ptr.p_int[k]+k, "DSSSetProblem: integrity check failed", _state);
        ae_assert(state->at.idx.cnt>=sparsea->ridx.ptr.p_int[k]+k, "DSSSetProblem: integrity check failed", _state);
        ae_assert(state->at.ridx.cnt>=ns+k+1, "DSSSetProblem: integrity check failed", _state);
        ae_assert(state->at.didx.cnt>=ns+k, "DSSSetProblem: integrity check failed", _state);
        ae_assert(state->at.uidx.cnt>=ns+k, "DSSSetProblem: integrity check failed", _state);
        offs = state->at.ridx.ptr.p_int[ns];
        for(i=0; i<=k-1; i++)
        {
            state->at.vals.ptr.p_double[offs+i] = -1.0;
            state->at.idx.ptr.p_int[offs+i] = i;
            state->at.ridx.ptr.p_int[ns+i+1] = state->at.ridx.ptr.p_int[ns+i]+1;
            state->at.ninitialized = state->at.ninitialized+1;
        }
        state->at.m = state->at.m+k;
        sparseinitduidx(&state->at, _state);
        sparsecopytransposecrsbuf(&state->at, &state->a, _state);
        processed = ae_true;
    }
    ae_assert(processed, "DSSSetProblem: integrity check failed (akind)", _state);
    
    /*
     * Copy AL, AU to BndL/BndT
     */
    for(i=0; i<=k-1; i++)
    {
        ae_assert(ae_isfinite(al->ptr.p_double[i], _state)||ae_isneginf(al->ptr.p_double[i], _state), "DSSSetProblem: AL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(au->ptr.p_double[i], _state)||ae_isposinf(au->ptr.p_double[i], _state), "DSSSetProblem: AU contains NAN or -INF", _state);
        state->primary.bndl.ptr.p_double[ns+i] = al->ptr.p_double[i]/state->rowscales.ptr.p_double[i];
        state->primary.bndu.ptr.p_double[ns+i] = au->ptr.p_double[i]/state->rowscales.ptr.p_double[i];
        
        /*
         * Set bound type
         */
        if( ae_isfinite(al->ptr.p_double[i], _state)&&ae_isfinite(au->ptr.p_double[i], _state) )
        {
            if( ae_fp_greater(al->ptr.p_double[i],au->ptr.p_double[i]) )
            {
                state->primary.bndt.ptr.p_int[ns+i] = reviseddualsimplex_ccinfeasible;
            }
            if( ae_fp_less(al->ptr.p_double[i],au->ptr.p_double[i]) )
            {
                state->primary.bndt.ptr.p_int[ns+i] = reviseddualsimplex_ccrange;
            }
            if( ae_fp_eq(al->ptr.p_double[i],au->ptr.p_double[i]) )
            {
                state->primary.bndt.ptr.p_int[ns+i] = reviseddualsimplex_ccfixed;
            }
            continue;
        }
        if( ae_isfinite(al->ptr.p_double[i], _state)&&!ae_isfinite(au->ptr.p_double[i], _state) )
        {
            state->primary.bndt.ptr.p_int[ns+i] = reviseddualsimplex_cclower;
            continue;
        }
        if( !ae_isfinite(al->ptr.p_double[i], _state)&&ae_isfinite(au->ptr.p_double[i], _state) )
        {
            state->primary.bndt.ptr.p_int[ns+i] = reviseddualsimplex_ccupper;
            continue;
        }
        ae_assert(ae_isneginf(al->ptr.p_double[i], _state)&&ae_isposinf(au->ptr.p_double[i], _state), "DSSSetProblem: integrity check faoled", _state);
        state->primary.bndt.ptr.p_int[ns+i] = reviseddualsimplex_ccfree;
    }
    
    /*
     * Depending on BasisInitType either start from all-logical basis
     * or try to reuse already existing basis.
     *
     * NOTE: current version does not support basis shrinkage, only
     *       growing basis can be reused.
     */
    basisinitialized = ae_false;
    if( basisinittype==2 )
    {
        
        /*
         * Import basis from one proposed by caller
         */
        ae_assert(proposedbasis->ns==state->primary.ns, "DSSSetProblemX: unable to import basis, sizes do not match", _state);
        ae_assert(proposedbasis->m==state->primary.m, "DSSSetProblemX: unable to import basis, sizes do not match", _state);
        basisinitialized = reviseddualsimplex_basistryimportfrom(&state->basis, proposedbasis, &state->at, settings, _state);
    }
    if( basisinittype==1&&state->primary.m>=oldm )
    {
        
        /*
         * New rows were added, try to reuse previous basis
         */
        for(i=oldm; i<=state->primary.m-1; i++)
        {
            state->primary.rawc.ptr.p_double[ns+i] = 0.0;
            state->primary.effc.ptr.p_double[ns+i] = 0.0;
            state->primary.xa.ptr.p_double[ns+i] = 0.0;
            state->primary.d.ptr.p_double[ns+i] = 0.0;
        }
        basisinitialized = reviseddualsimplex_basistryresize(&state->basis, state->primary.m, &state->at, settings, _state);
    }
    if( !basisinitialized )
    {
        
        /*
         * Straightforward code for all-logicals basis
         */
        for(i=0; i<=k-1; i++)
        {
            state->primary.rawc.ptr.p_double[ns+i] = 0.0;
            state->primary.effc.ptr.p_double[ns+i] = 0.0;
            state->primary.xa.ptr.p_double[ns+i] = 0.0;
            state->primary.d.ptr.p_double[ns+i] = 0.0;
        }
        reviseddualsimplex_basisinit(state->primary.ns, state->primary.m, &state->basis, _state);
        reviseddualsimplex_basisfreshtrf(&state->basis, &state->at, settings, _state);
    }
    rvectorgrowto(&state->replaglc, state->primary.m, _state);
    ivectorgrowto(&state->repstats, state->primary.ns+state->primary.m, _state);
}


/*************************************************************************
This function exports basis from the primary (phase II) subproblem.

INPUT PARAMETERS:
    State   -   structure
    
OUTPUT PARAMETERS
    Basis   -   current basis exported (no factorization, only set of
                basis/nonbasic variables)

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void dssexportbasis(const dualsimplexstate* state,
     dualsimplexbasis* basis,
     ae_state *_state)
{


    reviseddualsimplex_basisexportto(&state->basis, basis, _state);
}


/*************************************************************************
This function solves LP problem with dual simplex solver.

INPUT PARAMETERS:
    State   -   state
    
Solution results can be found in fields  of  State  which  are  explicitly
declared as accessible by external code.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void dssoptimize(dualsimplexstate* state,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t nx;
    double v;
    ae_int_t cnt1;
    ae_int_t cnt2;
    ae_int_t cntfx;
    ae_int_t cntfr;
    ae_int_t cntif;
    ae_int_t ttotal;


    nx = state->primary.ns+state->primary.m;
    ttotal = 0;
    
    /*
     * Trace settings
     */
    state->dotrace = ae_is_trace_enabled("DSS");
    state->dodetailedtrace = state->dotrace&&ae_is_trace_enabled("DSS.DETAILED");
    state->dotimers = ae_is_trace_enabled("TIMERS.DSS");
    
    /*
     * Init report fields
     */
    state->repiterationscount = 0;
    state->repiterationscount1 = 0;
    state->repiterationscount2 = 0;
    state->repiterationscount3 = 0;
    state->repterminationtype = 1;
    state->repphase1time = 0;
    state->repphase2time = 0;
    state->repphase3time = 0;
    state->repdualpricingtime = 0;
    state->repdualbtrantime = 0;
    state->repdualpivotrowtime = 0;
    state->repdualratiotesttime = 0;
    state->repdualftrantime = 0;
    state->repdualupdatesteptime = 0;
    state->repfillpivotrow = (double)(0);
    state->repfillpivotrowcnt = 0;
    state->repfillrhor = (double)(0);
    state->repfillrhorcnt = 0;
    state->repfilldensemu = (double)(0);
    state->repfilldensemucnt = 0;
    reviseddualsimplex_basisclearstats(&state->basis, _state);
    
    /*
     * Setup timer (if needed)
     */
    if( state->dotimers )
    {
        ttotal = ae_tickcount();
    }
    
    /*
     * Trace output (if needed)
     */
    if( state->dotrace||state->dotimers )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("//  DUAL SIMPLEX SOLVER STARTED                                                                   //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("> problem size:\n");
        ae_trace("N         = %12d (variables)\n",
            (int)(state->primary.ns));
        ae_trace("M         = %12d (constraints)\n",
            (int)(state->primary.m));
    }
    if( state->dotrace )
    {
        ae_trace("> variable stats:\n");
        if( state->dodetailedtrace )
        {
        }
        cnt1 = 0;
        cnt2 = 0;
        cntfx = 0;
        cntfr = 0;
        cntif = 0;
        for(i=0; i<=state->primary.ns-1; i++)
        {
            if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_cclower||state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccupper )
            {
                inc(&cnt1, _state);
            }
            if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccrange )
            {
                inc(&cnt2, _state);
            }
            if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccfixed )
            {
                inc(&cntfx, _state);
            }
            if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccfree )
            {
                inc(&cntfr, _state);
            }
            if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccinfeasible )
            {
                inc(&cntif, _state);
            }
        }
        ae_trace("UBnd/LBnd   = %12d\n",
            (int)(cnt1));
        ae_trace("Range       = %12d\n",
            (int)(cnt2));
        ae_trace("Fixed       = %12d\n",
            (int)(cntfx));
        ae_trace("Free        = %12d\n",
            (int)(cntfr));
        ae_trace("Infeas      = %12d\n",
            (int)(cntif));
        ae_trace("> constraint stats:\n");
        if( state->dodetailedtrace )
        {
        }
        cnt1 = 0;
        cnt2 = 0;
        cntfx = 0;
        cntfr = 0;
        cntif = 0;
        for(i=state->primary.ns-1; i<=nx-1; i++)
        {
            if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_cclower||state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccupper )
            {
                inc(&cnt1, _state);
            }
            if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccrange )
            {
                inc(&cnt2, _state);
            }
            if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccfixed )
            {
                inc(&cntfx, _state);
            }
            if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccfree )
            {
                inc(&cntfr, _state);
            }
            if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccinfeasible )
            {
                inc(&cntif, _state);
            }
        }
        ae_trace("ubnd/lbnd   = %12d\n",
            (int)(cnt1));
        ae_trace("range       = %12d\n",
            (int)(cnt2));
        ae_trace("fixed       = %12d\n",
            (int)(cntfx));
        ae_trace("free        = %12d\n",
            (int)(cntfr));
        ae_trace("infeas      = %12d\n",
            (int)(cntif));
        v = (double)(0);
        for(i=0; i<=state->primary.ns-1; i++)
        {
            if( ae_isfinite(state->primary.bndl.ptr.p_double[i], _state) )
            {
                v = ae_maxreal(v, ae_fabs(state->primary.bndl.ptr.p_double[i], _state), _state);
            }
        }
        ae_trace("|BndL|      = %0.3e\n",
            (double)(v));
        v = (double)(0);
        for(i=0; i<=state->primary.ns-1; i++)
        {
            if( ae_isfinite(state->primary.bndu.ptr.p_double[i], _state) )
            {
                v = ae_maxreal(v, ae_fabs(state->primary.bndu.ptr.p_double[i], _state), _state);
            }
        }
        ae_trace("|BndU|      = %0.3e\n",
            (double)(v));
        v = (double)(0);
        for(i=state->primary.ns; i<=nx-1; i++)
        {
            if( ae_isfinite(state->primary.bndl.ptr.p_double[i], _state) )
            {
                v = ae_maxreal(v, ae_fabs(state->primary.bndl.ptr.p_double[i], _state), _state);
            }
        }
        ae_trace("|AL|        = %0.3e\n",
            (double)(v));
        v = (double)(0);
        for(i=state->primary.ns; i<=nx-1; i++)
        {
            if( ae_isfinite(state->primary.bndu.ptr.p_double[i], _state) )
            {
                v = ae_maxreal(v, ae_fabs(state->primary.bndu.ptr.p_double[i], _state), _state);
            }
        }
        ae_trace("|AU|        = %0.3e\n",
            (double)(v));
    }
    
    /*
     * Call actual workhorse function
     */
    reviseddualsimplex_dssoptimizewrk(state, settings, _state);
    
    /*
     * Print reports
     */
    if( state->dotrace )
    {
        ae_trace("\n");
        ae_trace("****************************************************************************************************\n");
        ae_trace("*   PRINTING ITERATION STATISTICS                                                                  *\n");
        ae_trace("****************************************************************************************************\n");
        ae_trace("> iteration counts:\n");
        ae_trace("Phase 1     = %12d\n",
            (int)(state->repiterationscount1));
        ae_trace("Phase 2     = %12d\n",
            (int)(state->repiterationscount2));
        ae_trace("Phase 3     = %12d\n",
            (int)(state->repiterationscount3));
        ae_trace("> factorization statistics:\n");
        ae_trace("FactCnt     = %12d (LU factorizations)\n",
            (int)(state->basis.statfact));
        ae_trace("UpdtCnt     = %12d (LU updates)\n",
            (int)(state->basis.statupdt));
        ae_trace("RefactPeriod= %12.1f (average refactorization interval)\n",
            (double)((double)(state->basis.statfact+state->basis.statupdt)/coalesce((double)(state->basis.statfact), (double)(1), _state)));
        ae_trace("LU-NZR      = %12.1f (average LU nonzeros per row)\n",
            (double)(state->basis.statoffdiag/(coalesce((double)(state->m), (double)(1), _state)*coalesce((double)(state->basis.statfact+state->basis.statupdt), (double)(1), _state))));
        ae_trace("> sparsity counters (average fill factors):\n");
        if( state->dodetailedtrace )
        {
            ae_trace("RhoR        = %12.4f (BTran result)\n",
                (double)(state->repfillrhor/coalesce((double)(state->repfillrhorcnt), (double)(1), _state)));
            ae_trace("AlphaR      = %12.4f (pivot row)\n",
                (double)(state->repfillpivotrow/coalesce((double)(state->repfillpivotrowcnt), (double)(1), _state)));
            if( state->basis.trftype==3 )
            {
                ae_trace("Mu          = %12.4f (Forest-Tomlin factor)\n",
                    (double)(state->repfilldensemu/coalesce((double)(state->repfilldensemucnt), (double)(1), _state)));
            }
        }
        else
        {
            ae_trace("...skipped, need DUALSIMPLEX.DETAILED trace tag\n");
        }
    }
    if( state->dotimers )
    {
        ttotal = ae_tickcount()-ttotal;
        ae_trace("\n");
        ae_trace("****************************************************************************************************\n");
        ae_trace("*   PRINTING DUAL SIMPLEX TIMERS                                                                   *\n");
        ae_trace("****************************************************************************************************\n");
        ae_trace("> total time:\n");
        ae_trace("Time        = %12d ms\n",
            (int)(ttotal));
        ae_trace("> time by phase:\n");
        ae_trace("Phase 1     = %12d ms\n",
            (int)(state->repphase1time));
        ae_trace("Phase 2     = %12d ms\n",
            (int)(state->repphase2time));
        ae_trace("Phase 3     = %12d ms\n",
            (int)(state->repphase3time));
        ae_trace("> time by step (dual phases 1 and 2):\n");
        ae_trace("Pricing     = %12d ms\n",
            (int)(state->repdualpricingtime));
        ae_trace("BTran       = %12d ms\n",
            (int)(state->repdualbtrantime));
        ae_trace("PivotRow    = %12d ms\n",
            (int)(state->repdualpivotrowtime));
        ae_trace("RatioTest   = %12d ms\n",
            (int)(state->repdualratiotesttime));
        ae_trace("FTran       = %12d ms\n",
            (int)(state->repdualftrantime));
        ae_trace("Update      = %12d ms\n",
            (int)(state->repdualupdatesteptime));
    }
}


/*************************************************************************
This function initializes subproblem structure. Previously allocated memory
is reused as much as possible.

Default state of the problem is zero cost vector, all variables are  fixed
at zero, linear constraint matrix is zero.

  -- ALGLIB --
     Copyright 01.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_subprobleminit(ae_int_t n,
     dualsimplexsubproblem* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(n>0, "SubproblemInit: N<=0", _state);
    s->ns = n;
    s->m = 0;
    s->state = reviseddualsimplex_ssinvalid;
    rvectorsetlengthatleast(&s->xa, n, _state);
    rvectorsetlengthatleast(&s->xb, 0, _state);
    rvectorsetlengthatleast(&s->d, n, _state);
    rvectorsetlengthatleast(&s->rawc, n, _state);
    rvectorsetlengthatleast(&s->effc, n, _state);
    rvectorsetlengthatleast(&s->bndl, n, _state);
    rvectorsetlengthatleast(&s->bndu, n, _state);
    ivectorsetlengthatleast(&s->bndt, n, _state);
    for(i=0; i<=n-1; i++)
    {
        s->rawc.ptr.p_double[i] = (double)(0);
        s->effc.ptr.p_double[i] = (double)(0);
        s->bndl.ptr.p_double[i] = (double)(0);
        s->bndu.ptr.p_double[i] = (double)(0);
        s->bndt.ptr.p_int[i] = reviseddualsimplex_ccfixed;
        s->xa.ptr.p_double[i] = 0.0;
        s->d.ptr.p_double[i] = 0.0;
    }
}


/*************************************************************************
This function initializes phase #1 subproblem which minimizes sum of  dual
infeasibilities.  It is required that total count of  non-boxed  non-fixed
variables is at least M.

It splits out basic components of XA[] to XB[]

  -- ALGLIB --
     Copyright 01.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_subprobleminitphase1(const dualsimplexsubproblem* s0,
     const dualsimplexbasis* basis,
     dualsimplexsubproblem* s1,
     ae_state *_state)
{
    ae_int_t i;


    s1->ns = s0->ns;
    s1->m = s0->m;
    copyrealarray(&s0->rawc, &s1->rawc, _state);
    copyrealarray(&s0->effc, &s1->effc, _state);
    copyrealarray(&s0->bndl, &s1->bndl, _state);
    copyrealarray(&s0->bndu, &s1->bndu, _state);
    copyintegerarray(&s0->bndt, &s1->bndt, _state);
    copyrealarray(&s0->xa, &s1->xa, _state);
    copyrealarray(&s0->xb, &s1->xb, _state);
    copyrealarray(&s0->bndlb, &s1->bndlb, _state);
    copyrealarray(&s0->bndub, &s1->bndub, _state);
    copyintegerarray(&s0->bndtb, &s1->bndtb, _state);
    copyrealarray(&s0->bndtollb, &s1->bndtollb, _state);
    copyrealarray(&s0->bndtolub, &s1->bndtolub, _state);
    copyrealarray(&s0->d, &s1->d, _state);
    for(i=0; i<=s1->ns+s1->m-1; i++)
    {
        if( s1->bndt.ptr.p_int[i]==reviseddualsimplex_cclower )
        {
            s1->bndt.ptr.p_int[i] = reviseddualsimplex_ccrange;
            s1->bndl.ptr.p_double[i] = (double)(0);
            s1->bndu.ptr.p_double[i] = (double)(1);
            s1->xa.ptr.p_double[i] = (double)(0);
            continue;
        }
        if( s1->bndt.ptr.p_int[i]==reviseddualsimplex_ccupper )
        {
            s1->bndt.ptr.p_int[i] = reviseddualsimplex_ccrange;
            s1->bndl.ptr.p_double[i] = (double)(-1);
            s1->bndu.ptr.p_double[i] = (double)(0);
            s1->xa.ptr.p_double[i] = (double)(0);
            continue;
        }
        if( s1->bndt.ptr.p_int[i]==reviseddualsimplex_ccfree )
        {
            s1->bndt.ptr.p_int[i] = reviseddualsimplex_ccrange;
            s1->bndl.ptr.p_double[i] = (double)(-1);
            s1->bndu.ptr.p_double[i] = (double)(1);
            if( ae_fp_greater_eq(s1->effc.ptr.p_double[i],(double)(0)) )
            {
                s1->xa.ptr.p_double[i] = (double)(-1);
            }
            else
            {
                s1->xa.ptr.p_double[i] = (double)(1);
            }
            continue;
        }
        s1->bndt.ptr.p_int[i] = reviseddualsimplex_ccfixed;
        s1->bndl.ptr.p_double[i] = (double)(0);
        s1->bndu.ptr.p_double[i] = (double)(0);
        s1->xa.ptr.p_double[i] = (double)(0);
    }
    s1->state = reviseddualsimplex_ssvalidxn;
}


/*************************************************************************
This function initializes phase #3 subproblem which applies primal simplex
method to the result of the phase #2.

It also performs modification of the subproblem in order to ensure that
initial point is primal feasible.

NOTE: this function expects that all components (basic and nonbasic ones)
      are stored in XA[]

  -- ALGLIB --
     Copyright 01.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_subprobleminitphase3(const dualsimplexsubproblem* s0,
     dualsimplexsubproblem* s1,
     ae_state *_state)
{


    s1->ns = s0->ns;
    s1->m = s0->m;
    copyrealarray(&s0->rawc, &s1->rawc, _state);
    copyrealarray(&s0->effc, &s1->effc, _state);
    copyrealarray(&s0->bndl, &s1->bndl, _state);
    copyrealarray(&s0->bndu, &s1->bndu, _state);
    copyintegerarray(&s0->bndt, &s1->bndt, _state);
    copyrealarray(&s0->xa, &s1->xa, _state);
    copyrealarray(&s0->xb, &s1->xb, _state);
    copyrealarray(&s0->bndlb, &s1->bndlb, _state);
    copyrealarray(&s0->bndub, &s1->bndub, _state);
    copyintegerarray(&s0->bndtb, &s1->bndtb, _state);
    copyrealarray(&s0->bndtollb, &s1->bndtollb, _state);
    copyrealarray(&s0->bndtolub, &s1->bndtolub, _state);
    copyrealarray(&s0->d, &s1->d, _state);
    s1->state = reviseddualsimplex_ssvalidxn;
}


/*************************************************************************
This function infers nonbasic variables of X using sign of effective C[].

Only non-basic components of XN are changed; everything else is NOT updated.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_subprobleminferinitialxn(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t ii;
    ae_int_t bndt;


    for(ii=0; ii<=s->ns-1; ii++)
    {
        i = state->basis.nidx.ptr.p_int[ii];
        bndt = s->bndt.ptr.p_int[i];
        if( bndt==reviseddualsimplex_ccfixed||bndt==reviseddualsimplex_ccrange )
        {
            if( s->effc.ptr.p_double[i]>=(double)0 )
            {
                s->xa.ptr.p_double[i] = s->bndl.ptr.p_double[i];
            }
            else
            {
                s->xa.ptr.p_double[i] = s->bndu.ptr.p_double[i];
            }
            continue;
        }
        if( bndt==reviseddualsimplex_cclower )
        {
            s->xa.ptr.p_double[i] = s->bndl.ptr.p_double[i];
            continue;
        }
        if( bndt==reviseddualsimplex_ccupper )
        {
            s->xa.ptr.p_double[i] = s->bndu.ptr.p_double[i];
            continue;
        }
        if( bndt==reviseddualsimplex_ccfree )
        {
            s->xa.ptr.p_double[i] = 0.0;
            continue;
        }
        ae_assert(ae_false, "SubproblemInferInitialXN: integrity check failed (infeasible constraint)", _state);
    }
    s->state = reviseddualsimplex_ssvalidxn;
}


/*************************************************************************
This function infers basic variables of X using values of non-basic vars
and updates reduced cost vector D and target function Z. Sets state age
to zero.

D[] is allocated during computations.

Temporary vectors Tmp0 and Tmp1 are used (reallocated as needed).

NOTE: this function expects that both nonbasic and basic components are
      stored in XA[]. XB[] array is not referenced.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_subproblemhandlexnupdate(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t m;
    ae_int_t nn;


    ae_assert(s->state>=reviseddualsimplex_ssvalidxn, "SubproblemHandleXNUpdate: integrity check failed (XN is not valid)", _state);
    nn = s->ns;
    m = s->m;
    
    /*
     * Compute nonbasic components
     */
    reviseddualsimplex_computeanxn(state, s, &s->xa, &state->tmp0, _state);
    reviseddualsimplex_basissolve(&state->basis, &state->tmp0, &state->tmp1, &state->tmp2, _state);
    for(i=0; i<=m-1; i++)
    {
        s->xa.ptr.p_double[state->basis.idx.ptr.p_int[i]] = -state->tmp1.ptr.p_double[i];
    }
    
    /*
     * Compute D
     */
    for(i=0; i<=m-1; i++)
    {
        state->tmp0.ptr.p_double[i] = s->effc.ptr.p_double[state->basis.idx.ptr.p_int[i]];
    }
    reviseddualsimplex_basissolvet(&state->basis, &state->tmp0, &state->tmp1, &state->tmp2, _state);
    reviseddualsimplex_computeantv(state, &state->tmp1, &s->d, _state);
    for(i=0; i<=nn-1; i++)
    {
        j = state->basis.nidx.ptr.p_int[i];
        s->d.ptr.p_double[j] = s->effc.ptr.p_double[j]-s->d.ptr.p_double[j];
    }
    
    /*
     * Update state validity/age
     */
    s->state = reviseddualsimplex_ssvalid;
}


/*************************************************************************
This function performs initial dual feasibility correction on the subproblem.
It assumes that problem state is at least ssValidXN. After call to this
function the problem state is set to ssValid.

This function returns dual feasibility error after dual feasibility correction.

NOTE: this function expects that both nonbasic and basic components are
      stored in XA[]. XB[] array is not referenced.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static double reviseddualsimplex_initialdualfeasibilitycorrection(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector dummy;
    ae_int_t nn;
    ae_int_t m;
    ae_int_t ii;
    ae_int_t i;
    ae_int_t j;
    ae_bool flipped;
    double v;
    double dj;
    double xj;
    ae_int_t bndt;
    double result;

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

    nn = s->ns;
    m = s->m;
    ae_assert(s->state>=reviseddualsimplex_ssvalidxn, "InitialDualFeasibilityCorrection: XN is invalid", _state);
    
    /*
     * Prepare
     */
    rvectorsetlengthatleast(&state->dfctmp0, m, _state);
    rvectorsetlengthatleast(&state->dfctmp1, m, _state);
    
    /*
     * Recompute D[] using fresh factorization
     */
    reviseddualsimplex_basisfreshtrf(&state->basis, &state->at, settings, _state);
    for(i=0; i<=m-1; i++)
    {
        state->dfctmp0.ptr.p_double[i] = s->effc.ptr.p_double[state->basis.idx.ptr.p_int[i]];
    }
    reviseddualsimplex_basissolvet(&state->basis, &state->dfctmp0, &state->dfctmp1, &state->dfctmp2, _state);
    reviseddualsimplex_computeantv(state, &state->dfctmp1, &s->d, _state);
    for(i=0; i<=nn-1; i++)
    {
        j = state->basis.nidx.ptr.p_int[i];
        s->d.ptr.p_double[j] = s->effc.ptr.p_double[j]-s->d.ptr.p_double[j];
    }
    
    /*
     * Perform flips for dual-infeasible boxed variables
     */
    result = (double)(0);
    flipped = ae_false;
    for(ii=0; ii<=nn-1; ii++)
    {
        j = state->basis.nidx.ptr.p_int[ii];
        bndt = s->bndt.ptr.p_int[j];
        
        /*
         * Boxed variables, perform DFC
         */
        if( bndt==reviseddualsimplex_ccrange )
        {
            dj = s->d.ptr.p_double[j];
            xj = s->xa.ptr.p_double[j];
            if( xj==s->bndl.ptr.p_double[j]&&dj<(double)0 )
            {
                s->xa.ptr.p_double[j] = s->bndu.ptr.p_double[j];
                flipped = ae_true;
                continue;
            }
            if( xj==s->bndu.ptr.p_double[j]&&dj>(double)0 )
            {
                s->xa.ptr.p_double[j] = s->bndl.ptr.p_double[j];
                flipped = ae_true;
                continue;
            }
            continue;
        }
        
        /*
         * Non-boxed variables, compute dual feasibility error
         */
        if( bndt==reviseddualsimplex_ccfixed )
        {
            continue;
        }
        if( bndt==reviseddualsimplex_cclower )
        {
            v = -s->d.ptr.p_double[j];
            if( v>result )
            {
                result = v;
            }
            continue;
        }
        if( bndt==reviseddualsimplex_ccupper )
        {
            v = s->d.ptr.p_double[j];
            if( v>result )
            {
                result = v;
            }
            continue;
        }
        if( bndt==reviseddualsimplex_ccfree )
        {
            result = ae_maxreal(result, ae_fabs(s->d.ptr.p_double[j], _state), _state);
            continue;
        }
    }
    
    /*
     * Recompute basic components of X[]
     */
    if( flipped||s->state<reviseddualsimplex_ssvalid )
    {
        reviseddualsimplex_computeanxn(state, s, &s->xa, &state->dfctmp0, _state);
        reviseddualsimplex_basissolve(&state->basis, &state->dfctmp0, &state->dfctmp1, &state->dfctmp2, _state);
        for(i=0; i<=m-1; i++)
        {
            s->xa.ptr.p_double[state->basis.idx.ptr.p_int[i]] = -state->dfctmp1.ptr.p_double[i];
        }
    }
    
    /*
     * Update state validity/age
     */
    s->state = reviseddualsimplex_ssvalid;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function performs shifting using current algorithm  as  specified  by
settings.shifting.

It accepts following parameters:
* AlphaR - pivot row
* Delta - delta from pricing step
* Q - variable selected by ratio test
* AlphaRPiv - pivot element, Q-th element of  AlphaR  (because  alphaR  is
  stored in compressed format, we can't extract it easily)
* ThetaD - dual step length

If no shifts are necessary, it silently returns. If shifts are  necessary,
it modifies ThetaD, S.D, S.EffC according to shifting algorithm.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_shifting(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     const dssvector* alphar,
     double delta,
     ae_int_t q,
     double alpharpiv,
     double* thetad,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t dir;
    double sft;
    ae_int_t ii;
    ae_int_t j;
    ae_int_t bndt;


    
    /*
     * No shifts
     */
    if( settings->shifting==0 )
    {
        return;
    }
    if( q<0 )
    {
        return;
    }
    
    /*
     * EXPAND with ThetaD=0
     */
    if( settings->shifting==1 )
    {
        dir = ae_sign(delta, _state);
        if( ae_fp_greater_eq(*thetad*(double)dir,(double)(0)) )
        {
            return;
        }
        s->effc.ptr.p_double[q] = s->effc.ptr.p_double[q]-s->d.ptr.p_double[q];
        s->d.ptr.p_double[q] = (double)(0);
        *thetad = (double)(0);
        return;
    }
    
    /*
     * EXPAND with ThetaD=ShiftLen
     */
    if( settings->shifting==2 )
    {
        dir = ae_sign(delta, _state);
        if( ae_fp_greater(*thetad*(double)dir,(double)(0)) )
        {
            return;
        }
        
        /*
         * Ensure that non-zero step is performed
         */
        *thetad = (double)dir*reviseddualsimplex_shiftlen;
        
        /*
         * Shift Q-th coefficient
         */
        sft = *thetad*((double)dir*alpharpiv)-s->d.ptr.p_double[q];
        s->effc.ptr.p_double[q] = s->effc.ptr.p_double[q]+sft;
        s->d.ptr.p_double[q] = s->d.ptr.p_double[q]+sft;
        
        /*
         * Shift other coefficients
         */
        for(ii=0; ii<=alphar->k-1; ii++)
        {
            j = alphar->idx.ptr.p_int[ii];
            bndt = s->bndt.ptr.p_int[j];
            if( (j==q||bndt==reviseddualsimplex_ccfixed)||bndt==reviseddualsimplex_ccfree )
            {
                continue;
            }
            sft = *thetad*((double)dir*alphar->vals.ptr.p_double[ii])-s->d.ptr.p_double[j];
            
            /*
             * Handle variables at lower bound
             */
            if( bndt==reviseddualsimplex_cclower||(bndt==reviseddualsimplex_ccrange&&s->xa.ptr.p_double[j]==s->bndl.ptr.p_double[j]) )
            {
                sft = sft-settings->dtolabs;
                if( sft>(double)0 )
                {
                    s->effc.ptr.p_double[j] = s->effc.ptr.p_double[j]+sft;
                    s->d.ptr.p_double[j] = s->d.ptr.p_double[j]+sft;
                }
                continue;
            }
            if( bndt==reviseddualsimplex_ccupper||(bndt==reviseddualsimplex_ccrange&&s->xa.ptr.p_double[j]==s->bndu.ptr.p_double[j]) )
            {
                sft = sft+settings->dtolabs;
                if( sft<(double)0 )
                {
                    s->effc.ptr.p_double[j] = s->effc.ptr.p_double[j]+sft;
                    s->d.ptr.p_double[j] = s->d.ptr.p_double[j]+sft;
                }
                continue;
            }
        }
        
        /*
         * Done
         */
        return;
    }
    ae_assert(ae_false, "Shifting: unexpected shifting type", _state);
}


/*************************************************************************
This function performs pricing step

Additional parameters:
* Phase1Pricing - if True, then special Phase #1 restriction is applied to
  leaving variables: only those are eligible which will move to zero bound
  after basis change.
  
  This trick allows to accelerate and stabilize phase #1. See Robert Fourer,
  'Notes on the dual simplex method', draft report, 1994, for more info.

Returns:
* leaving variable index P
* its index R in the basis, in [0,M) range
* Delta - difference between variable value and corresponding bound

NOTE: this function expects that basic components are stored in XB[];
      corresponding entries of XA[] are ignored.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_pricingstep(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     ae_bool phase1pricing,
     ae_int_t* p,
     ae_int_t* r,
     double* delta,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t i;
    ae_int_t bi;
    double v;
    double vtarget;
    double xbi;
    double bndl;
    double bndu;
    double vdiff;
    double vtest;
    double invw;
    ae_int_t bndt;
    ae_bool hasboth;
    ae_bool hasl;
    ae_bool hasu;
    ae_int_t t0;

    *p = 0;
    *r = 0;
    *delta = 0.0;

    m = s->m;
    
    /*
     * Integrity checks
     */
    ae_assert(s->state==reviseddualsimplex_ssvalid, "PricingStep: invalid X", _state);
    ae_assert(m>0, "PricingStep: M<=0", _state);
    
    /*
     * Timers
     */
    t0 = 0;
    if( state->dotimers )
    {
        t0 = ae_tickcount();
    }
    
    /*
     * Pricing
     */
    if( settings->pricing==0 )
    {
        
        /*
         * "Most infeasible" pricing
         */
        *p = -1;
        *r = -1;
        *delta = (double)(0);
        vtarget = (double)(0);
        for(i=0; i<=m-1; i++)
        {
            bndt = s->bndtb.ptr.p_int[i];
            hasboth = bndt==3||bndt==0;
            hasl = hasboth||bndt==1;
            hasu = hasboth||bndt==2;
            xbi = s->xb.ptr.p_double[i];
            if( hasl )
            {
                bndl = s->bndlb.ptr.p_double[i];
                vdiff = xbi-bndl;
                v = -vdiff;
                if( v>s->bndtollb.ptr.p_double[i]&&v>vtarget )
                {
                    
                    /*
                     * Special phase 1 pricing: do not choose variables which move to non-zero bound
                     */
                    if( phase1pricing&&!(bndl==0.0) )
                    {
                        continue;
                    }
                    
                    /*
                     * Proceed as usual
                     */
                    *p = state->basis.idx.ptr.p_int[i];
                    *r = i;
                    *delta = vdiff;
                    vtarget = v;
                    continue;
                }
            }
            if( hasu )
            {
                bndu = s->bndub.ptr.p_double[i];
                vdiff = xbi-bndu;
                v = vdiff;
                if( v>s->bndtolub.ptr.p_double[i]&&v>vtarget )
                {
                    
                    /*
                     * Special phase 1 pricing: do not choose variables which move to non-zero bound
                     */
                    if( phase1pricing&&!(bndu==0.0) )
                    {
                        continue;
                    }
                    
                    /*
                     * Proceed as usual
                     */
                    *p = state->basis.idx.ptr.p_int[i];
                    *r = i;
                    *delta = vdiff;
                    vtarget = v;
                    continue;
                }
            }
        }
        
        /*
         * Trace/profile
         */
        if( state->dotrace )
        {
            ae_trace("> pricing: most infeasible variable removed\n");
            ae_trace("P           = %12d (R=%0d)\n",
                (int)(*p),
                (int)(*r));
            ae_trace("Delta       = %12.3e\n",
                (double)(*delta));
        }
        if( state->dotimers )
        {
            state->repdualpricingtime = state->repdualpricingtime+(ae_tickcount()-t0);
        }
        
        /*
         * Done
         */
        return;
    }
    if( settings->pricing==-1||settings->pricing==1 )
    {
        
        /*
         * Dual steepest edge pricing
         */
        reviseddualsimplex_basisrequestweights(&state->basis, settings, _state);
        *p = -1;
        *r = -1;
        *delta = (double)(0);
        vtarget = (double)(0);
        for(i=0; i<=m-1; i++)
        {
            bi = state->basis.idx.ptr.p_int[i];
            bndt = s->bndtb.ptr.p_int[i];
            hasboth = bndt==3||bndt==0;
            hasl = hasboth||bndt==1;
            hasu = hasboth||bndt==2;
            xbi = s->xb.ptr.p_double[i];
            invw = (double)1/state->basis.dseweights.ptr.p_double[i];
            if( hasl )
            {
                bndl = s->bndlb.ptr.p_double[i];
                vdiff = xbi-bndl;
                vtest = vdiff*vdiff*invw;
                if( vdiff<-s->bndtollb.ptr.p_double[i]&&(*p<0||vtest>vtarget) )
                {
                    
                    /*
                     * Special phase 1 pricing: do not choose variables which move to non-zero bound
                     */
                    if( phase1pricing&&!(bndl==0.0) )
                    {
                        continue;
                    }
                    
                    /*
                     * Proceed as usual
                     */
                    *p = bi;
                    *r = i;
                    *delta = vdiff;
                    vtarget = vtest;
                    continue;
                }
            }
            if( hasu )
            {
                bndu = s->bndub.ptr.p_double[i];
                vdiff = xbi-bndu;
                vtest = vdiff*vdiff*invw;
                if( vdiff>s->bndtolub.ptr.p_double[i]&&(*p<0||vtest>vtarget) )
                {
                    
                    /*
                     * Special phase 1 pricing: do not choose variables which move to non-zero bound
                     */
                    if( phase1pricing&&!(bndu==0.0) )
                    {
                        continue;
                    }
                    
                    /*
                     * Proceed as usual
                     */
                    *p = bi;
                    *r = i;
                    *delta = vdiff;
                    vtarget = vtest;
                    continue;
                }
            }
        }
        
        /*
         * Trace/profile
         */
        if( state->dotrace )
        {
            ae_trace("> dual steepest edge pricing: leaving variable found\n");
            ae_trace("P           = %12d  (variable index)\n",
                (int)(*p));
            ae_trace("R           = %12d  (variable index in basis)\n",
                (int)(*r));
            ae_trace("Delta       = %12.3e  (primal infeasibility removed)\n",
                (double)(*delta));
        }
        if( state->dotimers )
        {
            state->repdualpricingtime = state->repdualpricingtime+(ae_tickcount()-t0);
        }
        
        /*
         * Done
         */
        return;
    }
    ae_assert(ae_false, "PricingStep: unknown pricing type", _state);
}


/*************************************************************************
This function performs BTran step

Accepts:
* R, index of the leaving variable in the basis, in [0,M) range

Returns:
* RhoR, array[M], BTran result

  -- ALGLIB --
     Copyright 19.07.2020 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_btranstep(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     ae_int_t r,
     dssvector* rhor,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t i;
    ae_int_t t0;


    m = s->m;
    
    /*
     * Integrity checks
     */
    ae_assert(m>0, "BTranStep: M<=0", _state);
    
    /*
     * Timers
     */
    t0 = 0;
    if( state->dotimers )
    {
        t0 = ae_tickcount();
    }
    
    /*
     * BTran
     */
    rvectorsetlengthatleast(&state->btrantmp0, m, _state);
    rvectorsetlengthatleast(&state->btrantmp1, m, _state);
    rvectorsetlengthatleast(&state->btrantmp2, m, _state);
    for(i=0; i<=m-1; i++)
    {
        state->btrantmp0.ptr.p_double[i] = (double)(0);
    }
    state->btrantmp0.ptr.p_double[r] = (double)(1);
    reviseddualsimplex_dvalloc(rhor, m, _state);
    reviseddualsimplex_basissolvet(&state->basis, &state->btrantmp0, &rhor->dense, &state->btrantmp1, _state);
    reviseddualsimplex_dvdensetosparse(rhor, _state);
    
    /*
     * Timers
     */
    if( state->dotimers )
    {
        state->repdualbtrantime = state->repdualbtrantime+(ae_tickcount()-t0);
    }
}


/*************************************************************************
This function performs PivotRow step

Accepts:
* RhoR, BTRan result

Returns:
* AlphaR, array[N+M], pivot row

  -- ALGLIB --
     Copyright 19.07.2020 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_pivotrowstep(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     const dssvector* rhor,
     dssvector* alphar,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t ns;
    ae_int_t nx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t alphark;
    double v;
    ae_int_t t0;
    double avgcolwise;
    double avgrowwise;


    m = s->m;
    ns = s->ns;
    nx = s->ns+s->m;
    
    /*
     * Integrity checks
     */
    ae_assert(m>0, "BTranStep: M<=0", _state);
    
    /*
     * Timers
     */
    t0 = 0;
    if( state->dotimers )
    {
        t0 = ae_tickcount();
    }
    
    /*
     * Determine operation counts for columnwise and rowwise approaches
     */
    avgrowwise = (double)rhor->k*((double)state->at.ridx.ptr.p_int[nx]/(double)m);
    avgcolwise = (double)ns*((double)state->at.ridx.ptr.p_int[nx]/(double)nx);
    
    /*
     * Pivot row
     */
    if( ae_fp_less(avgrowwise,avgcolwise) )
    {
        
        /*
         * Use rowwise algorithm
         */
        reviseddualsimplex_dvinit(alphar, nx, _state);
        for(i=0; i<=rhor->k-1; i++)
        {
            k = rhor->idx.ptr.p_int[i];
            v = rhor->vals.ptr.p_double[i];
            j0 = state->a.ridx.ptr.p_int[k];
            j1 = state->a.ridx.ptr.p_int[k+1]-1;
            for(j=j0; j<=j1; j++)
            {
                jj = state->a.idx.ptr.p_int[j];
                alphar->dense.ptr.p_double[jj] = alphar->dense.ptr.p_double[jj]+v*state->a.vals.ptr.p_double[j];
            }
        }
        alphark = 0;
        for(i=0; i<=nx-1; i++)
        {
            if( !state->basis.isbasic.ptr.p_bool[i] )
            {
                
                /*
                 * Fetch nonbasic nonzeros to sparse part
                 */
                v = alphar->dense.ptr.p_double[i];
                if( v!=0.0 )
                {
                    alphar->idx.ptr.p_int[alphark] = i;
                    alphar->vals.ptr.p_double[alphark] = v;
                    alphark = alphark+1;
                }
            }
            else
            {
                
                /*
                 * Enforce condition that basic elements of AlphaR are exactly zero
                 */
                alphar->dense.ptr.p_double[i] = (double)(0);
            }
        }
        alphar->k = alphark;
    }
    else
    {
        
        /*
         * Use colwise algorithm
         */
        reviseddualsimplex_dvalloc(alphar, nx, _state);
        alphark = 0;
        for(i=0; i<=ns-1; i++)
        {
            k = state->basis.nidx.ptr.p_int[i];
            j0 = state->at.ridx.ptr.p_int[k];
            j1 = state->at.ridx.ptr.p_int[k+1]-1;
            v = (double)(0);
            for(j=j0; j<=j1; j++)
            {
                v = v+state->at.vals.ptr.p_double[j]*rhor->dense.ptr.p_double[state->at.idx.ptr.p_int[j]];
            }
            if( v!=0.0 )
            {
                alphar->idx.ptr.p_int[alphark] = k;
                alphar->vals.ptr.p_double[alphark] = v;
                alphark = alphark+1;
            }
        }
        alphar->k = alphark;
        reviseddualsimplex_dvsparsetodense(alphar, _state);
    }
    
    /*
     * Timers and tracing
     */
    if( state->dodetailedtrace )
    {
        reviseddualsimplex_updateavgcounter((double)rhor->k/coalesce((double)(rhor->n), (double)(1), _state), &state->repfillrhor, &state->repfillrhorcnt, _state);
        reviseddualsimplex_updateavgcounter((double)alphar->k/coalesce((double)(alphar->n), (double)(1), _state), &state->repfillpivotrow, &state->repfillpivotrowcnt, _state);
    }
    if( state->dotimers )
    {
        state->repdualpivotrowtime = state->repdualpivotrowtime+(ae_tickcount()-t0);
    }
}


/*************************************************************************
This function performs FTran step

Accepts:
* RhoR, array[M]
* Q, index of the entering variable, in [0,NX) range

Returns:
* AlphaQ,   array[M], FTran result
* AlphaQim, array[M], intermediate FTran result used by Forest-Tomlin update
* Tau,      array[M], used to compute DSE temporaries

  -- ALGLIB --
     Copyright 19.07.2020 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_ftranstep(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     const dssvector* rhor,
     ae_int_t q,
     /* Real    */ ae_vector* alphaq,
     /* Real    */ ae_vector* alphaqim,
     /* Real    */ ae_vector* tau,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t t0;


    m = s->m;
    
    /*
     * Integrity checks
     */
    ae_assert(m>0, "BTranStep: M<=0", _state);
    
    /*
     * Timers
     */
    t0 = 0;
    if( state->dotimers )
    {
        t0 = ae_tickcount();
    }
    
    /*
     * FTran
     */
    rvectorsetlengthatleast(&state->ftrantmp0, m, _state);
    for(i=0; i<=m-1; i++)
    {
        state->ftrantmp0.ptr.p_double[i] = (double)(0);
    }
    j0 = state->at.ridx.ptr.p_int[q];
    j1 = state->at.ridx.ptr.p_int[q+1]-1;
    for(j=j0; j<=j1; j++)
    {
        state->ftrantmp0.ptr.p_double[state->at.idx.ptr.p_int[j]] = state->at.vals.ptr.p_double[j];
    }
    reviseddualsimplex_basissolvex(&state->basis, &state->ftrantmp0, alphaq, alphaqim, ae_true, &state->ftrantmp1, _state);
    ae_assert((settings->pricing==-1||settings->pricing==0)||settings->pricing==1, "FTran: unexpected Settings.Pricing", _state);
    if( settings->pricing==1 )
    {
        reviseddualsimplex_basissolve(&state->basis, &rhor->dense, tau, &state->ftrantmp1, _state);
    }
    
    /*
     * Timers
     */
    if( state->dotimers )
    {
        state->repdualftrantime = state->repdualftrantime+(ae_tickcount()-t0);
    }
}


/*************************************************************************
This function performs ratio test, either simple one or BFRT.

It accepts following parameters:
* AlphaR - pivot row
* Delta - delta from pricing step
* P - index of leaving variable from pricing step

It returns following results:
* Q - non-negative value for success, negative for primal infeasible problem
* AlphaRPiv - AlphaR[Q] (due to AlphaR being stored in sparse format this
  value is difficult to extract by index Q).
* ThetaD - dual step length
* PossibleFlips[PossibleFlipsCnt] - for possible flip indexes (for BFRT
  this set coincides with actual flips, but stabilizing BFRT is a bit more
  complex - some variables in PossibleFlips[] may need flipping and some not)

Internally it uses following fields of State for temporaries:
* EligibleAlphaR

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_ratiotest(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     const dssvector* alphar,
     double delta,
     ae_int_t p,
     ae_int_t* q,
     double* alpharpiv,
     double* thetad,
     /* Integer */ ae_vector* possibleflips,
     ae_int_t* possibleflipscnt,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t nx;
    ae_int_t j;
    ae_int_t nj;
    ae_int_t dir;
    double vx;
    double vp;
    ae_int_t ej;
    double alpharej;
    double vtarget;
    double vtest;
    ae_int_t eligiblecnt;
    ae_int_t originaleligiblecnt;
    ae_int_t bndt;
    double alphawaver;
    double adelta;
    ae_int_t idx;
    double vtheta;
    ae_int_t t0;

    *q = 0;
    *alpharpiv = 0.0;
    *thetad = 0.0;

    nx = s->ns+s->m;
    ae_assert(ae_fp_neq(delta,(double)(0)), "RatioTest: zero delta", _state);
    ae_assert(s->state==reviseddualsimplex_ssvalid, "RatioTest: invalid X", _state);
    
    /*
     * Timers
     */
    t0 = 0;
    if( state->dotimers )
    {
        t0 = ae_tickcount();
    }
    
    /*
     * Clear output
     */
    *q = -1;
    *alpharpiv = (double)(0);
    *thetad = (double)(0);
    *possibleflipscnt = 0;
    
    /*
     * Prepare temporaries
     *
     * Scaled tolerances are used to test AlphaWaveR for positivity/negativity,
     * scale of I-th tolerance is calculated as ratio of ColScale[I] and ColScale[P].
     */
    dir = ae_sign(delta, _state);
    ivectorsetlengthatleast(possibleflips, nx, _state);
    
    /*
     * Prepare set of eligible variables
     *
     * NOTE: free variables are immediately chosen at this stage
     */
    ivectorsetlengthatleast(&state->eligiblealphar, alphar->k, _state);
    eligiblecnt = 0;
    for(j=0; j<=alphar->k-1; j++)
    {
        nj = alphar->idx.ptr.p_int[j];
        bndt = s->bndt.ptr.p_int[nj];
        
        /*
         * Handle fixed and free variables: fixed ones are not eligible,
         * free non-basic variables are always and immediately eligible
         */
        if( bndt==reviseddualsimplex_ccfixed )
        {
            continue;
        }
        if( bndt==reviseddualsimplex_ccfree )
        {
            *q = nj;
            *thetad = (double)(0);
            *alpharpiv = alphar->vals.ptr.p_double[j];
            if( state->dotrace )
            {
                ae_trace("> ratio test: quick exit, found free nonbasic variable\n");
                ae_trace("Q           = %12d  (variable selected)\n",
                    (int)(*q));
                ae_trace("ThetaD      = %12.3e  (dual step length)\n",
                    (double)(*thetad));
            }
            if( state->dotimers )
            {
                state->repdualratiotesttime = state->repdualratiotesttime+(ae_tickcount()-t0);
            }
            return;
        }
        
        /*
         * Handle lower/upper/range constraints
         */
        vx = s->xa.ptr.p_double[nj];
        vp = settings->pivottol;
        alphawaver = (double)dir*alphar->vals.ptr.p_double[j];
        if( bndt==reviseddualsimplex_cclower||(bndt==reviseddualsimplex_ccrange&&vx==s->bndl.ptr.p_double[nj]) )
        {
            if( alphawaver>vp )
            {
                state->eligiblealphar.ptr.p_int[eligiblecnt] = j;
                eligiblecnt = eligiblecnt+1;
                continue;
            }
        }
        if( bndt==reviseddualsimplex_ccupper||(bndt==reviseddualsimplex_ccrange&&vx==s->bndu.ptr.p_double[nj]) )
        {
            if( alphawaver<-vp )
            {
                state->eligiblealphar.ptr.p_int[eligiblecnt] = j;
                eligiblecnt = eligiblecnt+1;
                continue;
            }
        }
    }
    originaleligiblecnt = eligiblecnt;
    
    /*
     * Simple ratio test.
     */
    if( settings->ratiotest==0 )
    {
        
        /*
         * Ratio test
         */
        vtarget = (double)(0);
        for(j=0; j<=eligiblecnt-1; j++)
        {
            ej = state->eligiblealphar.ptr.p_int[j];
            nj = alphar->idx.ptr.p_int[ej];
            alpharej = alphar->vals.ptr.p_double[ej];
            
            /*
             * More general case
             */
            alphawaver = (double)dir*alpharej;
            vtest = s->d.ptr.p_double[nj]/alphawaver;
            if( *q<0||vtest<vtarget )
            {
                *q = nj;
                *alpharpiv = alpharej;
                vtarget = vtest;
                *thetad = s->d.ptr.p_double[nj]/alpharej;
            }
        }
        reviseddualsimplex_shifting(state, s, alphar, delta, *q, *alpharpiv, thetad, settings, _state);
        
        /*
         * Trace
         */
        if( state->dotrace )
        {
            ae_trace("> dual ratio test:\n");
            ae_trace("|E|         = %12d  (eligible set size)\n",
                (int)(originaleligiblecnt));
            ae_trace("Q           = %12d  (variable selected)\n",
                (int)(*q));
            ae_trace("ThetaD      = %12.3e  (dual step length)\n",
                (double)(*thetad));
        }
        if( state->dotimers )
        {
            state->repdualratiotesttime = state->repdualratiotesttime+(ae_tickcount()-t0);
        }
        
        /*
         * Done
         */
        return;
    }
    
    /*
     * Bounds flipping ratio test
     */
    if( settings->ratiotest==1 )
    {
        adelta = ae_fabs(delta, _state);
        
        /*
         * Quick exit
         */
        if( eligiblecnt==0 )
        {
            if( state->dotrace )
            {
                ae_trace("> ratio test: quick exit, no eligible variables\n");
            }
            return;
        }
        
        /*
         * BFRT
         */
        while(eligiblecnt>0)
        {
            
            /*
             * Find Q satisfying BFRT criteria
             */
            idx = -1;
            *q = -1;
            *alpharpiv = (double)(0);
            vtarget = (double)(0);
            for(j=0; j<=eligiblecnt-1; j++)
            {
                ej = state->eligiblealphar.ptr.p_int[j];
                nj = alphar->idx.ptr.p_int[ej];
                alpharej = alphar->vals.ptr.p_double[ej];
                vtheta = s->d.ptr.p_double[nj]/alpharej;
                vtest = (double)dir*vtheta;
                if( *q<0||vtest<vtarget )
                {
                    *q = nj;
                    *alpharpiv = alpharej;
                    vtarget = vtest;
                    *thetad = vtheta;
                    idx = j;
                }
            }
            ae_assert(*q>=0, "RatioTest: integrity check failed (BFRT)", _state);
            
            /*
             * BFRT mini-iterations will be terminated upon discovery
             * of non-boxed variable or upon exhausting of eligible set.
             */
            if( s->bndt.ptr.p_int[*q]!=reviseddualsimplex_ccrange )
            {
                break;
            }
            if( eligiblecnt==1 )
            {
                break;
            }
            
            /*
             * Update and test ADelta. Break BFRT mini-iterations once
             * we get negative slope.
             */
            adelta = adelta-(s->bndu.ptr.p_double[*q]-s->bndl.ptr.p_double[*q])*ae_fabs(*alpharpiv, _state);
            if( ae_fp_less_eq(adelta,(double)(0)) )
            {
                break;
            }
            
            /*
             * Update eligible set, record flip
             */
            possibleflips->ptr.p_int[*possibleflipscnt] = state->eligiblealphar.ptr.p_int[idx];
            *possibleflipscnt = *possibleflipscnt+1;
            state->eligiblealphar.ptr.p_int[idx] = state->eligiblealphar.ptr.p_int[eligiblecnt-1];
            eligiblecnt = eligiblecnt-1;
        }
        ae_assert(*q>=0, "RatioTest: unexpected failure", _state);
        *thetad = s->d.ptr.p_double[*q]/(*alpharpiv);
        reviseddualsimplex_shifting(state, s, alphar, delta, *q, *alpharpiv, thetad, settings, _state);
        
        /*
         * Trace
         */
        if( state->dotrace )
        {
            ae_trace("> dual bounds flipping ratio test:\n");
            ae_trace("|E|         = %12d  (eligible set size)\n",
                (int)(originaleligiblecnt));
            ae_trace("Q           = %12d  (variable selected)\n",
                (int)(*q));
            ae_trace("ThetaD      = %12.3e  (dual step length)\n",
                (double)(*thetad));
            ae_trace("Flips       = %12d  (possible bound flips)\n",
                (int)(state->possibleflipscnt));
        }
        if( state->dotimers )
        {
            state->repdualratiotesttime = state->repdualratiotesttime+(ae_tickcount()-t0);
        }
        
        /*
         * Done
         */
        return;
    }
    
    /*
     * Unknown test type
     */
    ae_assert(ae_false, "RatioTest: integrity check failed, unknown test type", _state);
}


/*************************************************************************
This function performs update of XB, XN, D and Z during final step of revised
dual simplex method.

It also updates basis cache of the subproblem (s.bcache field).

Depending on Settings.RatioTest, following operations are performed:
* Settings.RatioTest=0  ->  simple update is performed
* Settings.RatioTest=1  ->  bounds flipping ratio test update is performed
* Settings.RatioTest=2  ->  stabilizing bounds flipping ratio test update is performed

It accepts following parameters:
* P - index of leaving variable from pricing step
* Q - index of entering variable.
* R - index of leaving variable in AlphaQ
* Delta    - delta from pricing step
* AlphaPiv - pivot element (in absence of numerical rounding it is AlphaR[Q]=AlphaQ[R])
* ThetaP   - primal step length
* ThetaD   - dual step length
* AlphaQ   - pivot column
* AlphaQim - intermediate result from Ftran for AlphaQ, used for
             Forest-Tomlin update, not referenced when other update scheme is set
* AlphaR   - pivot row
* Tau - tau-vector for DSE pricing (ignored if simple pricing is used)
* PossibleAlphaRFlips, PossibleAlphaRFlipsCnt - outputs of the RatioTest()
  information about possible variable flips - indexes of AlphaR positions
  which are considered for flipping due to BFRT (however, we have to check
  residual costs before actually flipping variables - it is possible that some variables
  in this set actually do not need flipping)

It performs following operations:
* basis update
* update of XB/BndTB/BndLB/BndUB[] and XA[] (basic and nonbasic components), D
* update of pricing weights

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_updatestep(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     ae_int_t p,
     ae_int_t q,
     ae_int_t r,
     double delta,
     double alphapiv,
     double thetap,
     double thetad,
     /* Real    */ const ae_vector* alphaq,
     /* Real    */ const ae_vector* alphaqim,
     const dssvector* alphar,
     /* Real    */ const ae_vector* tau,
     /* Integer */ const ae_vector* possiblealpharflips,
     ae_int_t possiblealpharflipscnt,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t nx;
    ae_int_t m;
    ae_int_t ii;
    ae_int_t j;
    ae_int_t k;
    ae_int_t aj;
    ae_int_t k0;
    ae_int_t k1;
    double bndl;
    double bndu;
    ae_bool flipped;
    double flip;
    double dj;
    ae_int_t dir;
    ae_int_t idx;
    ae_int_t actualflipscnt;
    ae_int_t t0;
    ae_int_t alpharlen;


    nx = s->ns+s->m;
    m = s->m;
    
    /*
     * Integrity checks
     */
    ae_assert((settings->ratiotest==0||settings->ratiotest==1)||settings->ratiotest==2, "UpdateStep: invalid X", _state);
    ae_assert(s->state==reviseddualsimplex_ssvalid, "UpdateStep: invalid X", _state);
    ae_assert(p>=0&&q>=0, "UpdateStep: invalid P/Q", _state);
    ae_assert(ae_fp_neq(delta,(double)(0)), "UpdateStep: Delta=0", _state);
    ae_assert(ae_fp_neq(alphapiv,(double)(0)), "UpdateStep: AlphaPiv=0", _state);
    
    /*
     * Timers
     */
    t0 = 0;
    if( state->dotimers )
    {
        t0 = ae_tickcount();
    }
    
    /*
     * Prepare
     */
    dir = ae_sign(delta, _state);
    alpharlen = alphar->k;
    flip = (double)(0);
    rvectorsetlengthatleast(&state->tmp0, m, _state);
    for(k=0; k<=m-1; k++)
    {
        state->tmp0.ptr.p_double[k] = (double)(0);
    }
    ivectorsetlengthatleast(&state->ustmpi, nx, _state);
    actualflipscnt = 0;
    
    /*
     * Evaluate and update non-basic elements of D
     */
    for(ii=0; ii<=alpharlen-1; ii++)
    {
        j = alphar->idx.ptr.p_int[ii];
        s->d.ptr.p_double[j] = s->d.ptr.p_double[j]-thetad*alphar->vals.ptr.p_double[ii];
    }
    for(ii=0; ii<=possiblealpharflipscnt-1; ii++)
    {
        aj = possiblealpharflips->ptr.p_int[ii];
        j = alphar->idx.ptr.p_int[aj];
        dj = s->d.ptr.p_double[j];
        bndl = s->bndl.ptr.p_double[j];
        bndu = s->bndu.ptr.p_double[j];
        flipped = ae_false;
        if( s->xa.ptr.p_double[j]==bndl&&dj<(double)0 )
        {
            flip = bndu-bndl;
            flipped = ae_true;
        }
        else
        {
            if( s->xa.ptr.p_double[j]==bndu&&dj>(double)0 )
            {
                flip = bndl-bndu;
                flipped = ae_true;
            }
        }
        if( flipped )
        {
            delta = delta-(double)dir*(bndu-bndl)*ae_fabs(alphar->vals.ptr.p_double[aj], _state);
            state->ustmpi.ptr.p_int[actualflipscnt] = j;
            actualflipscnt = actualflipscnt+1;
            k0 = state->at.ridx.ptr.p_int[j];
            k1 = state->at.ridx.ptr.p_int[j+1]-1;
            for(k=k0; k<=k1; k++)
            {
                idx = state->at.idx.ptr.p_int[k];
                state->tmp0.ptr.p_double[idx] = state->tmp0.ptr.p_double[idx]+flip*state->at.vals.ptr.p_double[k];
            }
        }
    }
    s->d.ptr.p_double[p] = -thetad;
    s->d.ptr.p_double[q] = 0.0;
    
    /*
     * Apply BFRT update (aka long dual step) or simple ratio update
     */
    if( actualflipscnt>0 )
    {
        thetap = delta/alphapiv;
        k0 = state->at.ridx.ptr.p_int[q];
        k1 = state->at.ridx.ptr.p_int[q+1]-1;
        for(k=k0; k<=k1; k++)
        {
            idx = state->at.idx.ptr.p_int[k];
            state->tmp0.ptr.p_double[idx] = state->tmp0.ptr.p_double[idx]+thetap*state->at.vals.ptr.p_double[k];
        }
        reviseddualsimplex_basissolve(&state->basis, &state->tmp0, &state->tmp1, &state->tmp2, _state);
        for(j=0; j<=m-1; j++)
        {
            s->xb.ptr.p_double[j] = s->xb.ptr.p_double[j]-state->tmp1.ptr.p_double[j];
        }
        for(ii=0; ii<=actualflipscnt-1; ii++)
        {
            j = state->ustmpi.ptr.p_int[ii];
            if( s->xa.ptr.p_double[j]==s->bndl.ptr.p_double[j] )
            {
                s->xa.ptr.p_double[j] = s->bndu.ptr.p_double[j];
            }
            else
            {
                s->xa.ptr.p_double[j] = s->bndl.ptr.p_double[j];
            }
        }
        s->xb.ptr.p_double[r] = s->xa.ptr.p_double[q]+thetap;
        if( dir<0 )
        {
            s->xa.ptr.p_double[p] = s->bndl.ptr.p_double[p];
        }
        else
        {
            s->xa.ptr.p_double[p] = s->bndu.ptr.p_double[p];
        }
    }
    else
    {
        for(j=0; j<=m-1; j++)
        {
            s->xb.ptr.p_double[j] = s->xb.ptr.p_double[j]-thetap*alphaq->ptr.p_double[j];
        }
        s->xb.ptr.p_double[r] = s->xa.ptr.p_double[q]+thetap;
        if( dir<0 )
        {
            s->xa.ptr.p_double[p] = s->bndl.ptr.p_double[p];
        }
        else
        {
            s->xa.ptr.p_double[p] = s->bndu.ptr.p_double[p];
        }
    }
    
    /*
     * Update basis
     */
    reviseddualsimplex_basisupdatetrf(&state->basis, &state->at, p, q, alphaq, alphaqim, r, tau, settings, _state);
    
    /*
     * Update cached variables
     */
    reviseddualsimplex_cacheboundinfo(s, r, q, settings, _state);
    
    /*
     * Tracing and timers
     */
    if( state->dodetailedtrace )
    {
        if( state->basis.trftype==3 )
        {
            reviseddualsimplex_updateavgcounter(reviseddualsimplex_sparsityof(&state->basis.densemu, state->basis.trfage*m, _state), &state->repfilldensemu, &state->repfilldensemucnt, _state);
        }
    }
    if( state->dotimers )
    {
        state->repdualupdatesteptime = state->repdualupdatesteptime+(ae_tickcount()-t0);
    }
}


/*************************************************************************
This function performs several checks for accumulation of errors during
factorization update. It returns True if refactorization is advised.

  -- ALGLIB --
     Copyright 24.01.2019 by Bochkanov Sergey
*************************************************************************/
static ae_bool reviseddualsimplex_refactorizationrequired(const dualsimplexstate* state,
     const dualsimplexsubproblem* s,
     ae_int_t q,
     double alpharpiv,
     ae_int_t r,
     double alphaqpiv,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t i;
    double mx;
    double v;
    ae_bool result;


    m = s->m;
    result = ae_false;
    
    /*
     * Quick exit
     */
    if( state->basis.trfage<=reviseddualsimplex_safetrfage )
    {
        return result;
    }
    
    /*
     * Compare Q-th entry of the pivot row AlphaR with R-th entry of the AlphaQ;
     * ideally, both should match exactly. The difference is a rough estimate
     * of the magnitude of the numerical errors.
     */
    mx = 0.0;
    for(i=0; i<=m-1; i++)
    {
        v = state->alphaq.ptr.p_double[i];
        v = v*v;
        if( v>mx )
        {
            mx = v;
        }
    }
    mx = ae_sqrt(mx, _state);
    result = result||ae_fp_greater(ae_fabs(alphaqpiv-alpharpiv, _state),reviseddualsimplex_alphatrigger*(1.0+mx));
    result = result||ae_fp_greater(ae_fabs(alphaqpiv-alpharpiv, _state),reviseddualsimplex_alphatrigger2*ae_fabs(alpharpiv, _state));
    return result;
}


/*************************************************************************
This function caches information for I-th column of the  basis,  which  is
assumed to store variable K:
* lower bound in S.BndLB[I]=S.BndL[K]
* upper bound in S.BndUB[I]=S.BndU[K]
* bound type in  S.BndTB[I]=S.BndT[K]
* lower bound primal error tolerance in S.BndTolLB[I] (nonnegative)
* upper bound primal error tolerance in S.BndTolLB[I] (nonnegative).

  -- ALGLIB --
     Copyright 18.07.2020 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_cacheboundinfo(dualsimplexsubproblem* s,
     ae_int_t i,
     ae_int_t k,
     const dualsimplexsettings* settings,
     ae_state *_state)
{


    s->bndlb.ptr.p_double[i] = s->bndl.ptr.p_double[k];
    s->bndub.ptr.p_double[i] = s->bndu.ptr.p_double[k];
    s->bndtb.ptr.p_int[i] = s->bndt.ptr.p_int[k];
    s->bndtollb.ptr.p_double[i] = settings->xtolabs+settings->xtolrelabs*settings->xtolabs*ae_fabs(s->bndlb.ptr.p_double[i], _state);
    s->bndtolub.ptr.p_double[i] = settings->xtolabs+settings->xtolrelabs*settings->xtolabs*ae_fabs(s->bndub.ptr.p_double[i], _state);
}


/*************************************************************************
This function performs actual solution of dual simplex subproblem  (either
primary one or phase 1 one).

A problem with following properties is expected:
* M>0
* feasible box constraints
* dual feasible initial basis
* actual initial point XC and target value Z
* actual reduced cost vector D
* pricing weights being set to 1.0 or copied from previous problem

Returns:
    * Info = +1 for success, -3 for infeasible
    * IterationsCount is increased by amount of iterations performed

NOTE: this function internally uses separate storage of basic and nonbasic
      components; however, all inputs and outputs use single array S.XA[]
      to store both basic and nonbasic variables. It transparently splits
      variables on input and recombines them on output.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_solvesubproblemdual(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     ae_bool isphase1,
     const dualsimplexsettings* settings,
     ae_int_t* info,
     ae_state *_state)
{
    ae_int_t nx;
    ae_int_t m;
    ae_int_t i;
    ae_int_t p;
    ae_int_t r;
    ae_int_t q;
    double alpharpiv;
    double alphaqpiv;
    double thetad;
    double thetap;
    double delta;
    ae_int_t forcedrestarts;

    *info = 0;

    nx = s->ns+s->m;
    m = s->m;
    forcedrestarts = 0;
    
    /*
     * Integrity checks
     */
    ae_assert(s->state==reviseddualsimplex_ssvalid, "SolveSubproblemDual: X is not valid", _state);
    ae_assert(m>0, "SolveSubproblemDual: M<=0", _state);
    for(i=0; i<=nx-1; i++)
    {
        ae_assert(s->bndt.ptr.p_int[i]!=reviseddualsimplex_ccinfeasible, "SolveSubproblemDual: infeasible box constraints", _state);
    }
    ae_assert(reviseddualsimplex_isdualfeasible(state, s, settings, _state), "SolveSubproblemDual: dual infeasible initial basis", _state);
    
    /*
     * Actual processing
     */
    reviseddualsimplex_offloadbasiccomponents(s, &state->basis, settings, _state);
    *info = 0;
    rvectorsetlengthatleast(&state->tmp0, m, _state);
    for(;;)
    {
        
        /*
         * Iteration report
         */
        if( state->dotrace )
        {
            i = state->repiterationscount2;
            if( isphase1 )
            {
                i = state->repiterationscount1;
            }
            ae_trace("=== ITERATION %5d STARTED ========================================================================\n",
                (int)(i));
            if( state->dodetailedtrace )
            {
            }
        }
        
        /*
         * Pricing
         */
        reviseddualsimplex_pricingstep(state, s, isphase1, &p, &r, &delta, settings, _state);
        if( ae_fp_eq(delta,(double)(0)) )
        {
            
            /*
             * Solved! Feasible and bounded!
             */
            if( state->dotrace )
            {
                ae_trace("> pricing: feasible point found\n");
            }
            reviseddualsimplex_recombinebasicnonbasicx(s, &state->basis, _state);
            *info = 1;
            return;
        }
        
        /*
         * BTran
         */
        reviseddualsimplex_btranstep(state, s, r, &state->rhor, settings, _state);
        
        /*
         * Pivot row
         */
        reviseddualsimplex_pivotrowstep(state, s, &state->rhor, &state->alphar, settings, _state);
        
        /*
         * Ratio test
         */
        reviseddualsimplex_ratiotest(state, s, &state->alphar, delta, p, &q, &alpharpiv, &thetad, &state->possibleflips, &state->possibleflipscnt, settings, _state);
        if( q<0 )
        {
            
            /*
             * Do we have fresh factorization and state? If not,
             * refresh them prior to declaring that we have no solution.
             */
            if( state->basis.trfage>0&&forcedrestarts<reviseddualsimplex_maxforcedrestarts )
            {
                if( state->dotrace )
                {
                    ae_trace("> ratio test: failed, basis is old (age=%0d), forcing restart (%0d of %0d)\n",
                        (int)(state->basis.trfage),
                        (int)(forcedrestarts),
                        (int)(reviseddualsimplex_maxforcedrestarts-1));
                }
                reviseddualsimplex_basisfreshtrf(&state->basis, &state->at, settings, _state);
                reviseddualsimplex_subproblemhandlexnupdate(state, s, _state);
                reviseddualsimplex_offloadbasiccomponents(s, &state->basis, settings, _state);
                inc(&forcedrestarts, _state);
                continue;
            }
            
            /*
             * Dual unbounded, primal infeasible
             */
            if( state->dotrace )
            {
                ae_trace("> ratio test: failed, results are accepted\n");
            }
            reviseddualsimplex_recombinebasicnonbasicx(s, &state->basis, _state);
            *info = -3;
            return;
        }
        thetap = delta/alpharpiv;
        
        /*
         * FTran, including additional FTran for DSE weights (if needed)
         *
         * NOTE: AlphaQim is filled by intermediate FTran result which is useful
         *       for Forest-Tomlin update scheme. If not Forest-Tomlin update is
         *       used, then it is not set.
         */
        reviseddualsimplex_ftranstep(state, s, &state->rhor, q, &state->alphaq, &state->alphaqim, &state->tau, settings, _state);
        alphaqpiv = state->alphaq.ptr.p_double[r];
        
        /*
         * Check numerical accuracy, trigger refactorization if needed
         */
        if( reviseddualsimplex_refactorizationrequired(state, s, q, alpharpiv, r, alphaqpiv, _state) )
        {
            if( state->dotrace )
            {
                ae_trace("> refactorization test: numerical errors are too large, forcing refactorization and restart\n");
            }
            reviseddualsimplex_basisfreshtrf(&state->basis, &state->at, settings, _state);
            reviseddualsimplex_subproblemhandlexnupdate(state, s, _state);
            reviseddualsimplex_offloadbasiccomponents(s, &state->basis, settings, _state);
            continue;
        }
        
        /*
         * Basis change and update
         */
        reviseddualsimplex_updatestep(state, s, p, q, r, delta, alpharpiv, thetap, thetad, &state->alphaq, &state->alphaqim, &state->alphar, &state->tau, &state->possibleflips, state->possibleflipscnt, settings, _state);
        inc(&state->repiterationscount, _state);
        if( isphase1 )
        {
            inc(&state->repiterationscount1, _state);
        }
        else
        {
            inc(&state->repiterationscount2, _state);
        }
    }
}


/*************************************************************************
This function solves simplex subproblem using primal simplex method.

A problem with following properties is expected:
* M>0
* feasible box constraints
* primal feasible initial basis
* actual initial point XC and target value Z
* actual reduced cost vector D
* pricing weights being set to 1.0 or copied from previous problem

Returns:
    * Info = +1 for success, -3 for infeasible
    * IterationsCount is increased by amount of iterations performed

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_solvesubproblemprimal(dualsimplexstate* state,
     dualsimplexsubproblem* s,
     const dualsimplexsettings* settings,
     ae_int_t* info,
     ae_state *_state)
{
    ae_int_t nn;
    ae_int_t nx;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    double v;
    double vmax;
    ae_int_t bi;
    double dj;
    ae_int_t bndt;
    ae_int_t q;
    ae_int_t p;
    ae_int_t r;
    ae_int_t dir;
    double lim;
    ae_bool haslim;
    double thetap;
    double xbnd;
    double flip;
    ae_int_t canddir;
    double candlim;
    double candflip;
    ae_int_t j0;
    ae_int_t j1;
    double alphawave;
    double vp;
    double vb;
    double vx;
    double vtest;
    double vv;

    *info = 0;

    nn = s->ns;
    nx = s->ns+s->m;
    m = s->m;
    
    /*
     * Integrity checks
     */
    ae_assert(s->state==reviseddualsimplex_ssvalid, "SolveSubproblemPrimal: X is not valid", _state);
    ae_assert(m>0, "SolveSubproblemPrimal: M<=0", _state);
    for(i=0; i<=nx-1; i++)
    {
        ae_assert(s->bndt.ptr.p_int[i]!=reviseddualsimplex_ccinfeasible, "SolveSubproblemPrimal: infeasible box constraints", _state);
    }
    
    /*
     * Actual processing
     */
    *info = 1;
    rvectorsetlengthatleast(&state->tmp0, m, _state);
    for(;;)
    {
        
        /*
         * Iteration report
         */
        if( state->dotrace )
        {
            i = state->repiterationscount3;
            ae_trace("=== ITERATION %5d STARTED ========================================================================\n",
                (int)(i));
            if( state->dodetailedtrace )
            {
            }
        }
        
        /*
         * Primal simplex pricing step: we implement the very basic version
         * of the pricing step because it is expected that primal simplex method
         * is used just to apply quick correction after removal of the perturbation.
         */
        q = -1;
        vmax = (double)(0);
        dir = 0;
        lim = ae_maxrealnumber;
        haslim = ae_false;
        flip = (double)(0);
        canddir = 0;
        for(i=0; i<=nn-1; i++)
        {
            j = state->basis.nidx.ptr.p_int[i];
            dj = s->d.ptr.p_double[j];
            bndt = s->bndt.ptr.p_int[j];
            if( bndt==reviseddualsimplex_ccfixed )
            {
                continue;
            }
            if( bndt==reviseddualsimplex_ccrange )
            {
                v = (double)(0);
                candlim = s->bndu.ptr.p_double[j]-s->bndl.ptr.p_double[j];
                candflip = (double)(0);
                if( s->xa.ptr.p_double[j]==s->bndl.ptr.p_double[j] )
                {
                    v = -dj;
                    canddir = 1;
                    candflip = s->bndu.ptr.p_double[j];
                }
                if( s->xa.ptr.p_double[j]==s->bndu.ptr.p_double[j] )
                {
                    v = dj;
                    canddir = -1;
                    candflip = s->bndl.ptr.p_double[j];
                }
                if( v>vmax )
                {
                    vmax = v;
                    dir = canddir;
                    lim = candlim;
                    haslim = ae_true;
                    flip = candflip;
                    q = j;
                }
                continue;
            }
            v = (double)(0);
            canddir = 0;
            if( bndt==reviseddualsimplex_cclower )
            {
                v = -dj;
                canddir = 1;
            }
            if( bndt==reviseddualsimplex_ccupper )
            {
                v = dj;
                canddir = -1;
            }
            if( bndt==reviseddualsimplex_ccfree )
            {
                v = ae_fabs(dj, _state);
                canddir = -ae_sign(dj, _state);
            }
            if( v>vmax )
            {
                vmax = v;
                dir = canddir;
                lim = ae_maxrealnumber;
                haslim = ae_false;
                q = j;
            }
            continue;
        }
        if( vmax<=settings->dtolabs )
        {
            
            /*
             * Solved: primal and dual feasible!
             */
            if( state->dotrace )
            {
                ae_trace("> primal pricing: feasible point found\n");
            }
            return;
        }
        ae_assert(q>=0, "SolveSubproblemPrimal: integrity check failed", _state);
        if( state->dotrace )
        {
            ae_trace("> primal pricing: found entering variable\n");
            ae_trace("Q           = %12d  (variable selected)\n",
                (int)(q));
            ae_trace("|D|         = %12.3e  (dual infeasibility)\n",
                (double)(vmax));
        }
        
        /*
         * FTran and textbook ratio test (again, we expect primal phase to terminate quickly)
         *
         * NOTE: AlphaQim is filled by intermediate FTran result which is useful
         *       for Forest-Tomlin update scheme. If not Forest-Tomlin update is
         *       used, then it is not set.
         */
        for(i=0; i<=m-1; i++)
        {
            state->tmp0.ptr.p_double[i] = (double)(0);
        }
        j0 = state->at.ridx.ptr.p_int[q];
        j1 = state->at.ridx.ptr.p_int[q+1]-1;
        for(j=j0; j<=j1; j++)
        {
            state->tmp0.ptr.p_double[state->at.idx.ptr.p_int[j]] = state->at.vals.ptr.p_double[j];
        }
        reviseddualsimplex_basissolvex(&state->basis, &state->tmp0, &state->alphaq, &state->alphaqim, ae_true, &state->tmp2, _state);
        vp = settings->pivottol;
        p = -1;
        r = -1;
        thetap = (double)(0);
        xbnd = (double)(0);
        for(i=0; i<=m-1; i++)
        {
            bi = state->basis.idx.ptr.p_int[i];
            alphawave = -(double)dir*state->alphaq.ptr.p_double[i];
            vx = s->xa.ptr.p_double[bi];
            if( alphawave<-vp&&reviseddualsimplex_hasbndl(s, bi, _state) )
            {
                vb = s->bndl.ptr.p_double[bi];
                if( vx<=vb )
                {
                    
                    /*
                     * X[Bi] is already out of bounds due to rounding errors, perform shifting
                     */
                    vb = vx-reviseddualsimplex_shiftlen;
                    s->bndl.ptr.p_double[bi] = vx;
                }
                vtest = (vb-vx)/alphawave;
                if( p<0||vtest<thetap )
                {
                    p = bi;
                    r = i;
                    thetap = vtest;
                    xbnd = vb;
                }
            }
            if( alphawave>vp&&reviseddualsimplex_hasbndu(s, bi, _state) )
            {
                vb = s->bndu.ptr.p_double[bi];
                if( vx>=vb )
                {
                    
                    /*
                     * X[Bi] is already out of bounds due to rounding errors, perform shifting
                     */
                    vb = vx+reviseddualsimplex_shiftlen;
                    s->bndu.ptr.p_double[bi] = vb;
                }
                vtest = (vb-vx)/alphawave;
                if( p<0||vtest<thetap )
                {
                    p = bi;
                    r = i;
                    thetap = vtest;
                    xbnd = vb;
                }
            }
        }
        if( p<0&&!haslim )
        {
            
            /*
             * Primal unbounded
             */
            *info = -4;
            if( state->dotrace )
            {
                ae_trace("> primal ratio test: dual infeasible, primal unbounded\n");
            }
            return;
        }
        if( state->dotrace )
        {
            ae_trace("> primal ratio test: found leaving variable\n");
            ae_trace("P           = %12d  (variable index)\n",
                (int)(p));
            ae_trace("R           = %12d  (variable index in basis)\n",
                (int)(r));
            ae_trace("ThetaP      = %12.3e  (primal step length)\n",
                (double)(thetap));
        }
        
        /*
         * Update step
         */
        if( p>=0&&(!haslim||thetap<lim) )
        {
            
            /*
             * One of the basic variables hit the boundary and become non-basic.
             *
             * Perform update:
             * * update basic elements of X[] (X[p] is explicitly set to the
             *   boundary value) and X[q]
             * * update target value Z
             * * update factorization
             * * update D[]
             */
            rvectorsetlengthatleast(&state->tmp0, m, _state);
            for(i=0; i<=m-1; i++)
            {
                bi = state->basis.idx.ptr.p_int[i];
                vv = thetap*((double)dir*state->alphaq.ptr.p_double[i]);
                s->xa.ptr.p_double[bi] = s->xa.ptr.p_double[bi]-vv;
            }
            s->xa.ptr.p_double[p] = xbnd;
            s->xa.ptr.p_double[q] = s->xa.ptr.p_double[q]+(double)dir*thetap;
            for(i=0; i<=m-1; i++)
            {
                state->tmp0.ptr.p_double[i] = (double)(0);
            }
            reviseddualsimplex_basisupdatetrf(&state->basis, &state->at, p, q, &state->alphaq, &state->alphaqim, r, &state->tmp0, settings, _state);
            for(i=0; i<=m-1; i++)
            {
                state->tmp0.ptr.p_double[i] = s->effc.ptr.p_double[state->basis.idx.ptr.p_int[i]];
            }
            reviseddualsimplex_basissolvet(&state->basis, &state->tmp0, &state->tmp1, &state->tmp2, _state);
            reviseddualsimplex_computeantv(state, &state->tmp1, &s->d, _state);
            for(i=0; i<=nn-1; i++)
            {
                j = state->basis.nidx.ptr.p_int[i];
                s->d.ptr.p_double[j] = s->effc.ptr.p_double[j]-s->d.ptr.p_double[j];
            }
        }
        else
        {
            
            /*
             * Basis does not change because Qth variable flips from one bound
             * to another one long before we encounter the boundary
             */
            s->xa.ptr.p_double[q] = flip;
            for(i=0; i<=m-1; i++)
            {
                bi = state->basis.idx.ptr.p_int[i];
                vv = lim*((double)dir*state->alphaq.ptr.p_double[i]);
                s->xa.ptr.p_double[bi] = s->xa.ptr.p_double[bi]-vv;
            }
        }
        inc(&state->repiterationscount, _state);
        inc(&state->repiterationscount3, _state);
    }
}


/*************************************************************************
This function estimates feasibility properties of the  current  basis  and
invokes phase 1 if necessary.

A problem with following properties is expected:
* M>0
* feasible box constraints
* some initial basis (can be dual infeasible) with actual factorization
* actual initial point XC and target value Z
* actual reduced cost vector D

It returns:
* +1 if dual feasible basis was found
* -4 if problem is dual infeasible

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_invokephase1(dualsimplexstate* state,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t m;
    double dualerr;


    m = state->primary.m;
    state->repterminationtype = 0;
    
    /*
     * Integrity checks
     */
    ae_assert(state->primary.state==reviseddualsimplex_ssvalid, "InvokePhase1: invalid primary X", _state);
    ae_assert(m>0, "InvokePhase1: M<=0", _state);
    
    /*
     * Is it dual feasible from the very beginning (or maybe after initial DFC)?
     */
    if( state->dotrace )
    {
        ae_trace("> performing initial dual feasibility correction...\n");
    }
    dualerr = reviseddualsimplex_initialdualfeasibilitycorrection(state, &state->primary, settings, _state);
    if( state->dotrace )
    {
        ae_trace("> initial dual feasibility correction done\ndualErr = %0.3e\n",
            (double)(dualerr));
    }
    if( ae_fp_less_eq(dualerr,settings->dtolabs) )
    {
        if( state->dotrace )
        {
            ae_trace("> solution is dual feasible, phase 1 is done\n");
        }
        state->repterminationtype = 1;
        return;
    }
    if( state->dotrace )
    {
        ae_trace("> solution is not dual feasible, proceeding to full-scale phase 1\n");
        ae_trace("\n");
        ae_trace("****************************************************************************************************\n");
        ae_trace("*   PHASE 1 OF DUAL SIMPLEX SOLVER                                                                 *\n");
        ae_trace("****************************************************************************************************\n");
    }
    
    /*
     * Solve phase #1 subproblem
     */
    reviseddualsimplex_subprobleminitphase1(&state->primary, &state->basis, &state->phase1, _state);
    if( state->dotrace )
    {
        ae_trace("> performing phase 1 dual feasibility correction...\n");
    }
    dualerr = reviseddualsimplex_initialdualfeasibilitycorrection(state, &state->phase1, settings, _state);
    if( state->dotrace )
    {
        ae_trace("> phase 1 dual feasibility correction done\ndualErr = %0.3e\n",
            (double)(dualerr));
    }
    reviseddualsimplex_solvesubproblemdual(state, &state->phase1, ae_true, settings, &state->repterminationtype, _state);
    ae_assert(state->repterminationtype>0, "DualSimplexSolver: unexpected failure of phase #1", _state);
    state->repterminationtype = 1;
    
    /*
     * Setup initial basis for phase #2 using solution of phase #1
     */
    if( state->dotrace )
    {
        ae_trace("> setting up phase 2 initial solution\n");
    }
    reviseddualsimplex_subprobleminferinitialxn(state, &state->primary, _state);
    dualerr = reviseddualsimplex_initialdualfeasibilitycorrection(state, &state->primary, settings, _state);
    if( ae_fp_greater(dualerr,settings->dtolabs) )
    {
        if( state->dotrace )
        {
            ae_trace("> initial dual feasibility correction failed! terminating...\n");
        }
        state->repterminationtype = -4;
        return;
    }
    state->repterminationtype = 1;
}


/*************************************************************************
This function performs actual solution.

INPUT PARAMETERS:
    State   -   state
    
Solution results can be found in fields  of  State  which  are  explicitly
declared as accessible by external code.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_dssoptimizewrk(dualsimplexstate* state,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t nx;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    double v;
    hqrndstate rs;
    ae_int_t t0;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    _hqrndstate_init(&rs, _state, ae_true);

    nx = state->primary.ns+state->primary.m;
    m = state->primary.m;
    t0 = 0;
    
    /*
     * Handle case when M=0; after this block we assume that M>0.
     */
    if( m==0 )
    {
        
        /*
         * Trace
         */
        if( state->dotrace )
        {
            ae_trace("> box-only LP problem, quick solution\n");
        }
        
        /*
         * Solve
         */
        reviseddualsimplex_solveboxonly(state, _state);
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Most basic check for correctness of box and/or linear constraints
     */
    for(j=0; j<=nx-1; j++)
    {
        if( state->primary.bndt.ptr.p_int[j]==reviseddualsimplex_ccinfeasible )
        {
            
            /*
             * Set error flag and generate some point to return
             */
            if( state->dotrace )
            {
                ae_trace("[WARNING] infeasible box constraint (or range constraint with AL>AU) found, terminating\n");
            }
            state->repterminationtype = -3;
            reviseddualsimplex_setzeroxystats(state, _state);
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * Initialization:
     * * initial perturbed C[]
     */
    hqrndseed(7456, 2355, &rs, _state);
    for(i=0; i<=nx-1; i++)
    {
        if( !reviseddualsimplex_isfree(&state->primary, i, _state) )
        {
            
            /*
             * apply perturbation
             */
            v = settings->perturbmag*((double)1+ae_fabs(state->primary.rawc.ptr.p_double[i], _state))*((double)1+hqrnduniformr(&rs, _state));
            if( !reviseddualsimplex_hasbndl(&state->primary, i, _state) )
            {
                v = -v;
            }
            state->primary.effc.ptr.p_double[i] = state->primary.rawc.ptr.p_double[i]+v;
        }
    }
    
    /*
     * Solve phase 1 subproblem, then perturbed subproblem
     */
    reviseddualsimplex_basisfreshtrf(&state->basis, &state->at, settings, _state);
    if( state->primary.state==reviseddualsimplex_ssinvalid )
    {
        reviseddualsimplex_subprobleminferinitialxn(state, &state->primary, _state);
    }
    if( state->primary.state==reviseddualsimplex_ssvalidxn )
    {
        reviseddualsimplex_subproblemhandlexnupdate(state, &state->primary, _state);
    }
    ae_assert(state->primary.state==reviseddualsimplex_ssvalid, "DSS: integrity check failed (init)", _state);
    if( state->dotimers )
    {
        t0 = ae_tickcount();
    }
    reviseddualsimplex_invokephase1(state, settings, _state);
    if( state->dotimers )
    {
        state->repphase1time = ae_tickcount()-t0;
    }
    if( state->repterminationtype<=0 )
    {
        
        /*
         * Primal unbounded, dual infeasible
         */
        ae_assert(state->repterminationtype==-4, "DSS: integrity check for InvokePhase1() result failed", _state);
        if( state->dotrace )
        {
            ae_trace("> the problem is dual infeasible, primal unbounded\n> done\n");
        }
        reviseddualsimplex_setxydstats(state, &state->primary, &state->basis, &state->xydsbuf, &state->repx, &state->replagbc, &state->replaglc, &state->repstats, _state);
        ae_frame_leave(_state);
        return;
    }
    if( state->dotrace )
    {
        ae_trace("\n");
        ae_trace("****************************************************************************************************\n");
        ae_trace("*   PHASE 2 OF DUAL SIMPLEX SOLVER                                                                 *\n");
        ae_trace("****************************************************************************************************\n");
    }
    if( state->dotimers )
    {
        t0 = ae_tickcount();
    }
    reviseddualsimplex_solvesubproblemdual(state, &state->primary, ae_false, settings, &state->repterminationtype, _state);
    if( state->dotimers )
    {
        state->repphase2time = ae_tickcount()-t0;
    }
    if( state->repterminationtype<=0 )
    {
        
        /*
         * Primal infeasible
         */
        ae_assert(state->repterminationtype==-3, "DSS: integrity check for SolveSubproblemDual() result failed", _state);
        if( state->dotrace )
        {
            ae_trace("> the problem is primal infeasible\n> done\n");
        }
        reviseddualsimplex_setxydstats(state, &state->primary, &state->basis, &state->xydsbuf, &state->repx, &state->replagbc, &state->replaglc, &state->repstats, _state);
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Remove perturbation from the cost vector,
     * then use primal simplex to enforce dual feasibility
     * after removal of the perturbation (if necessary).
     */
    if( state->dotrace )
    {
        ae_trace("\n");
        ae_trace("****************************************************************************************************\n");
        ae_trace("*   PHASE 3 OF DUAL SIMPLEX SOLVER (perturbation removed from cost vector)                         *\n");
        ae_trace("****************************************************************************************************\n");
    }
    if( state->dotimers )
    {
        t0 = ae_tickcount();
    }
    reviseddualsimplex_subprobleminitphase3(&state->primary, &state->phase3, _state);
    for(i=0; i<=nx-1; i++)
    {
        state->phase3.effc.ptr.p_double[i] = state->primary.rawc.ptr.p_double[i];
    }
    ae_assert(state->phase3.state>=reviseddualsimplex_ssvalidxn, "DSS: integrity check failed (remove perturbation)", _state);
    reviseddualsimplex_subproblemhandlexnupdate(state, &state->phase3, _state);
    reviseddualsimplex_solvesubproblemprimal(state, &state->phase3, settings, &state->repterminationtype, _state);
    if( state->dotimers )
    {
        state->repphase3time = ae_tickcount()-t0;
    }
    if( state->repterminationtype<=0 )
    {
        
        /*
         * Dual infeasible, primal unbounded
         */
        ae_assert(state->repterminationtype==-4, "DSS: integrity check for SolveSubproblemPrimal() result failed", _state);
        if( state->dotrace )
        {
            ae_trace("> the problem is primal unbounded\n> done\n");
        }
        reviseddualsimplex_setxydstats(state, &state->phase3, &state->basis, &state->xydsbuf, &state->repx, &state->replagbc, &state->replaglc, &state->repstats, _state);
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=nx-1; i++)
    {
        state->primary.xa.ptr.p_double[i] = state->phase3.xa.ptr.p_double[i];
        if( reviseddualsimplex_hasbndl(&state->primary, i, _state) )
        {
            state->primary.xa.ptr.p_double[i] = ae_maxreal(state->primary.xa.ptr.p_double[i], state->primary.bndl.ptr.p_double[i], _state);
        }
        if( reviseddualsimplex_hasbndu(&state->primary, i, _state) )
        {
            state->primary.xa.ptr.p_double[i] = ae_minreal(state->primary.xa.ptr.p_double[i], state->primary.bndu.ptr.p_double[i], _state);
        }
    }
    
    /*
     * Primal and dual feasible, problem solved
     */
    state->repterminationtype = 1;
    reviseddualsimplex_setxydstats(state, &state->primary, &state->basis, &state->xydsbuf, &state->repx, &state->replagbc, &state->replaglc, &state->repstats, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Box-constrained solver; sets State.RepX, State.RepStats and State.RepTerminationType,
does not change other fields.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_solveboxonly(dualsimplexstate* state,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t ns;


    ns = state->primary.ns;
    ae_assert(state->primary.m==0, "SolveBoxOnly: integrity check failed", _state);
    rsetv(ns, 0.0, &state->replagbc, _state);
    for(i=0; i<=ns-1; i++)
    {
        
        /*
         * Handle infeasible variable
         */
        if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccinfeasible )
        {
            state->repterminationtype = -3;
            state->repx.ptr.p_double[i] = 0.5*(state->primary.bndl.ptr.p_double[i]+state->primary.bndu.ptr.p_double[i]);
            state->repstats.ptr.p_int[i] = 0;
            continue;
        }
        
        /*
         * Handle fixed variable
         */
        if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccfixed )
        {
            state->repx.ptr.p_double[i] = state->primary.bndl.ptr.p_double[i];
            state->repstats.ptr.p_int[i] = -1;
            state->replagbc.ptr.p_double[i] = -state->primary.rawc.ptr.p_double[i];
            continue;
        }
        
        /*
         * Handle non-zero cost component
         */
        if( ae_fp_greater(state->primary.rawc.ptr.p_double[i],(double)(0)) )
        {
            if( state->primary.bndt.ptr.p_int[i]!=reviseddualsimplex_ccrange&&state->primary.bndt.ptr.p_int[i]!=reviseddualsimplex_cclower )
            {
                if( state->repterminationtype>0 )
                {
                    state->repterminationtype = -4;
                }
                if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccupper )
                {
                    state->repx.ptr.p_double[i] = state->primary.bndu.ptr.p_double[i];
                    state->repstats.ptr.p_int[i] = 1;
                }
                else
                {
                    state->repx.ptr.p_double[i] = (double)(0);
                    state->repstats.ptr.p_int[i] = 0;
                }
                state->replagbc.ptr.p_double[i] = (double)(0);
            }
            else
            {
                state->repx.ptr.p_double[i] = state->primary.bndl.ptr.p_double[i];
                state->repstats.ptr.p_int[i] = -1;
                state->replagbc.ptr.p_double[i] = -state->primary.rawc.ptr.p_double[i];
            }
            continue;
        }
        if( ae_fp_less(state->primary.rawc.ptr.p_double[i],(double)(0)) )
        {
            if( state->primary.bndt.ptr.p_int[i]!=reviseddualsimplex_ccrange&&state->primary.bndt.ptr.p_int[i]!=reviseddualsimplex_ccupper )
            {
                if( state->repterminationtype>0 )
                {
                    state->repterminationtype = -4;
                }
                if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_cclower )
                {
                    state->repx.ptr.p_double[i] = state->primary.bndl.ptr.p_double[i];
                    state->repstats.ptr.p_int[i] = -1;
                }
                else
                {
                    state->repx.ptr.p_double[i] = (double)(0);
                    state->repstats.ptr.p_int[i] = 0;
                }
                state->replagbc.ptr.p_double[i] = (double)(0);
            }
            else
            {
                state->repx.ptr.p_double[i] = state->primary.bndu.ptr.p_double[i];
                state->repstats.ptr.p_int[i] = 1;
                state->replagbc.ptr.p_double[i] = -state->primary.rawc.ptr.p_double[i];
            }
            continue;
        }
        
        /*
         * Handle non-free variable with zero cost component
         */
        if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccupper||state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccrange )
        {
            state->repx.ptr.p_double[i] = state->primary.bndu.ptr.p_double[i];
            state->repstats.ptr.p_int[i] = 1;
            state->replagbc.ptr.p_double[i] = (double)(0);
            continue;
        }
        if( state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_cclower )
        {
            state->repx.ptr.p_double[i] = state->primary.bndl.ptr.p_double[i];
            state->repstats.ptr.p_int[i] = -1;
            state->replagbc.ptr.p_double[i] = (double)(0);
            continue;
        }
        
        /*
         * Free variable, zero cost component
         */
        ae_assert(state->primary.bndt.ptr.p_int[i]==reviseddualsimplex_ccfree, "DSSOptimize: integrity check failed", _state);
        state->repx.ptr.p_double[i] = (double)(0);
        state->repstats.ptr.p_int[i] = 0;
        state->replagbc.ptr.p_double[i] = (double)(0);
    }
}


/*************************************************************************
Zero-fill RepX, RepLagBC, RepLagLC, RepStats.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_setzeroxystats(dualsimplexstate* state,
     ae_state *_state)
{
    ae_int_t i;


    for(i=0; i<=state->primary.ns-1; i++)
    {
        state->repx.ptr.p_double[i] = (double)(0);
        state->replagbc.ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=state->primary.m-1; i++)
    {
        state->replaglc.ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=state->primary.ns+state->primary.m-1; i++)
    {
        state->repstats.ptr.p_int[i] = 0;
    }
}


/*************************************************************************
This function initializes basis structure; no triangular factorization is
prepared yet. Previously allocated memory is reused.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_basisinit(ae_int_t ns,
     ae_int_t m,
     dualsimplexbasis* s,
     ae_state *_state)
{
    ae_int_t i;


    s->ns = ns;
    s->m = m;
    ivectorgrowto(&s->idx, m, _state);
    ivectorgrowto(&s->nidx, ns, _state);
    bvectorgrowto(&s->isbasic, ns+m, _state);
    for(i=0; i<=ns-1; i++)
    {
        s->nidx.ptr.p_int[i] = i;
        s->isbasic.ptr.p_bool[i] = ae_false;
    }
    for(i=0; i<=m-1; i++)
    {
        s->idx.ptr.p_int[i] = ns+i;
        s->isbasic.ptr.p_bool[ns+i] = ae_true;
    }
    s->trftype = 3;
    s->trfage = 0;
    s->isvalidtrf = ae_false;
    rvectorsetlengthatleast(&s->dseweights, m, _state);
    for(i=0; i<=m-1; i++)
    {
        s->dseweights.ptr.p_double[i] = 1.0;
    }
    s->dsevalid = ae_false;
    reviseddualsimplex_basisclearstats(s, _state);
}


/*************************************************************************
This function clears internal performance counters of the basis

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_basisclearstats(dualsimplexbasis* s,
     ae_state *_state)
{


    s->statfact = 0;
    s->statupdt = 0;
    s->statoffdiag = (double)(0);
}


/*************************************************************************
This function resizes basis. It is assumed that constraint matrix is
completely overwritten by new one, but both matrices are similar enough
so we can reuse previous basis.

Dual steepest edge weights are invalidated by this function.

This function:
* tries to resize basis
* if possible, returns True and valid basis with valid factorization
* if resize is impossible (or abandoned due to stability reasons), it
  returns False and basis object is left in the invalid state (you have
  to reinitialize it by all-logicals basis)

Following types of resize are supported:
* new basis size is larger than previous one => logical elements are
  added to the new basis
* basis sizes match => no operation is performed
* new basis size is zero => basis is set to zero

This function:
* requires valid triangular factorization at S on entry
* replaces it by another, valid factorization
* checks that new factorization deviates from the previous one not too much
  by comparing magnitudes of min[abs(u_ii)] in both factorization (sharp
  decrease results in attempt to resize being abandoned

IMPORTANT: if smooth resize is not possible, this function throws an
           exception! It is responsibility of the caller to check that
           smooth resize is possible

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static ae_bool reviseddualsimplex_basistryresize(dualsimplexbasis* s,
     ae_int_t newm,
     const sparsematrix* at,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t ns;
    ae_int_t oldm;
    ae_int_t i;
    double oldminu;
    double newminu;
    ae_bool result;


    ns = s->ns;
    oldm = s->m;
    result = ae_false;
    
    /*
     * Quick exit strategies
     */
    if( newm==0 )
    {
        reviseddualsimplex_basisinit(ns, 0, s, _state);
        result = ae_true;
        return result;
    }
    
    /*
     * Same size or larger
     */
    if( newm>=oldm )
    {
        ae_assert(s->isvalidtrf||oldm==0, "BasisTryResize: needs valid TRF in S", _state);
        
        /*
         * Save information about matrix conditioning 
         */
        oldminu = reviseddualsimplex_basisminimumdiagonalelement(s, _state);
        
        /*
         * Growth if needed
         */
        s->m = newm;
        ivectorgrowto(&s->idx, newm, _state);
        bvectorgrowto(&s->isbasic, ns+newm, _state);
        for(i=oldm; i<=newm-1; i++)
        {
            s->idx.ptr.p_int[i] = ns+i;
            s->isbasic.ptr.p_bool[ns+i] = ae_true;
        }
        
        /*
         * DSE weights are invalid and filled by 1.0
         */
        rvectorgrowto(&s->dseweights, newm, _state);
        for(i=0; i<=newm-1; i++)
        {
            s->dseweights.ptr.p_double[i] = 1.0;
        }
        s->dsevalid = ae_false;
        
        /*
         * Invalidate TRF.
         * Try to refactorize.
         */
        s->isvalidtrf = ae_false;
        newminu = reviseddualsimplex_basisfreshtrfunsafe(s, at, settings, _state);
        result = ae_fp_greater_eq(newminu,reviseddualsimplex_maxudecay*oldminu);
        return result;
    }
    ae_assert(ae_false, "BasisTryResize: unexpected branch", _state);
    return result;
}


/*************************************************************************
This function returns minimum diagonal element of S. Result=1 is  returned
for M=0.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static double reviseddualsimplex_basisminimumdiagonalelement(const dualsimplexbasis* s,
     ae_state *_state)
{
    double v;
    double vv;
    ae_int_t i;
    ae_int_t m;
    double result;


    m = s->m;
    if( m==0 )
    {
        result = (double)(1);
        return result;
    }
    ae_assert(((s->trftype==0||s->trftype==1)||s->trftype==2)||s->trftype==3, "BasisMinimumDiagonalElement: unexpected TRF type", _state);
    ae_assert(s->isvalidtrf, "BasisMinimumDiagonalElement: TRF is invalid", _state);
    v = ae_maxrealnumber;
    for(i=0; i<=m-1; i++)
    {
        vv = (double)(0);
        if( s->trftype==0||s->trftype==1 )
        {
            vv = s->denselu.ptr.pp_double[i][i];
        }
        if( s->trftype==2||s->trftype==3 )
        {
            vv = sparsegetdiagonal(&s->sparseu, i, _state);
        }
        if( vv<(double)0 )
        {
            vv = -vv;
        }
        if( vv<v )
        {
            v = vv;
        }
    }
    result = v;
    return result;
}


/*************************************************************************
This function exports division of variables into basic/nonbasic ones; only
basic/nonbasic sets are exported - triangular factorization is NOT exported;
however, valid triangular factorization IS required in order to perform
exporting.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_basisexportto(const dualsimplexbasis* s0,
     dualsimplexbasis* s1,
     ae_state *_state)
{


    s1->ns = s0->ns;
    s1->m = s0->m;
    copyintegerarray(&s0->idx, &s1->idx, _state);
    copyintegerarray(&s0->nidx, &s1->nidx, _state);
    copybooleanarray(&s0->isbasic, &s1->isbasic, _state);
    s1->isvalidtrf = ae_false;
    s1->trftype = -1;
    s1->dsevalid = ae_false;
    if( s0->m>0 )
    {
        ae_assert(s0->isvalidtrf, "BasisExport: valid factorization is required for source basis", _state);
        s1->eminu = reviseddualsimplex_basisminimumdiagonalelement(s0, _state);
    }
    else
    {
        s1->eminu = (double)(1);
    }
}


/*************************************************************************
This function imports from S1 to S0 a division of variables into
basic/nonbasic ones; only basic/nonbasic sets are imported.

Triangular factorization is not imported; however,  this  function  checks
that new factorization deviates from the previous  one  not  too  much  by
comparing magnitudes of min[abs(u_ii)] in both factorization (basis being
imported stores statistics about U). Sharp decrease of diagonal elements
means that we have too unstable situation which results in import being
abandoned. In this case False is returned, and the basis S0 is left in the
indeterminate invalid state (you have to reinitialize it by all-logicals).

IMPORTANT: if metrics of S0 and S1 do not match, an exception will be generated.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static ae_bool reviseddualsimplex_basistryimportfrom(dualsimplexbasis* s0,
     const dualsimplexbasis* s1,
     const sparsematrix* at,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t i;
    double newminu;
    ae_bool result;


    ae_assert(s0->ns==s1->ns, "BasisImportFrom: structural variable counts do not match", _state);
    reviseddualsimplex_basisclearstats(s0, _state);
    s0->m = s1->m;
    for(i=0; i<=s0->m-1; i++)
    {
        s0->idx.ptr.p_int[i] = s1->idx.ptr.p_int[i];
    }
    for(i=0; i<=s0->ns-1; i++)
    {
        s0->nidx.ptr.p_int[i] = s1->nidx.ptr.p_int[i];
    }
    for(i=0; i<=s0->m+s0->ns-1; i++)
    {
        s0->isbasic.ptr.p_bool[i] = s1->isbasic.ptr.p_bool[i];
    }
    s0->isvalidtrf = ae_false;
    rvectorsetlengthatleast(&s0->dseweights, s1->m, _state);
    for(i=0; i<=s1->m-1; i++)
    {
        s0->dseweights.ptr.p_double[i] = 1.0;
    }
    s0->dsevalid = ae_false;
    newminu = reviseddualsimplex_basisfreshtrfunsafe(s0, at, settings, _state);
    result = ae_fp_greater_eq(newminu,reviseddualsimplex_maxudecay*s1->eminu);
    if( !result )
    {
        s0->isvalidtrf = ae_false;
        s0->trftype = -1;
    }
    return result;
}


/*************************************************************************
This function computes fresh triangular factorization.

If TRF of age 0 (fresh) is already present, no new factorization is calculated.
If factorization has exactly zero element along diagonal, this function
generates exception.


  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_basisfreshtrf(dualsimplexbasis* s,
     const sparsematrix* at,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    double v;


    v = reviseddualsimplex_basisfreshtrfunsafe(s, at, settings, _state);
    ae_assert(ae_fp_greater(v,(double)(0)), "BasisFreshTrf: degeneracy of B is detected", _state);
}


/*************************************************************************
This function computes fresh triangular factorization.

If TRF of age 0 (fresh) is already present, no new factorization is calculated.

It returns min[abs(u[i,i])] which can be used to determine whether factorization
is degenerate or not (it will factorize anything, the question is whether
it is possible to use factorization)

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static double reviseddualsimplex_basisfreshtrfunsafe(dualsimplexbasis* s,
     const sparsematrix* at,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t ns;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t k1;
    ae_int_t nzl;
    ae_int_t nzu;
    ae_int_t nlogical;
    ae_int_t nstructural;
    ae_int_t offs;
    ae_int_t offs1;
    ae_int_t offs2;
    double result;


    m = s->m;
    ns = s->ns;
    result = (double)(0);
    
    /*
     * Compare TRF type with one required by settings, invalidation and refresh otherwise
     */
    if( s->trftype!=settings->trftype )
    {
        s->trftype = settings->trftype;
        s->isvalidtrf = ae_false;
        result = reviseddualsimplex_basisfreshtrfunsafe(s, at, settings, _state);
        return result;
    }
    
    /*
     * Is it valid and fresh?
     */
    if( s->isvalidtrf&&s->trfage==0 )
    {
        result = reviseddualsimplex_basisminimumdiagonalelement(s, _state);
        return result;
    }
    
    /*
     * Dense TRF
     */
    if( s->trftype==0||s->trftype==1 )
    {
        ivectorsetlengthatleast(&s->colpermbwd, m, _state);
        for(i=0; i<=m-1; i++)
        {
            s->colpermbwd.ptr.p_int[i] = i;
        }
        rmatrixsetlengthatleast(&s->denselu, m, m, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                s->denselu.ptr.pp_double[i][j] = (double)(0);
            }
        }
        for(i=0; i<=m-1; i++)
        {
            j0 = at->ridx.ptr.p_int[s->idx.ptr.p_int[i]];
            j1 = at->ridx.ptr.p_int[s->idx.ptr.p_int[i]+1]-1;
            for(j=j0; j<=j1; j++)
            {
                s->denselu.ptr.pp_double[i][at->idx.ptr.p_int[j]] = at->vals.ptr.p_double[j];
            }
        }
        rmatrixlu(&s->denselu, m, m, &s->tmpi, _state);
        reviseddualsimplex_pivottobwd(&s->tmpi, m, &s->rowpermbwd, _state);
        s->isvalidtrf = ae_true;
        s->trfage = 0;
        s->statfact = s->statfact+1;
        s->statoffdiag = s->statoffdiag+ae_sqr((double)(m-1), _state);
        result = reviseddualsimplex_basisminimumdiagonalelement(s, _state);
        return result;
    }
    
    /*
     * Sparse TRF (with either PFI or Forest-Tomlin)
     */
    if( s->trftype==2||s->trftype==3 )
    {
        
        /*
         * Determine permutation which moves logical variables
         * to the beginning.
         *
         * NOTE: this reordering results in stable factorization
         *       because we prenormalized constraints with 2-norm,
         *       all elements in the logical columns are less than
         *       1.0 in magnitude.
         *
         * After this block is done we have following arrays:
         * * tCInvIdx[j], which is an inverse of ColPermBwf[]
         */
        ivectorsetlengthatleast(&s->tcinvidx, m, _state);
        ivectorsetlengthatleast(&s->rowpermbwd, m, _state);
        ivectorsetlengthatleast(&s->colpermbwd, m, _state);
        for(i=0; i<=m-1; i++)
        {
            s->tcinvidx.ptr.p_int[i] = i;
            s->rowpermbwd.ptr.p_int[i] = i;
            s->colpermbwd.ptr.p_int[i] = i;
        }
        nlogical = 0;
        for(i=0; i<=m-1; i++)
        {
            if( s->idx.ptr.p_int[i]>=ns )
            {
                j = s->rowpermbwd.ptr.p_int[nlogical];
                s->rowpermbwd.ptr.p_int[nlogical] = s->rowpermbwd.ptr.p_int[i];
                s->rowpermbwd.ptr.p_int[i] = j;
                j1 = s->tcinvidx.ptr.p_int[s->idx.ptr.p_int[i]-ns];
                j = s->colpermbwd.ptr.p_int[j1];
                s->colpermbwd.ptr.p_int[j1] = s->colpermbwd.ptr.p_int[nlogical];
                s->colpermbwd.ptr.p_int[nlogical] = j;
                s->tcinvidx.ptr.p_int[s->colpermbwd.ptr.p_int[nlogical]] = nlogical;
                s->tcinvidx.ptr.p_int[s->colpermbwd.ptr.p_int[j1]] = j1;
                nlogical = nlogical+1;
            }
        }
        sortmiddlei(&s->colpermbwd, nlogical, m-nlogical, _state);
        for(i=0; i<=m-1; i++)
        {
            s->tcinvidx.ptr.p_int[s->colpermbwd.ptr.p_int[i]] = i;
        }
        nstructural = m-nlogical;
        
        /*
         * Prepare SparseLU1 to receive factored out logical part of the matrix
         * and SparseLU2 to receive structural part of the matrix.
         */
        ivectorsetlengthatleast(&s->sparselu1.ridx, nstructural+1, _state);
        ivectorsetlengthatleast(&s->sparselu1.didx, nstructural, _state);
        ivectorsetlengthatleast(&s->sparselu1.uidx, nstructural, _state);
        s->sparselu1.matrixtype = 1;
        s->sparselu1.m = nstructural;
        s->sparselu1.n = nlogical;
        s->sparselu1.ridx.ptr.p_int[0] = 0;
        ivectorsetlengthatleast(&s->sparselu2.ridx, nstructural+1, _state);
        ivectorsetlengthatleast(&s->sparselu2.didx, nstructural, _state);
        ivectorsetlengthatleast(&s->sparselu2.uidx, nstructural, _state);
        s->sparselu2.matrixtype = 1;
        s->sparselu2.m = nstructural;
        s->sparselu2.n = nstructural;
        s->sparselu2.ridx.ptr.p_int[0] = 0;
        
        /*
         * Reorder array, perform LU factorization
         */
        for(k=0; k<=nstructural-1; k++)
        {
            
            /*
             * Make sure SparseLU1 and SparseLU2 have enough place.
             */
            offs1 = s->sparselu1.ridx.ptr.p_int[k];
            offs2 = s->sparselu2.ridx.ptr.p_int[k];
            ivectorgrowto(&s->sparselu1.idx, offs1+m, _state);
            rvectorgrowto(&s->sparselu1.vals, offs1+m, _state);
            ivectorgrowto(&s->sparselu2.idx, offs2+m, _state);
            rvectorgrowto(&s->sparselu2.vals, offs2+m, _state);
            
            /*
             * Extract K-th row of the SparseLU1/2 (I-th row of the original matrix)
             */
            i = s->rowpermbwd.ptr.p_int[k+nlogical];
            j0 = at->ridx.ptr.p_int[s->idx.ptr.p_int[i]];
            j1 = at->ridx.ptr.p_int[s->idx.ptr.p_int[i]+1]-1;
            for(j=j0; j<=j1; j++)
            {
                k1 = s->tcinvidx.ptr.p_int[at->idx.ptr.p_int[j]];
                if( k1<nlogical )
                {
                    
                    /*
                     * Append element to SparseLU1
                     */
                    s->sparselu1.idx.ptr.p_int[offs1] = k1;
                    s->sparselu1.vals.ptr.p_double[offs1] = at->vals.ptr.p_double[j];
                    offs1 = offs1+1;
                }
                else
                {
                    
                    /*
                     * Append element to SparseLU2
                     */
                    s->sparselu2.idx.ptr.p_int[offs2] = k1-nlogical;
                    s->sparselu2.vals.ptr.p_double[offs2] = at->vals.ptr.p_double[j];
                    offs2 = offs2+1;
                }
            }
            
            /*
             * Elements added to the last row of LU1 can be unordered,
             * so it needs resorting.
             *
             * LU2 does NOT need resorting because trailing NStructural
             * elements of permutation were post-sorted to produce
             * already sorted results.
             */
            tagsortmiddleir(&s->sparselu1.idx, &s->sparselu1.vals, s->sparselu1.ridx.ptr.p_int[k], offs1-s->sparselu1.ridx.ptr.p_int[k], _state);
            s->sparselu1.ridx.ptr.p_int[k+1] = offs1;
            s->sparselu2.ridx.ptr.p_int[k+1] = offs2;
        }
        s->sparselu1.ninitialized = s->sparselu1.ridx.ptr.p_int[nstructural];
        s->sparselu2.ninitialized = s->sparselu2.ridx.ptr.p_int[nstructural];
        sparseinitduidx(&s->sparselu1, _state);
        sparseinitduidx(&s->sparselu2, _state);
        if( nstructural>0 )
        {
            sptrflu(&s->sparselu2, 2, &s->densep2, &s->densep2c, &s->lubuf2, _state);
            for(i=0; i<=nstructural-1; i++)
            {
                j = s->rowpermbwd.ptr.p_int[i+nlogical];
                s->rowpermbwd.ptr.p_int[i+nlogical] = s->rowpermbwd.ptr.p_int[s->densep2.ptr.p_int[i]+nlogical];
                s->rowpermbwd.ptr.p_int[s->densep2.ptr.p_int[i]+nlogical] = j;
                j = s->colpermbwd.ptr.p_int[i+nlogical];
                s->colpermbwd.ptr.p_int[i+nlogical] = s->colpermbwd.ptr.p_int[s->densep2c.ptr.p_int[i]+nlogical];
                s->colpermbwd.ptr.p_int[s->densep2c.ptr.p_int[i]+nlogical] = j;
            }
            
            /*
             * Process L factor:
             *
             * 1. count number of non-zeros in the L factor,
             * 2. fill NLogical*NLogical leading block
             * 3. NStructural*M bottom block
             */
            nzl = nlogical;
            for(i=0; i<=nstructural-1; i++)
            {
                k = s->lubuf2.rowpermrawidx.ptr.p_int[i];
                nzl = nzl+(s->sparselu1.ridx.ptr.p_int[k+1]-s->sparselu1.ridx.ptr.p_int[k]);
                nzl = nzl+1+(s->sparselu2.didx.ptr.p_int[i]-s->sparselu2.ridx.ptr.p_int[i]);
            }
            rvectorsetlengthatleast(&s->sparsel.vals, nzl, _state);
            ivectorsetlengthatleast(&s->sparsel.idx, nzl, _state);
            ivectorsetlengthatleast(&s->sparsel.ridx, m+1, _state);
            ivectorsetlengthatleast(&s->sparsel.didx, m, _state);
            ivectorsetlengthatleast(&s->sparsel.uidx, m, _state);
            s->sparsel.matrixtype = 1;
            s->sparsel.m = m;
            s->sparsel.n = m;
            s->sparsel.ninitialized = nzl;
            s->sparsel.ridx.ptr.p_int[0] = 0;
            for(i=0; i<=nlogical-1; i++)
            {
                s->sparsel.idx.ptr.p_int[i] = i;
                s->sparsel.vals.ptr.p_double[i] = 1.0;
                s->sparsel.ridx.ptr.p_int[i+1] = i+1;
            }
            for(i=0; i<=nstructural-1; i++)
            {
                offs = s->sparsel.ridx.ptr.p_int[nlogical+i];
                k = s->lubuf2.rowpermrawidx.ptr.p_int[i];
                j0 = s->sparselu1.ridx.ptr.p_int[k];
                j1 = s->sparselu1.ridx.ptr.p_int[k+1]-1;
                for(j=j0; j<=j1; j++)
                {
                    s->sparsel.idx.ptr.p_int[offs] = s->sparselu1.idx.ptr.p_int[j];
                    s->sparsel.vals.ptr.p_double[offs] = -s->sparselu1.vals.ptr.p_double[j];
                    offs = offs+1;
                }
                j0 = s->sparselu2.ridx.ptr.p_int[i];
                j1 = s->sparselu2.didx.ptr.p_int[i]-1;
                for(j=j0; j<=j1; j++)
                {
                    s->sparsel.idx.ptr.p_int[offs] = nlogical+s->sparselu2.idx.ptr.p_int[j];
                    s->sparsel.vals.ptr.p_double[offs] = s->sparselu2.vals.ptr.p_double[j];
                    offs = offs+1;
                }
                s->sparsel.idx.ptr.p_int[offs] = nlogical+i;
                s->sparsel.vals.ptr.p_double[offs] = 1.0;
                offs = offs+1;
                s->sparsel.ridx.ptr.p_int[nlogical+i+1] = offs;
            }
            ae_assert(s->sparsel.ninitialized==s->sparsel.ridx.ptr.p_int[m], "BasisFreshTrf: integrity check failed", _state);
            sparseinitduidx(&s->sparsel, _state);
            
            /*
             * Process U factor:
             *
             * 1. count number of non-zeros in the U factor,
             * 2. fill NLogical*NLogical leading block
             * 3. NStructural*NStructural bottom block
             */
            nzu = nlogical;
            for(i=0; i<=nstructural-1; i++)
            {
                nzu = nzu+1+(s->sparselu2.ridx.ptr.p_int[i+1]-s->sparselu2.uidx.ptr.p_int[i]);
            }
            rvectorsetlengthatleast(&s->sparseu.vals, nzu, _state);
            ivectorsetlengthatleast(&s->sparseu.idx, nzu, _state);
            ivectorsetlengthatleast(&s->sparseu.ridx, m+1, _state);
            ivectorsetlengthatleast(&s->sparseu.didx, m, _state);
            ivectorsetlengthatleast(&s->sparseu.uidx, m, _state);
            s->sparseu.matrixtype = 1;
            s->sparseu.m = m;
            s->sparseu.n = m;
            s->sparseu.ninitialized = nzu;
            s->sparseu.ridx.ptr.p_int[0] = 0;
            for(i=0; i<=nlogical-1; i++)
            {
                s->sparseu.idx.ptr.p_int[i] = i;
                s->sparseu.vals.ptr.p_double[i] = -1.0;
                s->sparseu.ridx.ptr.p_int[i+1] = i+1;
            }
            for(i=0; i<=nstructural-1; i++)
            {
                offs = s->sparseu.ridx.ptr.p_int[nlogical+i];
                s->sparseu.idx.ptr.p_int[offs] = nlogical+i;
                j = s->sparselu2.didx.ptr.p_int[i];
                if( j<s->sparselu2.uidx.ptr.p_int[i] )
                {
                    ae_assert(s->sparselu2.idx.ptr.p_int[j]==i, "BasisFreshTrf: integrity check failed", _state);
                    s->sparseu.vals.ptr.p_double[offs] = s->sparselu2.vals.ptr.p_double[j];
                }
                else
                {
                    s->sparseu.vals.ptr.p_double[offs] = (double)(0);
                }
                offs = offs+1;
                j0 = s->sparselu2.uidx.ptr.p_int[i];
                j1 = s->sparselu2.ridx.ptr.p_int[i+1]-1;
                for(j=j0; j<=j1; j++)
                {
                    s->sparseu.idx.ptr.p_int[offs] = nlogical+s->sparselu2.idx.ptr.p_int[j];
                    s->sparseu.vals.ptr.p_double[offs] = s->sparselu2.vals.ptr.p_double[j];
                    offs = offs+1;
                }
                s->sparseu.ridx.ptr.p_int[nlogical+i+1] = offs;
            }
            ae_assert(s->sparseu.ninitialized==s->sparseu.ridx.ptr.p_int[m], "BasisFreshTrf: integrity check failed", _state);
            sparseinitduidx(&s->sparseu, _state);
        }
        else
        {
            ivectorsetlengthatleast(&s->nrs, m, _state);
            for(i=0; i<=m-1; i++)
            {
                s->nrs.ptr.p_int[i] = 1;
            }
            sparsecreatecrsbuf(m, m, &s->nrs, &s->sparsel, _state);
            for(i=0; i<=nlogical-1; i++)
            {
                sparseset(&s->sparsel, i, i, 1.0, _state);
            }
            sparsecreatecrsbuf(m, m, &s->nrs, &s->sparseu, _state);
            for(i=0; i<=nlogical-1; i++)
            {
                sparseset(&s->sparseu, i, i, -1.0, _state);
            }
        }
        sparsecopytransposecrsbuf(&s->sparseu, &s->sparseut, _state);
        s->isvalidtrf = ae_true;
        s->trfage = 0;
        s->statfact = s->statfact+1;
        s->statoffdiag = s->statoffdiag+(double)(s->sparsel.ridx.ptr.p_int[m]-m)+(double)(s->sparseu.ridx.ptr.p_int[m]-m);
        result = reviseddualsimplex_basisminimumdiagonalelement(s, _state);
        return result;
    }
    
    /*
     *
     */
    ae_assert(ae_false, "BasisFreshTrf: unexpected TRF type", _state);
    return result;
}


/*************************************************************************
This function fills S.DSEWeights by actual weights according to current
settings and sets validity flag.

Basis object MUST store valid triangular factorization, otherwise this
function throws an exception.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_basisrequestweights(dualsimplexbasis* s,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t ns;
    ae_int_t i;
    ae_int_t j;
    double v;
    double vv;


    m = s->m;
    ns = s->ns;
    ae_assert((settings->pricing==-1||settings->pricing==0)||settings->pricing==1, "BasisRequestWeights: unknown pricing type", _state);
    ae_assert(s->isvalidtrf, "BasisRequestWeights: factorization is not computed prior to calling this function", _state);
    
    /*
     * If weights are valid, return immediately
     */
    if( s->dsevalid )
    {
        return;
    }
    
    /*
     * Compute weights from scratch
     */
    if( settings->pricing==-1||settings->pricing==1 )
    {
        for(i=0; i<=m-1; i++)
        {
            if( s->idx.ptr.p_int[i]<ns )
            {
                
                /*
                 * Structural variable, DSE weight is computed by definition
                 */
                rvectorsetlengthatleast(&s->wtmp0, m, _state);
                rvectorsetlengthatleast(&s->wtmp1, m, _state);
                for(j=0; j<=m-1; j++)
                {
                    s->wtmp0.ptr.p_double[j] = (double)(0);
                }
                s->wtmp0.ptr.p_double[i] = (double)(1);
                reviseddualsimplex_basissolvet(s, &s->wtmp0, &s->wtmp1, &s->wtmp2, _state);
                v = (double)(0);
                for(j=0; j<=m-1; j++)
                {
                    vv = s->wtmp1.ptr.p_double[j];
                    v = v+vv*vv;
                }
                s->dseweights.ptr.p_double[i] = v;
            }
            else
            {
                
                /*
                 * Logical variable, weight can be set to 1.0
                 */
                s->dseweights.ptr.p_double[i] = 1.0;
            }
        }
        s->dsevalid = ae_true;
        return;
    }
    
    /*
     * Compute weights from scratch
     */
    if( settings->pricing==0 )
    {
        for(i=0; i<=m-1; i++)
        {
            s->dseweights.ptr.p_double[i] = 1.0;
        }
        s->dsevalid = ae_true;
        return;
    }
    ae_assert(ae_false, "BasisRequestWeights: unexpected pricing type", _state);
}


/*************************************************************************
This function updates triangular factorization by adding Q  to  basis  and
removing P from basis. It also updates index tables IsBasic[], BasicIdx[],
Basis.NIdx[].

AlphaQim contains intermediate result from Ftran for AlphaQ, it is used
by Forest-Tomlin update scheme. If other update is used, it is not referenced
at all.

X[], D[], Z are NOT recomputed.

Tau is used if Settings.Pricing=1, ignored otherwise.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_basisupdatetrf(dualsimplexbasis* s,
     const sparsematrix* at,
     ae_int_t p,
     ae_int_t q,
     /* Real    */ const ae_vector* alphaq,
     /* Real    */ const ae_vector* alphaqim,
     ae_int_t r,
     /* Real    */ const ae_vector* tau,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t nn;
    ae_int_t i;
    ae_int_t j;
    ae_bool processed;
    double invaq;
    ae_int_t dstoffs;
    ae_int_t srcoffs;
    ae_int_t srcidx;
    double srcval;
    double vcorner;
    ae_int_t idxd;
    double v;


    m = s->m;
    nn = s->ns;
    
    /*
     * Update index tables
     *
     * TODO: better code!!!!!!!!!!!!!!!!!!!!!!!
     */
    s->isbasic.ptr.p_bool[p] = ae_false;
    s->isbasic.ptr.p_bool[q] = ae_true;
    for(i=0; i<=m-1; i++)
    {
        if( s->idx.ptr.p_int[i]==p )
        {
            s->idx.ptr.p_int[i] = q;
            break;
        }
    }
    for(i=0; i<=nn-1; i++)
    {
        if( s->nidx.ptr.p_int[i]==q )
        {
            s->nidx.ptr.p_int[i] = p;
            break;
        }
    }
    
    /*
     * Update dense factorization
     */
    if( ((s->trftype!=settings->trftype||s->trftype==0)||!s->isvalidtrf)||s->trfage>=settings->maxtrfage )
    {
        
        /*
         * Complete refresh is needed for factorization
         */
        s->isvalidtrf = ae_false;
        reviseddualsimplex_basisfreshtrf(s, at, settings, _state);
    }
    else
    {
        processed = ae_false;
        if( (s->trftype==0||s->trftype==1)||s->trftype==2 )
        {
            
            /*
             * Dense/sparse factorizations with dense PFI
             */
            ae_assert(ae_fp_neq(alphaq->ptr.p_double[r],(double)(0)), "BasisUpdateTrf: integrity check failed, AlphaQ[R]=0", _state);
            rvectorgrowto(&s->densepfieta, (s->trfage+1)*m, _state);
            ivectorgrowto(&s->rk, s->trfage+1, _state);
            s->rk.ptr.p_int[s->trfage] = r;
            invaq = 1.0/alphaq->ptr.p_double[r];
            for(i=0; i<=m-1; i++)
            {
                if( i!=r )
                {
                    s->densepfieta.ptr.p_double[s->trfage*m+i] = -alphaq->ptr.p_double[i]*invaq;
                }
                else
                {
                    s->densepfieta.ptr.p_double[s->trfage*m+i] = invaq;
                }
            }
            inc(&s->trfage, _state);
            s->statupdt = s->statupdt+1;
            s->statoffdiag = s->statoffdiag+ae_sqr((double)(m-1), _state);
            processed = ae_true;
        }
        if( s->trftype==3 )
        {
            
            /*
             * Sparse factorization with Forest-Tomlin update
             */
            ae_assert(ae_fp_neq(alphaq->ptr.p_double[r],(double)(0)), "BasisUpdateTrf: integrity check failed, AlphaQ[R]=0", _state);
            rvectorgrowto(&s->densemu, (s->trfage+1)*m, _state);
            ivectorgrowto(&s->rk, s->trfage+1, _state);
            ivectorgrowto(&s->dk, s->trfage+1, _state);
            rvectorsetlengthatleast(&s->utmp0, m, _state);
            
            /*
             * Determine D - index of row being overwritten by Forest-Tomlin update
             */
            idxd = -1;
            for(i=0; i<=m-1; i++)
            {
                if( s->rowpermbwd.ptr.p_int[i]==r )
                {
                    idxd = i;
                    break;
                }
            }
            ae_assert(idxd>=0, "BasisUpdateTrf: unexpected integrity check failure", _state);
            s->rk.ptr.p_int[s->trfage] = r;
            s->dk.ptr.p_int[s->trfage] = idxd;
            
            /*
             * Modify L with permutation which moves D-th row/column to the end:
             * * rows 0...D-1 are left intact
             * * rows D+1...M-1 are moved one position up, with columns 0..D-1
             *   retained as is, and columns D+1...M-1 being moved one position left.
             * * last row is filled by permutation/modification of AlphaQim
             * Determine FT update coefficients in the process.
             */
            ivectorgrowto(&s->sparsel.idx, s->sparsel.ridx.ptr.p_int[m]+m, _state);
            rvectorgrowto(&s->sparsel.vals, s->sparsel.ridx.ptr.p_int[m]+m, _state);
            for(i=0; i<=m-1; i++)
            {
                s->utmp0.ptr.p_double[i] = (double)(0);
            }
            for(i=idxd+1; i<=m-1; i++)
            {
                j = s->sparsel.ridx.ptr.p_int[i+1]-1;
                if( s->sparsel.idx.ptr.p_int[j]!=i||s->sparsel.vals.ptr.p_double[j]!=(double)1 )
                {
                    ae_assert(ae_false, "UpdateTrf: integrity check failed for sparse L", _state);
                }
                dstoffs = s->sparsel.ridx.ptr.p_int[i-1];
                srcoffs = s->sparsel.ridx.ptr.p_int[i];
                
                /*
                 * Read first element in the row (it has at least one - unit diagonal)
                 */
                srcidx = s->sparsel.idx.ptr.p_int[srcoffs];
                srcval = s->sparsel.vals.ptr.p_double[srcoffs];
                
                /*
                 * Read/write columns 0...D-1
                 */
                while(srcidx<idxd)
                {
                    s->sparsel.idx.ptr.p_int[dstoffs] = srcidx;
                    s->sparsel.vals.ptr.p_double[dstoffs] = srcval;
                    dstoffs = dstoffs+1;
                    srcoffs = srcoffs+1;
                    srcidx = s->sparsel.idx.ptr.p_int[srcoffs];
                    srcval = s->sparsel.vals.ptr.p_double[srcoffs];
                }
                
                /*
                 * If we have non-zero element in column D, use it as
                 * right-hand side of intermediate linear system which
                 * is used to determine coefficients of update matrix.
                 */
                if( srcidx==idxd )
                {
                    s->utmp0.ptr.p_double[i-1] = srcval;
                    srcoffs = srcoffs+1;
                    srcidx = s->sparsel.idx.ptr.p_int[srcoffs];
                    srcval = s->sparsel.vals.ptr.p_double[srcoffs];
                }
                
                /*
                 * Process columns D+1...I-1
                 */
                v = s->utmp0.ptr.p_double[i-1];
                while(srcidx<i)
                {
                    s->sparsel.idx.ptr.p_int[dstoffs] = srcidx-1;
                    s->sparsel.vals.ptr.p_double[dstoffs] = srcval;
                    v = v-srcval*s->utmp0.ptr.p_double[srcidx-1];
                    dstoffs = dstoffs+1;
                    srcoffs = srcoffs+1;
                    srcidx = s->sparsel.idx.ptr.p_int[srcoffs];
                    srcval = s->sparsel.vals.ptr.p_double[srcoffs];
                }
                s->utmp0.ptr.p_double[i-1] = v;
                
                /*
                 * Write out unit diagonal, finalize row
                 */
                s->sparsel.idx.ptr.p_int[dstoffs] = i-1;
                s->sparsel.vals.ptr.p_double[dstoffs] = (double)(1);
                dstoffs = dstoffs+1;
                s->sparsel.ridx.ptr.p_int[i] = dstoffs;
            }
            s->utmp0.ptr.p_double[m-1] = (double)(1);
            dstoffs = s->sparsel.ridx.ptr.p_int[m-1];
            for(j=0; j<=idxd-1; j++)
            {
                v = alphaqim->ptr.p_double[j];
                if( v!=(double)0 )
                {
                    s->sparsel.idx.ptr.p_int[dstoffs] = j;
                    s->sparsel.vals.ptr.p_double[dstoffs] = v;
                    dstoffs = dstoffs+1;
                }
            }
            vcorner = alphaqim->ptr.p_double[idxd];
            for(j=idxd+1; j<=m-1; j++)
            {
                v = alphaqim->ptr.p_double[j];
                if( v!=(double)0 )
                {
                    s->sparsel.idx.ptr.p_int[dstoffs] = j-1;
                    s->sparsel.vals.ptr.p_double[dstoffs] = v;
                    dstoffs = dstoffs+1;
                    vcorner = vcorner-v*s->utmp0.ptr.p_double[j-1];
                }
            }
            s->sparsel.idx.ptr.p_int[dstoffs] = m-1;
            s->sparsel.vals.ptr.p_double[dstoffs] = (double)(1);
            dstoffs = dstoffs+1;
            s->sparsel.ridx.ptr.p_int[m] = dstoffs;
            s->sparsel.ninitialized = s->sparsel.ridx.ptr.p_int[m];
            for(i=0; i<=m-1; i++)
            {
                j = s->sparsel.ridx.ptr.p_int[i+1];
                s->sparsel.didx.ptr.p_int[i] = j-1;
                s->sparsel.uidx.ptr.p_int[i] = j;
            }
            ae_assert(vcorner!=(double)0, "UpdateTrf: corner element is zero, degeneracy detected", _state);
            v = (double)1/vcorner;
            for(i=0; i<=m-2; i++)
            {
                s->densemu.ptr.p_double[s->trfage*m+i] = -s->utmp0.ptr.p_double[i]*v;
            }
            s->densemu.ptr.p_double[s->trfage*m+m-1] = v;
            
            /*
             * Multiply row permutation matrix by cyclic permutation applied to L
             */
            reviseddualsimplex_inversecyclicpermutation(&s->rowpermbwd, m, idxd, &s->utmpi, _state);
            
            /*
             * Done
             */
            inc(&s->trfage, _state);
            s->statupdt = s->statupdt+1;
            s->statoffdiag = s->statoffdiag+(double)(s->sparsel.ridx.ptr.p_int[m]-m)+(double)(s->sparseu.ridx.ptr.p_int[m]-m);
            processed = ae_true;
        }
        ae_assert(processed, "BasisUpdateTrf: unexpected TRF type", _state);
    }
    
    /*
     * Update pricing weights
     */
    ae_assert((settings->pricing==-1||settings->pricing==0)||settings->pricing==1, "BasisUpdateTrf: unexpected Settings.Pricing", _state);
    processed = ae_false;
    if( settings->pricing==-1 )
    {
        
        /*
         * Weights are recomputed from scratch at every step.
         * VERY, VERY time consuming, used only for debug purposes.
         */
        s->dsevalid = ae_false;
        reviseddualsimplex_basisrequestweights(s, settings, _state);
        processed = ae_true;
    }
    if( settings->pricing==0 )
    {
        
        /*
         * Weights are filled by 1.0
         */
        if( !s->dsevalid )
        {
            for(i=0; i<=m-1; i++)
            {
                s->dseweights.ptr.p_double[i] = 1.0;
            }
            s->dsevalid = ae_true;
        }
        processed = ae_true;
    }
    if( settings->pricing==1 )
    {
        
        /*
         * Weights are computed using DSE update formula.
         */
        if( s->dsevalid )
        {
            
            /*
             * Compute using update formula
             */
            for(i=0; i<=m-1; i++)
            {
                if( i!=r )
                {
                    s->dseweights.ptr.p_double[i] = s->dseweights.ptr.p_double[i]-(double)2*(alphaq->ptr.p_double[i]/alphaq->ptr.p_double[r])*tau->ptr.p_double[i]+s->dseweights.ptr.p_double[r]*ae_sqr(alphaq->ptr.p_double[i]/alphaq->ptr.p_double[r], _state);
                    s->dseweights.ptr.p_double[i] = ae_maxreal(s->dseweights.ptr.p_double[i], reviseddualsimplex_minbeta, _state);
                }
            }
            s->dseweights.ptr.p_double[r] = s->dseweights.ptr.p_double[r]/(alphaq->ptr.p_double[r]*alphaq->ptr.p_double[r]);
        }
        else
        {
            
            /*
             * No prior values, compute from scratch (usually it is done only once)
             */
            reviseddualsimplex_basisrequestweights(s, settings, _state);
        }
        processed = ae_true;
    }
    ae_assert(processed, "BasisUpdateTrf: unexpected pricing type", _state);
}


/*************************************************************************
This function computes solution to B*x=r.

Output array is reallocated if needed. Temporary array TmpX[] is used  and
reallocated if necessary.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_basissolve(const dualsimplexbasis* s,
     /* Real    */ const ae_vector* r,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* tmpx,
     ae_state *_state)
{


    reviseddualsimplex_basissolvex(s, r, x, x, ae_false, tmpx, _state);
}


/*************************************************************************
This function computes solution to B*x=r. It  also   additionally  outputs
intermediate  result  of multiplication by inv(DS)*inv(U)*inv(colPerm),  a
value essential for Forest-Tomlin update.

Output arrays are reallocated if needed. Temporary  array  TX[]   can   be
used/reallocated.

If NeedIntermediate is False or Forest-Tomlin updates are not used,
then Xim[] is not referenced at all.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_basissolvex(const dualsimplexbasis* s,
     /* Real    */ const ae_vector* r,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* xim,
     ae_bool needintermediate,
     /* Real    */ ae_vector* tx,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t i;
    ae_int_t d;
    ae_int_t k;
    double v;
    double vd;
    double vv;
    ae_bool processed;


    ae_assert(s->isvalidtrf, "BasisSolve: integrity check failed", _state);
    m = s->m;
    processed = ae_false;
    rvectorsetlengthatleast(tx, m, _state);
    
    /*
     * Dense/sparse factorizations with dense PFI
     *
     * NOTE: although we solve B*x=r, internally we store factorization of B^T
     */
    if( (s->trftype==0||s->trftype==1)||s->trftype==2 )
    {
        ae_assert(s->trfage==0||s->trftype!=0, "BasisSolve: integrity check failed TrfAge vs TrfType", _state);
        rvectorsetlengthatleast(x, m, _state);
        for(i=0; i<=m-1; i++)
        {
            x->ptr.p_double[i] = r->ptr.p_double[s->colpermbwd.ptr.p_int[i]];
        }
        if( s->trftype==0||s->trftype==1 )
        {
            
            /*
             * Dense TRF
             */
            rmatrixtrsv(m, &s->denselu, 0, 0, ae_true, ae_false, 1, x, 0, _state);
            rmatrixtrsv(m, &s->denselu, 0, 0, ae_false, ae_true, 1, x, 0, _state);
        }
        else
        {
            
            /*
             * Sparse TRF
             */
            sparsetrsv(&s->sparseu, ae_true, ae_false, 1, x, _state);
            sparsetrsv(&s->sparsel, ae_false, ae_false, 1, x, _state);
        }
        for(i=0; i<=m-1; i++)
        {
            tx->ptr.p_double[s->rowpermbwd.ptr.p_int[i]] = x->ptr.p_double[i];
        }
        for(i=0; i<=m-1; i++)
        {
            x->ptr.p_double[i] = tx->ptr.p_double[i];
        }
        for(k=0; k<=s->trfage-1; k++)
        {
            v = x->ptr.p_double[s->rk.ptr.p_int[k]];
            for(i=0; i<=m-1; i++)
            {
                x->ptr.p_double[i] = x->ptr.p_double[i]+s->densepfieta.ptr.p_double[k*m+i]*v;
            }
            x->ptr.p_double[s->rk.ptr.p_int[k]] = x->ptr.p_double[s->rk.ptr.p_int[k]]-v;
        }
        processed = ae_true;
    }
    
    /*
     * Sparse factorization with Forest-Tomlin update
     *
     * NOTE: although we solve B*x=r, internally we store factorization of B^T
     */
    if( s->trftype==3 )
    {
        rvectorsetlengthatleast(x, m, _state);
        for(i=0; i<=m-1; i++)
        {
            x->ptr.p_double[i] = r->ptr.p_double[s->colpermbwd.ptr.p_int[i]];
        }
        sparsetrsv(&s->sparseu, ae_true, ae_false, 1, x, _state);
        for(k=0; k<=s->trfage-1; k++)
        {
            
            /*
             * The code below is an amalgamation of two parts:
             *
             * cyclic permutation
             * V:=X[D];
             * for I:=D to M-2 do
             *     X[I]:=X[I+1];
             * X[M-1]:=V;
             * 
             * and triangular factor
             * V:=0;
             * for I:=D to M-1 do
             *     V:=V+X[I]*S.DenseMu[K*M+I];
             * X[M-1]:=V;
             */
            d = s->dk.ptr.p_int[k];
            vv = (double)(0);
            vd = x->ptr.p_double[d];
            for(i=d; i<=m-2; i++)
            {
                v = x->ptr.p_double[i+1];
                x->ptr.p_double[i] = v;
                vv = vv+v*s->densemu.ptr.p_double[k*m+i];
            }
            x->ptr.p_double[m-1] = vv+vd*s->densemu.ptr.p_double[k*m+m-1];
        }
        if( needintermediate )
        {
            rvectorsetlengthatleast(xim, m, _state);
            for(i=0; i<=m-1; i++)
            {
                xim->ptr.p_double[i] = x->ptr.p_double[i];
            }
        }
        sparsetrsv(&s->sparsel, ae_false, ae_false, 1, x, _state);
        for(i=0; i<=m-1; i++)
        {
            tx->ptr.p_double[s->rowpermbwd.ptr.p_int[i]] = x->ptr.p_double[i];
        }
        for(i=0; i<=m-1; i++)
        {
            x->ptr.p_double[i] = tx->ptr.p_double[i];
        }
        processed = ae_true;
    }
    
    /*
     * Integrity check
     */
    ae_assert(processed, "BasisSolve: unsupported TRF type", _state);
    v = (double)(0);
    for(i=0; i<=m-1; i++)
    {
        v = v+x->ptr.p_double[i];
    }
    ae_assert(ae_isfinite(v, _state), "BasisSolve: integrity check failed (degeneracy in B?)", _state);
}


/*************************************************************************
This function computes solution to (B^T)*x=r.

Output array is reallocated if needed. TX[] temporary is reallocated if
needed

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_basissolvet(const dualsimplexbasis* s,
     /* Real    */ const ae_vector* r,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* tx,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t i;
    ae_int_t d;
    ae_int_t k;
    double v;
    double vm;
    ae_bool processed;


    ae_assert(s->isvalidtrf, "BasisSolveT: integrity check failed", _state);
    m = s->m;
    processed = ae_false;
    rvectorsetlengthatleast(tx, m, _state);
    
    /*
     * Dense factorizations
     */
    if( (s->trftype==0||s->trftype==1)||s->trftype==2 )
    {
        ae_assert(s->trfage==0||s->trftype!=0, "BasisSolveT: integrity check failed TrfAge vs TrfType", _state);
        rvectorsetlengthatleast(x, m, _state);
        for(i=0; i<=m-1; i++)
        {
            x->ptr.p_double[i] = r->ptr.p_double[i];
        }
        for(k=s->trfage-1; k>=0; k--)
        {
            v = (double)(0);
            for(i=0; i<=m-1; i++)
            {
                v = v+s->densepfieta.ptr.p_double[k*m+i]*x->ptr.p_double[i];
            }
            x->ptr.p_double[s->rk.ptr.p_int[k]] = v;
        }
        for(i=0; i<=m-1; i++)
        {
            tx->ptr.p_double[i] = x->ptr.p_double[s->rowpermbwd.ptr.p_int[i]];
        }
        for(i=0; i<=m-1; i++)
        {
            x->ptr.p_double[i] = tx->ptr.p_double[i];
        }
        if( s->trftype==0||s->trftype==1 )
        {
            
            /*
             * Dense TRF
             */
            rmatrixtrsv(m, &s->denselu, 0, 0, ae_false, ae_true, 0, x, 0, _state);
            rmatrixtrsv(m, &s->denselu, 0, 0, ae_true, ae_false, 0, x, 0, _state);
        }
        else
        {
            
            /*
             * Sparse TRF
             */
            sparsetrsv(&s->sparsel, ae_false, ae_false, 0, x, _state);
            sparsetrsv(&s->sparseu, ae_true, ae_false, 0, x, _state);
        }
        for(i=0; i<=m-1; i++)
        {
            tx->ptr.p_double[s->colpermbwd.ptr.p_int[i]] = x->ptr.p_double[i];
        }
        for(i=0; i<=m-1; i++)
        {
            x->ptr.p_double[i] = tx->ptr.p_double[i];
        }
        processed = ae_true;
    }
    
    /*
     * Sparse factorization with Forest-Tomlin update
     */
    if( s->trftype==3 )
    {
        rvectorsetlengthatleast(x, m, _state);
        for(i=0; i<=m-1; i++)
        {
            x->ptr.p_double[i] = r->ptr.p_double[i];
        }
        for(i=0; i<=m-1; i++)
        {
            tx->ptr.p_double[i] = x->ptr.p_double[s->rowpermbwd.ptr.p_int[i]];
        }
        for(i=0; i<=m-1; i++)
        {
            x->ptr.p_double[i] = tx->ptr.p_double[i];
        }
        sparsetrsv(&s->sparsel, ae_false, ae_false, 0, x, _state);
        for(k=s->trfage-1; k>=0; k--)
        {
            
            /*
             * The code below is an amalgamation of two parts:
             *
             * triangular factor
             * V:=X[M-1];
             * for I:=D to M-2 do
             *     X[I]:=X[I]+S.DenseMu[K*M+I]*V;
             * X[M-1]:=S.DenseMu[K*M+(M-1)]*V;
             * 
             * inverse of cyclic permutation
             * V:=X[M-1];
             * for I:=M-1 downto D+1 do
             *     X[I]:=X[I-1];
             * X[D]:=V;
             */
            d = s->dk.ptr.p_int[k];
            vm = x->ptr.p_double[m-1];
            v = s->densemu.ptr.p_double[k*m+(m-1)]*vm;
            if( vm!=(double)0 )
            {
                
                /*
                 * X[M-1] is non-zero, apply update
                 */
                for(i=m-2; i>=d; i--)
                {
                    x->ptr.p_double[i+1] = x->ptr.p_double[i]+s->densemu.ptr.p_double[k*m+i]*vm;
                }
            }
            else
            {
                
                /*
                 * X[M-1] is zero, just cyclic permutation
                 */
                for(i=m-2; i>=d; i--)
                {
                    x->ptr.p_double[i+1] = x->ptr.p_double[i];
                }
            }
            x->ptr.p_double[d] = v;
        }
        sparsetrsv(&s->sparseut, ae_false, ae_false, 1, x, _state);
        for(i=0; i<=m-1; i++)
        {
            tx->ptr.p_double[s->colpermbwd.ptr.p_int[i]] = x->ptr.p_double[i];
        }
        for(i=0; i<=m-1; i++)
        {
            x->ptr.p_double[i] = tx->ptr.p_double[i];
        }
        processed = ae_true;
    }
    
    /*
     * Integrity check
     */
    ae_assert(processed, "BasisSolveT: unsupported TRF type", _state);
    v = (double)(0);
    for(i=0; i<=m-1; i++)
    {
        v = v+x->ptr.p_double[i];
    }
    ae_assert(ae_isfinite(v, _state), "BasisSolveT: integrity check failed (degeneracy in B?)", _state);
}


/*************************************************************************
This function computes product AN*XN, where AN is a  non-basic  subset  of
columns of A, and XN is a non-basic subset of columns of X.

Output array is reallocated if its size is too small.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_computeanxn(const dualsimplexstate* state,
     const dualsimplexsubproblem* subproblem,
     /* Real    */ const ae_vector* x,
     /* Real    */ ae_vector* y,
     ae_state *_state)
{
    ae_int_t nn;
    ae_int_t nx;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t j0;
    ae_int_t j1;
    double v;


    nx = subproblem->ns+subproblem->m;
    m = subproblem->m;
    nn = nx-m;
    
    /*
     * Integrity check
     */
    ae_assert(subproblem->state>=reviseddualsimplex_ssvalidxn, "ComputeANXN: XN is invalid", _state);
    
    /*
     * Compute
     */
    rvectorsetlengthatleast(y, m, _state);
    for(i=0; i<=m-1; i++)
    {
        y->ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=nn-1; i++)
    {
        j0 = state->at.ridx.ptr.p_int[state->basis.nidx.ptr.p_int[i]];
        j1 = state->at.ridx.ptr.p_int[state->basis.nidx.ptr.p_int[i]+1]-1;
        v = x->ptr.p_double[state->basis.nidx.ptr.p_int[i]];
        for(j=j0; j<=j1; j++)
        {
            k = state->at.idx.ptr.p_int[j];
            y->ptr.p_double[k] = y->ptr.p_double[k]+v*state->at.vals.ptr.p_double[j];
        }
    }
}


/*************************************************************************
This function computes product (AN^T)*y, where AN is a non-basic subset of
columns of A, and y is some vector.

Output array is set to full NX-sized length, with basic components of  the
output being set to zeros.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_computeantv(const dualsimplexstate* state,
     /* Real    */ const ae_vector* y,
     /* Real    */ ae_vector* r,
     ae_state *_state)
{
    ae_int_t nn;
    ae_int_t nx;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j0;
    ae_int_t j1;
    double v;


    nx = state->ns+state->m;
    m = state->m;
    nn = nx-m;
    
    /*
     * Allocate output, set to zero
     */
    rvectorsetlengthatleast(r, nx, _state);
    for(i=0; i<=nx-1; i++)
    {
        r->ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=nn-1; i++)
    {
        j0 = state->at.ridx.ptr.p_int[state->basis.nidx.ptr.p_int[i]];
        j1 = state->at.ridx.ptr.p_int[state->basis.nidx.ptr.p_int[i]+1]-1;
        v = (double)(0);
        for(j=j0; j<=j1; j++)
        {
            v = v+state->at.vals.ptr.p_double[j]*y->ptr.p_double[state->at.idx.ptr.p_int[j]];
        }
        r->ptr.p_double[state->basis.nidx.ptr.p_int[i]] = v;
    }
}


/*************************************************************************
Returns True if I-th lower bound is present

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static ae_bool reviseddualsimplex_hasbndl(const dualsimplexsubproblem* subproblem,
     ae_int_t i,
     ae_state *_state)
{
    ae_int_t k;
    ae_bool result;


    k = subproblem->bndt.ptr.p_int[i];
    result = ae_false;
    if( (k==0||k==1)||k==3 )
    {
        result = ae_true;
        return result;
    }
    if( k==2||k==4 )
    {
        result = ae_false;
        return result;
    }
    ae_assert(ae_false, "HasBndL: integrity check failed", _state);
    return result;
}


/*************************************************************************
Returns True if I-th upper bound is present

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static ae_bool reviseddualsimplex_hasbndu(const dualsimplexsubproblem* subproblem,
     ae_int_t i,
     ae_state *_state)
{
    ae_int_t k;
    ae_bool result;


    k = subproblem->bndt.ptr.p_int[i];
    result = ae_false;
    if( (k==0||k==2)||k==3 )
    {
        result = ae_true;
        return result;
    }
    if( k==1||k==4 )
    {
        result = ae_false;
        return result;
    }
    ae_assert(ae_false, "HasBndL: integrity check failed", _state);
    return result;
}


/*************************************************************************
Returns True if I-th variable if free

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static ae_bool reviseddualsimplex_isfree(const dualsimplexsubproblem* subproblem,
     ae_int_t i,
     ae_state *_state)
{
    ae_int_t k;
    ae_bool result;


    k = subproblem->bndt.ptr.p_int[i];
    result = ae_false;
    if( ((k==0||k==1)||k==2)||k==3 )
    {
        result = ae_false;
        return result;
    }
    if( k==4 )
    {
        result = ae_true;
        return result;
    }
    ae_assert(ae_false, "IsFree: integrity check failed", _state);
    return result;
}


/*************************************************************************
Downgrades problem state to the specified one (if status is lower than one
specified by user, nothing is changed)

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_downgradestate(dualsimplexsubproblem* subproblem,
     ae_int_t s,
     ae_state *_state)
{


    subproblem->state = ae_minint(subproblem->state, s, _state);
}


/*************************************************************************
Returns maximum dual infeasibility (only non-basic variables are  checked,
we assume that basic variables are good enough).

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static double reviseddualsimplex_dualfeasibilityerror(const dualsimplexstate* state,
     const dualsimplexsubproblem* s,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t nn;
    ae_int_t bndt;
    double result;


    nn = s->ns;
    ae_assert(s->state==reviseddualsimplex_ssvalid, "DualFeasibilityError: invalid X", _state);
    result = (double)(0);
    for(i=0; i<=nn-1; i++)
    {
        j = state->basis.nidx.ptr.p_int[i];
        bndt = s->bndt.ptr.p_int[j];
        if( bndt==reviseddualsimplex_ccfixed )
        {
            continue;
        }
        if( bndt==reviseddualsimplex_ccrange )
        {
            if( s->xa.ptr.p_double[j]==s->bndl.ptr.p_double[j] )
            {
                result = ae_maxreal(result, -s->d.ptr.p_double[j], _state);
                continue;
            }
            if( s->xa.ptr.p_double[j]==s->bndu.ptr.p_double[j] )
            {
                result = ae_maxreal(result, s->d.ptr.p_double[j], _state);
                continue;
            }
            ae_assert(ae_false, "DualFeasibilityError: integrity check failed", _state);
        }
        if( bndt==reviseddualsimplex_cclower )
        {
            ae_assert(s->xa.ptr.p_double[j]==s->bndl.ptr.p_double[j], "DualFeasibilityError: integrity check failed", _state);
            result = ae_maxreal(result, -s->d.ptr.p_double[j], _state);
            continue;
        }
        if( bndt==reviseddualsimplex_ccupper )
        {
            ae_assert(s->xa.ptr.p_double[j]==s->bndu.ptr.p_double[j], "DualFeasibilityError: integrity check failed", _state);
            result = ae_maxreal(result, s->d.ptr.p_double[j], _state);
            continue;
        }
        if( bndt==reviseddualsimplex_ccfree )
        {
            result = ae_maxreal(result, ae_fabs(s->d.ptr.p_double[j], _state), _state);
            continue;
        }
        ae_assert(ae_false, "DSSOptimize: integrity check failed (infeasible constraint)", _state);
    }
    return result;
}


/*************************************************************************
Returns True for dual feasible basis (some minor dual feasibility error is
allowed), False otherwise

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
static ae_bool reviseddualsimplex_isdualfeasible(const dualsimplexstate* state,
     const dualsimplexsubproblem* s,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_bool result;


    result = ae_fp_less_eq(reviseddualsimplex_dualfeasibilityerror(state, s, _state),settings->dtolabs);
    return result;
}


/*************************************************************************
Transforms sequence of pivot permutations P0*P1*...*Pm to forward/backward
permutation representation.

  -- ALGLIB --
     Copyright 12.09.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_pivottobwd(/* Integer */ const ae_vector* p,
     ae_int_t m,
     /* Integer */ ae_vector* bwd,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t k;
    ae_int_t t;


    ivectorsetlengthatleast(bwd, m, _state);
    for(i=0; i<=m-1; i++)
    {
        bwd->ptr.p_int[i] = i;
    }
    for(i=0; i<=m-1; i++)
    {
        k = p->ptr.p_int[i];
        if( k!=i )
        {
            t = bwd->ptr.p_int[i];
            bwd->ptr.p_int[i] = bwd->ptr.p_int[k];
            bwd->ptr.p_int[k] = t;
        }
    }
}


/*************************************************************************
Applies inverse cyclic permutation of [D,M-1) (element D is moved to the end, the
rest of elements is shifted one position backward) to the already existing
permutation.

  -- ALGLIB --
     Copyright 12.09.2018 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_inversecyclicpermutation(/* Integer */ ae_vector* bwd,
     ae_int_t m,
     ae_int_t d,
     /* Integer */ ae_vector* tmpi,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t k;


    
    /*
     * update Bwd[]
     */
    k = bwd->ptr.p_int[d];
    for(i=d; i<=m-2; i++)
    {
        bwd->ptr.p_int[i] = bwd->ptr.p_int[i+1];
    }
    bwd->ptr.p_int[m-1] = k;
}


/*************************************************************************
Offloads basic components of X[], BndT[], BndL[], BndU[] to XB/BndTB/BndLB/BndUB.

  -- ALGLIB --
     Copyright 24.01.2019 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_offloadbasiccomponents(dualsimplexsubproblem* s,
     const dualsimplexbasis* basis,
     const dualsimplexsettings* settings,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t m;


    m = basis->m;
    for(i=0; i<=m-1; i++)
    {
        s->xb.ptr.p_double[i] = s->xa.ptr.p_double[basis->idx.ptr.p_int[i]];
        reviseddualsimplex_cacheboundinfo(s, i, basis->idx.ptr.p_int[i], settings, _state);
    }
}


/*************************************************************************
Recombines basic and non-basic components in X[]

  -- ALGLIB --
     Copyright 24.01.2019 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_recombinebasicnonbasicx(dualsimplexsubproblem* s,
     const dualsimplexbasis* basis,
     ae_state *_state)
{
    ae_int_t m;
    ae_int_t i;


    m = basis->m;
    for(i=0; i<=m-1; i++)
    {
        s->xa.ptr.p_double[basis->idx.ptr.p_int[i]] = s->xb.ptr.p_double[i];
    }
}


/*************************************************************************
Computes Stats array

INPUT PARAMETERS:
    S           -   problem, contains current solution at S.XA
    Basis       -   basis
    X           -   possibly preallocated output buffer
    LagBC       -   possibly preallocated output buffer
    LagLC       -   possibly preallocated output buffer
    Stats       -   possibly preallocated output buffer
    Buffers     -   temporary buffers

OUTPUT PARAMETERS:
    X           -   array[NS], solution
    LagBC       -   array[NS], Lagrange multipliers for box constraints
    LagLC       -   array[M], Lagrange multipliers for linear constraints
    Stats       -   array[NS+M], primary/slack variable stats:
                    * -1 = variable at lower bound
                    * +1 = variable at upper bound
                    *  0 = basic or free (possibly nonbasic) variable
                    fixed variables may be set to +1 or -1

  -- ALGLIB --
     Copyright 24.01.2019 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_setxydstats(const dualsimplexstate* state,
     const dualsimplexsubproblem* s,
     const dualsimplexbasis* basis,
     apbuffers* buffers,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* laglc,
     /* Integer */ ae_vector* stats,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t m;
    ae_int_t ns;
    ae_int_t nx;


    
    /*
     * Prepare
     */
    m = s->m;
    ns = s->ns;
    nx = s->ns+s->m;
    rvectorsetlengthatleast(x, ns, _state);
    rvectorsetlengthatleast(laglc, m, _state);
    ivectorsetlengthatleast(stats, nx, _state);
    rsetallocv(ns, 0.0, lagbc, _state);
    
    /*
     * Compute Y (in Buffers.RA1) and D (in Buffers.RA3)
     */
    rvectorsetlengthatleast(&buffers->ra0, m, _state);
    rvectorsetlengthatleast(&buffers->ra1, m, _state);
    rvectorsetlengthatleast(&buffers->ra3, nx, _state);
    for(i=0; i<=m-1; i++)
    {
        buffers->ra0.ptr.p_double[i] = s->rawc.ptr.p_double[basis->idx.ptr.p_int[i]];
    }
    reviseddualsimplex_basissolvet(basis, &buffers->ra0, &buffers->ra1, &buffers->ra2, _state);
    reviseddualsimplex_computeantv(state, &buffers->ra1, &buffers->ra3, _state);
    for(i=0; i<=ns-1; i++)
    {
        j = state->basis.nidx.ptr.p_int[i];
        buffers->ra3.ptr.p_double[j] = state->primary.rawc.ptr.p_double[j]-buffers->ra3.ptr.p_double[j];
        if( j<ns )
        {
            lagbc->ptr.p_double[j] = -buffers->ra3.ptr.p_double[j];
        }
    }
    for(i=0; i<=m-1; i++)
    {
        buffers->ra3.ptr.p_double[state->basis.idx.ptr.p_int[i]] = (double)(0);
    }
    
    /*
     * Compute X, Y, Stats
     */
    for(i=0; i<=ns-1; i++)
    {
        x->ptr.p_double[i] = s->xa.ptr.p_double[i];
        if( ae_isfinite(state->rawbndl.ptr.p_double[i], _state) )
        {
            x->ptr.p_double[i] = ae_maxreal(x->ptr.p_double[i], state->rawbndl.ptr.p_double[i], _state);
        }
        if( ae_isfinite(state->rawbndu.ptr.p_double[i], _state) )
        {
            x->ptr.p_double[i] = ae_minreal(x->ptr.p_double[i], state->rawbndu.ptr.p_double[i], _state);
        }
    }
    for(i=0; i<=ns-1; i++)
    {
        if( basis->isbasic.ptr.p_bool[i] )
        {
            lagbc->ptr.p_double[i] = (double)(0);
            continue;
        }
        if( s->bndt.ptr.p_int[i]==reviseddualsimplex_ccfixed )
        {
            continue;
        }
        if( reviseddualsimplex_hasbndl(s, i, _state)&&ae_fp_eq(s->xa.ptr.p_double[i],s->bndl.ptr.p_double[i]) )
        {
            lagbc->ptr.p_double[i] = ae_minreal(lagbc->ptr.p_double[i], 0.0, _state);
            continue;
        }
        if( reviseddualsimplex_hasbndu(s, i, _state)&&ae_fp_eq(s->xa.ptr.p_double[i],s->bndu.ptr.p_double[i]) )
        {
            lagbc->ptr.p_double[i] = ae_maxreal(lagbc->ptr.p_double[i], 0.0, _state);
            continue;
        }
        ae_assert(!reviseddualsimplex_hasbndl(s, i, _state)&&!reviseddualsimplex_hasbndu(s, i, _state), "SetStats: integrity check failed (zetta5)", _state);
        lagbc->ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=m-1; i++)
    {
        laglc->ptr.p_double[i] = -buffers->ra1.ptr.p_double[i]/state->rowscales.ptr.p_double[i];
    }
    for(i=0; i<=nx-1; i++)
    {
        if( basis->isbasic.ptr.p_bool[i] )
        {
            stats->ptr.p_int[i] = 0;
            continue;
        }
        if( reviseddualsimplex_hasbndl(s, i, _state)&&ae_fp_eq(s->xa.ptr.p_double[i],s->bndl.ptr.p_double[i]) )
        {
            stats->ptr.p_int[i] = -1;
            continue;
        }
        if( reviseddualsimplex_hasbndu(s, i, _state)&&ae_fp_eq(s->xa.ptr.p_double[i],s->bndu.ptr.p_double[i]) )
        {
            stats->ptr.p_int[i] = 1;
            continue;
        }
        ae_assert(!reviseddualsimplex_hasbndl(s, i, _state)&&!reviseddualsimplex_hasbndu(s, i, _state), "SetStats: integrity check failed (zetta5)", _state);
        stats->ptr.p_int[i] = 0;
    }
}


/*************************************************************************
Initializes vector, sets all internal arrays to length N (so that  we  may
store any vector without reallocation).  Previously  allocated  memory  is
reused as much as possible.

No zero-filling is performed, X.K is undefined. Only X.N is set.

INPUT PARAMETERS:
    X           -   temporary buffers

OUTPUT PARAMETERS:
    X           -   preallocated vector, X.N=N, contents undefined

  -- ALGLIB --
     Copyright 24.07.2020 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_dvalloc(dssvector* x,
     ae_int_t n,
     ae_state *_state)
{


    ivectorsetlengthatleast(&x->idx, n, _state);
    rvectorsetlengthatleast(&x->vals, n, _state);
    rvectorsetlengthatleast(&x->dense, n, _state);
    x->n = n;
}


/*************************************************************************
Initializes vector, sets all internal arrays to length  N  and  zero-fills
them. Previously allocated memory is reused as much as possible.

INPUT PARAMETERS:
    X           -   temporary buffers

OUTPUT PARAMETERS:
    X           -   preallocated vector:
                    * X.N=N
                    * X.K=0
                    * X.Dense is zero-filled.

  -- ALGLIB --
     Copyright 24.07.2020 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_dvinit(dssvector* x,
     ae_int_t n,
     ae_state *_state)
{


    ivectorsetlengthatleast(&x->idx, n, _state);
    rvectorsetlengthatleast(&x->vals, n, _state);
    rvectorsetlengthatleast(&x->dense, n, _state);
    rsetv(n, 0.0, &x->dense, _state);
    x->n = n;
    x->k = 0;
}


/*************************************************************************
Copies dense part to sparse one.

INPUT PARAMETERS:
    X           -   allocated vector; dense part must be valid

OUTPUT PARAMETERS:
    X           -   both dense and sparse parts are valid.

  -- ALGLIB --
     Copyright 24.07.2020 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_dvdensetosparse(dssvector* x,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t k;
    double v;


    n = x->n;
    ivectorsetlengthatleast(&x->idx, n, _state);
    rvectorsetlengthatleast(&x->vals, n, _state);
    k = 0;
    for(i=0; i<=n-1; i++)
    {
        v = x->dense.ptr.p_double[i];
        if( v!=0.0 )
        {
            x->idx.ptr.p_int[k] = i;
            x->vals.ptr.p_double[k] = v;
            k = k+1;
        }
    }
    x->k = k;
}


/*************************************************************************
Copies sparse part to dense one.

INPUT PARAMETERS:
    X           -   allocated vector; sparse part must be valid

OUTPUT PARAMETERS:
    X           -   both dense and sparse parts are valid.

  -- ALGLIB --
     Copyright 24.07.2020 by Bochkanov Sergey
*************************************************************************/
static void reviseddualsimplex_dvsparsetodense(dssvector* x,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t k;


    n = x->n;
    k = x->k;
    rsetv(n, 0.0, &x->dense, _state);
    for(i=0; i<=k-1; i++)
    {
        x->dense.ptr.p_double[x->idx.ptr.p_int[i]] = x->vals.ptr.p_double[i];
    }
}


static double reviseddualsimplex_sparsityof(/* Real    */ const ae_vector* x,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t k;
    double mx;
    double result;


    if( n<=1 )
    {
        result = (double)(0);
        return result;
    }
    mx = 1.0;
    for(i=0; i<=n-1; i++)
    {
        mx = ae_maxreal(mx, ae_fabs(x->ptr.p_double[i], _state), _state);
    }
    mx = 1.0E5*ae_machineepsilon*mx;
    k = 0;
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater(ae_fabs(x->ptr.p_double[i], _state),mx) )
        {
            k = k+1;
        }
    }
    result = (double)k/(double)n;
    return result;
}


static void reviseddualsimplex_updateavgcounter(double v,
     double* acc,
     ae_int_t* cnt,
     ae_state *_state)
{


    *acc = *acc+v;
    *cnt = *cnt+1;
}


void _dualsimplexsettings_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    dualsimplexsettings *p = (dualsimplexsettings*)_p;
    ae_touch_ptr((void*)p);
}


void _dualsimplexsettings_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    dualsimplexsettings       *dst = (dualsimplexsettings*)_dst;
    const dualsimplexsettings *src = (const dualsimplexsettings*)_src;
    dst->pivottol = src->pivottol;
    dst->perturbmag = src->perturbmag;
    dst->maxtrfage = src->maxtrfage;
    dst->trftype = src->trftype;
    dst->ratiotest = src->ratiotest;
    dst->pricing = src->pricing;
    dst->shifting = src->shifting;
    dst->xtolabs = src->xtolabs;
    dst->xtolrelabs = src->xtolrelabs;
    dst->dtolabs = src->dtolabs;
}


void _dualsimplexsettings_clear(void* _p)
{
    dualsimplexsettings *p = (dualsimplexsettings*)_p;
    ae_touch_ptr((void*)p);
}


void _dualsimplexsettings_destroy(void* _p)
{
    dualsimplexsettings *p = (dualsimplexsettings*)_p;
    ae_touch_ptr((void*)p);
}


void _dssvector_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    dssvector *p = (dssvector*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->idx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->vals, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dense, 0, DT_REAL, _state, make_automatic);
}


void _dssvector_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    dssvector       *dst = (dssvector*)_dst;
    const dssvector *src = (const dssvector*)_src;
    dst->n = src->n;
    dst->k = src->k;
    ae_vector_init_copy(&dst->idx, &src->idx, _state, make_automatic);
    ae_vector_init_copy(&dst->vals, &src->vals, _state, make_automatic);
    ae_vector_init_copy(&dst->dense, &src->dense, _state, make_automatic);
}


void _dssvector_clear(void* _p)
{
    dssvector *p = (dssvector*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->idx);
    ae_vector_clear(&p->vals);
    ae_vector_clear(&p->dense);
}


void _dssvector_destroy(void* _p)
{
    dssvector *p = (dssvector*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->idx);
    ae_vector_destroy(&p->vals);
    ae_vector_destroy(&p->dense);
}


void _dualsimplexbasis_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    dualsimplexbasis *p = (dualsimplexbasis*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->idx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->nidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->isbasic, 0, DT_BOOL, _state, make_automatic);
    ae_matrix_init(&p->denselu, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparsel, _state, make_automatic);
    _sparsematrix_init(&p->sparseu, _state, make_automatic);
    _sparsematrix_init(&p->sparseut, _state, make_automatic);
    ae_vector_init(&p->rowpermbwd, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->colpermbwd, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->densepfieta, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->densemu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rk, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->dk, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->dseweights, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->wtmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->wtmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->wtmp2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nrs, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->tcinvidx, 0, DT_INT, _state, make_automatic);
    ae_matrix_init(&p->denselu2, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->densep2, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->densep2c, 0, DT_INT, _state, make_automatic);
    _sparsematrix_init(&p->sparselu1, _state, make_automatic);
    _sparsematrix_init(&p->sparselu2, _state, make_automatic);
    _sluv2buffer_init(&p->lubuf2, _state, make_automatic);
    ae_vector_init(&p->tmpi, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->utmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->utmpi, 0, DT_INT, _state, make_automatic);
    _sparsematrix_init(&p->sparseludbg, _state, make_automatic);
}


void _dualsimplexbasis_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    dualsimplexbasis       *dst = (dualsimplexbasis*)_dst;
    const dualsimplexbasis *src = (const dualsimplexbasis*)_src;
    dst->ns = src->ns;
    dst->m = src->m;
    ae_vector_init_copy(&dst->idx, &src->idx, _state, make_automatic);
    ae_vector_init_copy(&dst->nidx, &src->nidx, _state, make_automatic);
    ae_vector_init_copy(&dst->isbasic, &src->isbasic, _state, make_automatic);
    dst->trftype = src->trftype;
    dst->isvalidtrf = src->isvalidtrf;
    dst->trfage = src->trfage;
    ae_matrix_init_copy(&dst->denselu, &src->denselu, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsel, &src->sparsel, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparseu, &src->sparseu, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparseut, &src->sparseut, _state, make_automatic);
    ae_vector_init_copy(&dst->rowpermbwd, &src->rowpermbwd, _state, make_automatic);
    ae_vector_init_copy(&dst->colpermbwd, &src->colpermbwd, _state, make_automatic);
    ae_vector_init_copy(&dst->densepfieta, &src->densepfieta, _state, make_automatic);
    ae_vector_init_copy(&dst->densemu, &src->densemu, _state, make_automatic);
    ae_vector_init_copy(&dst->rk, &src->rk, _state, make_automatic);
    ae_vector_init_copy(&dst->dk, &src->dk, _state, make_automatic);
    ae_vector_init_copy(&dst->dseweights, &src->dseweights, _state, make_automatic);
    dst->dsevalid = src->dsevalid;
    dst->eminu = src->eminu;
    dst->statfact = src->statfact;
    dst->statupdt = src->statupdt;
    dst->statoffdiag = src->statoffdiag;
    ae_vector_init_copy(&dst->wtmp0, &src->wtmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->wtmp1, &src->wtmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->wtmp2, &src->wtmp2, _state, make_automatic);
    ae_vector_init_copy(&dst->nrs, &src->nrs, _state, make_automatic);
    ae_vector_init_copy(&dst->tcinvidx, &src->tcinvidx, _state, make_automatic);
    ae_matrix_init_copy(&dst->denselu2, &src->denselu2, _state, make_automatic);
    ae_vector_init_copy(&dst->densep2, &src->densep2, _state, make_automatic);
    ae_vector_init_copy(&dst->densep2c, &src->densep2c, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparselu1, &src->sparselu1, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparselu2, &src->sparselu2, _state, make_automatic);
    _sluv2buffer_init_copy(&dst->lubuf2, &src->lubuf2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpi, &src->tmpi, _state, make_automatic);
    ae_vector_init_copy(&dst->utmp0, &src->utmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->utmpi, &src->utmpi, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparseludbg, &src->sparseludbg, _state, make_automatic);
}


void _dualsimplexbasis_clear(void* _p)
{
    dualsimplexbasis *p = (dualsimplexbasis*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->idx);
    ae_vector_clear(&p->nidx);
    ae_vector_clear(&p->isbasic);
    ae_matrix_clear(&p->denselu);
    _sparsematrix_clear(&p->sparsel);
    _sparsematrix_clear(&p->sparseu);
    _sparsematrix_clear(&p->sparseut);
    ae_vector_clear(&p->rowpermbwd);
    ae_vector_clear(&p->colpermbwd);
    ae_vector_clear(&p->densepfieta);
    ae_vector_clear(&p->densemu);
    ae_vector_clear(&p->rk);
    ae_vector_clear(&p->dk);
    ae_vector_clear(&p->dseweights);
    ae_vector_clear(&p->wtmp0);
    ae_vector_clear(&p->wtmp1);
    ae_vector_clear(&p->wtmp2);
    ae_vector_clear(&p->nrs);
    ae_vector_clear(&p->tcinvidx);
    ae_matrix_clear(&p->denselu2);
    ae_vector_clear(&p->densep2);
    ae_vector_clear(&p->densep2c);
    _sparsematrix_clear(&p->sparselu1);
    _sparsematrix_clear(&p->sparselu2);
    _sluv2buffer_clear(&p->lubuf2);
    ae_vector_clear(&p->tmpi);
    ae_vector_clear(&p->utmp0);
    ae_vector_clear(&p->utmpi);
    _sparsematrix_clear(&p->sparseludbg);
}


void _dualsimplexbasis_destroy(void* _p)
{
    dualsimplexbasis *p = (dualsimplexbasis*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->idx);
    ae_vector_destroy(&p->nidx);
    ae_vector_destroy(&p->isbasic);
    ae_matrix_destroy(&p->denselu);
    _sparsematrix_destroy(&p->sparsel);
    _sparsematrix_destroy(&p->sparseu);
    _sparsematrix_destroy(&p->sparseut);
    ae_vector_destroy(&p->rowpermbwd);
    ae_vector_destroy(&p->colpermbwd);
    ae_vector_destroy(&p->densepfieta);
    ae_vector_destroy(&p->densemu);
    ae_vector_destroy(&p->rk);
    ae_vector_destroy(&p->dk);
    ae_vector_destroy(&p->dseweights);
    ae_vector_destroy(&p->wtmp0);
    ae_vector_destroy(&p->wtmp1);
    ae_vector_destroy(&p->wtmp2);
    ae_vector_destroy(&p->nrs);
    ae_vector_destroy(&p->tcinvidx);
    ae_matrix_destroy(&p->denselu2);
    ae_vector_destroy(&p->densep2);
    ae_vector_destroy(&p->densep2c);
    _sparsematrix_destroy(&p->sparselu1);
    _sparsematrix_destroy(&p->sparselu2);
    _sluv2buffer_destroy(&p->lubuf2);
    ae_vector_destroy(&p->tmpi);
    ae_vector_destroy(&p->utmp0);
    ae_vector_destroy(&p->utmpi);
    _sparsematrix_destroy(&p->sparseludbg);
}


void _dualsimplexsubproblem_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    dualsimplexsubproblem *p = (dualsimplexsubproblem*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->rawc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndt, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->xa, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xb, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndlb, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndub, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndtb, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->bndtollb, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndtolub, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->effc, 0, DT_REAL, _state, make_automatic);
}


void _dualsimplexsubproblem_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    dualsimplexsubproblem       *dst = (dualsimplexsubproblem*)_dst;
    const dualsimplexsubproblem *src = (const dualsimplexsubproblem*)_src;
    dst->ns = src->ns;
    dst->m = src->m;
    ae_vector_init_copy(&dst->rawc, &src->rawc, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    ae_vector_init_copy(&dst->bndt, &src->bndt, _state, make_automatic);
    ae_vector_init_copy(&dst->xa, &src->xa, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    dst->state = src->state;
    ae_vector_init_copy(&dst->xb, &src->xb, _state, make_automatic);
    ae_vector_init_copy(&dst->bndlb, &src->bndlb, _state, make_automatic);
    ae_vector_init_copy(&dst->bndub, &src->bndub, _state, make_automatic);
    ae_vector_init_copy(&dst->bndtb, &src->bndtb, _state, make_automatic);
    ae_vector_init_copy(&dst->bndtollb, &src->bndtollb, _state, make_automatic);
    ae_vector_init_copy(&dst->bndtolub, &src->bndtolub, _state, make_automatic);
    ae_vector_init_copy(&dst->effc, &src->effc, _state, make_automatic);
}


void _dualsimplexsubproblem_clear(void* _p)
{
    dualsimplexsubproblem *p = (dualsimplexsubproblem*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->rawc);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->bndt);
    ae_vector_clear(&p->xa);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->xb);
    ae_vector_clear(&p->bndlb);
    ae_vector_clear(&p->bndub);
    ae_vector_clear(&p->bndtb);
    ae_vector_clear(&p->bndtollb);
    ae_vector_clear(&p->bndtolub);
    ae_vector_clear(&p->effc);
}


void _dualsimplexsubproblem_destroy(void* _p)
{
    dualsimplexsubproblem *p = (dualsimplexsubproblem*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->rawc);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->bndt);
    ae_vector_destroy(&p->xa);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->xb);
    ae_vector_destroy(&p->bndlb);
    ae_vector_destroy(&p->bndub);
    ae_vector_destroy(&p->bndtb);
    ae_vector_destroy(&p->bndtollb);
    ae_vector_destroy(&p->bndtolub);
    ae_vector_destroy(&p->effc);
}


void _dualsimplexstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    dualsimplexstate *p = (dualsimplexstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->rowscales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawbndu, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->a, _state, make_automatic);
    _sparsematrix_init(&p->at, _state, make_automatic);
    _dualsimplexbasis_init(&p->basis, _state, make_automatic);
    _dualsimplexsubproblem_init(&p->primary, _state, make_automatic);
    _dualsimplexsubproblem_init(&p->phase1, _state, make_automatic);
    _dualsimplexsubproblem_init(&p->phase3, _state, make_automatic);
    ae_vector_init(&p->repx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replagbc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replaglc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->repstats, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->btrantmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->btrantmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->btrantmp2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ftrantmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ftrantmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->possibleflips, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->dfctmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dfctmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dfctmp2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ustmpi, 0, DT_INT, _state, make_automatic);
    _apbuffers_init(&p->xydsbuf, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp2, 0, DT_REAL, _state, make_automatic);
    _dssvector_init(&p->alphar, _state, make_automatic);
    _dssvector_init(&p->rhor, _state, make_automatic);
    ae_vector_init(&p->tau, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->alphaq, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->alphaqim, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->eligiblealphar, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->harrisset, 0, DT_INT, _state, make_automatic);
}


void _dualsimplexstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    dualsimplexstate       *dst = (dualsimplexstate*)_dst;
    const dualsimplexstate *src = (const dualsimplexstate*)_src;
    ae_vector_init_copy(&dst->rowscales, &src->rowscales, _state, make_automatic);
    ae_vector_init_copy(&dst->rawbndl, &src->rawbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->rawbndu, &src->rawbndu, _state, make_automatic);
    dst->ns = src->ns;
    dst->m = src->m;
    _sparsematrix_init_copy(&dst->a, &src->a, _state, make_automatic);
    _sparsematrix_init_copy(&dst->at, &src->at, _state, make_automatic);
    _dualsimplexbasis_init_copy(&dst->basis, &src->basis, _state, make_automatic);
    _dualsimplexsubproblem_init_copy(&dst->primary, &src->primary, _state, make_automatic);
    _dualsimplexsubproblem_init_copy(&dst->phase1, &src->phase1, _state, make_automatic);
    _dualsimplexsubproblem_init_copy(&dst->phase3, &src->phase3, _state, make_automatic);
    ae_vector_init_copy(&dst->repx, &src->repx, _state, make_automatic);
    ae_vector_init_copy(&dst->replagbc, &src->replagbc, _state, make_automatic);
    ae_vector_init_copy(&dst->replaglc, &src->replaglc, _state, make_automatic);
    ae_vector_init_copy(&dst->repstats, &src->repstats, _state, make_automatic);
    dst->repterminationtype = src->repterminationtype;
    dst->repiterationscount = src->repiterationscount;
    dst->repiterationscount1 = src->repiterationscount1;
    dst->repiterationscount2 = src->repiterationscount2;
    dst->repiterationscount3 = src->repiterationscount3;
    dst->repphase1time = src->repphase1time;
    dst->repphase2time = src->repphase2time;
    dst->repphase3time = src->repphase3time;
    dst->repdualpricingtime = src->repdualpricingtime;
    dst->repdualbtrantime = src->repdualbtrantime;
    dst->repdualpivotrowtime = src->repdualpivotrowtime;
    dst->repdualratiotesttime = src->repdualratiotesttime;
    dst->repdualftrantime = src->repdualftrantime;
    dst->repdualupdatesteptime = src->repdualupdatesteptime;
    dst->repfillpivotrow = src->repfillpivotrow;
    dst->repfillpivotrowcnt = src->repfillpivotrowcnt;
    dst->repfillrhor = src->repfillrhor;
    dst->repfillrhorcnt = src->repfillrhorcnt;
    dst->repfilldensemu = src->repfilldensemu;
    dst->repfilldensemucnt = src->repfilldensemucnt;
    dst->dotrace = src->dotrace;
    dst->dodetailedtrace = src->dodetailedtrace;
    dst->dotimers = src->dotimers;
    ae_vector_init_copy(&dst->btrantmp0, &src->btrantmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->btrantmp1, &src->btrantmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->btrantmp2, &src->btrantmp2, _state, make_automatic);
    ae_vector_init_copy(&dst->ftrantmp0, &src->ftrantmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->ftrantmp1, &src->ftrantmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->possibleflips, &src->possibleflips, _state, make_automatic);
    dst->possibleflipscnt = src->possibleflipscnt;
    ae_vector_init_copy(&dst->dfctmp0, &src->dfctmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->dfctmp1, &src->dfctmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->dfctmp2, &src->dfctmp2, _state, make_automatic);
    ae_vector_init_copy(&dst->ustmpi, &src->ustmpi, _state, make_automatic);
    _apbuffers_init_copy(&dst->xydsbuf, &src->xydsbuf, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp2, &src->tmp2, _state, make_automatic);
    _dssvector_init_copy(&dst->alphar, &src->alphar, _state, make_automatic);
    _dssvector_init_copy(&dst->rhor, &src->rhor, _state, make_automatic);
    ae_vector_init_copy(&dst->tau, &src->tau, _state, make_automatic);
    ae_vector_init_copy(&dst->alphaq, &src->alphaq, _state, make_automatic);
    ae_vector_init_copy(&dst->alphaqim, &src->alphaqim, _state, make_automatic);
    ae_vector_init_copy(&dst->eligiblealphar, &src->eligiblealphar, _state, make_automatic);
    ae_vector_init_copy(&dst->harrisset, &src->harrisset, _state, make_automatic);
}


void _dualsimplexstate_clear(void* _p)
{
    dualsimplexstate *p = (dualsimplexstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->rowscales);
    ae_vector_clear(&p->rawbndl);
    ae_vector_clear(&p->rawbndu);
    _sparsematrix_clear(&p->a);
    _sparsematrix_clear(&p->at);
    _dualsimplexbasis_clear(&p->basis);
    _dualsimplexsubproblem_clear(&p->primary);
    _dualsimplexsubproblem_clear(&p->phase1);
    _dualsimplexsubproblem_clear(&p->phase3);
    ae_vector_clear(&p->repx);
    ae_vector_clear(&p->replagbc);
    ae_vector_clear(&p->replaglc);
    ae_vector_clear(&p->repstats);
    ae_vector_clear(&p->btrantmp0);
    ae_vector_clear(&p->btrantmp1);
    ae_vector_clear(&p->btrantmp2);
    ae_vector_clear(&p->ftrantmp0);
    ae_vector_clear(&p->ftrantmp1);
    ae_vector_clear(&p->possibleflips);
    ae_vector_clear(&p->dfctmp0);
    ae_vector_clear(&p->dfctmp1);
    ae_vector_clear(&p->dfctmp2);
    ae_vector_clear(&p->ustmpi);
    _apbuffers_clear(&p->xydsbuf);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmp1);
    ae_vector_clear(&p->tmp2);
    _dssvector_clear(&p->alphar);
    _dssvector_clear(&p->rhor);
    ae_vector_clear(&p->tau);
    ae_vector_clear(&p->alphaq);
    ae_vector_clear(&p->alphaqim);
    ae_vector_clear(&p->eligiblealphar);
    ae_vector_clear(&p->harrisset);
}


void _dualsimplexstate_destroy(void* _p)
{
    dualsimplexstate *p = (dualsimplexstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->rowscales);
    ae_vector_destroy(&p->rawbndl);
    ae_vector_destroy(&p->rawbndu);
    _sparsematrix_destroy(&p->a);
    _sparsematrix_destroy(&p->at);
    _dualsimplexbasis_destroy(&p->basis);
    _dualsimplexsubproblem_destroy(&p->primary);
    _dualsimplexsubproblem_destroy(&p->phase1);
    _dualsimplexsubproblem_destroy(&p->phase3);
    ae_vector_destroy(&p->repx);
    ae_vector_destroy(&p->replagbc);
    ae_vector_destroy(&p->replaglc);
    ae_vector_destroy(&p->repstats);
    ae_vector_destroy(&p->btrantmp0);
    ae_vector_destroy(&p->btrantmp1);
    ae_vector_destroy(&p->btrantmp2);
    ae_vector_destroy(&p->ftrantmp0);
    ae_vector_destroy(&p->ftrantmp1);
    ae_vector_destroy(&p->possibleflips);
    ae_vector_destroy(&p->dfctmp0);
    ae_vector_destroy(&p->dfctmp1);
    ae_vector_destroy(&p->dfctmp2);
    ae_vector_destroy(&p->ustmpi);
    _apbuffers_destroy(&p->xydsbuf);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmp1);
    ae_vector_destroy(&p->tmp2);
    _dssvector_destroy(&p->alphar);
    _dssvector_destroy(&p->rhor);
    ae_vector_destroy(&p->tau);
    ae_vector_destroy(&p->alphaq);
    ae_vector_destroy(&p->alphaqim);
    ae_vector_destroy(&p->eligiblealphar);
    ae_vector_destroy(&p->harrisset);
}


#endif
#if defined(AE_COMPILE_LPSOLVERS) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
                            LINEAR PROGRAMMING

The subroutine creates LP  solver.  After  initial  creation  it  contains
default optimization problem with zero cost vector and all variables being
fixed to zero values and no constraints.

In order to actually solve something you should:
* set cost vector with minlpsetcost()
* set variable bounds with minlpsetbc() or minlpsetbcall()
* specify constraint matrix with one of the following functions:
  [*] minlpsetlc()        for dense one-sided constraints
  [*] minlpsetlc2dense()  for dense two-sided constraints
  [*] minlpsetlc2()       for sparse two-sided constraints
  [*] minlpaddlc2dense()  to add one dense row to constraint matrix
  [*] minlpaddlc2()       to add one row to constraint matrix (compressed format)
* call minlpoptimize() to run the solver and  minlpresults()  to  get  the
  solution vector and additional information.
  
By  default,  LP  solver uses best algorithm available. As of ALGLIB 3.17,
sparse interior point (barrier) solver is used. Future releases of  ALGLIB
may introduce other solvers.

User may choose specific LP algorithm by calling:
* minlpsetalgodss() for revised dual simplex method with DSE  pricing  and
  bounds flipping ratio test (aka long dual step).  Large-scale  sparse LU
  solverwith  Forest-Tomlin update is used internally  as  linear  algebra
  driver.
* minlpsetalgoipm() for sparse interior point method

INPUT PARAMETERS:
    N       -   problem size
    
OUTPUT PARAMETERS:
    State   -   optimizer in the default state

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpcreate(ae_int_t n, minlpstate* state, ae_state *_state)
{
    ae_int_t i;

    _minlpstate_clear(state);

    ae_assert(n>=1, "MinLPCreate: N<1", _state);
    
    /*
     * Initialize
     */
    state->n = n;
    state->m = 0;
    minlpsetalgoipm(state, 0.0, _state);
    state->ipmlambda = (double)(0);
    ae_vector_set_length(&state->c, n, _state);
    ae_vector_set_length(&state->s, n, _state);
    ae_vector_set_length(&state->bndl, n, _state);
    ae_vector_set_length(&state->bndu, n, _state);
    ae_vector_set_length(&state->xs, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->bndl.ptr.p_double[i] = (double)(0);
        state->bndu.ptr.p_double[i] = (double)(0);
        state->c.ptr.p_double[i] = 0.0;
        state->s.ptr.p_double[i] = 1.0;
        state->xs.ptr.p_double[i] = 1.0;
    }
    lpsolvers_clearreportfields(state, _state);
    xqcinit(n, &state->dummyxqc, _state);
    xccinit(n, &state->dummyxcc, _state);
}


/*************************************************************************
This function sets LP algorithm to revised dual simplex method.

ALGLIB implementation of dual simplex method supports advanced performance
and stability improvements like DSE pricing , bounds flipping  ratio  test
(aka long dual step), Forest-Tomlin update, shifting.

INPUT PARAMETERS:
    State   -   optimizer
    Eps     -   stopping condition, Eps>=0:
                * should be small number about 1E-6 or 1E-7.
                * zero value means that solver automatically selects good
                  value (can be different in different ALGLIB versions)
                * default value is zero
                Algorithm stops when relative error is less than Eps.

===== TRACING DSS SOLVER =================================================

DSS solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'DSS'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'DSS.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'DSS'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("DSS,PREC.F6", "path/to/trace.log")
> 

  -- ALGLIB --
     Copyright 08.11.2020 by Bochkanov Sergey
*************************************************************************/
void minlpsetalgodss(minlpstate* state, double eps, ae_state *_state)
{


    ae_assert(ae_isfinite(eps, _state), "MinLPSetAlgoDSS: Eps is not finite number", _state);
    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "MinLPSetAlgoDSS: Eps<0", _state);
    state->algokind = 1;
    if( ae_fp_eq(eps,(double)(0)) )
    {
        eps = 1.0E-6;
    }
    state->dsseps = eps;
}


/*************************************************************************
This function sets LP algorithm to sparse interior point method.

ALGORITHM INFORMATION:

* this  algorithm  is  our implementation  of  interior  point  method  as
  formulated by  R.J.Vanderbei, with minor modifications to the  algorithm
  (damped Newton directions are extensively used)
* like all interior point methods, this algorithm  tends  to  converge  in
  roughly same number of iterations (between 15 and 50) independently from
  the problem dimensionality

INPUT PARAMETERS:
    State   -   optimizer
    Eps     -   stopping condition, Eps>=0:
                * should be small number about 1E-6 or 1E-8.
                * zero value means that solver automatically selects good
                  value (can be different in different ALGLIB versions)
                * default value is zero
                Algorithm  stops  when  primal  error  AND  dual error AND
                duality gap are less than Eps.

===== TRACING IPM SOLVER =================================================

IPM solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'IPM'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'IPM.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'IPM'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("IPM,PREC.F6", "path/to/trace.log")
> 

  -- ALGLIB --
     Copyright 08.11.2020 by Bochkanov Sergey
*************************************************************************/
void minlpsetalgoipm(minlpstate* state, double eps, ae_state *_state)
{


    ae_assert(ae_isfinite(eps, _state), "MinLPSetAlgoIPM: Eps is not finite number", _state);
    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "MinLPSetAlgoIPM: Eps<0", _state);
    state->algokind = 2;
    state->ipmeps = eps;
    state->ipmlambda = 0.0;
}


/*************************************************************************
This function sets cost term for LP solver.

By default, cost term is zero.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    C       -   cost term, array[N].

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetcost(minlpstate* state,
     /* Real    */ const ae_vector* c,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;


    n = state->n;
    ae_assert(c->cnt>=n, "MinLPSetCost: Length(C)<N", _state);
    ae_assert(isfinitevector(c, n, _state), "MinLPSetCost: C contains infinite or NaN elements", _state);
    for(i=0; i<=n-1; i++)
    {
        state->c.ptr.p_double[i] = c->ptr.p_double[i];
    }
}


/*************************************************************************
This function sets scaling coefficients.

ALGLIB optimizers use scaling matrices to test stopping  conditions and as
preconditioner.

Scale of the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the
   function

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetscale(minlpstate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(s->cnt>=state->n, "MinLPSetScale: Length(S)<N", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "MinLPSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "MinLPSetScale: S contains zero elements", _state);
    }
    for(i=0; i<=state->n-1; i++)
    {
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function sets box constraints for LP solver (all variables  at  once,
different constraints for different variables).

The default state of constraints is to have all variables fixed  at  zero.
You have to overwrite it by your own constraint vector. Constraint  status
is preserved until constraints are  explicitly  overwritten  with  another
minlpsetbc()  call,   overwritten   with  minlpsetbcall(),  or   partially
overwritten with minlmsetbci() call.

Following types of constraints are supported:

    DESCRIPTION         CONSTRAINT              HOW TO SPECIFY
    fixed variable      x[i]=Bnd[i]             BndL[i]=BndU[i]
    lower bound         BndL[i]<=x[i]           BndU[i]=+INF
    upper bound         x[i]<=BndU[i]           BndL[i]=-INF
    range               BndL[i]<=x[i]<=BndU[i]  ...
    free variable       -                       BndL[I]=-INF, BndU[I]+INF

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
    BndU    -   upper bounds, array[N].

NOTE: infinite values can be specified by means of Double.PositiveInfinity
      and  Double.NegativeInfinity  (in  C#)  and  alglib::fp_posinf   and
      alglib::fp_neginf (in C++).
      
NOTE: you may replace infinities by very small/very large values,  but  it
      is not recommended because large numbers may introduce large numerical
      errors in the algorithm.
      
NOTE: if constraints for all variables are same you may use minlpsetbcall()
      which allows to specify constraints without using arrays.
      
NOTE: BndL>BndU will result in LP problem being recognized as infeasible.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetbc(minlpstate* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_assert(bndl->cnt>=n, "MinLPSetBC: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=n, "MinLPSetBC: Length(BndU)<N", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "MinLPSetBC: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "MinLPSetBC: BndU contains NAN or -INF", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
    }
}


/*************************************************************************
This function sets box constraints for LP solver (all variables  at  once,
same constraints for all variables)

The default state of constraints is to have all variables fixed  at  zero.
You have to overwrite it by your own constraint vector. Constraint  status
is preserved until constraints are  explicitly  overwritten  with  another
minlpsetbc() call or partially overwritten with minlpsetbcall().

Following types of constraints are supported:

    DESCRIPTION         CONSTRAINT              HOW TO SPECIFY
    fixed variable      x[i]=Bnd[i]             BndL[i]=BndU[i]
    lower bound         BndL[i]<=x[i]           BndU[i]=+INF
    upper bound         x[i]<=BndU[i]           BndL[i]=-INF
    range               BndL[i]<=x[i]<=BndU[i]  ...
    free variable       -                       BndL[I]=-INF, BndU[I]+INF

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bound, same for all variables
    BndU    -   upper bound, same for all variables

NOTE: infinite values can be specified by means of Double.PositiveInfinity
      and  Double.NegativeInfinity  (in  C#)  and  alglib::fp_posinf   and
      alglib::fp_neginf (in C++).
      
NOTE: you may replace infinities by very small/very large values,  but  it
      is not recommended because large numbers may introduce large numerical
      errors in the algorithm.
      
NOTE: minlpsetbc() can  be  used  to  specify  different  constraints  for
      different variables.
      
NOTE: BndL>BndU will result in LP problem being recognized as infeasible.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetbcall(minlpstate* state,
     double bndl,
     double bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_assert(ae_isfinite(bndl, _state)||ae_isneginf(bndl, _state), "MinLPSetBCAll: BndL is NAN or +INF", _state);
    ae_assert(ae_isfinite(bndu, _state)||ae_isposinf(bndu, _state), "MinLPSetBCAll: BndU is NAN or -INF", _state);
    for(i=0; i<=n-1; i++)
    {
        state->bndl.ptr.p_double[i] = bndl;
        state->bndu.ptr.p_double[i] = bndu;
    }
}


/*************************************************************************
This function sets box constraints for I-th variable (other variables are
not modified).

The default state of constraints is to have all variables fixed  at  zero.
You have to overwrite it by your own constraint vector.

Following types of constraints are supported:

    DESCRIPTION         CONSTRAINT              HOW TO SPECIFY
    fixed variable      x[i]=Bnd[i]             BndL[i]=BndU[i]
    lower bound         BndL[i]<=x[i]           BndU[i]=+INF
    upper bound         x[i]<=BndU[i]           BndL[i]=-INF
    range               BndL[i]<=x[i]<=BndU[i]  ...
    free variable       -                       BndL[I]=-INF, BndU[I]+INF

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    I       -   variable index, in [0,N)
    BndL    -   lower bound for I-th variable
    BndU    -   upper bound for I-th variable

NOTE: infinite values can be specified by means of Double.PositiveInfinity
      and  Double.NegativeInfinity  (in  C#)  and  alglib::fp_posinf   and
      alglib::fp_neginf (in C++).
      
NOTE: you may replace infinities by very small/very large values,  but  it
      is not recommended because large numbers may introduce large numerical
      errors in the algorithm.
      
NOTE: minlpsetbc() can  be  used  to  specify  different  constraints  for
      different variables.
      
NOTE: BndL>BndU will result in LP problem being recognized as infeasible.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetbci(minlpstate* state,
     ae_int_t i,
     double bndl,
     double bndu,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(i>=0&&i<n, "MinLPSetBCi: I is outside of [0,N)", _state);
    ae_assert(ae_isfinite(bndl, _state)||ae_isneginf(bndl, _state), "MinLPSetBCi: BndL is NAN or +INF", _state);
    ae_assert(ae_isfinite(bndu, _state)||ae_isposinf(bndu, _state), "MinLPSetBCi: BndU is NAN or -INF", _state);
    state->bndl.ptr.p_double[i] = bndl;
    state->bndu.ptr.p_double[i] = bndu;
}


/*************************************************************************
This function sets one-sided linear constraints A*x ~ AU, where "~" can be
a mix of "<=", "=" and ">=".

IMPORTANT: this function is provided here for compatibility with the  rest
           of ALGLIB optimizers which accept constraints  in  format  like
           this one. Many real-life problems feature two-sided constraints
           like a0 <= a*x <= a1. It is really inefficient to add them as a
           pair of one-sided constraints.
           
           Use minlpsetlc2dense(), minlpsetlc2(), minlpaddlc2()  (or   its
           sparse version) wherever possible.

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    A       -   linear constraints, array[K,N+1]. Each row of A represents
                one constraint, with first N elements being linear coefficients,
                and last element being right side.
    CT      -   constraint types, array[K]:
                * if CT[i]>0, then I-th constraint is A[i,*]*x >= A[i,n]
                * if CT[i]=0, then I-th constraint is A[i,*]*x  = A[i,n]
                * if CT[i]<0, then I-th constraint is A[i,*]*x <= A[i,n]
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A and CT.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetlc(minlpstate* state,
     /* Real    */ const ae_matrix* a,
     /* Integer */ const ae_vector* ct,
     ae_int_t k,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector al;
    ae_vector au;
    ae_int_t n;
    ae_int_t i;

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

    n = state->n;
    ae_assert(k>=0, "MinLPSetLC: K<0", _state);
    ae_assert(k==0||a->cols>=n+1, "MinLPSetLC: Cols(A)<N+1", _state);
    ae_assert(a->rows>=k, "MinLPSetLC: Rows(A)<K", _state);
    ae_assert(ct->cnt>=k, "MinLPSetLC: Length(CT)<K", _state);
    ae_assert(apservisfinitematrix(a, k, n+1, _state), "MinLPSetLC: A contains infinite or NaN values!", _state);
    
    /*
     * Handle zero K
     */
    if( k==0 )
    {
        state->m = 0;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Convert constraints to two-sided storage format, call another function
     */
    ae_vector_set_length(&al, k, _state);
    ae_vector_set_length(&au, k, _state);
    for(i=0; i<=k-1; i++)
    {
        if( ct->ptr.p_int[i]>0 )
        {
            al.ptr.p_double[i] = a->ptr.pp_double[i][n];
            au.ptr.p_double[i] = _state->v_posinf;
            continue;
        }
        if( ct->ptr.p_int[i]<0 )
        {
            al.ptr.p_double[i] = _state->v_neginf;
            au.ptr.p_double[i] = a->ptr.pp_double[i][n];
            continue;
        }
        al.ptr.p_double[i] = a->ptr.pp_double[i][n];
        au.ptr.p_double[i] = a->ptr.pp_double[i][n];
    }
    minlpsetlc2dense(state, a, &al, &au, k, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU.

This version accepts dense matrix as  input;  internally  LP  solver  uses
sparse storage  anyway  (most  LP  problems  are  sparse),  but  for  your
convenience it may accept dense inputs. This  function  overwrites  linear
constraints set by previous calls (if such calls were made).

We recommend you to use sparse version of this function unless  you  solve
small-scale LP problem (less than few hundreds of variables).

NOTE: there also exist several versions of this function:
      * one-sided dense version which  accepts  constraints  in  the  same
        format as one used by QP and  NLP solvers
      * two-sided sparse version which accepts sparse matrix
      * two-sided dense  version which allows you to add constraints row by row
      * two-sided sparse version which allows you to add constraints row by row

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetlc2dense(minlpstate* state,
     /* Real    */ const ae_matrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t k,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t nz;
    ae_vector nrs;

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

    n = state->n;
    ae_assert(k>=0, "MinLPSetLC2Dense: K<0", _state);
    ae_assert(k==0||a->cols>=n, "MinLPSetLC2Dense: Cols(A)<N", _state);
    ae_assert(a->rows>=k, "MinLPSetLC2Dense: Rows(A)<K", _state);
    ae_assert(al->cnt>=k, "MinLPSetLC2Dense: Length(AL)<K", _state);
    ae_assert(au->cnt>=k, "MinLPSetLC2Dense: Length(AU)<K", _state);
    ae_assert(apservisfinitematrix(a, k, n, _state), "MinLPSetLC2Dense: A contains infinite or NaN values!", _state);
    
    /*
     * Count actual (different from -INF<=A*x<=+INF) constraints;
     * count non-zero elements in each row.
     */
    ae_vector_set_length(&nrs, k, _state);
    state->m = k;
    if( state->m==0 )
    {
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=k-1; i++)
    {
        ae_assert(ae_isfinite(al->ptr.p_double[i], _state)||ae_isneginf(al->ptr.p_double[i], _state), "MinLPSetLC2Dense: AL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(au->ptr.p_double[i], _state)||ae_isposinf(au->ptr.p_double[i], _state), "MinLPSetLC2Dense: AU contains NAN or -INF", _state);
        nz = 0;
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_neq(a->ptr.pp_double[i][j],(double)(0)) )
            {
                inc(&nz, _state);
            }
        }
        nrs.ptr.p_int[i] = nz;
    }
    
    /*
     * Allocate storage, copy
     */
    rvectorsetlengthatleast(&state->al, state->m, _state);
    rvectorsetlengthatleast(&state->au, state->m, _state);
    sparsecreatecrsbuf(state->m, n, &nrs, &state->a, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_neq(a->ptr.pp_double[i][j],(double)(0)) )
            {
                sparseset(&state->a, i, j, a->ptr.pp_double[i][j], _state);
            }
        }
        state->al.ptr.p_double[i] = al->ptr.p_double[i];
        state->au.ptr.p_double[i] = au->ptr.p_double[i];
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
sparse constraining matrix A. Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    A       -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpsetlc2(minlpstate* state,
     const sparsematrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t k,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;


    n = state->n;
    
    /*
     * Quick exit
     */
    if( k==0 )
    {
        state->m = 0;
        return;
    }
    
    /*
     * Integrity checks
     */
    ae_assert(k>0, "MinLPSetLC2: K<0", _state);
    ae_assert(sparsegetncols(a, _state)==n, "MinLPSetLC2: Cols(A)<>N", _state);
    ae_assert(sparsegetnrows(a, _state)==k, "MinLPSetLC2: Rows(A)<>K", _state);
    ae_assert(al->cnt>=k, "MinLPSetLC2: Length(AL)<K", _state);
    ae_assert(au->cnt>=k, "MinLPSetLC2: Length(AU)<K", _state);
    for(i=0; i<=k-1; i++)
    {
        ae_assert(ae_isfinite(al->ptr.p_double[i], _state)||ae_isneginf(al->ptr.p_double[i], _state), "MinLPSetLC2: AL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(au->ptr.p_double[i], _state)||ae_isposinf(au->ptr.p_double[i], _state), "MinLPSetLC2: AU contains NAN or -INF", _state);
    }
    
    /*
     * Copy
     */
    state->m = k;
    sparsecopytocrsbuf(a, &state->a, _state);
    rvectorsetlengthatleast(&state->al, k, _state);
    rvectorsetlengthatleast(&state->au, k, _state);
    for(i=0; i<=k-1; i++)
    {
        state->al.ptr.p_double[i] = al->ptr.p_double[i];
        state->au.ptr.p_double[i] = au->ptr.p_double[i];
    }
}


/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present constraints.

This version accepts dense constraint vector as input, but  sparsifies  it
for internal storage and processing. Thus, time to add one  constraint  in
is O(N) - we have to scan entire array of length N. Sparse version of this
function is order of magnitude faster for  constraints  with  just  a  few
nonzeros per row.

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    A       -   linear constraint coefficient, array[N], right side is NOT
                included.
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint Ai*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint Ai*x<=AU
                * AU=+INF  => one-sided constraint AL<=Ai*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpaddlc2dense(minlpstate* state,
     /* Real    */ const ae_vector* a,
     double al,
     double au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;
    ae_int_t nnz;


    n = state->n;
    ae_assert(a->cnt>=n, "MinLPAddLC2Dense: Length(A)<N", _state);
    ae_assert(isfinitevector(a, n, _state), "MinLPAddLC2Dense: A contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(al, _state)||ae_isneginf(al, _state), "MinLPAddLC2Dense: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(au, _state)||ae_isposinf(au, _state), "MinLPAddLC2Dense: AU is NAN or -INF", _state);
    ivectorsetlengthatleast(&state->adddtmpi, n, _state);
    rvectorsetlengthatleast(&state->adddtmpr, n, _state);
    nnz = 0;
    for(i=0; i<=n-1; i++)
    {
        if( a->ptr.p_double[i]!=0.0 )
        {
            state->adddtmpi.ptr.p_int[nnz] = i;
            state->adddtmpr.ptr.p_double[nnz] = a->ptr.p_double[i];
            nnz = nnz+1;
        }
    }
    minlpaddlc2(state, &state->adddtmpi, &state->adddtmpr, nnz, al, au, _state);
}


/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present constraints.

Constraint is passed in compressed format: as list of non-zero entries  of
coefficient vector A. Such approach is more efficient than  dense  storage
for highly sparse constraint vectors.

INPUT PARAMETERS:
    State   -   structure previously allocated with minlpcreate() call.
    IdxA    -   array[NNZ], indexes of non-zero elements of A:
                * can be unsorted
                * can include duplicate indexes (corresponding entries  of
                  ValA[] will be summed)
    ValA    -   array[NNZ], values of non-zero elements of A
    NNZ     -   number of non-zero coefficients in A
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minlpaddlc2(minlpstate* state,
     /* Integer */ const ae_vector* idxa,
     /* Real    */ const ae_vector* vala,
     ae_int_t nnz,
     double al,
     double au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t offs;
    ae_int_t offsdst;
    ae_int_t m;
    ae_int_t n;
    ae_int_t didx;
    ae_int_t uidx;


    m = state->m;
    n = state->n;
    
    /*
     * Check inputs
     */
    ae_assert(nnz>=0, "MinLPAddLC2: NNZ<0", _state);
    ae_assert(idxa->cnt>=nnz, "MinLPAddLC2: Length(IdxA)<NNZ", _state);
    ae_assert(vala->cnt>=nnz, "MinLPAddLC2: Length(ValA)<NNZ", _state);
    for(i=0; i<=nnz-1; i++)
    {
        ae_assert(idxa->ptr.p_int[i]>=0&&idxa->ptr.p_int[i]<n, "MinLPAddLC2: IdxA contains indexes outside of [0,N) range", _state);
    }
    ae_assert(isfinitevector(vala, nnz, _state), "MinLPAddLC2: ValA contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(al, _state)||ae_isneginf(al, _state), "MinLPAddLC2Dense: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(au, _state)||ae_isposinf(au, _state), "MinLPAddLC2Dense: AU is NAN or -INF", _state);
    
    /*
     * If M=0, it means that A is uninitialized.
     * Prepare sparse matrix structure
     */
    if( m==0 )
    {
        state->a.matrixtype = 1;
        state->a.m = 0;
        state->a.n = n;
        state->a.ninitialized = 0;
        ivectorsetlengthatleast(&state->a.ridx, 1, _state);
        state->a.ridx.ptr.p_int[0] = 0;
    }
    
    /*
     * Reallocate storage
     */
    offs = state->a.ridx.ptr.p_int[m];
    ivectorgrowto(&state->a.idx, offs+nnz, _state);
    rvectorgrowto(&state->a.vals, offs+nnz, _state);
    ivectorgrowto(&state->a.didx, m+1, _state);
    ivectorgrowto(&state->a.uidx, m+1, _state);
    ivectorgrowto(&state->a.ridx, m+2, _state);
    rvectorgrowto(&state->al, m+1, _state);
    rvectorgrowto(&state->au, m+1, _state);
    
    /*
     * If NNZ=0, perform quick and simple row append. 
     */
    if( nnz==0 )
    {
        state->a.didx.ptr.p_int[m] = state->a.ridx.ptr.p_int[m];
        state->a.uidx.ptr.p_int[m] = state->a.ridx.ptr.p_int[m];
        state->a.ridx.ptr.p_int[m+1] = state->a.ridx.ptr.p_int[m];
        state->al.ptr.p_double[m] = al;
        state->au.ptr.p_double[m] = au;
        state->a.m = m+1;
        state->m = m+1;
        return;
    }
    
    /*
     * Now we are sure that A contains properly initialized sparse
     * matrix (or some appropriate dummy for M=0) and we have NNZ>0
     * (no need to care about degenerate cases).
     *
     * Append rows to A:
     * * append data
     * * sort in place
     * * merge duplicate indexes
     * * compute DIdx and UIdx
     *
     */
    for(i=0; i<=nnz-1; i++)
    {
        state->a.idx.ptr.p_int[offs+i] = idxa->ptr.p_int[i];
        state->a.vals.ptr.p_double[offs+i] = vala->ptr.p_double[i];
    }
    tagsortmiddleir(&state->a.idx, &state->a.vals, offs, nnz, _state);
    offsdst = offs;
    for(i=1; i<=nnz-1; i++)
    {
        if( state->a.idx.ptr.p_int[offsdst]!=state->a.idx.ptr.p_int[offs+i] )
        {
            offsdst = offsdst+1;
            state->a.idx.ptr.p_int[offsdst] = state->a.idx.ptr.p_int[offs+i];
            state->a.vals.ptr.p_double[offsdst] = state->a.vals.ptr.p_double[offs+i];
        }
        else
        {
            state->a.vals.ptr.p_double[offsdst] = state->a.vals.ptr.p_double[offsdst]+state->a.vals.ptr.p_double[offs+i];
        }
    }
    nnz = offsdst-offs+1;
    uidx = -1;
    didx = -1;
    for(j=offs; j<=offsdst; j++)
    {
        k = state->a.idx.ptr.p_int[j];
        if( k==m )
        {
            didx = j;
        }
        else
        {
            if( k>m&&uidx==-1 )
            {
                uidx = j;
                break;
            }
        }
    }
    if( uidx==-1 )
    {
        uidx = offsdst+1;
    }
    if( didx==-1 )
    {
        didx = uidx;
    }
    state->a.didx.ptr.p_int[m] = didx;
    state->a.uidx.ptr.p_int[m] = uidx;
    state->a.ridx.ptr.p_int[m+1] = offsdst+1;
    state->a.m = m+1;
    state->a.ninitialized = state->a.ninitialized+nnz;
    state->al.ptr.p_double[m] = al;
    state->au.ptr.p_double[m] = au;
    state->m = m+1;
}


/*************************************************************************
This function solves LP problem.

INPUT PARAMETERS:
    State   -   algorithm state

You should use minlpresults() function to access results  after  calls  to
this function.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey.
*************************************************************************/
void minlpoptimize(minlpstate* state, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    double v;
    dualsimplexsettings settings;
    ae_vector dummy1;
    ae_matrix dummy;
    dualsimplexbasis dummybasis;
    ae_bool dotracepresolve;

    ae_frame_make(_state, &_frame_block);
    memset(&settings, 0, sizeof(settings));
    memset(&dummy1, 0, sizeof(dummy1));
    memset(&dummy, 0, sizeof(dummy));
    memset(&dummybasis, 0, sizeof(dummybasis));
    _dualsimplexsettings_init(&settings, _state, ae_true);
    ae_vector_init(&dummy1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&dummy, 0, 0, DT_REAL, _state, ae_true);
    _dualsimplexbasis_init(&dummybasis, _state, ae_true);

    n = state->n;
    m = state->m;
    dotracepresolve = ae_is_trace_enabled("IPM")||ae_is_trace_enabled("DSS");
    lpsolvers_clearreportfields(state, _state);
    
    /*
     * Run presolver
     */
    presolvelp(&state->s, &state->c, &state->bndl, &state->bndu, n, &state->a, &state->al, &state->au, m, &state->dummyxqc, &state->dummyxcc, dotracepresolve, &state->presolver, _state);
    if( state->presolver.problemstatus==-3||state->presolver.problemstatus==-2 )
    {
        state->repterminationtype = state->presolver.problemstatus;
        state->repn = n;
        state->repm = m;
        rsetallocv(n, 0.0, &state->xs, _state);
        rsetallocv(n, 0.0, &state->lagbc, _state);
        rsetallocv(m, 0.0, &state->laglc, _state);
        isetallocv(n+m, 0, &state->cs, _state);
        state->repf = (double)(0);
        state->repprimalerror = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            if( ae_isfinite(state->bndl.ptr.p_double[i], _state) )
            {
                state->repprimalerror = ae_maxreal(state->repprimalerror, state->bndl.ptr.p_double[i]-(double)0, _state);
            }
            if( ae_isfinite(state->bndu.ptr.p_double[i], _state) )
            {
                state->repprimalerror = ae_maxreal(state->repprimalerror, (double)0-state->bndu.ptr.p_double[i], _state);
            }
        }
        for(i=0; i<=m-1; i++)
        {
            if( ae_isfinite(state->al.ptr.p_double[i], _state) )
            {
                state->repprimalerror = ae_maxreal(state->repprimalerror, state->al.ptr.p_double[i]-(double)0, _state);
            }
            if( ae_isfinite(state->au.ptr.p_double[i], _state) )
            {
                state->repprimalerror = ae_maxreal(state->repprimalerror, (double)0-state->au.ptr.p_double[i], _state);
            }
        }
        state->repdualerror = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            state->repdualerror = ae_maxreal(state->repdualerror, ae_fabs(state->c.ptr.p_double[i], _state), _state);
        }
        state->repslackerror = (double)(0);
        ae_frame_leave(_state);
        return;
    }
    ae_assert(state->presolver.problemstatus==0, "MINLP: integrity check 4432 failed", _state);
    
    /*
     * Call current solver
     */
    if( state->algokind==1||state->algokind==2 )
    {
        
        /*
         * If presolver did NOT remove all variables (NewN>0), call the current solver
         */
        if( state->presolver.newn>0 )
        {
            if( state->algokind==1 )
            {
                
                /*
                 * Dual simplex method with presolve
                 */
                dsssettingsinit(&settings, _state);
                settings.xtolabs = state->dsseps;
                settings.dtolabs = state->dsseps;
                dssinit(state->presolver.newn, &state->dss, _state);
                dsssetproblem(&state->dss, &state->presolver.c, &state->presolver.bndl, &state->presolver.bndu, &dummy, &state->presolver.sparsea, 1, &state->presolver.al, &state->presolver.au, state->presolver.newm, &dummybasis, lpsolvers_alllogicalsbasis, &settings, _state);
                dssoptimize(&state->dss, &settings, _state);
                
                /*
                 * Export results, convert from presolve
                 */
                rcopyallocv(state->presolver.newn, &state->dss.repx, &state->xs, _state);
                rcopyallocv(state->presolver.newn, &state->dss.replagbc, &state->lagbc, _state);
                rcopyallocv(state->presolver.newm, &state->dss.replaglc, &state->laglc, _state);
                icopyallocv(state->presolver.newn+state->presolver.newm, &state->dss.repstats, &state->cs, _state);
                state->repiterationscount = state->dss.repiterationscount;
                state->repterminationtype = state->dss.repterminationtype;
            }
            if( state->algokind==2 )
            {
                
                /*
                 * Interior point method with presolve
                 */
                rsetallocv(state->presolver.newn, 1.0, &state->units, _state);
                rsetallocv(state->presolver.newn, 0.0, &state->zeroorigin, _state);
                sparsecreatesksbandbuf(state->presolver.newn, state->presolver.newn, 0, &state->ipmquadratic, _state);
                for(i=0; i<=state->presolver.newn-1; i++)
                {
                    sparseset(&state->ipmquadratic, i, i, state->ipmlambda, _state);
                }
                sparseconverttocrs(&state->ipmquadratic, _state);
                ipm2init(&state->ipm2, &state->units, &state->zeroorigin, state->presolver.newn, &dummy, &state->ipmquadratic, 1, ae_false, &dummy, &dummy1, 0, &state->presolver.c, 0.0, &state->presolver.bndl, &state->presolver.bndu, &state->presolver.sparsea, state->presolver.newm, &dummy, 0, &state->presolver.al, &state->presolver.au, ae_false, ae_false, _state);
                ipm2setcond(&state->ipm2, state->ipmeps, state->ipmeps, state->ipmeps, _state);
                ipm2optimize(&state->ipm2, ae_true, &state->xs, &state->lagbc, &state->laglc, &state->repterminationtype, _state);
                state->repiterationscount = state->ipm2.repiterationscount;
                isetallocv(state->presolver.newn+state->presolver.newm, 0, &state->cs, _state);
            }
        }
        else
        {
            
            /*
             * Presolver removed all variables, manually set up XS and Lagrange multipliers
             */
            rsetallocv(state->presolver.newm, 0.0, &state->laglc, _state);
            isetallocv(state->presolver.newn+state->presolver.newm, 0, &state->cs, _state);
            state->repterminationtype = 1;
            state->repiterationscount = 0;
        }
        
        /*
         * Convert back from presolved format
         */
        presolvebwd(&state->presolver, &state->xs, &state->cs, ae_true, &state->lagbc, &state->laglc, &state->dummylagqc, _state);
        state->repn = n;
        state->repm = m;
        
        /*
         * Compute F, primal and dual errors
         */
        state->repf = rdotv(n, &state->xs, &state->c, _state);
        state->repprimalerror = (double)(0);
        state->repdualerror = (double)(0);
        state->repslackerror = (double)(0);
        rcopyallocv(n, &state->c, &state->tmpg, _state);
        if( m>0 )
        {
            sparsemv(&state->a, &state->xs, &state->tmpax, _state);
            sparsegemv(&state->a, 1.0, 1, &state->laglc, 0, 1.0, &state->tmpg, 0, _state);
        }
        raddv(n, 1.0, &state->lagbc, &state->tmpg, _state);
        for(i=0; i<=n-1; i++)
        {
            if( ae_isfinite(state->bndl.ptr.p_double[i], _state) )
            {
                state->repprimalerror = ae_maxreal(state->repprimalerror, state->bndl.ptr.p_double[i]-state->xs.ptr.p_double[i], _state);
                state->repslackerror = ae_maxreal(state->repslackerror, ae_maxreal(state->xs.ptr.p_double[i]-state->bndl.ptr.p_double[i], 0.0, _state)*ae_maxreal(-state->lagbc.ptr.p_double[i], 0.0, _state), _state);
            }
            if( ae_isfinite(state->bndu.ptr.p_double[i], _state) )
            {
                state->repprimalerror = ae_maxreal(state->repprimalerror, state->xs.ptr.p_double[i]-state->bndu.ptr.p_double[i], _state);
                state->repslackerror = ae_maxreal(state->repslackerror, ae_maxreal(state->bndu.ptr.p_double[i]-state->xs.ptr.p_double[i], 0.0, _state)*ae_maxreal(state->lagbc.ptr.p_double[i], 0.0, _state), _state);
            }
            state->repdualerror = ae_maxreal(state->repdualerror, ae_fabs(state->tmpg.ptr.p_double[i], _state), _state);
        }
        for(i=0; i<=m-1; i++)
        {
            v = state->tmpax.ptr.p_double[i];
            if( ae_isfinite(state->al.ptr.p_double[i], _state) )
            {
                state->repprimalerror = ae_maxreal(state->repprimalerror, state->al.ptr.p_double[i]-v, _state);
                state->repslackerror = ae_maxreal(state->repslackerror, ae_maxreal(v-state->al.ptr.p_double[i], 0.0, _state)*ae_maxreal(-state->laglc.ptr.p_double[i], 0.0, _state), _state);
            }
            if( ae_isfinite(state->au.ptr.p_double[i], _state) )
            {
                state->repprimalerror = ae_maxreal(state->repprimalerror, v-state->au.ptr.p_double[i], _state);
                state->repslackerror = ae_maxreal(state->repslackerror, ae_maxreal(state->au.ptr.p_double[i]-v, 0.0, _state)*ae_maxreal(state->laglc.ptr.p_double[i], 0.0, _state), _state);
            }
        }
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Integrity check failed - unknown solver
     */
    ae_assert(ae_false, "MinQPOptimize: integrity check failed - unknown solver", _state);
    ae_frame_leave(_state);
}


/*************************************************************************
LP solver results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[N], solution (on failure: last trial point)
    Rep     -   optimization report. You should check Rep.TerminationType,
                which contains completion code, and you may check  another
                fields which contain another information  about  algorithm
                functioning.
                
                Failure codes returned by algorithm are:
                * -4    LP problem is primal unbounded (dual infeasible)
                * -3    LP problem is primal infeasible (dual unbounded)
                * -2    IPM solver detected that problem is either
                        infeasible or unbounded
                
                Success codes:
                *  1..4 successful completion
                *  5    MaxIts steps was taken

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minlpresults(const minlpstate* state,
     /* Real    */ ae_vector* x,
     minlpreport* rep,
     ae_state *_state)
{

    ae_vector_clear(x);
    _minlpreport_clear(rep);

    minlpresultsbuf(state, x, rep, _state);
}


/*************************************************************************
LP results

Buffered implementation of MinLPResults() which uses pre-allocated  buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 11.01.2011 by Bochkanov Sergey
*************************************************************************/
void minlpresultsbuf(const minlpstate* state,
     /* Real    */ ae_vector* x,
     minlpreport* rep,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t repn;
    ae_int_t repm;


    repn = state->repn;
    repm = state->repm;
    if( x->cnt<repn )
    {
        ae_vector_set_length(x, repn, _state);
    }
    ae_vector_set_length(&rep->y, repm, _state);
    ae_vector_set_length(&rep->stats, repn+repm, _state);
    rep->f = state->repf;
    rep->primalerror = state->repprimalerror;
    rep->dualerror = state->repdualerror;
    rep->slackerror = state->repslackerror;
    rep->iterationscount = state->repiterationscount;
    rep->terminationtype = state->repterminationtype;
    rcopyallocv(repm, &state->laglc, &rep->laglc, _state);
    rcopyallocv(repn, &state->lagbc, &rep->lagbc, _state);
    for(i=0; i<=repn-1; i++)
    {
        x->ptr.p_double[i] = state->xs.ptr.p_double[i];
    }
    for(i=0; i<=repm-1; i++)
    {
        rep->y.ptr.p_double[i] = -rep->laglc.ptr.p_double[i];
    }
    for(i=0; i<=repn+repm-1; i++)
    {
        rep->stats.ptr.p_int[i] = state->cs.ptr.p_int[i];
    }
}


/*************************************************************************
Clear report fields prior to the optimization.

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey.
*************************************************************************/
static void lpsolvers_clearreportfields(minlpstate* state,
     ae_state *_state)
{


    state->repf = 0.0;
    state->repprimalerror = 0.0;
    state->repdualerror = 0.0;
    state->repiterationscount = 0;
    state->repterminationtype = 0;
    state->repn = 0;
    state->repm = 0;
}


void _minlpstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minlpstate *p = (minlpstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->c, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->a, _state, make_automatic);
    ae_vector_init(&p->al, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->au, 0, DT_REAL, _state, make_automatic);
    _xquadraticconstraints_init(&p->dummyxqc, _state, make_automatic);
    _xconicconstraints_init(&p->dummyxcc, _state, make_automatic);
    ae_vector_init(&p->xs, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagbc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->laglc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cs, 0, DT_INT, _state, make_automatic);
    _dualsimplexstate_init(&p->dss, _state, make_automatic);
    _vipmstate_init(&p->ipm, _state, make_automatic);
    _ipm2state_init(&p->ipm2, _state, make_automatic);
    ae_vector_init(&p->adddtmpi, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->adddtmpr, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpax, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpg, 0, DT_REAL, _state, make_automatic);
    _presolveinfo_init(&p->presolver, _state, make_automatic);
    ae_vector_init(&p->zeroorigin, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->units, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->ipmquadratic, _state, make_automatic);
    ae_vector_init(&p->dummylagqc, 0, DT_REAL, _state, make_automatic);
}


void _minlpstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minlpstate       *dst = (minlpstate*)_dst;
    const minlpstate *src = (const minlpstate*)_src;
    dst->n = src->n;
    dst->algokind = src->algokind;
    dst->ipmlambda = src->ipmlambda;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->c, &src->c, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    dst->m = src->m;
    _sparsematrix_init_copy(&dst->a, &src->a, _state, make_automatic);
    ae_vector_init_copy(&dst->al, &src->al, _state, make_automatic);
    ae_vector_init_copy(&dst->au, &src->au, _state, make_automatic);
    _xquadraticconstraints_init_copy(&dst->dummyxqc, &src->dummyxqc, _state, make_automatic);
    _xconicconstraints_init_copy(&dst->dummyxcc, &src->dummyxcc, _state, make_automatic);
    ae_vector_init_copy(&dst->xs, &src->xs, _state, make_automatic);
    ae_vector_init_copy(&dst->lagbc, &src->lagbc, _state, make_automatic);
    ae_vector_init_copy(&dst->laglc, &src->laglc, _state, make_automatic);
    ae_vector_init_copy(&dst->cs, &src->cs, _state, make_automatic);
    dst->repf = src->repf;
    dst->repprimalerror = src->repprimalerror;
    dst->repdualerror = src->repdualerror;
    dst->repslackerror = src->repslackerror;
    dst->repiterationscount = src->repiterationscount;
    dst->repterminationtype = src->repterminationtype;
    dst->repn = src->repn;
    dst->repm = src->repm;
    dst->dsseps = src->dsseps;
    dst->ipmeps = src->ipmeps;
    _dualsimplexstate_init_copy(&dst->dss, &src->dss, _state, make_automatic);
    _vipmstate_init_copy(&dst->ipm, &src->ipm, _state, make_automatic);
    _ipm2state_init_copy(&dst->ipm2, &src->ipm2, _state, make_automatic);
    ae_vector_init_copy(&dst->adddtmpi, &src->adddtmpi, _state, make_automatic);
    ae_vector_init_copy(&dst->adddtmpr, &src->adddtmpr, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpax, &src->tmpax, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpg, &src->tmpg, _state, make_automatic);
    _presolveinfo_init_copy(&dst->presolver, &src->presolver, _state, make_automatic);
    ae_vector_init_copy(&dst->zeroorigin, &src->zeroorigin, _state, make_automatic);
    ae_vector_init_copy(&dst->units, &src->units, _state, make_automatic);
    _sparsematrix_init_copy(&dst->ipmquadratic, &src->ipmquadratic, _state, make_automatic);
    ae_vector_init_copy(&dst->dummylagqc, &src->dummylagqc, _state, make_automatic);
}


void _minlpstate_clear(void* _p)
{
    minlpstate *p = (minlpstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->c);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    _sparsematrix_clear(&p->a);
    ae_vector_clear(&p->al);
    ae_vector_clear(&p->au);
    _xquadraticconstraints_clear(&p->dummyxqc);
    _xconicconstraints_clear(&p->dummyxcc);
    ae_vector_clear(&p->xs);
    ae_vector_clear(&p->lagbc);
    ae_vector_clear(&p->laglc);
    ae_vector_clear(&p->cs);
    _dualsimplexstate_clear(&p->dss);
    _vipmstate_clear(&p->ipm);
    _ipm2state_clear(&p->ipm2);
    ae_vector_clear(&p->adddtmpi);
    ae_vector_clear(&p->adddtmpr);
    ae_vector_clear(&p->tmpax);
    ae_vector_clear(&p->tmpg);
    _presolveinfo_clear(&p->presolver);
    ae_vector_clear(&p->zeroorigin);
    ae_vector_clear(&p->units);
    _sparsematrix_clear(&p->ipmquadratic);
    ae_vector_clear(&p->dummylagqc);
}


void _minlpstate_destroy(void* _p)
{
    minlpstate *p = (minlpstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->c);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    _sparsematrix_destroy(&p->a);
    ae_vector_destroy(&p->al);
    ae_vector_destroy(&p->au);
    _xquadraticconstraints_destroy(&p->dummyxqc);
    _xconicconstraints_destroy(&p->dummyxcc);
    ae_vector_destroy(&p->xs);
    ae_vector_destroy(&p->lagbc);
    ae_vector_destroy(&p->laglc);
    ae_vector_destroy(&p->cs);
    _dualsimplexstate_destroy(&p->dss);
    _vipmstate_destroy(&p->ipm);
    _ipm2state_destroy(&p->ipm2);
    ae_vector_destroy(&p->adddtmpi);
    ae_vector_destroy(&p->adddtmpr);
    ae_vector_destroy(&p->tmpax);
    ae_vector_destroy(&p->tmpg);
    _presolveinfo_destroy(&p->presolver);
    ae_vector_destroy(&p->zeroorigin);
    ae_vector_destroy(&p->units);
    _sparsematrix_destroy(&p->ipmquadratic);
    ae_vector_destroy(&p->dummylagqc);
}


void _minlpreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minlpreport *p = (minlpreport*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->lagbc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->laglc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->y, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->stats, 0, DT_INT, _state, make_automatic);
}


void _minlpreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minlpreport       *dst = (minlpreport*)_dst;
    const minlpreport *src = (const minlpreport*)_src;
    dst->f = src->f;
    ae_vector_init_copy(&dst->lagbc, &src->lagbc, _state, make_automatic);
    ae_vector_init_copy(&dst->laglc, &src->laglc, _state, make_automatic);
    ae_vector_init_copy(&dst->y, &src->y, _state, make_automatic);
    ae_vector_init_copy(&dst->stats, &src->stats, _state, make_automatic);
    dst->primalerror = src->primalerror;
    dst->dualerror = src->dualerror;
    dst->slackerror = src->slackerror;
    dst->iterationscount = src->iterationscount;
    dst->terminationtype = src->terminationtype;
}


void _minlpreport_clear(void* _p)
{
    minlpreport *p = (minlpreport*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->lagbc);
    ae_vector_clear(&p->laglc);
    ae_vector_clear(&p->y);
    ae_vector_clear(&p->stats);
}


void _minlpreport_destroy(void* _p)
{
    minlpreport *p = (minlpreport*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->lagbc);
    ae_vector_destroy(&p->laglc);
    ae_vector_destroy(&p->y);
    ae_vector_destroy(&p->stats);
}


#endif
#if defined(AE_COMPILE_NLS) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
                DERIVATIVE-FREE NONLINEAR LEAST SQUARES

DESCRIPTION:

This function creates a NLS  solver  configured  to  solve  a  constrained
nonlinear least squares problem

    min F(x) = f[0]^2 + f[1]^2 + ... + f[m-1]^2

where f[i] are available, but not their derivatives.

The  functions  f[i]  are  assumed  to  be   smooth,  but  may  have  some
amount of numerical noise (either  random  noise  or  deterministic  noise
arising from numerical simulations or other complex numerical processes).

INPUT PARAMETERS:
    N       -   dimension, N>1
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    M       -   number of functions f[i], M>=1
    X       -   initial point, array[N]

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlscreatedfo(ae_int_t n,
     ae_int_t m,
     /* Real    */ const ae_vector* x,
     nlsstate* state,
     ae_state *_state)
{

    _nlsstate_clear(state);

    ae_assert(n>=1, "NLSCreateDFO: N<1!", _state);
    ae_assert(m>=1, "NLSCreateDFO: M<1!", _state);
    ae_assert(x->cnt>=n, "NLSCreateDFO: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, n, _state), "NLSCreateDFO: X contains infinite or NaN values!", _state);
    
    /*
     * initialize
     */
    state->protocolversion = 2;
    state->n = n;
    state->m = m;
    state->problemtype = 0;
    state->diffstep = (double)(0);
    state->xrep = ae_false;
    state->rad0 = (double)(0);
    state->maxfev = 0;
    critinitdefault(&state->criteria, _state);
    bsetallocv(n, ae_false, &state->hasbndl, _state);
    bsetallocv(n, ae_false, &state->hasbndu, _state);
    rsetallocv(n, _state->v_neginf, &state->bndl, _state);
    rsetallocv(n, _state->v_posinf, &state->bndu, _state);
    state->nnlc = 0;
    state->cntlc = 0;
    rsetallocv(n, 1.0, &state->s, _state);
    nlssetalgo2ps(state, 0, _state);
    nlsrestartfrom(state, x, _state);
}


/*************************************************************************
This function sets the derivative-free NLS optimization algorithm  to  the
2PS (2-Point Stencil) algorithm.

This solver is recommended for the following cases:
* an expensive target function is minimized  by the commercial ALGLIB with
  callback  parallelism  activated  (see  ALGLIB Reference Manual for more
  information about parallel callbacks)
* an inexpensive target function is minimized by any ALGLIB edition  (free
  or commercial)

This function works only with solvers created with nlscreatedfo(), i.e. in
the derivative-free mode.

See the end of this comment for more information about the algorithm.

INPUT PARAMETERS:
    State           -   solver; must be created with nlscreatedfo() call -
                        passing  an  object   initialized   with   another
                        constructor function will result in an exception.
    NNoisyRestarts  -   number of restarts performed to combat a noise  in
                        the target. (see below, section 'RESTARTS', for  a
                        detailed discussion):
                        * 0     means that no restarts is performed, the
                                solver stops as soon as stopping  criteria
                                are met. Recommended for noise-free tasks.
                        * >0    means  that when the stopping criteria are
                                met, the solver will  perform  a  restart:
                                increase the trust   radius  and  resample
                                points. It often helps to  solve  problems
                                with random or deterministic noise.
                
ALGORITHM DESCRIPTION AND DISCUSSION

The 2PS algorithm is a derivative-free model-based nonlinear least squares
solver which builds local models by evaluating the target at  N additional
points around the current one, with geometry similar to the 2-point finite
difference stencil.

Similarly to the Levenberg-Marquardt algorithm, the solver shows quadratic
convergence despite the fact that it builds linear models.

When compared with the DFO-LSA solver, the 2PS algorithm has the following
distinctive properties:
* the 2PS algorithm performs more target function evaluations per iteration
  (at least N+1 instead of 1-2 usually performed by the DFO-LSA)
* 2PS requires several times less iterations than the DFO-LSA because each
  iteration extracts and utilizes more information about the  target. This
  difference tends to exaggerate when N increases
* contrary to that, DFO-LSA is much better at reuse of previously computed
  points. Thus, DFO-LSA needs several times less target  evaluations  than
  2PS, usually about 3-4 times less (this ratio seems to be more  or  less
  constant independently of N).

The summary is that:
* for  expensive  targets  2PS  provides better parallelism potential than
  DFO-LSA because the former issues many  simultaneous  target  evaluation
  requests which can be easily parallelized. It  is  possible  for  2PS to
  outperform DFO-LSA by parallelism  alone,  despite  the  fact  that  the
  latter needs 3-4 times less target function evaluations.
* for inexpensive targets 2PS may win  because  it  needs many  times less
  iterations, and thus the overhead associated with the working set updates
  is also many times less.
  
RESTARTS

Restarts is a strategy used to deal with random and deterministic noise in
the target/constraints.

Noise in the objective function can be random, arising from measurement or
simulation   uncertainty,  or  deterministic,   resulting   from   complex
underlying phenomena like numerical errors or branches in the target.  Its
influence is especially high at last stages of the optimization, when  all
computations are performed with small values of a trust radius.

Restarts allow the optimization algorithm to be robust against both  types
of noise by temporarily increasing a trust radius in order  to  capture  a
global structure of the target and avoid being trapped  by  noise-produced
local features.

A restart is usually performed when the stopping criteria  are  triggered.
Instead of stopping, the solver increases  trust  radius  to  its  initial
value and tries to rebuild a model.

If you decide to optimize with restarts,  it  is  recommended  to  perform
a small amount of restarts, up to 5. Generally, restarts do not allow  one
to completely solve the problem of noise, but still   it  is  possible  to
achieve some additional progress.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlssetalgo2ps(nlsstate* state,
     ae_int_t nnoisyrestarts,
     ae_state *_state)
{


    ae_assert(state->problemtype==0, "NLSSetAlgo2PS: the solver MUST be created in a derivative-free mode (i.e. with nlscreatedfo() function)", _state);
    ae_assert(nnoisyrestarts>=0, "NLSSetAlgo2PS: negative NNoisyRestarts", _state);
    state->algorithm = 0;
    state->nnoisyrestarts = nnoisyrestarts;
}


/*************************************************************************
This function sets the derivative-free NLS optimization algorithm  to  the
DFO-LSA algorithm, an ALGLIB implementation (with several modifications)of
the original DFO-LS algorithm  by  Cartis, C., Fiala, J., Marteau, B.  and
Roberts, L. ('Improving the  Flexibility  and  Robustness  of  Model-Based
Derivative-Free Optimization Solvers', 2019). The A in DFO-LSA stands  for
ALGLIB, in order to distinguish our slightly modified implementation  from
the original algorithm.

This solver is recommended for the following  case:  an  expensive  target
function is minimized without parallelism being used (either  free  ALGLIB
is used or commercial one is used but the target callback is non-reentrant
i.e. it can not be simultaneously called from multiple threads)

This function works only with solvers created with nlscreatedfo(), i.e. in
the derivative-free mode.

See the end of this comment for more information about the algorithm.

INPUT PARAMETERS:
    State           -   solver; must be created with nlscreatedfo() call -
                        passing  an  object   initialized   with   another
                        constructor function will result in an exception.
    NNoisyRestarts  -   number of restarts performed to combat a noise  in
                        the target. (see below, section 'RESTARTS', for  a
                        detailed discussion):
                        * 0     means that no restarts is performed, the
                                solver stops as soon as stopping  criteria
                                are met. Recommended for noise-free tasks.
                        * >0    means  that when the stopping criteria are
                                met, the solver will  perform  a  restart:
                                increase the trust   radius  and  resample
                                points. It often helps to  solve  problems
                                with random or deterministic noise.

ALGORITHM DESCRIPTION AND DISCUSSION

The DFO-LSA algorithm is a derivative-free model-based  NLS  solver  which
builds local models by remembering N+1 previously computed  target  values
and updating them as optimization progresses.

Similarly to the Levenberg-Marquardt algorithm, the solver shows quadratic
convergence  despite  the  fact  that  it  builds   linear   models.   Our
implementation generally follows the same lines as the  original  DFO-LSA,
with several modifications to trust radius  update  strategies,  stability
fixes (unlike original DFO-LS, our implementation can handle  and  recover
from the target breaking down due to infeasible arguments) and other minor
implementation details.

When compared with the 2PS solver, the DFO-LSA algorithm has the following
distinctive properties:
* the 2PS algorithm performs more target function evaluations per iteration
  (at least N+1 instead of 1-2 usually performed by DFO-LSA)
* 2PS requires several times less iterations  than  DFO-LSA  because  each
  iterations extracts and utilizes more information about the target. This
  difference tends to exaggerate when N increases
* contrary to that, DFO-LSA is much better at reuse of previously computed
  points. Thus, DFO-LSA needs several times less target  evaluations  than
  2PS, usually about 3-4 times less (this ratio seems to be more  or  less
  constant independently of N).

The summary is that:
* for  expensive  targets DFO-LSA is much more efficient than 2PS  because
  it reuses previously computed target values as much as possible.
* however, DFO-LSA has little parallelism potential because  (unlike  2PS)
  it  does  not  evaluate the target  in several points simultaneously and
  independently
* additionally, because DFO-LSA performs many times more  iterations  than
  2PS, iteration overhead (working set updates and matrix  inversions)  is
  an issue here. For inexpensive targets it is possible for DFO-LSA to  be
  outperformed by 2PS merely because of the linear algebra cost.
  
RESTARTS

Restarts is a strategy used to deal with random and deterministic noise in
the target/constraints.

Noise in the objective function can be random, arising from measurement or
simulation   uncertainty,  or  deterministic,   resulting   from   complex
underlying phenomena like numerical errors or branches in the target.  Its
influence is especially high at last stages of the optimization, when  all
computations are performed with small values of a trust radius.

Restarts allow the optimization algorithm to be robust against both  types
of noise by temporarily increasing a trust radius in order  to  capture  a
global structure of the target and avoid being trapped  by  noise-produced
local features.

A restart is usually performed when the stopping criteria  are  triggered.
Instead of stopping, the solver increases  trust  radius  to  its  initial
value and tries to rebuild a model.

If you decide to optimize with restarts,  it  is  recommended  to  perform
a small amount of restarts, up to 5. Generally, restarts do not allow  one
to completely solve the problem of noise, but still   it  is  possible  to
achieve some additional progress.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlssetalgodfolsa(nlsstate* state,
     ae_int_t nnoisyrestarts,
     ae_state *_state)
{


    ae_assert(state->problemtype==0, "NLSSetAlgoDFOLSA: the solver MUST be created in a derivative-free mode (i.e. with nlscreatedfo() function)", _state);
    ae_assert(nnoisyrestarts>=0, "NLSSetAlgo2PS: negative NNoisyRestarts", _state);
    state->algorithm = 1;
    state->nnoisyrestarts = nnoisyrestarts;
}


/*************************************************************************
This function sets stopping conditions

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsX    -   stop when the scaled trust region radius is  smaller  than
                EpsX.
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations   is    unlimited.

Passing  EpsX=0  and  MaxIts=0  (simultaneously)  will  lead  to automatic
stopping criterion selection (small EpsX).

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlssetcond(nlsstate* state,
     double epsx,
     ae_int_t maxits,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsx, _state), "NLSSetCond: EpsX is not finite number!", _state);
    ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "NLSSetCond: negative EpsX!", _state);
    ae_assert(maxits>=0, "NLSSetCond: negative MaxIts!", _state);
    if( ae_fp_eq(epsx,(double)(0))&&maxits==0 )
    {
        epsx = 1.0E-6;
    }
    critsetcondv1(&state->criteria, 0.0, epsx, maxits, _state);
}


/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to NLSOptimize().

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlssetxrep(nlsstate* state, ae_bool needxrep, ae_state *_state)
{


    state->xrep = needxrep;
}


/*************************************************************************
This function sets variable scales

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Generally, scale is NOT considered to be a  form  of  preconditioner.  But
derivative-free optimizers often use scaling matrix both  in  the stopping
condition tests and as a preconditioner.

Proper scaling is very important for the algorithm performance. It is less
important for the quality of results, but still has some influence (it  is
easier  to  converge  when  variables  are  properly  scaled, so premature
stopping is possible when very badly scalled variables are  combined  with
relaxed stopping conditions).

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlssetscale(nlsstate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(s->cnt>=state->n, "NLSSetScale: Length(S)<N", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "NLSSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "NLSSetScale: S contains zero elements", _state);
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function sets box constraints

Box constraints are inactive by default (after initial creation). They are
preserved until explicitly turned off with another SetBC() call.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF (the latter is recommended).
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF (the latter is recommended).

NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

NOTE 2: unless  explicitly  mentioned  in  the   specific   NLS  algorithm
        description, the following holds:
        * box constraints are always satisfied exactly
        * the target is NOT evaluated outside of the box-constrained area

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlssetbc(nlsstate* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_assert(bndl->cnt>=n, "NLSSetBC: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=n, "NLSSetBC: Length(BndU)<N", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "NLSSetBC: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "NLSSetBC: BndU contains NAN or -INF", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
    }
}


/*************************************************************************

CALLBACK PARALLELISM

The  NLS  optimizer  supports   parallel   model   evaluation   ('callback
parallelism').  This  feature,  which  is  present  in  commercial  ALGLIB
editions, greatly accelerates  optimization  when  using  a  solver  which
issues batch requests, i.e. multiple requests  for  target  values,  which
can be computed independently by different threads.

Callback parallelism is  usually   beneficial  when   processing  a  batch
request requires more than several  milliseconds.  It  also  requires  the
solver which issues requests in convenient batches, e.g. 2PS solver.

See ALGLIB Reference Manual, 'Working with commercial version' section for
more information.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
ae_bool nlsiteration(nlsstate* state, ae_state *_state)
{
    ae_int_t nraw;
    ae_int_t nreduced;
    ae_int_t m;
    ae_int_t nnlc;
    ae_int_t i;
    ae_int_t originalrequest;
    ae_bool b;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        nraw = state->rstate.ia.ptr.p_int[0];
        nreduced = state->rstate.ia.ptr.p_int[1];
        m = state->rstate.ia.ptr.p_int[2];
        nnlc = state->rstate.ia.ptr.p_int[3];
        i = state->rstate.ia.ptr.p_int[4];
        originalrequest = state->rstate.ia.ptr.p_int[5];
        b = state->rstate.ba.ptr.p_bool[0];
    }
    else
    {
        nraw = 359;
        nreduced = -58;
        m = -919;
        nnlc = -909;
        i = 81;
        originalrequest = 255;
        b = ae_false;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    
    /*
     * Routine body
     */
    
    /*
     * prepare
     */
    nraw = state->n;
    m = state->m;
    nnlc = state->nnlc;
    state->repiterationscount = 0;
    state->repterminationtype = 0;
    state->repnrequests = 0;
    state->repnfunc = 0;
    state->userterminationneeded = ae_false;
    
    /*
     * Trace status
     */
    state->dotrace = ae_false;
    b = ae_false;
    if( state->algorithm==0 )
    {
        state->dotrace = ae_is_trace_enabled("2PS")||ae_is_trace_enabled("DFGM");
        b = ae_true;
    }
    if( state->algorithm==1 )
    {
        state->dotrace = ae_is_trace_enabled("DFOLSA")||ae_is_trace_enabled("DFGM");
        b = ae_true;
    }
    ae_assert(b, "NLS: integrity check 8718 failed", _state);
    if( state->dotrace )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("//  NLS SOLVER STARTED                                                                            //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("N             = %6d (original variables count)\n",
            (int)(nraw));
    }
    
    /*
     * Check correctness of box constraints, remove fixed variables (most derivative-free solvers do not allow
     * fixed variables because it overcomplicates geometry improvement code).
     */
    iallocv(nraw, &state->idxraw2red, _state);
    rallocv(nraw, &state->redbl, _state);
    rallocv(nraw, &state->redbu, _state);
    rallocv(nraw, &state->reds, _state);
    rallocv(nraw, &state->redx0, _state);
    nreduced = 0;
    for(i=0; i<=nraw-1; i++)
    {
        if( ae_isfinite(state->bndl.ptr.p_double[i], _state)&&ae_isfinite(state->bndu.ptr.p_double[i], _state) )
        {
            if( ae_fp_greater(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                if( state->dotrace )
                {
                    ae_trace("> variable %0d has inconsistent box constraints, terminating with code -3\n",
                        (int)(i));
                }
                state->repterminationtype = -3;
                result = ae_false;
                return result;
            }
            if( ae_fp_eq(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                state->idxraw2red.ptr.p_int[i] = -1;
                continue;
            }
        }
        state->redx0.ptr.p_double[nreduced] = state->x0.ptr.p_double[i];
        state->reds.ptr.p_double[nreduced] = state->s.ptr.p_double[i];
        state->redbl.ptr.p_double[nreduced] = state->bndl.ptr.p_double[i];
        state->redbu.ptr.p_double[nreduced] = state->bndu.ptr.p_double[i];
        state->idxraw2red.ptr.p_int[i] = nreduced;
        nreduced = nreduced+1;
    }
    if( nreduced==0 )
    {
        
        /*
         * All variables are fixed, quick exit
         */
        if( state->dotrace )
        {
            ae_trace("> all variables are fixed by their box constraints, no need to run the solver. Success!\n");
        }
        state->repterminationtype = 2;
        state->repiterationscount = 0;
        rcopyv(nraw, &state->bndl, &state->xc, _state);
        result = ae_false;
        return result;
    }
    if( state->dotrace )
    {
        ae_trace("N_reduced     = %6d (variables after removing fixed ones)\n",
            (int)(nreduced));
        ae_trace("M             = %6d (nonlinear least squares target functions)\n",
            (int)(m));
        ae_trace("cntLC         = %6d (linear constraints)\n",
            (int)(state->cntlc));
        ae_trace("cntNLC        = %6d (nonlinear constraints)\n",
            (int)(state->nnlc));
    }
    
    /*
     * Allocate temporaries, as mandated by the V2 protocol
     */
    ae_assert(state->protocolversion==2, "NLS: integrity check 5124 failed", _state);
    b = ae_false;
    if( state->problemtype==0 )
    {
        rallocv(nraw, &state->reportx, _state);
        rallocv(m+nnlc, &state->tmpf1, _state);
        rallocv(nraw, &state->tmpg1, _state);
        rallocv(nraw, &state->tmpx1, _state);
        b = ae_true;
    }
    ae_assert(b, "NLS: integrity check 6628 failed", _state);
    
    /*
     * Initialization phase
     */
    b = ae_false;
    if( state->algorithm==0||state->algorithm==1 )
    {
        
        /*
         * Initialize 2PS solver
         */
        i = -999999;
        if( state->algorithm==0 )
        {
            i = 0;
        }
        if( state->algorithm==1 )
        {
            i = 1;
        }
        ae_assert(i>=0, "NLS: integrity check 7930 failed", _state);
        dfgminitbuf(&state->redbl, &state->redbu, &state->reds, &state->redx0, nreduced, m, ae_true, i, &state->criteria, state->nnoisyrestarts, state->rad0, state->maxfev, &state->dfgmsolver, _state);
        b = ae_true;
    }
    ae_assert(b, "NLS: integrity check 1620 failed", _state);
    
    /*
     * Iteration phase
     */
lbl_2:
    if( ae_false )
    {
        goto lbl_3;
    }
    
    /*
     * Run iteration function and load evaluation point
     */
    state->requesttype = 0;
    b = ae_false;
    if( state->algorithm==0||state->algorithm==1 )
    {
        
        /*
         * 2PS solver
         */
        if( !dfgmiteration(&state->dfgmsolver, state->userterminationneeded, _state) )
        {
            goto lbl_3;
        }
        if( state->dfgmsolver.requesttype==4 )
        {
            rallocv(nraw*state->dfgmsolver.querysize, &state->unscaled, _state);
            state->batchsize = state->dfgmsolver.querysize;
            nls_unscalebatch(state, &state->dfgmsolver.querydata, state->dfgmsolver.querysize, &state->idxraw2red, nreduced, &state->dfgmsolver.scaledbndl, &state->dfgmsolver.scaledbndu, &state->unscaled, _state);
            originalrequest = 4;
            b = ae_true;
        }
        if( state->dfgmsolver.requesttype==-1 )
        {
            nls_unscalebatch(state, &state->dfgmsolver.reportx, 1, &state->idxraw2red, nreduced, &state->dfgmsolver.scaledbndl, &state->dfgmsolver.scaledbndu, &state->reportx, _state);
            state->reportf = state->dfgmsolver.reportf;
            state->requesttype = -1;
            originalrequest = -1;
            b = ae_true;
        }
    }
    ae_assert(b, "NLS: integrity check 5510 failed", _state);
    
    /*
     * Process evaluation request, perform numerical differentiation if necessary, offload results back to the optimizer
     */
    b = ae_false;
    if( originalrequest!=-1 )
    {
        goto lbl_4;
    }
    
    /*
     * Report
     */
    ae_assert(state->protocolversion==2, "NLS: integrity check 6811 failed", _state);
    if( !state->xrep )
    {
        goto lbl_6;
    }
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
lbl_6:
    b = ae_true;
lbl_4:
    if( originalrequest!=4 )
    {
        goto lbl_8;
    }
    state->repnfunc = state->repnfunc+state->batchsize;
    state->repnrequests = state->repnrequests+1;
    rallocv(nraw, &state->querydata, _state);
    rallocv(m+nnlc, &state->replyfi, _state);
    rallocv((m+nnlc)*state->batchsize, &state->combined, _state);
    i = 0;
lbl_10:
    if( i>state->batchsize-1 )
    {
        goto lbl_12;
    }
    state->requesttype = 4;
    state->queryfuncs = m+nnlc;
    state->queryvars = nraw;
    state->querydim = 0;
    state->querysize = 1;
    rcopyvx(nraw, &state->unscaled, i*nraw, &state->querydata, 0, _state);
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    rcopyvx(m+nnlc, &state->replyfi, 0, &state->combined, i*(m+nnlc), _state);
    i = i+1;
    goto lbl_10;
lbl_12:
    b = ae_true;
lbl_8:
    ae_assert(b, "NLS: integrity check 7814 failed", _state);
    
    /*
     * Send results back to the optimizer
     */
    ae_assert(originalrequest==-1||originalrequest==4, "NLS: integrity check 8314 failed", _state);
    b = ae_false;
    if( originalrequest==-1 )
    {
        
        /*
         * Do nothing
         */
        b = ae_true;
    }
    if( originalrequest==4&&(state->algorithm==0||state->algorithm==1) )
    {
        
        /*
         * 2PS solver
         */
        ae_assert(state->protocolversion==2, "NLS: integrity check 9915 failed", _state);
        rcopyv((m+nnlc)*state->batchsize, &state->combined, &state->dfgmsolver.replyfi, _state);
        b = ae_true;
    }
    ae_assert(b, "MINNLC: integrity check 0038 failed", _state);
    goto lbl_2;
lbl_3:
    
    /*
     * Results phase
     */
    b = ae_false;
    if( state->algorithm==0||state->algorithm==1 )
    {
        
        /*
         * 2PS results
         */
        state->repterminationtype = state->dfgmsolver.repterminationtype;
        state->repiterationscount = state->dfgmsolver.repiterationscount;
        nls_unscalebatch(state, &state->dfgmsolver.xk, 1, &state->idxraw2red, nreduced, &state->dfgmsolver.scaledbndl, &state->dfgmsolver.scaledbndu, &state->xc, _state);
        b = ae_true;
    }
    ae_assert(b, "NLS: integrity check 1825 failed", _state);
    if( state->dotrace )
    {
        ae_trace("> analyzing callback parallelism potential:\n");
        ae_trace("best_speedup  = %0.1f (assuming expensive target function and negligible iteration overhead)\n",
            (double)((double)state->repnfunc/(double)state->repnrequests));
    }
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = nraw;
    state->rstate.ia.ptr.p_int[1] = nreduced;
    state->rstate.ia.ptr.p_int[2] = m;
    state->rstate.ia.ptr.p_int[3] = nnlc;
    state->rstate.ia.ptr.p_int[4] = i;
    state->rstate.ia.ptr.p_int[5] = originalrequest;
    state->rstate.ba.ptr.p_bool[0] = b;
    return result;
}


/*************************************************************************
Nonlinear least squares solver results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[N], solution
    Rep     -   optimization  report;  includes  termination   codes   and
                additional information. Termination codes are returned  in
                rep.terminationtype field, its possible values are  listed
                below, see comments for this structure for more info.
                
                The termination code is a sum of a basic code (success  or
                failure)  and  one/several  additional  codes.  Additional
                codes are returned only for successful termination.
                
                The following basic codes can be returned:
                * -8    optimizer detected NAN/INF values in the target or
                        nonlinear constraints and failed to recover
                * -3    box constraints are inconsistent
                *  2    relative step is no more than EpsX.
                *  5    MaxIts steps was taken
                *  7    stopping conditions are too stringent,
                        further improvement is impossible
                *  8    terminated by user who called nlsrequesttermination().
                        X contains point which was "current accepted" when
                        termination request was submitted.
                        
                The following additional codes can be returned  (added  to
                a basic code):
                * +800      if   during  algorithm  execution  the  solver
                            encountered NAN/INF values in  the  target  or
                            constraints but managed to recover by reducing
                            trust region radius, the  solver  returns  one
                            of SUCCESS codes but adds +800 to the code.

  -- ALGLIB --
     Copyright 15.10.2023 by Bochkanov Sergey
*************************************************************************/
void nlsresults(const nlsstate* state,
     /* Real    */ ae_vector* x,
     nlsreport* rep,
     ae_state *_state)
{

    ae_vector_clear(x);
    _nlsreport_clear(rep);

    nlsresultsbuf(state, x, rep, _state);
}


/*************************************************************************
Buffered implementation of NLSResults(), which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 10.03.2009 by Bochkanov Sergey
*************************************************************************/
void nlsresultsbuf(const nlsstate* state,
     /* Real    */ ae_vector* x,
     nlsreport* rep,
     ae_state *_state)
{


    if( x->cnt<state->n )
    {
        ae_vector_set_length(x, state->n, _state);
    }
    rcopyv(state->n, &state->xc, x, _state);
    rep->iterationscount = state->repiterationscount;
    rep->terminationtype = state->repterminationtype;
    rep->nfunc = state->repnfunc;
}


/*************************************************************************
This  subroutine  restarts solver from the new point. All optimization
parameters are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   optimizer
    X       -   new starting point.

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
void nlsrestartfrom(nlsstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{


    ae_assert(x->cnt>=state->n, "NLSRestartFrom: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, state->n, _state), "NLSRestartFrom: X contains infinite or NaN values!", _state);
    rcopyallocv(state->n, x, &state->x0, _state);
    rcopyallocv(state->n, x, &state->xc, _state);
    ae_vector_set_length(&state->rstate.ia, 5+1, _state);
    ae_vector_set_length(&state->rstate.ba, 0+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.
      
NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void nlsrequesttermination(nlsstate* state, ae_state *_state)
{


    state->userterminationneeded = ae_true;
}


/*************************************************************************
Set V2 reverse communication protocol
*************************************************************************/
void nlssetprotocolv2(nlsstate* state, ae_state *_state)
{


    state->protocolversion = 2;
    ae_vector_set_length(&state->rstate.ia, 5+1, _state);
    ae_vector_set_length(&state->rstate.ba, 0+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Unscales X (converts from scaled variables to original ones), paying special
attention to box constraints (output is always feasible; active constraints
are mapped to active ones).

Can handle batch requests. Can handle problems with some variables being
fixed and removed from consideration.

Parameters:
    State           solver
    XS              array[NReduced*BatchSize] - variables after scaling and
                    removal of fixed vars
    idxRaw2Red      array[NRaw], maps original variable indexes to ones
                    after removal of fixed vars. Contains -1 for fixed vars.
    scaledBndL,
    ScaledBndU      array[NReduced], lower/upper bounds after removal of
                    the fixed vars
                    
output:
    XU              array[NRaw*BatchSize], vars after unscaling and
                    substitution of the fixed vars
*************************************************************************/
static void nls_unscalebatch(const nlsstate* state,
     /* Real    */ const ae_vector* xs,
     ae_int_t batchsize,
     /* Integer */ const ae_vector* idxraw2red,
     ae_int_t nreduced,
     /* Real    */ const ae_vector* scaledbndl,
     /* Real    */ const ae_vector* scaledbndu,
     /* Real    */ ae_vector* xu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t jr;
    ae_int_t nraw;
    double vs;


    nraw = state->n;
    if( nreduced==nraw )
    {
        
        /*
         * No fixed vars, simple code
         */
        for(k=0; k<=batchsize-1; k++)
        {
            for(j=0; j<=nraw-1; j++)
            {
                i = k*nraw+j;
                vs = xs->ptr.p_double[i];
                if( state->hasbndl.ptr.p_bool[j]&&vs<=scaledbndl->ptr.p_double[j] )
                {
                    xu->ptr.p_double[i] = state->bndl.ptr.p_double[j];
                    continue;
                }
                if( state->hasbndu.ptr.p_bool[j]&&vs>=scaledbndu->ptr.p_double[j] )
                {
                    xu->ptr.p_double[i] = state->bndu.ptr.p_double[j];
                    continue;
                }
                xu->ptr.p_double[i] = vs*state->s.ptr.p_double[j];
                if( state->hasbndl.ptr.p_bool[j]&&xu->ptr.p_double[i]<state->bndl.ptr.p_double[j] )
                {
                    xu->ptr.p_double[i] = state->bndl.ptr.p_double[j];
                }
                if( state->hasbndu.ptr.p_bool[j]&&xu->ptr.p_double[i]>state->bndu.ptr.p_double[j] )
                {
                    xu->ptr.p_double[i] = state->bndu.ptr.p_double[j];
                }
            }
        }
    }
    else
    {
        
        /*
         * Some vars are fixed
         */
        for(k=0; k<=batchsize-1; k++)
        {
            for(j=0; j<=nraw-1; j++)
            {
                i = k*nraw+j;
                jr = idxraw2red->ptr.p_int[j];
                if( jr<0 )
                {
                    xu->ptr.p_double[i] = state->bndl.ptr.p_double[j];
                    continue;
                }
                vs = xs->ptr.p_double[k*nreduced+jr];
                if( state->hasbndl.ptr.p_bool[j]&&vs<=scaledbndl->ptr.p_double[jr] )
                {
                    xu->ptr.p_double[i] = state->bndl.ptr.p_double[j];
                    continue;
                }
                if( state->hasbndu.ptr.p_bool[j]&&vs>=scaledbndu->ptr.p_double[jr] )
                {
                    xu->ptr.p_double[i] = state->bndu.ptr.p_double[j];
                    continue;
                }
                xu->ptr.p_double[i] = vs*state->s.ptr.p_double[j];
                if( state->hasbndl.ptr.p_bool[j]&&xu->ptr.p_double[i]<state->bndl.ptr.p_double[j] )
                {
                    xu->ptr.p_double[i] = state->bndl.ptr.p_double[j];
                }
                if( state->hasbndu.ptr.p_bool[j]&&xu->ptr.p_double[i]>state->bndu.ptr.p_double[j] )
                {
                    xu->ptr.p_double[i] = state->bndu.ptr.p_double[j];
                }
            }
        }
    }
}


void _nlsstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    nlsstate *p = (nlsstate*)_p;
    ae_touch_ptr((void*)p);
    _nlpstoppingcriteria_init(&p->criteria, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->idxraw2red, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->redbl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->redbu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->reds, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->redx0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->reportx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->querydata, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyfi, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replydj, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->replysj, _state, make_automatic);
    ae_vector_init(&p->tmpx1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpc1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpf1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpg1, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpj1, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->tmps1, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    _dfgmstate_init(&p->dfgmsolver, _state, make_automatic);
    ae_vector_init(&p->xc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->unscaled, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->combined, 0, DT_REAL, _state, make_automatic);
}


void _nlsstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    nlsstate       *dst = (nlsstate*)_dst;
    const nlsstate *src = (const nlsstate*)_src;
    dst->protocolversion = src->protocolversion;
    dst->n = src->n;
    dst->m = src->m;
    dst->problemtype = src->problemtype;
    dst->algorithm = src->algorithm;
    dst->diffstep = src->diffstep;
    _nlpstoppingcriteria_init_copy(&dst->criteria, &src->criteria, _state, make_automatic);
    dst->xrep = src->xrep;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    dst->rad0 = src->rad0;
    dst->maxfev = src->maxfev;
    dst->nnoisyrestarts = src->nnoisyrestarts;
    dst->nnlc = src->nnlc;
    dst->cntlc = src->cntlc;
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    ae_vector_init_copy(&dst->idxraw2red, &src->idxraw2red, _state, make_automatic);
    ae_vector_init_copy(&dst->redbl, &src->redbl, _state, make_automatic);
    ae_vector_init_copy(&dst->redbu, &src->redbu, _state, make_automatic);
    ae_vector_init_copy(&dst->reds, &src->reds, _state, make_automatic);
    ae_vector_init_copy(&dst->redx0, &src->redx0, _state, make_automatic);
    dst->userterminationneeded = src->userterminationneeded;
    dst->requesttype = src->requesttype;
    ae_vector_init_copy(&dst->reportx, &src->reportx, _state, make_automatic);
    dst->reportf = src->reportf;
    dst->querysize = src->querysize;
    dst->queryfuncs = src->queryfuncs;
    dst->queryvars = src->queryvars;
    dst->querydim = src->querydim;
    dst->queryformulasize = src->queryformulasize;
    ae_vector_init_copy(&dst->querydata, &src->querydata, _state, make_automatic);
    ae_vector_init_copy(&dst->replyfi, &src->replyfi, _state, make_automatic);
    ae_vector_init_copy(&dst->replydj, &src->replydj, _state, make_automatic);
    _sparsematrix_init_copy(&dst->replysj, &src->replysj, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpx1, &src->tmpx1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpc1, &src->tmpc1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpf1, &src->tmpf1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpg1, &src->tmpg1, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpj1, &src->tmpj1, _state, make_automatic);
    _sparsematrix_init_copy(&dst->tmps1, &src->tmps1, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    _dfgmstate_init_copy(&dst->dfgmsolver, &src->dfgmsolver, _state, make_automatic);
    dst->dotrace = src->dotrace;
    ae_vector_init_copy(&dst->xc, &src->xc, _state, make_automatic);
    dst->repnfunc = src->repnfunc;
    dst->repiterationscount = src->repiterationscount;
    dst->repterminationtype = src->repterminationtype;
    dst->repnrequests = src->repnrequests;
    ae_vector_init_copy(&dst->unscaled, &src->unscaled, _state, make_automatic);
    ae_vector_init_copy(&dst->combined, &src->combined, _state, make_automatic);
    dst->batchsize = src->batchsize;
}


void _nlsstate_clear(void* _p)
{
    nlsstate *p = (nlsstate*)_p;
    ae_touch_ptr((void*)p);
    _nlpstoppingcriteria_clear(&p->criteria);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->idxraw2red);
    ae_vector_clear(&p->redbl);
    ae_vector_clear(&p->redbu);
    ae_vector_clear(&p->reds);
    ae_vector_clear(&p->redx0);
    ae_vector_clear(&p->reportx);
    ae_vector_clear(&p->querydata);
    ae_vector_clear(&p->replyfi);
    ae_vector_clear(&p->replydj);
    _sparsematrix_clear(&p->replysj);
    ae_vector_clear(&p->tmpx1);
    ae_vector_clear(&p->tmpc1);
    ae_vector_clear(&p->tmpf1);
    ae_vector_clear(&p->tmpg1);
    ae_matrix_clear(&p->tmpj1);
    _sparsematrix_clear(&p->tmps1);
    _rcommstate_clear(&p->rstate);
    _dfgmstate_clear(&p->dfgmsolver);
    ae_vector_clear(&p->xc);
    ae_vector_clear(&p->unscaled);
    ae_vector_clear(&p->combined);
}


void _nlsstate_destroy(void* _p)
{
    nlsstate *p = (nlsstate*)_p;
    ae_touch_ptr((void*)p);
    _nlpstoppingcriteria_destroy(&p->criteria);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->idxraw2red);
    ae_vector_destroy(&p->redbl);
    ae_vector_destroy(&p->redbu);
    ae_vector_destroy(&p->reds);
    ae_vector_destroy(&p->redx0);
    ae_vector_destroy(&p->reportx);
    ae_vector_destroy(&p->querydata);
    ae_vector_destroy(&p->replyfi);
    ae_vector_destroy(&p->replydj);
    _sparsematrix_destroy(&p->replysj);
    ae_vector_destroy(&p->tmpx1);
    ae_vector_destroy(&p->tmpc1);
    ae_vector_destroy(&p->tmpf1);
    ae_vector_destroy(&p->tmpg1);
    ae_matrix_destroy(&p->tmpj1);
    _sparsematrix_destroy(&p->tmps1);
    _rcommstate_destroy(&p->rstate);
    _dfgmstate_destroy(&p->dfgmsolver);
    ae_vector_destroy(&p->xc);
    ae_vector_destroy(&p->unscaled);
    ae_vector_destroy(&p->combined);
}


void _nlsreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    nlsreport *p = (nlsreport*)_p;
    ae_touch_ptr((void*)p);
}


void _nlsreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    nlsreport       *dst = (nlsreport*)_dst;
    const nlsreport *src = (const nlsreport*)_src;
    dst->iterationscount = src->iterationscount;
    dst->terminationtype = src->terminationtype;
    dst->nfunc = src->nfunc;
}


void _nlsreport_clear(void* _p)
{
    nlsreport *p = (nlsreport*)_p;
    ae_touch_ptr((void*)p);
}


void _nlsreport_destroy(void* _p)
{
    nlsreport *p = (nlsreport*)_p;
    ae_touch_ptr((void*)p);
}


#endif
#if defined(AE_COMPILE_GIPM2) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
Get recommended initial value for mu.

  -- ALGLIB --
     Copyright 01.02.2025 by Bochkanov Sergey
*************************************************************************/
double gipm2getinitprimdual(ae_state *_state)
{
    double result;


    result = ae_sqrt(gipm2_initmu, _state);
    return result;
}


/*************************************************************************
Initialize GIPM structure

  -- ALGLIB --
     Copyright 01.02.2025 by Bochkanov Sergey
*************************************************************************/
void gipm2initbuf(/* Real    */ const ae_vector* x0,
     ae_int_t np,
     double eps,
     ae_int_t maxits,
     gipm2state* state,
     ae_state *_state)
{


    state->np = np;
    state->mflex = 0;
    state->meq = 0;
    state->mrlxeq = 0;
    state->m1 = 0;
    state->mrlx1 = 0;
    state->hashardbc = ae_false;
    state->hassoftbc = ae_false;
    state->mudependent = ae_false;
    gipm2setprofilequasinewton(state, ae_true, _state);
    
    /*
     * Save initial point for further processing
     */
    rcopyallocv(np, x0, &state->x0, _state);
    
    /*
     * Prepare RCOMM-V2 protocol
     */
    ae_vector_set_length(&state->rstate.ia, 7+1, _state);
    ae_vector_set_length(&state->rstate.ba, 4+1, _state);
    ae_vector_set_length(&state->rstate.ra, 22+1, _state);
    state->rstate.stage = -1;
    
    /*
     * Stopping criteria and settings
     */
    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "GIPM: integrity check 9756 failed", _state);
    state->eps = coalesce(eps, (double)10*ae_sqrt(ae_machineepsilon, _state), _state);
    state->maxits = maxits;
    
    /*
     * Report fields
     */
    state->repterminationtype = 0;
    state->repiterationscount = 0;
    state->repbcerr = (double)(0);
    state->repbcidx = 0;
    state->repnlcerr = (double)(0);
    state->repnlcidx = 0;
    state->repsclerr = (double)(0);
    
    /*
     * Trace status
     */
    state->dotracelaconic = ae_is_trace_enabled("GIPM2.LACONIC");
    state->dotrace = ae_is_trace_enabled("GIPM2")&&!state->dotracelaconic;
}


/*************************************************************************
Set profile for a quasi-Newton version of the step (linear convergence)

  -- ALGLIB --
     Copyright 01.02.2025 by Bochkanov Sergey
*************************************************************************/
void gipm2setprofilequasinewton(gipm2state* state,
     ae_bool isprimal,
     ae_state *_state)
{


    state->isprimal = isprimal;
    state->muepsfactor = (double)(10);
    state->mudecaylinear = 0.2;
    state->mudecaypower = 1.0;
    state->multupdatedelay = icase2(isprimal, 1, 0, _state);
    state->maxcheckpointstagnationits = 100;
}


/*************************************************************************
Set profile for a Newton version of the step (superlinear convergence)

  -- ALGLIB --
     Copyright 01.02.2025 by Bochkanov Sergey
*************************************************************************/
void gipm2setprofilenewton(gipm2state* state, ae_state *_state)
{


    state->isprimal = ae_true;
    state->muepsfactor = 0.1;
    state->mudecaylinear = 0.2;
    state->mudecaypower = 1.2;
    state->multupdatedelay = 1;
    state->maxcheckpointstagnationits = 20;
}


/*************************************************************************
Set hard box constraints (do not support box equality constraints)

For a general problem of the form

    min f(x) subject to ci(x)>=0, ce(x)=0
    
rewritten as a barrier-penalty problem with parameter mu

    min f(x) + SUM(-mu*ln(ci(x))) + SUM(1/2mu*ce(x)^2)
    
Its primal step direction can be written as a solution of the following
nonsymmetric system:

    [ H+D0 -J' ] [ dx ]   [ -grad(f) + J'z ]   [ rp  ]
    [          ] [    ] = [                ] = [     ]
    [ U*J   W  ] [ dz ]   [  W*(z_prim-z)  ]   [ rz0 ]
    

or, alternatively, a solution of a symmetrized augmented system

    [ H+D0  J' ] [ dx ]   [ rp  ]
    [          ] [    ] = [     ]
    [ J     D1 ] [-dz ]   [ rz1 ]
    
where
    
    rz1 = -rz0/U
    x is a vector primal variables
    z is a vector of dual multipliers, which is taken to be equal to z_prim for the primal algorithm
    z_prim is a vector of primal multiplier estimates, which is -ci(x)/mu for ce-rows ; mu/ci(x)  for ci-rows
    J is a combined Jacobian of ci(x) and ce(x)
    U  = [        1 for ce-rows ; z or z_prim for ci-rows ]
    W  = [       mu for ce-rows ;     ci(x)   for ci-rows ]
    D0 = 0  (it will become non-zero in condensed formulations)
    D1 = -W/U
    
Due to linearity this form changes in a decomposable, linear way when moving
to more specialized formulations.

--------------------------------------------------------------------------

For a case of box constraints XL <= x <= XU subject to additional relaxation
coefficients QL in {0,1}, QU in {0,1}, we have

    c = { x[i]+qxl[i]*sxl[i]-xl[i]>=0, -x[i]+qxu[i]*sxu[i]+xu[i]>=0, sxl[i]>=0, sxu[i]>=0, msxl[i]-sxl[i]>=0, msxu[i]-sxu[i]>= }
      = { VXL[i], VXU[i], SXL[I], SXU[I], SXLCAP[I], SXUCAP[I] } (at current point)

and also we have f modified by adding barrier and penalty terms for sxl[i], sxu[i]

    f~ = f - mu*ln(sxl[i]) - mu*ln(sxu[i]) - mu*ln(msxl[i]-sxl[i]) - mu*ln(msxu[i]-sxu[i]) + pxl[i]*sxl[i] + pxu[i]*sxu[i]
    
and, assuming diagonal regularizer L, the nonsymmetric system is

    (H+D0+L)*dx[i] - dzxl[i] + dzxu[i]                      = rp[i]         = -grad(f)+zxl[i]-zxu[i]
   -qxl[i]*dzxl[i] + L*dsxl[i] - dzsxl[i] + dzsxlcap[i]     = rpsxl[i]      = -pxl[i]+zxl[i]*qxl[i]+zsxl[i]-zsxlcap[i]
   -qxu[i]*dzxu[i] + L*dsxu[i] - dzsxu[i] + dzsxucap[i]     = rpsxu[i]      = -pxu[i]+zxu[i]*qxu[i]+zsxu[i]-zsxucap[i]
    zxl[i]*dx[i] + zxl[i]*qxl[i]*dsxl[i] + vxl[i]*dzxl[i]   = rdxl[i]       = vxl[i]*(zxl-zxl_prim) 
   -zxu[i]*dx[i] + zxu[i]*qxu[i]*dsxu[i] + vxu[i]*dzxu[i]   = rdxu[i]       = vxu[i]*(zxu-zxu_prim)
    zsxl[i]*dsxl[i] + sxl[i]*dzsxl[i]                       = rdsxl[i]      = sxl[i]*(zsxl-zsxl_prim)
    zsxu[i]*dsxu[i] + sxu[i]*dzsxu[i]                       = rdsxu[i]      = sxu[i]*(zsxu-zsxu_prim)
    -zsxlcap[i]*dsxl[i] + sxlcap[i]*dzsxlcap[i]             = rdsxlcap[i]   = sxlcap[i]*(zsxlcap-zsxlcap_prim)
    -zsxucap[i]*dsxu[i] + sxucap[i]*dzsxucap[i]             = rdsxucap[i]   = sxucap[i]*(zsxucap-zsxucap_prim)
    
with last 4 rows giving us substitutions
    
    TMPSX0[i]   = 1/sxl[i] or 0
    TMPSX1[i]   = 1/sxu[i] or 0
    TMPSX2[i]   = 1/sxlcap[i] or 0
    TMPSX3[i]   = 1/sxucap[i] or 0
    dzsxl[i]    = rdsxl[i]*TMPSX0[i] - (zsxl[i]*TMPSX0[i])*dsxl[i]
    dzsxu[i]    = rdsxu[i]*TMPSX1[i] - (zsxu[i]*TMPSX1[i])*dsxu[i]
    dzsxlcap[i] = rdsxlcap[i]*TMPSX2[i] + (zsxlcap[i]*TMPSX2[i])*dsxl[i]
    dzsxucap[i] = rdsxucap[i]*TMPSX3[i] + (zsxucap[i]*TMPSX3[i])*dsxu[i]
    
that transform original system into

    TMPSX4[i] = 1/(L+zsxl[i]*TMPSX0[i]+zsxlcap[i]*TMPSX2[i])
    TMPSX5[i] = 1/(L+zsxu[i]*TMPSX1[i]+zsxucap[i]*TMPSX3[i])
    TMPSX6[i] = rpsxl[i] + rdsxl[i]*TMPSX0[i] - rdsxlcap[i]*TMPSX2[i]
    TMPSX7[i] = rpsxu[i] + rdsxu[i]*TMPSX1[i] - rdsxucap[i]*TMPSX3[i]

    (H+D0+L)*dx[i] - dzxl[i] + dzxu[i] = rp[i]
   -qxl[i]*dzxl[i] + (1/TMPSX4[i])*dsxl[i] = TMPSX6[i]
   -qxu[i]*dzxu[i] + (1/TMPSX5[i])*dsxu[i] = TMPSX7[i]
    zxl[i]*dx[i] + zxl[i]*qxl[i]*dsxl[i] + vxl[i]*dzxl[i] = rdxl[i]
   -zxu[i]*dx[i] + zxu[i]*qxu[i]*dsxu[i] + vxu[i]*dzxu[i] = rdxu[i]
   
which gives us

    dsxl[i] = TMPSX4[i]*TMPSX6[i] + (TMPSX4[i]*qxl[i])*dzxl[i]
    dsxu[i] = TMPSX5[i]*TMPSX7[i] + (TMPSX5[i]*qxu[i])*dzxu[i]
    
which, in turn, transform the system to

    TMPSX8[i] = 1/(zxl[i]*qxl[i]*TMPSX4[i]*qxl[i]+vxl[i])
    TMPSX9[i] = 1/(zxu[i]*qxu[i]*TMPSX5[i]*qxu[i]+vxu[i])
    TMPSX10[i] = rdxl[i] - zxl[i]*qxl[i]*TMPSX4[i]*TMPSX6[i]
    TMPSX11[i] = rdxu[i] - zxu[i]*qxu[i]*TMPSX5[i]*TMPSX7[i]

    (H+D0+L)*dx[i] - dzxl[i] + dzxu[i] = rp[i]
    zxl[i]*dx[i] + (1/TMPSX8[i])*dzxl[i] = TMPSX10[i]
   -zxu[i]*dx[i] + (1/TMPSX9[i])*dzxu[i] = TMPSX11[i]
   
and, with the final pair of substitutions

    dzxl[i] = TMPSX8[i]*TMPSX10[i] - TMPSX8[i]*zxl[i]*dx[i]
    dzxu[i] = TMPSX9[i]*TMPSX11[i] + TMPSX9[i]*zxu[i]*dx[i]
    
we get

    (H+D0+L+TMPSX8[i]*zxl[i]+TMPSX9[i]*zxu[i])*dx[i] = rp[i] + TMPSX8[i]*TMPSX10[i] - TMPSX9[i]*TMPSX11[i]
    
which results in the following condensed solution algorithm
    
    TMPSX0[i]   = 1/sxl[i] or 0
    TMPSX1[i]   = 1/sxu[i] or 0
    TMPSX2[i]   = 1/sxlcap[i] or 0
    TMPSX3[i]   = 1/sxucap[i] or 0
    TMPSX4[i] = 1/(L+zsxl[i]*TMPSX0[i]+zsxlcap[i]*TMPSX2[i])
    TMPSX5[i] = 1/(L+zsxu[i]*TMPSX1[i]+zsxucap[i]*TMPSX3[i])
    TMPSX6[i] = rpsxl[i] + rdsxl[i]*TMPSX0[i] - rdsxlcap[i]*TMPSX2[i]
    TMPSX7[i] = rpsxu[i] + rdsxu[i]*TMPSX1[i] - rdsxucap[i]*TMPSX3[i]
    TMPSX8[i] = 1/(zxl[i]*qxl[i]*TMPSX4[i]*qxl[i]+vxl[i])
    TMPSX9[i] = 1/(zxu[i]*qxu[i]*TMPSX5[i]*qxu[i]+vxu[i])
    TMPSX10[i] = rdxl[i] - zxl[i]*qxl[i]*TMPSX4[i]*TMPSX6[i]
    TMPSX11[i] = rdxu[i] - zxu[i]*qxu[i]*TMPSX5[i]*TMPSX7[i]
    
    WX := rp[i] + TMPSX8[i]*TMPSX10[i] - TMPSX9[i]*TMPSX11[i]
    D0 := D0 + TMPSX8[i]*zxl[i] + TMPSX9[i]*zxu[i]
    
    [ solve (H+L+D0)*dx = WX ]

    dzxl[i] = TMPSX8[i]*(TMPSX10[i] - zxl[i]*dx[i])
    dzxu[i] = TMPSX9[i]*(TMPSX11[i] + zxu[i]*dx[i])
    
    dsxl[i] = TMPSX4[i]*(TMPSX6[i] + qxl[i]*dzxl[i])
    dsxu[i] = TMPSX5[i]*(TMPSX7[i] + qxu[i]*dzxu[i])
    
    dzsxl[i]    = TMPSX0[i]*(rdsxl[i] - zsxl[i]*dsxl[i])
    dzsxu[i]    = TMPSX1[i]*(rdsxu[i] - zsxu[i]*dsxu[i])
    dzsxlcap[i] = TMPSX2[i]*(rdsxlcap[i] + zsxlcap[i]*dsxl[i])
    dzsxucap[i] = TMPSX3[i]*(rdsxucap[i] + zsxucap[i]*dsxu[i])

or, for hard-only box constraints

    TMPX8[i] = 1/vxl[i]
    TMPX9[i] = 1/vxu[i]
    
    WX := RP + rdxl[i]*TMPX8[i] - rdxu[i]*TMPX9[i]
    D0 := D0 + zxl[i]*TMPX8[i] + zxu[i]*TMPX9[i]
    
    [ solve (H+D0)*dx = WX ]
    
    dzxl[i] = (rdxl[i] - zxl[i]*dx[i])*TMPX8[i]
    dzxu[i] = (rdxu[i] + zxu[i]*dx[i])*TMPX9[i]
    
and, for primal-only problem (z=z_prim, rdxl=rdxu=0), even more simple formulation

    TMPX8[i] = 1/vxl[i]
    TMPX9[i] = 1/vxu[i]
    
    RP := RP + zxl[i]-zxu[i]
    WX := RP
    D0 := D0 + zxl[i]*TMPX8[i] + zxu[i]*TMPX9[i]
    
    [ solve (H+D0)*dx = WX ]
    
    dzxl[i] = -zxl[i]*dx[i]*TMPX8[i]
    dzxu[i] = +zxu[i]*dx[i]*TMPX9[i]

    
  -- ALGLIB --
     Copyright 01.02.2025 by Bochkanov Sergey
*************************************************************************/
void gipm2sethardbc(gipm2state* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{


    state->hashardbc = ae_true;
    state->hassoftbc = ae_false;
    rcopyallocv(state->np, bndl, &state->hardbndl, _state);
    rcopyallocv(state->np, bndu, &state->hardbndu, _state);
}


void gipm2setsoftbc(gipm2state* state,
     /* Real    */ const ae_vector* bndl,
     /* Boolean */ const ae_vector* issoftl,
     /* Real    */ const ae_vector* bndu,
     /* Boolean */ const ae_vector* issoftu,
     ae_state *_state)
{
    ae_int_t i;


    state->hashardbc = ae_false;
    state->hassoftbc = ae_true;
    rcopyallocv(state->np, bndl, &state->hardbndl, _state);
    rcopyallocv(state->np, bndu, &state->hardbndu, _state);
    rsetallocv(state->np, 0.0, &state->softbcql, _state);
    rsetallocv(state->np, 0.0, &state->softbcqu, _state);
    for(i=0; i<=state->np-1; i++)
    {
        if( ae_isfinite(bndl->ptr.p_double[i], _state)&&issoftl->ptr.p_bool[i] )
        {
            state->softbcql.ptr.p_double[i] = 1.0;
        }
        if( ae_isfinite(bndu->ptr.p_double[i], _state)&&issoftu->ptr.p_bool[i] )
        {
            state->softbcqu.ptr.p_double[i] = 1.0;
        }
    }
}


void gipm2setflexnlc(gipm2state* state,
     /* Real    */ const ae_vector* cl,
     /* Real    */ const ae_vector* cu,
     ae_int_t mflex,
     ae_state *_state)
{
    ae_int_t i;


    state->mflex = mflex;
    rcopyallocv(mflex, cl, &state->flexcl, _state);
    rcopyallocv(mflex, cu, &state->flexcu, _state);
    rsetallocv(mflex, 0.0, &state->maskflexcl, _state);
    rsetallocv(mflex, 0.0, &state->maskflexcu, _state);
    bsetallocv(mflex, ae_false, &state->hasflexcl, _state);
    bsetallocv(mflex, ae_false, &state->hasflexcu, _state);
    for(i=0; i<=mflex-1; i++)
    {
        if( ae_isfinite(cl->ptr.p_double[i], _state) )
        {
            state->maskflexcl.ptr.p_double[i] = 1.0;
            state->hasflexcl.ptr.p_bool[i] = ae_true;
        }
        if( ae_isfinite(cu->ptr.p_double[i], _state) )
        {
            state->maskflexcu.ptr.p_double[i] = 1.0;
            state->hasflexcu.ptr.p_bool[i] = ae_true;
        }
    }
}


/*************************************************************************
This function performs actual processing for GIPM2 algorithm.

  -- ALGLIB --
     Copyright 01.02.2025 by Bochkanov Sergey
*************************************************************************/
ae_bool gipm2iteration(gipm2state* state,
     smoothnessmonitor* smonitor,
     ae_bool userterminationneeded,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t mtotal;
    ae_int_t i;
    double gnrm;
    double v;
    double vv;
    double v2;
    ae_int_t rfsidx;
    double alpha;
    double eprim;
    double edual;
    double ecmpl;
    double emax;
    double eprim2;
    double edual2;
    double ecmpl2;
    double rhs0;
    double rhsk;
    double merit0;
    double merit1;
    double cmpl0;
    double cmpl1;
    double initprimdual;
    ae_bool accepted;
    ae_bool worsethanbest;
    double minedual;
    ae_bool monotonicallyaccepted;
    double lastcheckpointf;
    double lastcheckpointd;
    double lastcheckpointc;
    ae_int_t lastcheckpointidx;
    ae_bool violated;
    ae_bool lambdafound;
    ae_int_t smalltrials;
    ae_int_t btits;
    ae_int_t jj;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        np = state->rstate.ia.ptr.p_int[0];
        mtotal = state->rstate.ia.ptr.p_int[1];
        i = state->rstate.ia.ptr.p_int[2];
        rfsidx = state->rstate.ia.ptr.p_int[3];
        lastcheckpointidx = state->rstate.ia.ptr.p_int[4];
        smalltrials = state->rstate.ia.ptr.p_int[5];
        btits = state->rstate.ia.ptr.p_int[6];
        jj = state->rstate.ia.ptr.p_int[7];
        accepted = state->rstate.ba.ptr.p_bool[0];
        worsethanbest = state->rstate.ba.ptr.p_bool[1];
        monotonicallyaccepted = state->rstate.ba.ptr.p_bool[2];
        violated = state->rstate.ba.ptr.p_bool[3];
        lambdafound = state->rstate.ba.ptr.p_bool[4];
        gnrm = state->rstate.ra.ptr.p_double[0];
        v = state->rstate.ra.ptr.p_double[1];
        vv = state->rstate.ra.ptr.p_double[2];
        v2 = state->rstate.ra.ptr.p_double[3];
        alpha = state->rstate.ra.ptr.p_double[4];
        eprim = state->rstate.ra.ptr.p_double[5];
        edual = state->rstate.ra.ptr.p_double[6];
        ecmpl = state->rstate.ra.ptr.p_double[7];
        emax = state->rstate.ra.ptr.p_double[8];
        eprim2 = state->rstate.ra.ptr.p_double[9];
        edual2 = state->rstate.ra.ptr.p_double[10];
        ecmpl2 = state->rstate.ra.ptr.p_double[11];
        rhs0 = state->rstate.ra.ptr.p_double[12];
        rhsk = state->rstate.ra.ptr.p_double[13];
        merit0 = state->rstate.ra.ptr.p_double[14];
        merit1 = state->rstate.ra.ptr.p_double[15];
        cmpl0 = state->rstate.ra.ptr.p_double[16];
        cmpl1 = state->rstate.ra.ptr.p_double[17];
        initprimdual = state->rstate.ra.ptr.p_double[18];
        minedual = state->rstate.ra.ptr.p_double[19];
        lastcheckpointf = state->rstate.ra.ptr.p_double[20];
        lastcheckpointd = state->rstate.ra.ptr.p_double[21];
        lastcheckpointc = state->rstate.ra.ptr.p_double[22];
    }
    else
    {
        np = 359;
        mtotal = -58;
        i = -919;
        rfsidx = -909;
        lastcheckpointidx = 81;
        smalltrials = 255;
        btits = 74;
        jj = -788;
        accepted = ae_true;
        worsethanbest = ae_true;
        monotonicallyaccepted = ae_false;
        violated = ae_true;
        lambdafound = ae_false;
        gnrm = 763.0;
        v = -541.0;
        vv = -698.0;
        v2 = -900.0;
        alpha = -318.0;
        eprim = -940.0;
        edual = 1016.0;
        ecmpl = -229.0;
        emax = -536.0;
        eprim2 = 487.0;
        edual2 = -115.0;
        ecmpl2 = 886.0;
        rhs0 = 346.0;
        rhsk = -722.0;
        merit0 = -413.0;
        merit1 = -461.0;
        cmpl0 = 927.0;
        cmpl1 = 201.0;
        initprimdual = 922.0;
        minedual = -154.0;
        lastcheckpointf = 306.0;
        lastcheckpointd = -1011.0;
        lastcheckpointc = 951.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    if( state->rstate.stage==6 )
    {
        goto lbl_6;
    }
    if( state->rstate.stage==7 )
    {
        goto lbl_7;
    }
    if( state->rstate.stage==8 )
    {
        goto lbl_8;
    }
    if( state->rstate.stage==9 )
    {
        goto lbl_9;
    }
    if( state->rstate.stage==10 )
    {
        goto lbl_10;
    }
    if( state->rstate.stage==11 )
    {
        goto lbl_11;
    }
    if( state->rstate.stage==12 )
    {
        goto lbl_12;
    }
    if( state->rstate.stage==13 )
    {
        goto lbl_13;
    }
    if( state->rstate.stage==14 )
    {
        goto lbl_14;
    }
    if( state->rstate.stage==15 )
    {
        goto lbl_15;
    }
    if( state->rstate.stage==16 )
    {
        goto lbl_16;
    }
    if( state->rstate.stage==17 )
    {
        goto lbl_17;
    }
    
    /*
     * Routine body
     */
    np = state->np;
    mtotal = state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1;
    ae_nxpool_alloc(&state->mnx1pool, ae_maxint(np, mtotal, _state)+1, _state);
    
    /*
     * Prepare rcomm interface
     */
    rallocv(np, &state->reportx, _state);
    rallocv(np+mtotal, &state->querydata, _state);
    rallocv(1+mtotal, &state->replyfi, _state);
    rallocv(1+mtotal, &state->replyv, _state);
    
    /*
     * Choose initial mu and initial variable values
     */
    initprimdual = gipm2getinitprimdual(_state);
    state->currentmu = gipm2_initmu;
    state->prevmu = state->currentmu;
    state->mumin = ae_maxreal(0.0001*state->eps/((double)1+gipm2_relaxfactor), (double)100*ae_machineepsilon, _state);
    
    /*
     * Prepare variable bounds
     */
    rsetallocv(np, -ae_sqrt(ae_maxrealnumber, _state), &state->finitebndl, _state);
    rsetallocv(np, ae_sqrt(ae_maxrealnumber, _state), &state->finitebndu, _state);
    bsetallocv(np, ae_false, &state->hasbndl, _state);
    bsetallocv(np, ae_false, &state->hasbndu, _state);
    rsetallocv(np, 0.0, &state->elp, _state);
    rsetallocv(np, 0.0, &state->eup, _state);
    if( state->hashardbc )
    {
        ae_assert(!state->hassoftbc, "GIPM2: 711152", _state);
        for(i=0; i<=np-1; i++)
        {
            if( ae_isfinite(state->hardbndl.ptr.p_double[i], _state) )
            {
                state->finitebndl.ptr.p_double[i] = state->hardbndl.ptr.p_double[i];
                state->elp.ptr.p_double[i] = (double)(1);
                state->hasbndl.ptr.p_bool[i] = ae_true;
            }
            if( ae_isfinite(state->hardbndu.ptr.p_double[i], _state) )
            {
                state->finitebndu.ptr.p_double[i] = state->hardbndu.ptr.p_double[i];
                state->eup.ptr.p_double[i] = (double)(1);
                state->hasbndu.ptr.p_bool[i] = ae_true;
            }
        }
    }
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 597212", _state);
        for(i=0; i<=np-1; i++)
        {
            if( ae_isfinite(state->hardbndl.ptr.p_double[i], _state) )
            {
                state->finitebndl.ptr.p_double[i] = state->hardbndl.ptr.p_double[i];
                state->elp.ptr.p_double[i] = (double)(1);
                state->hasbndl.ptr.p_bool[i] = ae_true;
            }
            if( ae_isfinite(state->hardbndu.ptr.p_double[i], _state) )
            {
                state->finitebndu.ptr.p_double[i] = state->hardbndu.ptr.p_double[i];
                state->eup.ptr.p_double[i] = (double)(1);
                state->hasbndu.ptr.p_bool[i] = ae_true;
            }
        }
    }
    gipm2_safeboundsfromfinitetightened(state, _state);
    
    /*
     * Initialize algorithm state
     * * initial function scales (vector of 1's)
     * * current approximation of the Hessian matrix H (unit matrix)
     */
    state->fstagnationcnt = 0;
    gipm2_varsinitbyzero(&state->best, np, state->mflex, state->meq, state->mrlxeq, state->m1, state->mrlx1, state->hashardbc, state->hassoftbc, _state);
    rsetallocv(np, 0.0, &state->bestlagbc, _state);
    rsetallocv(mtotal, 0.0, &state->bestlagnlc, _state);
    gipm2_varsinitbyzero(&state->current, np, state->mflex, state->meq, state->mrlxeq, state->m1, state->mrlx1, state->hashardbc, state->hassoftbc, _state);
    gipm2_varsinitbyzero(&state->cand, np, state->mflex, state->meq, state->mrlxeq, state->m1, state->mrlx1, state->hashardbc, state->hassoftbc, _state);
    gipm2_varsinitbyzero(&state->corr, np, state->mflex, state->meq, state->mrlxeq, state->m1, state->mrlx1, state->hashardbc, state->hassoftbc, _state);
    gipm2_varsinitbyzero(&state->delta, np, state->mflex, state->meq, state->mrlxeq, state->m1, state->mrlx1, state->hashardbc, state->hassoftbc, _state);
    gipm2_multinitbyzero(&state->deltamult, np, state->mflex, state->meq, state->mrlxeq, state->m1, state->mrlx1, state->hashardbc, state->hassoftbc, _state);
    gipm2_multinitbyzero(&state->corrmult, np, state->mflex, state->meq, state->mrlxeq, state->m1, state->mrlx1, state->hashardbc, state->hassoftbc, _state);
    rcopyv(np, &state->x0, &state->current.xp, _state);
    ae_assert(mtotal-state->mflex==0, "GIPM2: 824204", _state);
    if( state->hashardbc )
    {
        ae_assert(!state->hassoftbc, "GIPM2: 823200", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i] )
            {
                v = 0.5*(state->hardbndl.ptr.p_double[i]+state->hardbndu.ptr.p_double[i]);
                ae_assert(ae_fp_greater(v,state->hardbndl.ptr.p_double[i])&&ae_fp_less(v,state->hardbndu.ptr.p_double[i]), "GIPM: check 833206 failed, bounds are too tight", _state);
                state->current.xp.ptr.p_double[i] = boundval(state->current.xp.ptr.p_double[i], ae_minreal(state->hardbndl.ptr.p_double[i]+initprimdual, v, _state), ae_maxreal(state->hardbndu.ptr.p_double[i]-initprimdual, v, _state), _state);
                continue;
            }
            if( state->hasbndl.ptr.p_bool[i] )
            {
                state->current.xp.ptr.p_double[i] = ae_maxreal(state->current.xp.ptr.p_double[i], state->hardbndl.ptr.p_double[i]+initprimdual, _state);
                continue;
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                state->current.xp.ptr.p_double[i] = ae_minreal(state->current.xp.ptr.p_double[i], state->hardbndu.ptr.p_double[i]-initprimdual, _state);
                continue;
            }
        }
    }
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 672222", _state);
        rsetallocv(np, gipm2_initrlxpenalty, &state->pxl, _state);
        rsetallocv(np, gipm2_initrlxpenalty, &state->pxu, _state);
        rsetallocv(np, 1.0, &state->softbcmsxl, _state);
        rsetallocv(np, 1.0, &state->softbcmsxu, _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i] )
            {
                
                /*
                 * Both bounds are present
                 */
                state->current.sxl.ptr.p_double[i] = initprimdual;
                state->current.sxu.ptr.p_double[i] = initprimdual;
                if( ae_fp_eq(state->softbcql.ptr.p_double[i],(double)(0))&&ae_fp_eq(state->softbcqu.ptr.p_double[i],(double)(0)) )
                {
                    
                    /*
                     * Both bounds are hard
                     */
                    v = 0.5*(state->hardbndl.ptr.p_double[i]+state->hardbndu.ptr.p_double[i]);
                    ae_assert(ae_fp_greater(v,state->hardbndl.ptr.p_double[i])&&ae_fp_less(v,state->hardbndu.ptr.p_double[i]), "GIPM: check 730238 failed, hard bounds are too tight", _state);
                    state->current.xp.ptr.p_double[i] = boundval(state->current.xp.ptr.p_double[i], ae_minreal(state->hardbndl.ptr.p_double[i]+initprimdual, v, _state), ae_maxreal(state->hardbndu.ptr.p_double[i]-initprimdual, v, _state), _state);
                }
                else
                {
                    
                    /*
                     * At least one bound is soft
                     */
                    if( ae_fp_eq(state->softbcql.ptr.p_double[i],(double)(0)) )
                    {
                        state->current.xp.ptr.p_double[i] = ae_maxreal(state->current.xp.ptr.p_double[i], state->hardbndl.ptr.p_double[i]+initprimdual, _state);
                    }
                    else
                    {
                        state->current.sxl.ptr.p_double[i] = state->current.sxl.ptr.p_double[i]+ae_maxreal(state->hardbndl.ptr.p_double[i]-state->current.xp.ptr.p_double[i], (double)(0), _state)/state->softbcql.ptr.p_double[i];
                    }
                    if( ae_fp_eq(state->softbcqu.ptr.p_double[i],(double)(0)) )
                    {
                        state->current.xp.ptr.p_double[i] = ae_minreal(state->current.xp.ptr.p_double[i], state->hardbndu.ptr.p_double[i]-initprimdual, _state);
                    }
                    else
                    {
                        state->current.sxu.ptr.p_double[i] = state->current.sxu.ptr.p_double[i]+ae_maxreal(state->current.xp.ptr.p_double[i]-state->hardbndu.ptr.p_double[i], (double)(0), _state)/state->softbcqu.ptr.p_double[i];
                    }
                }
                state->softbcmsxl.ptr.p_double[i] = (double)2*state->current.sxl.ptr.p_double[i]+(double)1;
                state->softbcmsxu.ptr.p_double[i] = (double)2*state->current.sxu.ptr.p_double[i]+(double)1;
                continue;
            }
            
            /*
             * At most one bound is present
             */
            if( state->hasbndl.ptr.p_bool[i] )
            {
                state->current.sxl.ptr.p_double[i] = initprimdual;
                if( ae_fp_eq(state->softbcql.ptr.p_double[i],(double)(0)) )
                {
                    state->current.xp.ptr.p_double[i] = ae_maxreal(state->current.xp.ptr.p_double[i], state->hardbndl.ptr.p_double[i]+initprimdual, _state);
                }
                else
                {
                    state->current.sxl.ptr.p_double[i] = state->current.sxl.ptr.p_double[i]+ae_maxreal(state->hardbndl.ptr.p_double[i]-state->current.xp.ptr.p_double[i], (double)(0), _state)/state->softbcql.ptr.p_double[i];
                }
                state->softbcmsxl.ptr.p_double[i] = (double)2*state->current.sxl.ptr.p_double[i]+(double)1;
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                state->current.sxu.ptr.p_double[i] = initprimdual;
                if( ae_fp_eq(state->softbcqu.ptr.p_double[i],(double)(0)) )
                {
                    state->current.xp.ptr.p_double[i] = ae_minreal(state->current.xp.ptr.p_double[i], state->hardbndu.ptr.p_double[i]-initprimdual, _state);
                }
                else
                {
                    state->current.sxu.ptr.p_double[i] = state->current.sxu.ptr.p_double[i]+ae_maxreal(state->current.xp.ptr.p_double[i]-state->hardbndu.ptr.p_double[i], (double)(0), _state)/state->softbcqu.ptr.p_double[i];
                }
                state->softbcmsxu.ptr.p_double[i] = (double)2*state->current.sxu.ptr.p_double[i]+(double)1;
            }
        }
    }
    gipm2_sendx(state, &state->current.xp, _state);
    state->requesttype = 1;
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    if( !gipm2_retrievefij(state, &state->current, _state) )
    {
        
        /*
         * Failed to retrieve function/Jacobian, infinities detected!
         */
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    ae_assert(mtotal-state->mflex==0, "GIPM2: 824204", _state);
    for(i=0; i<=state->mflex-1; i++)
    {
        state->current.sxf.ptr.p_double[i] = state->current.fi.ptr.p_double[1+i];
        if( state->hasflexcl.ptr.p_bool[i] )
        {
            state->current.gl.ptr.p_double[i] = ae_maxreal(state->current.sxf.ptr.p_double[i]-state->flexcl.ptr.p_double[i], initprimdual, _state);
        }
        if( state->hasflexcu.ptr.p_bool[i] )
        {
            state->current.gu.ptr.p_double[i] = ae_maxreal(state->flexcu.ptr.p_double[i]-state->current.sxf.ptr.p_double[i], initprimdual, _state);
        }
    }
    gipm2_computeprimalmult(state, &state->current, state->currentmu, &state->delayedmult, _state);
    gipm2_computeprimalmult(state, &state->current, state->currentmu, &state->currentmult, _state);
    state->requesttype = 1000;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->requesttype = 1004;
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    if( state->replywasrescale )
    {
        if( state->dotrace )
        {
            ae_trace("> initial objective/constraints rescaling was performed\n");
        }
        gipm2_rescalecurrentbest(state, &state->replyv, ae_false, _state);
        gipm2_computeprimalmult(state, &state->current, state->currentmu, &state->delayedmult, _state);
        gipm2_computeprimalmult(state, &state->current, state->currentmu, &state->currentmult, _state);
    }
    state->requesttype = 1005;
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    rcopyallocv(1+mtotal, &state->replyv, &state->callerfscales, _state);
    state->nrmx0 = rmaxabsv(np, &state->current.xp, _state);
    state->absf0 = ae_fabs(state->current.fi.ptr.p_double[0], _state);
    ae_assert(mtotal-state->mflex==0, "GIPM2: 042333", _state);
    rcopyv(state->np, &state->current.xp, &state->reportx, _state);
    state->reportf = state->current.fi.ptr.p_double[0];
    state->requesttype = -1;
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    gipm2_updateerrorestimates(state, _state);
    
    /*
     * Prepare best point.
     *
     * NOTE: BestErr is initialized by MaxRealNumber, which is intentional - it means that
     *       during the very first iteration any point is better than the best, and some
     *       code (in particular, one using SengLagrangianInfo()  function) relies on the property.
     */
    state->besterr = ae_maxrealnumber;
    state->hasbest = ae_false;
    gipm2_varscopy(&state->current, &state->best, _state);
    gipm2_lagcoeffsfrommultipliers(&state->currentmult, &state->bestlagbc, &state->bestlagnlc, _state);
    state->requesttype = 1006;
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    
    /*
     * Outer iterations
     */
    state->curbigval = gipm2_initbigval;
    state->curbiggrowth = gipm2_initbiggrowth;
    state->lambdav = state->currentmu;
    state->lambdadecay = 1.0;
    state->lambdafound = ae_false;
    state->infinitiesdetected = ae_false;
    state->failedtorecoverfrominfinities = ae_false;
    state->subsequentfactfailures = 0;
    state->firstouteriteration = ae_true;
    state->linesearchalwayssuccessful = ae_true;
    state->lastprim = ae_sqrt(ae_maxrealnumber, _state);
    state->lastdual = state->lastprim;
    state->lastcmpl = state->lastprim;
    gnrm = (double)(1);
lbl_18:
    if( ae_false )
    {
        goto lbl_19;
    }
    
    /*
     * Reset parameters and flags at the beginning of inner iterations
     */
    state->unboundednesssuspected = ae_false;
    lastcheckpointf = ae_maxrealnumber;
    lastcheckpointd = ae_maxrealnumber;
    lastcheckpointc = ae_maxrealnumber;
    lastcheckpointidx = state->repiterationscount;
    state->rhsstagnationcounter = 0;
    state->goodinnerits = 0;
    state->lambdav = ae_maxreal(state->currentmu, state->lambdav, _state);
    state->lambdadecay = 1.0;
    state->rejectedwithineps = 0;
    state->nonmonotonicmemlen = -1;
    state->inneriterationidx = 0;
    worsethanbest = ae_true;
    minedual = ae_maxrealnumber;
    state->hessiandecay = 1.0;
    
    /*
     * If the problem formulation is mu-dependent, recompute target/objective using
     * current value of Mu.
     *
     *if State.MuDependent then
     *begin
     *    State.RequestType:=1;
     *    State.QueryMu:=State.Mu;
     *    rAllocV(NRaw+MTotal, State.QueryData);
     *    rCopyVX(NRaw,  State.Current.XP, 0, State.QueryData, 0);
     *    rCopyVX(MFlex, State.Current.YP, 0, State.QueryData, NRaw);
     *    rCopyVX(MHard, State.Current.YV, 0, State.QueryData, NRaw+MFlex);
     *    rAllocV(1+MTotal, State.ReplyFi);
     *    RComm;
     *    vfjInitFromSparse(State.Current.XP, NRaw, State.ReplyFi, 1+MTotal, State.ReplySJ, State.CurrentFJ);
     *end;
     */
    ae_assert(!state->mudependent, "GIPM2: integrity check 646212 failed", _state);
    
    /*
     * Inner iterations for fixed Mu.
     *
     * After this loop is done we have current point in State.Current and current residual
     * in State.RHS. Additionally, the current point is remembered as the one where the
     * Hessian should be calculated.
     */
lbl_20:
    if( ae_false )
    {
        goto lbl_21;
    }
    if( userterminationneeded )
    {
        goto lbl_21;
    }
    if( state->maxits>0&&state->repiterationscount>=state->maxits )
    {
        if( state->dotrace )
        {
            ae_trace("> maximum number of iterations performed, stopping\n");
        }
        state->repterminationtype = 5;
        goto lbl_21;
    }
    
    /*
     * Issue rescale request, recompute merit function at the initial point
     */
    state->requesttype = 1004;
    state->rstate.stage = 6;
    goto lbl_rcomm;
lbl_6:
    if( !state->replywasrescale )
    {
        goto lbl_22;
    }
    
    /*
     * Handle rescale request
     */
    if( state->dotrace )
    {
        ae_trace("> target/constraints were rescaled due to gradient norms deviating too much from 1.0, clearing nonmonotonic memory");
    }
    if( state->dotrace )
    {
        ae_trace("\n");
    }
    state->nonmonotonicmemlen = -1;
    state->absf0 = state->absf0*state->replyv.ptr.p_double[0];
    gipm2_rescalecurrentbest(state, &state->replyv, ae_true, _state);
    ae_assert(mtotal-state->mflex==0, "GIPM: 177010", _state);
    
    /*
     * Update callerFScales[]
     */
    state->requesttype = 1005;
    state->rstate.stage = 7;
    goto lbl_rcomm;
lbl_7:
    rcopyallocv(1+mtotal, &state->replyv, &state->callerfscales, _state);
    
    /*
     * Reset checkpoint information
     */
    lastcheckpointf = ae_maxrealnumber;
    lastcheckpointd = ae_maxrealnumber;
    lastcheckpointidx = state->repiterationscount;
lbl_22:
    if( state->isprimal )
    {
        merit0 = gipm2_meritfunction(state, &state->current, state->currentmu, _state);
    }
    else
    {
        merit0 = gipm2_meritfunctionpd(state, &state->current, &state->currentmult, state->currentmu, _state);
    }
    
    /*
     * Trace iteration start
     */
    if( state->dotrace )
    {
        ae_trace("\n=== ITERATION %5d STARTED ========================================================================\n",
            (int)(state->repiterationscount));
        ae_trace("mu            = %11.3e\n",
            (double)(state->currentmu));
        ae_trace("lambda        = %11.3e (damping term for the Hessian)\n",
            (double)(state->lambdav));
        ae_trace("lambda_decay  = %7.4f     (decay for the damping term)\n",
            (double)(state->lambdadecay));
        ae_trace("|x|inf        = %11.3e\n",
            (double)(rmaxabsv(np, &state->current.xp, _state)));
        if( state->hassoftbc )
        {
            ae_trace("|sxl/u|inf    = %11.3e (relaxation slacks for box constraints)\n",
                (double)(ae_maxreal(rmaxabsv(np, &state->current.sxl, _state), rmaxabsv(np, &state->current.sxu, _state), _state)));
        }
        ae_assert(mtotal-state->mflex==0, "GIPM2: 154352", _state);
        if( state->hassoftbc )
        {
            ae_trace("|pxl/u|inf    = %11.3e (relaxation penalties for box constraints)\n",
                (double)(ae_maxreal(rmaxabsv(np, &state->pxl, _state), rmaxabsv(np, &state->pxu, _state), _state)));
        }
        ae_assert(mtotal-state->mflex==0, "GIPM2: 157352", _state);
        ae_trace("err.bc        = %11.3e\n",
            (double)(state->repbcerr));
        ae_trace("err.nlc       = %11.3e\n",
            (double)(state->repnlcerr));
        ae_trace("\n");
    }
    
    /*
     * Recompute vector of primal multipliers (for primal algorithm).
     *
     * We use PrevMu instead of CurrentMu in order to gracefully handle decrease of
     * barrier parameter.
     */
    if( state->isprimal )
    {
        gipm2_computeprimalmult(state, &state->current, state->currentmu, &state->currentmult, _state);
        if( state->inneriterationidx>=state->multupdatedelay )
        {
            gipm2_computeprimalmult(state, &state->current, state->currentmu, &state->delayedmult, _state);
        }
    }
    
    /*
     * Compute residual for the non-condensed unsymmetric primal-dual system.
     * Compute errors, check stopping and stagnation criteria.
     */
    gipm2_computerhs(state, &state->current, state->currentmu, &state->currentmult, &state->rhs, &eprim, &edual, &ecmpl, &v, &vv, &v2, _state);
    minedual = ae_minreal(minedual, edual, _state);
    gnrm = (double)(0);
    gnrm = gnrm+rdotv2(np, &state->rhs.rp, _state);
    if( state->hassoftbc )
    {
        gnrm = gnrm+rdotv2(np, &state->rhs.rpsxl, _state);
        gnrm = gnrm+rdotv2(np, &state->rhs.rpsxu, _state);
    }
    if( state->mflex>0 )
    {
        gnrm = gnrm+rdotv2(state->mflex, &state->rhs.rpgl, _state);
        gnrm = gnrm+rdotv2(state->mflex, &state->rhs.rpgu, _state);
        gnrm = gnrm+rdotv2(state->mflex, &state->rhs.rpsxf, _state);
    }
    ae_assert(mtotal-state->mflex==0, "GIPM: 149243", _state);
    gnrm = ae_sqrt(gnrm, _state);
    rhs0 = ae_maxreal(eprim, edual, _state);
    emax = rmax3(eprim, edual, ecmpl, _state);
    if( state->dotrace )
    {
        ae_trace("> computing absolute errors for the barrier problem:\n");
        ae_trace("primal        = %0.3e\n",
            (double)(eprim));
        ae_trace("dual          = %0.3e\n",
            (double)(edual));
        ae_trace("cmpl          = %0.3e\n",
            (double)(ecmpl));
        ae_trace("e.max         = %0.3e\n",
            (double)(emax));
    }
    worsethanbest = ae_fp_greater(gipm2_aggregateerr(eprim, edual, state->currentmu+ecmpl, _state),state->besterr);
    if( (ae_fp_less(merit0,lastcheckpointf-gipm2_checkpointacceptancef*ae_fabs(lastcheckpointf, _state))||ae_fp_less(edual,gipm2_checkpointacceptanced*lastcheckpointd))||ae_fp_less(ecmpl,gipm2_checkpointacceptancec*lastcheckpointc) )
    {
        
        /*
         * Significant progress achieved for at least one parameter. Save checkpoint (used to detect stagnation).
         *
         * NOTE: it is important that the comparison is strict! Non-strict comparison is likely to cause inability to
         *       detect stagnation should any error become exactly zero.
         */
        lastcheckpointf = ae_minreal(lastcheckpointf, merit0, _state);
        lastcheckpointd = ae_minreal(lastcheckpointd, edual, _state);
        lastcheckpointc = ae_minreal(lastcheckpointc, ecmpl, _state);
        lastcheckpointidx = state->repiterationscount;
    }
    if( ae_fp_less_eq(rmaxabs3(eprim/v, edual/vv, ecmpl/v2, _state),state->muepsfactor*state->currentmu) )
    {
        if( state->dotrace )
        {
            ae_trace("> stopping criteria for inner iterations met (relative error is small enough), exit to outer iteration\n");
        }
        goto lbl_21;
    }
    if( state->rejectedwithineps>=gipm2_maxrejectedwithineps )
    {
        if( state->dotrace )
        {
            ae_trace("> %0d short steps (within Eps) or more in a row were rejected, exit to outer iteration\n",
                (int)(gipm2_maxrejectedwithineps));
        }
        goto lbl_21;
    }
    if( ae_fp_greater_eq(state->lambdav,gipm2_lambdamax) )
    {
        if( state->dotrace )
        {
            ae_trace("> regularization is at maximum, stopping inner iterations\n");
        }
        state->lambdav = (double)(1);
        state->lambdadecay = (double)(1);
        goto lbl_21;
    }
    if( state->repiterationscount>lastcheckpointidx+state->maxcheckpointstagnationits )
    {
        if( state->dotrace )
        {
            ae_trace("> iteration stagnated (no new checkpoints for %0d its)\n",
                (int)(state->maxcheckpointstagnationits));
        }
        state->repterminationtype = 7;
        goto lbl_21;
    }
    
    /*
     * Condense LHS (system matrix) and perform modified RCOMM request to factorize it
     */
    if( state->isprimal )
    {
        gipm2_condenselhs(state, &state->current, &state->delayedmult, state->currentmu, state->lambdav, &state->condensed, _state);
    }
    else
    {
        gipm2_condenselhs(state, &state->current, &state->currentmult, state->currentmu, state->lambdav, &state->condensed, _state);
    }
    state->requesttype = 1001;
    state->factsuccess = ae_false;
    rallocv(np+mtotal, &state->querydata, _state);
    rcopyvx(np, &state->condensed.d0, 0, &state->querydata, 0, _state);
    rcopyvx(mtotal, &state->condensed.d1, 0, &state->querydata, np, _state);
    state->rstate.stage = 8;
    goto lbl_rcomm;
lbl_8:
    if( !state->factsuccess )
    {
        if( state->subsequentfactfailures<=gipm2_maxsubsequentfailuresforweakgrowth )
        {
            
            /*
             * Slowly growing Lambda
             */
            if( state->dotrace )
            {
                ae_trace("> factorization failed, Hessian is not sufficiently positive definite, increasing damping factor\n");
            }
            state->lambdav = rcase2(lambdafound, gipm2_lambdagrowth, (double)(10), _state)*ae_maxreal(state->lambdav, state->currentmu, _state);
            state->lambdadecay = 1.0;
        }
        else
        {
            
            /*
             * Many subsequent failures, rapid growth
             */
            if( state->dotrace )
            {
                ae_trace("> factorization failed too many times in a row, rapid growth of LambdaV is activated\n");
            }
            state->lambdav = ae_maxreal(gipm2_lambdarapidgrowth*state->lambdav, 0.001*state->currentmu, _state);
            state->lambdadecay = 1.0;
        }
        state->subsequentfactfailures = state->subsequentfactfailures+1;
        goto lbl_20;
    }
    state->subsequentfactfailures = 0;
    lambdafound = lambdafound||ae_fp_neq(state->lambdav,(double)(0));
    
    /*
     * Run iterative refinement
     */
    gipm2_copyrhs(state, &state->rhs, &state->tmprhs, _state);
    rhsk = ae_maxrealnumber;
    gipm2_varsinitbyzero(&state->delta, np, state->mflex, state->meq, state->mrlxeq, state->m1, state->mrlx1, state->hashardbc, state->hassoftbc, _state);
    gipm2_multinitbyzero(&state->deltamult, np, state->mflex, state->meq, state->mrlxeq, state->m1, state->mrlx1, state->hashardbc, state->hassoftbc, _state);
    rfsidx = 0;
lbl_24:
    if( rfsidx>gipm2_maxrfsits-1 )
    {
        goto lbl_26;
    }
    if( state->isprimal )
    {
        gipm2_condenserhs(state, &state->current, &state->delayedmult, &state->tmprhs, state->currentmu, state->lambdav, &state->condensed, _state);
    }
    else
    {
        gipm2_condenserhs(state, &state->current, &state->currentmult, &state->tmprhs, state->currentmu, state->lambdav, &state->condensed, _state);
    }
    state->requesttype = 1002;
    rallocv(np+mtotal, &state->querydata, _state);
    rcopyvx(np, &state->condensed.wx, 0, &state->querydata, 0, _state);
    rcopyvx(mtotal, &state->condensed.wy, 0, &state->querydata, np, _state);
    rallocv(np+mtotal, &state->replysol, _state);
    state->rstate.stage = 9;
    goto lbl_rcomm;
lbl_9:
    if( state->isprimal )
    {
        gipm2_unpacksolution(state, &state->current, &state->delayedmult, state->currentmu, state->lambdav, &state->tmprhs, &state->condensed, &state->replysol, &state->corr, &state->corrmult, _state);
    }
    else
    {
        gipm2_unpacksolution(state, &state->current, &state->currentmult, state->currentmu, state->lambdav, &state->tmprhs, &state->condensed, &state->replysol, &state->corr, &state->corrmult, _state);
    }
    gipm2_varsapplystep(&state->delta, &state->corr, 1.0, _state);
    gipm2_multapplystep(&state->deltamult, &state->corrmult, 1.0, _state);
    state->requesttype = 1003;
    ae_assert(mtotal==state->mflex, "GIPM2: 015342", _state);
    rallocv(np+mtotal, &state->querydata, _state);
    rallocv(np+mtotal+np, &state->replyprod, _state);
    rcopyvx(np, &state->corr.xp, 0, &state->querydata, 0, _state);
    rcopyvx(mtotal, &state->corrmult.zcf, 0, &state->querydata, np, _state);
    state->rstate.stage = 10;
    goto lbl_rcomm;
lbl_10:
    rallocv(np, &state->tmphdxp, _state);
    rallocv(mtotal, &state->tmpjdxp, _state);
    rallocv(np, &state->tmpjtdy, _state);
    rcopyvx(np, &state->replyprod, 0, &state->tmphdxp, 0, _state);
    rcopyvx(mtotal, &state->replyprod, np, &state->tmpjdxp, 0, _state);
    rcopyvx(np, &state->replyprod, np+mtotal, &state->tmpjtdy, 0, _state);
    if( state->isprimal )
    {
        rhsk = gipm2_computeupdateresidual(state, &state->current, &state->delayedmult, state->currentmu, state->lambdav, &state->corr, &state->corrmult, &state->tmphdxp, &state->tmpjdxp, &state->tmpjtdy, &state->tmprhs, _state);
    }
    else
    {
        rhsk = gipm2_computeupdateresidual(state, &state->current, &state->currentmult, state->currentmu, state->lambdav, &state->corr, &state->corrmult, &state->tmphdxp, &state->tmpjdxp, &state->tmpjtdy, &state->tmprhs, _state);
    }
    if( state->dotrace )
    {
        ae_trace("> analyzing solution to the linear system, relative residual norm is %0.2e\n",
            (double)(rhsk/coalesce(rhs0, (double)(1), _state)));
    }
    if( ae_fp_less(rhsk,gipm2_rhstol*rhs0) )
    {
        if( state->dotrace )
        {
            ae_trace("> residual norm is small enough, stopping refinement\n");
        }
        goto lbl_26;
    }
    rfsidx = rfsidx+1;
    goto lbl_24;
lbl_26:
    if( ae_fp_greater(rhsk,ae_maxreal((double)2*gipm2_rhstol, 0.001, _state)*rhs0) )
    {
        if( state->dotrace )
        {
            ae_trace("> iterative refinement failed to sufficiently decrease residual, increasing Lambda and retrying\n");
        }
        state->lambdav = ae_maxreal((double)10*state->lambdav, (double)1000*ae_machineepsilon*((double)1/state->currentmu), _state);
        state->lambdadecay = 1.0;
        state->repiterationscount = state->repiterationscount+1;
        goto lbl_20;
    }
    state->dxpnrm = rmaxabsv(np, &state->delta.xp, _state);
    
    /*
     * Perform line search
     */
    cmpl0 = gipm2_computeecmpl2(state, &state->rhs, _state);
    if( state->nonmonotonicmemlen<0 )
    {
        gipm2_nonmonotonicinit(state, gipm2_maxnd, merit0, _state);
    }
    if( state->isprimal )
    {
        alpha = gipm2_computesteplengthp(state, &state->current, &state->delta, _state);
    }
    else
    {
        alpha = gipm2_computesteplengthpd(state, &state->current, &state->delta, &state->currentmult, &state->deltamult, _state);
    }
    if( state->dotrace )
    {
        ae_trace("> |dXP|=%0.2e, computed maximum step length: alpha=%0.6f\n",
            (double)(state->dxpnrm),
            (double)(alpha));
    }
    smalltrials = 0;
    btits = 0;
    monotonicallyaccepted = ae_false;
    accepted = ae_false;
lbl_27:
    if( ae_false )
    {
        goto lbl_28;
    }
    
    /*
     * Preliminary checks
     */
    if( ae_fp_less(alpha,gipm2_smallstep) )
    {
        smalltrials = smalltrials+1;
    }
    if( smalltrials>=gipm2_maxsmalltrials )
    {
        alpha = (double)(0);
        goto lbl_28;
    }
    
    /*
     * Evaluate objective/constraints at the trial point, update multipliers according to the step and method (primal or primal-dual)
     */
    gipm2_varscopy(&state->current, &state->cand, _state);
    gipm2_varsapplystep(&state->cand, &state->delta, alpha, _state);
    gipm2_sendx(state, &state->cand.xp, _state);
    state->requesttype = 1;
    state->rstate.stage = 11;
    goto lbl_rcomm;
lbl_11:
    if( !gipm2_retrievefij(state, &state->cand, _state) )
    {
        
        /*
         * Failed to retrieve function/Jacobian, infinities detected!
         */
        if( state->dotrace )
        {
            ae_trace("> objective/constraints contain infinities or NANs, promoting shorter steps\n");
        }
        state->infinitiesdetected = ae_true;
        state->failedtorecoverfrominfinities = ae_true;
        alpha = 0.5*alpha;
        goto lbl_27;
    }
    if( !state->isprimal )
    {
        gipm2_multcopy(&state->currentmult, &state->candmult, _state);
        gipm2_multapplystep(&state->candmult, &state->deltamult, alpha, _state);
    }
    else
    {
        gipm2_computeprimalmult(state, &state->cand, state->currentmu, &state->candmult, _state);
    }
    state->failedtorecoverfrominfinities = ae_false;
    
    /*
     * Control violation of hard constraints
     */
    violated = ae_false;
    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 240129", _state);
    if( violated )
    {
        btits = btits+1;
        alpha = 0.5*alpha;
        goto lbl_27;
    }
    
    /*
     * Compute merit function and report step
     */
    gipm2_computerhs(state, &state->cand, state->currentmu, &state->candmult, &state->candrhs, &eprim2, &edual2, &ecmpl2, &v, &vv, &v2, _state);
    if( state->isprimal )
    {
        merit1 = gipm2_meritfunction(state, &state->cand, state->currentmu, _state);
    }
    else
    {
        merit1 = gipm2_meritfunctionpd(state, &state->cand, &state->candmult, state->currentmu, _state);
    }
    cmpl1 = gipm2_computeecmpl2(state, &state->candrhs, _state);
    if( state->dotrace )
    {
        ae_trace("> stp=%11.3e    dMerit=%14.6e\n",
            (double)(alpha),
            (double)(merit1-merit0));
    }
    
    /*
     * Decide
     */
    monotonicallyaccepted = ae_fp_less(merit1,merit0);
    accepted = monotonicallyaccepted||ae_fp_less(merit1,merit0+gipm2_nonmonotonicgetcorr(state, merit0, _state));
    if( accepted )
    {
        goto lbl_28;
    }
    
    /*
     * The step is not accepted, decrease Alpha
     */
    btits = btits+1;
    alpha = 0.5*alpha;
    goto lbl_27;
lbl_28:
    
    /*
     * Handle line search end
     */
    if( !accepted )
    {
        if( state->dotrace )
        {
            ae_trace("> line search failed to provide sufficient decrease, stopping optimizer with TerminationType=7\n");
        }
        state->repterminationtype = 7;
        goto lbl_21;
    }
    if( btits>=3 )
    {
        state->lambdav = ae_maxreal((double)2*state->lambdav, 0.0001*ae_sqrt(gnrm, _state), _state);
        state->lambdadecay = 1.0;
        if( state->dotrace )
        {
            ae_trace("> too many backtracking its performed, increasing regularization coeff LambdaV to %0.2e\n",
                (double)(state->lambdav));
        }
    }
    else
    {
        if( monotonicallyaccepted )
        {
            state->lambdav = ae_maxreal(state->lambdav*state->lambdadecay, ae_machineepsilon*ae_machineepsilon, _state);
            state->lambdadecay = ae_maxreal(state->lambdadecay*gipm2_lambdadecayacc, ae_sqrt(ae_sqrt(ae_machineepsilon, _state), _state), _state);
        }
        else
        {
            state->lambdadecay = 1.0;
        }
    }
    if( state->dotrace )
    {
        ae_trace("> line search finished with alpha=%0.2e\n",
            (double)(alpha));
        ae_trace("> f:         %0.9e -> %0.9e (delta=%0.3e)\n",
            (double)(state->current.fi.ptr.p_double[0]),
            (double)(state->cand.fi.ptr.p_double[0]),
            (double)(state->cand.fi.ptr.p_double[0]-state->current.fi.ptr.p_double[0]));
        ae_trace("> meritf:    %0.9e -> %0.9e (delta=%0.3e)\n",
            (double)(merit0),
            (double)(merit1),
            (double)(merit1-merit0));
    }
    if( state->dotrace )
    {
        ae_trace("> evaluating proposed step:\n");
        ae_trace("targetF:        %17.9e -> %17.9e (delta=%11.3e, raw)\n",
            (double)(state->callerfscales.ptr.p_double[0]*state->current.fi.ptr.p_double[0]),
            (double)(state->callerfscales.ptr.p_double[0]*state->cand.fi.ptr.p_double[0]),
            (double)(state->callerfscales.ptr.p_double[0]*(state->cand.fi.ptr.p_double[0]-state->current.fi.ptr.p_double[0])));
        ae_trace("meritF:         %17.9e -> %17.9e (delta=%11.3e, target + barrier + penalty, predicted=%11.3e)\n",
            (double)(merit0),
            (double)(merit1),
            (double)(merit1-merit0),
            (double)(alpha*gipm2_computedg(state, &state->delta, &state->rhs, _state)));
        ae_trace("meritF+corr:    %17.9e -> %17.9e (delta=%11.3e, step subject to nonmonotonic correction)\n",
            (double)(merit0),
            (double)(merit1-gipm2_nonmonotonicgetcorr(state, merit0, _state)),
            (double)(merit1-merit0-gipm2_nonmonotonicgetcorr(state, merit0, _state)));
        ae_trace("dualErr:        %17.9e -> %17.9e (delta=%11.3e, gradient inf-norm)\n",
            (double)(edual),
            (double)(edual2),
            (double)(edual2-edual));
        if( !state->isprimal )
        {
            ae_trace("cmplErr:        %17.9e -> %17.9e (delta=%11.3e, complementarity residual inf-norm)\n",
                (double)(ecmpl),
                (double)(ecmpl2),
                (double)(ecmpl2-ecmpl));
        }
        ae_trace("(D,G):          %17.9e -> %17.9e\n",
            (double)(gipm2_computedg(state, &state->delta, &state->rhs, _state)),
            (double)(gipm2_computedg(state, &state->delta, &state->candrhs, _state)));
        if( ae_fp_less(merit1,merit0) )
        {
            ae_trace("> step is accepted\n");
        }
        else
        {
            ae_trace("> step is nonmonotonically accepted\n");
        }
    }
    ae_assert(!state->isprimal, "GIPM2: 004122", _state);
    gipm2_sendlagrangianinfo(state, &state->current, &state->candmult, &state->cand, &state->candmult, ae_false, _state);
    state->requesttype = 1008;
    state->rstate.stage = 12;
    goto lbl_rcomm;
lbl_12:
    gipm2_varscopy(&state->cand, &state->current, _state);
    gipm2_multcopy(&state->candmult, &state->currentmult, _state);
    rcopyv(state->np, &state->current.xp, &state->reportx, _state);
    state->reportf = state->current.fi.ptr.p_double[0];
    state->requesttype = -1;
    state->rstate.stage = 13;
    goto lbl_rcomm;
lbl_13:
    
    /*
     * Next iteration
     */
    state->requesttype = 1000;
    state->rstate.stage = 14;
    goto lbl_rcomm;
lbl_14:
    state->lastprim = eprim;
    state->lastdual = edual;
    state->lastcmpl = ecmpl;
    ae_assert(ae_fp_neq(state->lambdav,(double)(0)), "GIPM2: 429219", _state);
    state->inneriterationidx = state->inneriterationidx+1;
    state->repiterationscount = state->repiterationscount+1;
    state->rhsstagnationcounter = state->rhsstagnationcounter+1;
    state->goodinnerits = state->goodinnerits+1;
    state->prevmu = state->currentmu;
    state->failedtorecoverfrominfinities = ae_false;
    gipm2_updateerrorestimates(state, _state);
    gipm2_nonmonotonicpush(state, merit1, _state);
    goto lbl_20;
lbl_21:
    state->firstouteriteration = ae_false;
    if( state->dotrace )
    {
        ae_trace("\n----- outer iteration ------------------------------------------------------------------------------\n");
    }
    if( userterminationneeded )
    {
        if( state->dotrace )
        {
            ae_trace("> user requested immediate termination, stopping\n");
        }
        state->repterminationtype = 8;
        goto lbl_19;
    }
    if( state->repterminationtype!=0 )
    {
        goto lbl_19;
    }
    if( state->unboundednesssuspected )
    {
        if( (ae_fp_less_eq(state->currentmu,1.001*state->mumin)&&ae_fp_greater_eq(state->curbigval,0.999*gipm2_maxbigval))&&ae_fp_greater_eq(state->curbiggrowth,0.999*gipm2_maxbiggrowth) )
        {
            if( state->dotrace )
            {
                ae_trace("> unboundedness detected (mu at minimum, thresholds at maximum, the problem is still unbounded)\n");
            }
            state->repterminationtype = -4;
            goto lbl_19;
        }
        if( state->dotrace )
        {
            ae_trace("> decreasing mu, increasing unboundedness detection thresholds, continuing iterations\n");
        }
        state->currentmu = ae_maxreal(state->mumin, 0.01*state->currentmu, _state);
        state->curbigval = ae_minreal(gipm2_vggrowth*state->curbigval, gipm2_maxbigval, _state);
        state->curbiggrowth = ae_minreal(gipm2_vggrowth*state->curbiggrowth, gipm2_maxbiggrowth, _state);
        goto lbl_18;
    }
    
    /*
     * Recompute RHS from scratch (less dependency on the internal loop).
     * Check primal/dual/complementarity errors.
     */
    gipm2_computerhs(state, &state->current, state->currentmu, &state->currentmult, &state->rhs, &eprim, &edual, &ecmpl, &v, &vv, &v2, _state);
    emax = gipm2_aggregateerr(eprim, edual, state->currentmu+ecmpl, _state);
    if( state->dotrace )
    {
        ae_trace("> recomputing absolute errors, taking complementarity error in account:\n");
        ae_trace("primal        = %0.3e\n",
            (double)(eprim));
        ae_trace("dual          = %0.3e\n",
            (double)(edual));
        ae_trace("cmpl          = %0.3e\n",
            (double)(state->currentmu+ecmpl));
    }
    if( ae_fp_less_eq(rmax3(eprim/v, edual/vv, state->currentmu+ecmpl/v2, _state),state->eps) )
    {
        if( state->dotrace )
        {
            ae_trace("> stopping criteria for outer iterations are met (relative error is below Eps=%0.2e), solved\n",
                (double)(state->eps));
        }
        state->repterminationtype = 2;
        goto lbl_19;
    }
    if( ae_fp_less_eq(state->currentmu,1.001*state->mumin) )
    {
        if( state->dotrace )
        {
            ae_trace("> mu is at minimum, stagnation detected, stopping\n");
        }
        state->repterminationtype = 7;
        goto lbl_19;
    }
    
    /*
     * Stopping criteria are not met, continuing iteration, save the best point so far
     */
    if( ae_fp_greater_eq(emax,state->besterr) )
    {
        goto lbl_29;
    }
    gipm2_varscopy(&state->current, &state->best, _state);
    gipm2_lagcoeffsfrommultipliers(&state->currentmult, &state->bestlagbc, &state->bestlagnlc, _state);
    state->besterr = emax;
    state->hasbest = ae_true;
    state->requesttype = 1006;
    state->rstate.stage = 15;
    goto lbl_rcomm;
lbl_15:
    if( state->dotrace )
    {
        ae_trace("> the point provides the best prim/dual errors so far, saving\n");
    }
    goto lbl_30;
lbl_29:
    if( state->dotrace )
    {
        ae_trace("> the point does not improve over the best prim/dual errors so far, not saved\n");
    }
lbl_30:
    
    /*
     * Inner iterations are over, tighten/relax bounds if necessary and decrease Mu
     */
    gipm2_tunepenalties(state, &state->currentmult, _state);
    gipm2_tightenrelaxbounds(state, _state);
    state->currentmu = ae_maxreal(state->mumin, ae_minreal(state->mudecaylinear*state->currentmu, ae_pow(state->currentmu, state->mudecaypower, _state), _state), _state);
    goto lbl_18;
lbl_19:
    
    /*
     * Almost done:
     * * compare error at State.Current with one at State.Best, restore from State.Best
     *   if optimizer went in wrong direction
     * * set completion flags
     */
    if( state->dotrace )
    {
        ae_trace("\n=== STOPPED ========================================================================================\n");
    }
    gipm2_computerhs(state, &state->current, state->currentmu, &state->currentmult, &state->rhs, &eprim, &edual, &ecmpl, &v, &vv, &v2, _state);
    if( ae_fp_less_eq(gipm2_aggregateerr(eprim, edual, state->currentmu+ecmpl, _state),state->besterr) )
    {
        goto lbl_31;
    }
    
    /*
     * Restore the best point so far.
     *
     * It is essential that Request=1007 is ussued prior to Request=-1
     */
    gipm2_varscopy(&state->best, &state->current, _state);
    rcopyv(state->np, &state->current.xp, &state->reportx, _state);
    state->requesttype = 1007;
    state->rstate.stage = 16;
    goto lbl_rcomm;
lbl_16:
    state->reportf = state->current.fi.ptr.p_double[0];
    state->requesttype = -1;
    state->rstate.stage = 17;
    goto lbl_rcomm;
lbl_17:
    gipm2_updateerrorestimates(state, _state);
    if( state->dotrace )
    {
        ae_trace("> the last accepted point is worse than the best one seen so far (p/d/c err: %0.2e vs %0.2e), restoring the best one\n",
            (double)(gipm2_aggregateerr(eprim, edual, state->currentmu+ecmpl, _state)),
            (double)(state->besterr));
    }
lbl_31:
    if( state->infinitiesdetected )
    {
        state->repterminationtype = icase2(state->failedtorecoverfrominfinities, -8, state->repterminationtype+800, _state);
    }
    if( state->dotrace )
    {
        if( state->infinitiesdetected&&state->failedtorecoverfrominfinities )
        {
            ae_trace("> infinities were detected, but we failed to recover by decreasing trust radius; declaring failure with the code -8\n");
        }
        ae_trace("raw target:     %20.12e\n",
            (double)(state->current.fi.ptr.p_double[0]*state->callerfscales.ptr.p_double[0]));
    }
    smoothnessmonitortracestatus(smonitor, state->dotrace, _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = np;
    state->rstate.ia.ptr.p_int[1] = mtotal;
    state->rstate.ia.ptr.p_int[2] = i;
    state->rstate.ia.ptr.p_int[3] = rfsidx;
    state->rstate.ia.ptr.p_int[4] = lastcheckpointidx;
    state->rstate.ia.ptr.p_int[5] = smalltrials;
    state->rstate.ia.ptr.p_int[6] = btits;
    state->rstate.ia.ptr.p_int[7] = jj;
    state->rstate.ba.ptr.p_bool[0] = accepted;
    state->rstate.ba.ptr.p_bool[1] = worsethanbest;
    state->rstate.ba.ptr.p_bool[2] = monotonicallyaccepted;
    state->rstate.ba.ptr.p_bool[3] = violated;
    state->rstate.ba.ptr.p_bool[4] = lambdafound;
    state->rstate.ra.ptr.p_double[0] = gnrm;
    state->rstate.ra.ptr.p_double[1] = v;
    state->rstate.ra.ptr.p_double[2] = vv;
    state->rstate.ra.ptr.p_double[3] = v2;
    state->rstate.ra.ptr.p_double[4] = alpha;
    state->rstate.ra.ptr.p_double[5] = eprim;
    state->rstate.ra.ptr.p_double[6] = edual;
    state->rstate.ra.ptr.p_double[7] = ecmpl;
    state->rstate.ra.ptr.p_double[8] = emax;
    state->rstate.ra.ptr.p_double[9] = eprim2;
    state->rstate.ra.ptr.p_double[10] = edual2;
    state->rstate.ra.ptr.p_double[11] = ecmpl2;
    state->rstate.ra.ptr.p_double[12] = rhs0;
    state->rstate.ra.ptr.p_double[13] = rhsk;
    state->rstate.ra.ptr.p_double[14] = merit0;
    state->rstate.ra.ptr.p_double[15] = merit1;
    state->rstate.ra.ptr.p_double[16] = cmpl0;
    state->rstate.ra.ptr.p_double[17] = cmpl1;
    state->rstate.ra.ptr.p_double[18] = initprimdual;
    state->rstate.ra.ptr.p_double[19] = minedual;
    state->rstate.ra.ptr.p_double[20] = lastcheckpointf;
    state->rstate.ra.ptr.p_double[21] = lastcheckpointd;
    state->rstate.ra.ptr.p_double[22] = lastcheckpointc;
    return result;
}


/*************************************************************************
Extract solution

INPUT PARAMETERS:
    State               -   solver instance
    
OUTPUT PARAMETERS:
    XS                  -   array[N], solution
    LagBC               -   array[N], Lagrange multipliers for box constraints
    LagXC               -   array[MTotal], Lagrange multipliers for a;; constraints
    TerminationType     -   completion code, positive values for success,
                            negative for failures (XS constrains best point
                            found so far):
                            * -2    the task is either unbounded or infeasible;
                                    the IPM solver has difficulty distinguishing between these two.
                            * +1    stopping criteria are met
                            * +7    stopping criteria are too stringent

RESULT:

This function ALWAYS returns something  meaningful in XS, LagBC, LagLC - 
either solution or the best point so far, even for negative TerminationType.

  -- ALGLIB --
     Copyright 25.05.2024 by Bochkanov Sergey
*************************************************************************/
void gipm2results(gipm2state* state,
     /* Real    */ ae_vector* xs,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* lagxc,
     ae_int_t* terminationtype,
     ae_state *_state)
{

    *terminationtype = 0;

    *terminationtype = state->repterminationtype;
    rcopyallocv(state->np, &state->current.xp, xs, _state);
    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 566358 failed", _state);
    rcopyallocv(state->np, &state->bestlagbc, lagbc, _state);
    rcopyallocv(state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1, &state->bestlagnlc, lagxc, _state);
}


/*************************************************************************
Allocates place for variables of IPM and fills by zeros.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_varsinitbyzero(gipm2vars* vstate,
     ae_int_t np,
     ae_int_t mflex,
     ae_int_t meq,
     ae_int_t mrlxeq,
     ae_int_t m1,
     ae_int_t mrlx1,
     ae_bool hashardbc,
     ae_bool hassoftbc,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t mtotal;


    mtotal = mflex+meq+mrlxeq+m1+mrlx1;
    ae_assert(np>=1, "VarsInitByZero: N<1", _state);
    ae_assert(mtotal-mflex==0&&!(hashardbc&&hassoftbc), "VarsInitByZero: integrity check 471355 failed", _state);
    vstate->hashardbc = hashardbc;
    vstate->hassoftbc = hassoftbc;
    vstate->np = np;
    vstate->mflex = mflex;
    vstate->meq = 0;
    vstate->mrlxeq = 0;
    vstate->m1 = 0;
    vstate->mrlx1 = 0;
    rsetallocv(np, 0.0, &vstate->xp, _state);
    if( hassoftbc )
    {
        rsetallocv(np, 0.0, &vstate->sxl, _state);
        rsetallocv(np, 0.0, &vstate->sxu, _state);
    }
    if( mflex!=0 )
    {
        rsetallocv(mflex, 0.0, &vstate->sxf, _state);
        rsetallocv(mflex, 0.0, &vstate->gl, _state);
        rsetallocv(mflex, 0.0, &vstate->gu, _state);
    }
    rsetallocv(1+mtotal, 0.0, &vstate->fi, _state);
    sparsecreatecrsemptybuf(np, &vstate->jac, _state);
    for(i=0; i<=mtotal-1; i++)
    {
        sparseappendemptyrow(&vstate->jac, _state);
    }
}


/*************************************************************************
Allocates place for variables of IPM and fills by zeros.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_multinitbyzero(gipm2mult* vstate,
     ae_int_t np,
     ae_int_t mflex,
     ae_int_t meq,
     ae_int_t mrlxeq,
     ae_int_t m1,
     ae_int_t mrlx1,
     ae_bool hashardbc,
     ae_bool hassoftbc,
     ae_state *_state)
{
    ae_int_t mtotal;


    mtotal = mflex+meq+mrlxeq+m1+mrlx1;
    ae_assert(np>=1, "MultInitByZero: N<1", _state);
    ae_assert(mtotal-mflex==0&&!(hashardbc&&hassoftbc), "MultInitByZero: integrity check 496128 failed", _state);
    vstate->hashardbc = hashardbc;
    vstate->hassoftbc = hassoftbc;
    vstate->np = np;
    vstate->mflex = mflex;
    vstate->meq = 0;
    vstate->mrlxeq = 0;
    vstate->m1 = 0;
    vstate->mrlx1 = 0;
    if( hashardbc )
    {
        rsetallocv(np, 0.0, &vstate->zxl, _state);
        rsetallocv(np, 0.0, &vstate->zxu, _state);
    }
    if( hassoftbc )
    {
        rsetallocv(np, 0.0, &vstate->zxl, _state);
        rsetallocv(np, 0.0, &vstate->zxu, _state);
        rsetallocv(np, 0.0, &vstate->zsxl, _state);
        rsetallocv(np, 0.0, &vstate->zsxu, _state);
        rsetallocv(np, 0.0, &vstate->zsxlcap, _state);
        rsetallocv(np, 0.0, &vstate->zsxucap, _state);
    }
    if( mflex>0 )
    {
        rsetallocv(mflex, 0.0, &vstate->zcf, _state);
        rsetallocv(mflex, 0.0, &vstate->zgl, _state);
        rsetallocv(mflex, 0.0, &vstate->zgu, _state);
        rsetallocv(mflex, 0.0, &vstate->zcfgl, _state);
        rsetallocv(mflex, 0.0, &vstate->zcfgu, _state);
    }
}


/*************************************************************************
Applies step to primal and slack  vars.  Invalidates  all  potential  dual
information, function vector and Jacobian

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_varsapplystep(gipm2vars* vstate,
     const gipm2vars* delta,
     double alpha,
     ae_state *_state)
{


    raddv(vstate->np, alpha, &delta->xp, &vstate->xp, _state);
    if( vstate->hassoftbc )
    {
        raddv(vstate->np, alpha, &delta->sxl, &vstate->sxl, _state);
        raddv(vstate->np, alpha, &delta->sxu, &vstate->sxu, _state);
    }
    if( vstate->mflex!=0 )
    {
        raddv(vstate->mflex, alpha, &delta->sxf, &vstate->sxf, _state);
        raddv(vstate->mflex, alpha, &delta->gl, &vstate->gl, _state);
        raddv(vstate->mflex, alpha, &delta->gu, &vstate->gu, _state);
    }
    ae_assert(vstate->meq+vstate->mrlxeq+vstate->m1+vstate->mrlx1==0, "GIPM2: 414056", _state);
}


/*************************************************************************
Applies step to multipliers

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_multapplystep(gipm2mult* vstate,
     const gipm2mult* delta,
     double alpha,
     ae_state *_state)
{


    if( vstate->hashardbc )
    {
        ae_assert(!vstate->hassoftbc, "GIPM2: 586955", _state);
        raddv(vstate->np, alpha, &delta->zxl, &vstate->zxl, _state);
        raddv(vstate->np, alpha, &delta->zxu, &vstate->zxu, _state);
    }
    if( vstate->hassoftbc )
    {
        ae_assert(!vstate->hashardbc, "GIPM2: 592956", _state);
        raddv(vstate->np, alpha, &delta->zxl, &vstate->zxl, _state);
        raddv(vstate->np, alpha, &delta->zxu, &vstate->zxu, _state);
        raddv(vstate->np, alpha, &delta->zsxl, &vstate->zsxl, _state);
        raddv(vstate->np, alpha, &delta->zsxu, &vstate->zsxu, _state);
        raddv(vstate->np, alpha, &delta->zsxlcap, &vstate->zsxlcap, _state);
        raddv(vstate->np, alpha, &delta->zsxucap, &vstate->zsxucap, _state);
    }
    if( vstate->mflex>0 )
    {
        raddv(vstate->mflex, alpha, &delta->zcf, &vstate->zcf, _state);
        raddv(vstate->mflex, alpha, &delta->zgl, &vstate->zgl, _state);
        raddv(vstate->mflex, alpha, &delta->zgu, &vstate->zgu, _state);
        raddv(vstate->mflex, alpha, &delta->zcfgl, &vstate->zcfgl, _state);
        raddv(vstate->mflex, alpha, &delta->zcfgu, &vstate->zcfgu, _state);
    }
    ae_assert(vstate->meq+vstate->mrlxeq+vstate->m1+vstate->mrlx1==0, "GIPM2: 547132", _state);
}


/*************************************************************************
Copy vars

  -- ALGLIB --
     Copyright 06.02.2025 by Bochkanov Sergey
*************************************************************************/
static void gipm2_varscopy(const gipm2vars* src,
     gipm2vars* dst,
     ae_state *_state)
{
    ae_int_t mtotal;


    mtotal = src->mflex+src->meq+src->mrlxeq+src->m1+src->mrlx1;
    dst->hashardbc = src->hashardbc;
    dst->hassoftbc = src->hassoftbc;
    dst->np = src->np;
    dst->mflex = src->mflex;
    dst->meq = src->meq;
    dst->mrlxeq = src->mrlxeq;
    dst->m1 = src->m1;
    dst->mrlx1 = src->mrlx1;
    rcopyallocv(src->np, &src->xp, &dst->xp, _state);
    if( src->hassoftbc )
    {
        rcopyallocv(src->np, &src->sxl, &dst->sxl, _state);
        rcopyallocv(src->np, &src->sxu, &dst->sxu, _state);
    }
    rcopyallocv(src->mflex, &src->sxf, &dst->sxf, _state);
    rcopyallocv(src->mflex, &src->gl, &dst->gl, _state);
    rcopyallocv(src->mflex, &src->gu, &dst->gu, _state);
    ae_assert(mtotal-src->mflex==0, "GIPM2: integrity check 554157 failed", _state);
    rcopyallocv(1+mtotal, &src->fi, &dst->fi, _state);
    sparsecopybuf(&src->jac, &dst->jac, _state);
}


/*************************************************************************
Copy multipliers

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_multcopy(const gipm2mult* src,
     gipm2mult* dst,
     ae_state *_state)
{
    ae_int_t mtotal;


    mtotal = src->mflex+src->meq+src->mrlxeq+src->m1+src->mrlx1;
    dst->hashardbc = src->hashardbc;
    dst->hassoftbc = src->hassoftbc;
    dst->np = src->np;
    dst->mflex = src->mflex;
    dst->meq = src->meq;
    dst->mrlxeq = src->mrlxeq;
    dst->m1 = src->m1;
    dst->mrlx1 = src->mrlx1;
    if( src->hashardbc )
    {
        rcopyallocv(src->np, &src->zxl, &dst->zxl, _state);
        rcopyallocv(src->np, &src->zxu, &dst->zxu, _state);
    }
    if( src->hassoftbc )
    {
        rcopyallocv(src->np, &src->zxl, &dst->zxl, _state);
        rcopyallocv(src->np, &src->zxu, &dst->zxu, _state);
        rcopyallocv(src->np, &src->zsxl, &dst->zsxl, _state);
        rcopyallocv(src->np, &src->zsxu, &dst->zsxu, _state);
        rcopyallocv(src->np, &src->zsxlcap, &dst->zsxlcap, _state);
        rcopyallocv(src->np, &src->zsxucap, &dst->zsxucap, _state);
    }
    if( src->mflex>0 )
    {
        rcopyallocv(src->mflex, &src->zcf, &dst->zcf, _state);
        rcopyallocv(src->mflex, &src->zgl, &dst->zgl, _state);
        rcopyallocv(src->mflex, &src->zgu, &dst->zgu, _state);
        rcopyallocv(src->mflex, &src->zcfgl, &dst->zcfgl, _state);
        rcopyallocv(src->mflex, &src->zcfgu, &dst->zcfgu, _state);
    }
    ae_assert(mtotal-src->mflex==0, "MultCopy: integrity check 825139 failed", _state);
}


/*************************************************************************
Computes step length

  -- ALGLIB --
     Copyright 11.02.2025 by Bochkanov Sergey
*************************************************************************/
static double gipm2_computesteplengthp(const gipm2state* state,
     const gipm2vars* current,
     const gipm2vars* delta,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t i;
    double eps2;
    double result;


    np = state->np;
    eps2 = (double)1/ae_sqrt(ae_maxrealnumber, _state);
    result = ae_maxrealnumber;
    ae_assert(state->isprimal, "GIPM2: 828359 failed", _state);
    
    /*
     * Analyze NP-sized components
     */
    if( state->hashardbc )
    {
        ae_assert(!state->hassoftbc, "GIPM2: 585353", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i]&&delta->xp.ptr.p_double[i]<0.0 )
            {
                result = ae_minreal(result, (current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i])/(eps2-delta->xp.ptr.p_double[i]), _state);
            }
            if( state->hasbndu.ptr.p_bool[i]&&delta->xp.ptr.p_double[i]>0.0 )
            {
                result = ae_minreal(result, (state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i])/(eps2+delta->xp.ptr.p_double[i]), _state);
            }
        }
    }
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 026790", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                if( delta->xp.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*delta->sxl.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, (current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*current->sxl.ptr.p_double[i])/(eps2-(delta->xp.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*delta->sxl.ptr.p_double[i])), _state);
                }
                if( delta->sxl.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, current->sxl.ptr.p_double[i]/(eps2-delta->sxl.ptr.p_double[i]), _state);
                }
                if( delta->sxl.ptr.p_double[i]>0.0 )
                {
                    result = ae_minreal(result, (state->softbcmsxl.ptr.p_double[i]-current->sxl.ptr.p_double[i])/(eps2+delta->sxl.ptr.p_double[i]), _state);
                }
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                if( -delta->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*delta->sxu.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, (state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*current->sxu.ptr.p_double[i])/(eps2-(-delta->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*delta->sxu.ptr.p_double[i])), _state);
                }
                if( delta->sxu.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, current->sxu.ptr.p_double[i]/(eps2-delta->sxu.ptr.p_double[i]), _state);
                }
                if( delta->sxu.ptr.p_double[i]>0.0 )
                {
                    result = ae_minreal(result, (state->softbcmsxu.ptr.p_double[i]-current->sxu.ptr.p_double[i])/(eps2+delta->sxu.ptr.p_double[i]), _state);
                }
            }
        }
    }
    
    /*
     * analyze MFlex-sized components
     */
    for(i=0; i<=state->mflex-1; i++)
    {
        if( delta->gl.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, current->gl.ptr.p_double[i]/(eps2-delta->gl.ptr.p_double[i]), _state);
        }
        if( delta->gu.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, current->gu.ptr.p_double[i]/(eps2-delta->gu.ptr.p_double[i]), _state);
        }
    }
    
    /*
     * Other constraints
     */
    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 585352", _state);
    
    /*
     * Apply fraction to boundary
     */
    result = ae_minreal(gipm2_fractobnd*result, 1.0, _state);
    return result;
}


/*************************************************************************
Computes step length

  -- ALGLIB --
     Copyright 11.02.2025 by Bochkanov Sergey
*************************************************************************/
static double gipm2_computesteplengthpd(const gipm2state* state,
     const gipm2vars* current,
     const gipm2vars* delta,
     const gipm2mult* currentmult,
     const gipm2mult* deltamult,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t i;
    double eps2;
    double result;


    np = state->np;
    eps2 = (double)1/ae_sqrt(ae_maxrealnumber, _state);
    result = ae_maxrealnumber;
    
    /*
     * Analyze NP-sized components
     */
    if( state->hashardbc )
    {
        ae_assert(!state->hassoftbc, "GIPM2: 585353", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                if( delta->xp.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, (current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i])/(eps2-delta->xp.ptr.p_double[i]), _state);
                }
                if( deltamult->zxl.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, currentmult->zxl.ptr.p_double[i]/(eps2-deltamult->zxl.ptr.p_double[i]), _state);
                }
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                if( delta->xp.ptr.p_double[i]>0.0 )
                {
                    result = ae_minreal(result, (state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i])/(eps2+delta->xp.ptr.p_double[i]), _state);
                }
                if( deltamult->zxu.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, currentmult->zxu.ptr.p_double[i]/(eps2-deltamult->zxu.ptr.p_double[i]), _state);
                }
            }
        }
    }
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 026790", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                if( delta->xp.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*delta->sxl.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, (current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*current->sxl.ptr.p_double[i])/(eps2-(delta->xp.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*delta->sxl.ptr.p_double[i])), _state);
                }
                if( delta->sxl.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, current->sxl.ptr.p_double[i]/(eps2-delta->sxl.ptr.p_double[i]), _state);
                }
                if( delta->sxl.ptr.p_double[i]>0.0 )
                {
                    result = ae_minreal(result, (state->softbcmsxl.ptr.p_double[i]-current->sxl.ptr.p_double[i])/(eps2+delta->sxl.ptr.p_double[i]), _state);
                }
                if( deltamult->zxl.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, currentmult->zxl.ptr.p_double[i]/(eps2-deltamult->zxl.ptr.p_double[i]), _state);
                }
                if( deltamult->zsxl.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, currentmult->zsxl.ptr.p_double[i]/(eps2-deltamult->zsxl.ptr.p_double[i]), _state);
                }
                if( deltamult->zsxlcap.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, currentmult->zsxlcap.ptr.p_double[i]/(eps2-deltamult->zsxlcap.ptr.p_double[i]), _state);
                }
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                if( -delta->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*delta->sxu.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, (state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*current->sxu.ptr.p_double[i])/(eps2-(-delta->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*delta->sxu.ptr.p_double[i])), _state);
                }
                if( delta->sxu.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, current->sxu.ptr.p_double[i]/(eps2-delta->sxu.ptr.p_double[i]), _state);
                }
                if( delta->sxu.ptr.p_double[i]>0.0 )
                {
                    result = ae_minreal(result, (state->softbcmsxu.ptr.p_double[i]-current->sxu.ptr.p_double[i])/(eps2+delta->sxu.ptr.p_double[i]), _state);
                }
                if( deltamult->zxu.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, currentmult->zxu.ptr.p_double[i]/(eps2-deltamult->zxu.ptr.p_double[i]), _state);
                }
                if( deltamult->zsxu.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, currentmult->zsxu.ptr.p_double[i]/(eps2-deltamult->zsxu.ptr.p_double[i]), _state);
                }
                if( deltamult->zsxucap.ptr.p_double[i]<0.0 )
                {
                    result = ae_minreal(result, currentmult->zsxucap.ptr.p_double[i]/(eps2-deltamult->zsxucap.ptr.p_double[i]), _state);
                }
            }
        }
    }
    
    /*
     * analyze MFlex-sized components
     */
    for(i=0; i<=state->mflex-1; i++)
    {
        if( delta->gl.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, current->gl.ptr.p_double[i]/(eps2-delta->gl.ptr.p_double[i]), _state);
        }
        if( deltamult->zgl.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, currentmult->zgl.ptr.p_double[i]/(eps2-deltamult->zgl.ptr.p_double[i]), _state);
        }
        if( delta->gu.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, current->gu.ptr.p_double[i]/(eps2-delta->gu.ptr.p_double[i]), _state);
        }
        if( deltamult->zgu.ptr.p_double[i]<0.0 )
        {
            result = ae_minreal(result, currentmult->zgu.ptr.p_double[i]/(eps2-deltamult->zgu.ptr.p_double[i]), _state);
        }
    }
    
    /*
     * Other constraints
     */
    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 585352", _state);
    
    /*
     * Apply fraction to boundary
     */
    result = ae_minreal(gipm2_fractobnd*result, 1.0, _state);
    return result;
}


/*************************************************************************
Compute primal multiplier estimates

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_computeprimalmult(gipm2state* state,
     const gipm2vars* current,
     double currentmu,
     gipm2mult* mult,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t np;
    ae_int_t mtotal;
    ae_int_t i;
    ae_vector tmp;
    ae_int_t rowoffs;
    double invmu;

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

    np = current->np;
    mtotal = state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1;
    invmu = (double)1/currentmu;
    ae_nxpool_retrieve(&state->mnx1pool, &tmp, _state);
    ae_assert(tmp.cnt>=ae_maxint(np, mtotal, _state), "GIPM2: integrity check 7328 failed", _state);
    
    /*
     * Initialize fields
     */
    mult->hashardbc = state->hashardbc;
    mult->hassoftbc = state->hassoftbc;
    mult->np = state->np;
    mult->mflex = state->mflex;
    mult->meq = state->meq;
    mult->mrlxeq = state->mrlxeq;
    mult->m1 = state->m1;
    mult->mrlx1 = state->mrlx1;
    
    /*
     * Compute primal multiplier estimates for box constraints
     */
    if( state->hashardbc )
    {
        ae_assert(!state->hassoftbc, "GIPM2: 782132", _state);
        rsetallocv(state->np, currentmu, &mult->zxl, _state);
        rcopyv(state->np, &current->xp, &tmp, _state);
        raddv(state->np, -1.0, &state->finitebndl, &tmp, _state);
        rmergedivv(state->np, &tmp, &mult->zxl, _state);
        rmergemulv(state->np, &state->elp, &mult->zxl, _state);
        rsetallocv(state->np, currentmu, &mult->zxu, _state);
        rcopyv(state->np, &state->finitebndu, &tmp, _state);
        raddv(state->np, -1.0, &current->xp, &tmp, _state);
        rmergedivv(state->np, &tmp, &mult->zxu, _state);
        rmergemulv(state->np, &state->eup, &mult->zxu, _state);
    }
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 782132", _state);
        rsetallocv(state->np, currentmu, &mult->zxl, _state);
        rcopyv(state->np, &current->xp, &tmp, _state);
        raddv(state->np, -1.0, &state->finitebndl, &tmp, _state);
        rmuladdv(state->np, &state->softbcql, &current->sxl, &tmp, _state);
        rmergedivv(state->np, &tmp, &mult->zxl, _state);
        rmergemulv(state->np, &state->elp, &mult->zxl, _state);
        rsetallocv(state->np, currentmu, &mult->zxu, _state);
        rcopyv(state->np, &state->finitebndu, &tmp, _state);
        raddv(state->np, -1.0, &current->xp, &tmp, _state);
        rmuladdv(state->np, &state->softbcqu, &current->sxu, &tmp, _state);
        rmergedivv(state->np, &tmp, &mult->zxu, _state);
        rmergemulv(state->np, &state->eup, &mult->zxu, _state);
        rsetallocv(state->np, currentmu, &mult->zsxl, _state);
        rsetv(state->np, ae_sqrt(ae_minrealnumber, _state), &tmp, _state);
        raddv(state->np, 1.0, &current->sxl, &tmp, _state);
        rmergedivv(state->np, &tmp, &mult->zsxl, _state);
        rmergemulv(state->np, &state->elp, &mult->zsxl, _state);
        rsetallocv(state->np, currentmu, &mult->zsxu, _state);
        rsetv(state->np, ae_sqrt(ae_minrealnumber, _state), &tmp, _state);
        raddv(state->np, 1.0, &current->sxu, &tmp, _state);
        rmergedivv(state->np, &tmp, &mult->zsxu, _state);
        rmergemulv(state->np, &state->eup, &mult->zsxu, _state);
        rsetallocv(state->np, currentmu, &mult->zsxlcap, _state);
        rsetv(state->np, ae_sqrt(ae_minrealnumber, _state), &tmp, _state);
        raddv(state->np, 1.0, &state->softbcmsxl, &tmp, _state);
        raddv(state->np, -1.0, &current->sxl, &tmp, _state);
        rmergedivv(state->np, &tmp, &mult->zsxlcap, _state);
        rmergemulv(state->np, &state->elp, &mult->zsxlcap, _state);
        rsetallocv(state->np, currentmu, &mult->zsxucap, _state);
        rsetv(state->np, ae_sqrt(ae_minrealnumber, _state), &tmp, _state);
        raddv(state->np, 1.0, &state->softbcmsxu, &tmp, _state);
        raddv(state->np, -1.0, &current->sxu, &tmp, _state);
        rmergedivv(state->np, &tmp, &mult->zsxucap, _state);
        rmergemulv(state->np, &state->eup, &mult->zsxucap, _state);
    }
    
    /*
     * Compute primal multiplier estimates for non-box constraints
     */
    rowoffs = 1;
    rsetallocv(state->mflex, 0.0, &mult->zcf, _state);
    rsetallocv(state->mflex, 0.0, &mult->zcfgl, _state);
    rsetallocv(state->mflex, 0.0, &mult->zcfgu, _state);
    rsetallocv(state->mflex, 0.0, &mult->zgl, _state);
    rsetallocv(state->mflex, 0.0, &mult->zgu, _state);
    for(i=0; i<=state->mflex-1; i++)
    {
        mult->zcf.ptr.p_double[i] = -(current->fi.ptr.p_double[rowoffs]-current->sxf.ptr.p_double[i])*invmu;
        if( state->hasflexcl.ptr.p_bool[i] )
        {
            mult->zcfgl.ptr.p_double[i] = -(current->sxf.ptr.p_double[i]-current->gl.ptr.p_double[i]-state->flexcl.ptr.p_double[i])*invmu;
            mult->zgl.ptr.p_double[i] = currentmu/current->gl.ptr.p_double[i];
        }
        if( state->hasflexcu.ptr.p_bool[i] )
        {
            mult->zcfgu.ptr.p_double[i] = -(current->sxf.ptr.p_double[i]+current->gu.ptr.p_double[i]-state->flexcu.ptr.p_double[i])*invmu;
            mult->zgu.ptr.p_double[i] = currentmu/current->gu.ptr.p_double[i];
        }
        rowoffs = rowoffs+1;
    }
    ae_assert(rowoffs==mtotal+1, "GIPM2: 801132", _state);
    
    /*
     * Done
     */
    ae_nxpool_recycle(&state->mnx1pool, &tmp, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Compute RP part of right-hand side of the Newton system. Used as a part of
ComputeRHS() or to issue Lagrangian updates.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_computerp(gipm2state* state,
     const gipm2vars* current,
     double currentmu,
     const gipm2mult* mult,
     /* Real    */ ae_vector* rp,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t i;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t rowidx;
    double v;


    np = current->np;
    rsetallocv(np, 0.0, rp, _state);
    j0 = current->jac.ridx.ptr.p_int[0];
    j1 = current->jac.ridx.ptr.p_int[1]-1;
    for(jj=j0; jj<=j1; jj++)
    {
        rp->ptr.p_double[current->jac.idx.ptr.p_int[jj]] = -current->jac.vals.ptr.p_double[jj];
    }
    raddv(np, -gipm2_damptau*currentmu, &current->xp, rp, _state);
    if( mult->hashardbc||mult->hassoftbc )
    {
        raddv(np, 1.0, &mult->zxl, rp, _state);
        raddv(np, -1.0, &mult->zxu, rp, _state);
    }
    rowidx = 1;
    for(i=0; i<=state->mflex-1; i++)
    {
        v = mult->zcf.ptr.p_double[i];
        j0 = current->jac.ridx.ptr.p_int[rowidx+0];
        j1 = current->jac.ridx.ptr.p_int[rowidx+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            rp->ptr.p_double[current->jac.idx.ptr.p_int[jj]] = rp->ptr.p_double[current->jac.idx.ptr.p_int[jj]]+v*current->jac.vals.ptr.p_double[jj];
        }
        rowidx = rowidx+1;
    }
    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 021021", _state);
}


/*************************************************************************
Compute RHS for a Newton system, additionally saving dual multiplier
estimates into RHS.

Returns inf-norms of raw (unnormalized) primal, dual, complementarity errors.
Also returns normalizing coefficients for errors.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_computerhs(gipm2state* state,
     const gipm2vars* current,
     double currentmu,
     const gipm2mult* currentmult,
     gipm2rhs* rhs,
     double* epriminf,
     double* edualinf,
     double* ecmplinf,
     double* sprim,
     double* sdual,
     double* scmpl,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t np;
    ae_int_t mtotal;
    ae_int_t i;
    ae_vector tmp;
    ae_int_t rowidx;

    ae_frame_make(_state, &_frame_block);
    memset(&tmp, 0, sizeof(tmp));
    *epriminf = 0.0;
    *edualinf = 0.0;
    *ecmplinf = 0.0;
    *sprim = 0.0;
    *sdual = 0.0;
    *scmpl = 0.0;
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);

    np = current->np;
    mtotal = state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1;
    ae_nxpool_retrieve(&state->mnx1pool, &tmp, _state);
    ae_assert(tmp.cnt>=ae_maxint(np, mtotal, _state), "GIPM2: integrity check 7328 failed", _state);
    *epriminf = (double)(0);
    *edualinf = (double)(0);
    *ecmplinf = (double)(0);
    *sprim = (double)(1);
    *sdual = (double)(1);
    *scmpl = (double)(1);
    
    /*
     * Compute RP = -gmod(xp) + J'*y + ZXL - ZXU
     */
    gipm2_computerp(state, current, currentmu, currentmult, &rhs->rp, _state);
    *edualinf = ae_maxreal(*edualinf, rmaxabsv(np, &rhs->rp, _state), _state);
    
    /*
     * Compute RPSXL, RPSXU for a problem with soft box constraints
     */
    if( state->hassoftbc )
    {
        rallocv(np, &rhs->rpsxl, _state);
        rallocv(np, &rhs->rpsxu, _state);
        rcopymuladdv(np, &currentmult->zxl, &state->softbcql, &currentmult->zsxl, &rhs->rpsxl, _state);
        rcopymuladdv(np, &currentmult->zxu, &state->softbcqu, &currentmult->zsxu, &rhs->rpsxu, _state);
        raddv(np, -1.0, &currentmult->zsxlcap, &rhs->rpsxl, _state);
        raddv(np, -1.0, &currentmult->zsxucap, &rhs->rpsxu, _state);
        raddv(np, -1.0, &state->pxl, &rhs->rpsxl, _state);
        raddv(np, -1.0, &state->pxu, &rhs->rpsxu, _state);
        rmergemulv(np, &state->elp, &rhs->rpsxl, _state);
        rmergemulv(np, &state->eup, &rhs->rpsxu, _state);
        *edualinf = ae_maxreal(*edualinf, rmaxabsv(np, &rhs->rpsxl, _state), _state);
        *edualinf = ae_maxreal(*edualinf, rmaxabsv(np, &rhs->rpsxu, _state), _state);
    }
    
    /*
     * Compute RDXL/RDXU, RDSXL/RDSXU and RDSXLCAP/RDSXUCAP, which are zero for a primal-only problem with hard constraints
     */
    rsetallocv(np, 0.0, &rhs->rdxl, _state);
    rsetallocv(np, 0.0, &rhs->rdxu, _state);
    if( state->isprimal )
    {
        if( state->hassoftbc )
        {
            rsetallocv(np, 0.0, &rhs->rdsxl, _state);
            rsetallocv(np, 0.0, &rhs->rdsxu, _state);
            rsetallocv(np, 0.0, &rhs->rdsxlcap, _state);
            rsetallocv(np, 0.0, &rhs->rdsxucap, _state);
        }
    }
    else
    {
        if( state->hashardbc )
        {
            ae_assert(!state->hassoftbc, "GIPM2: 162459 failed", _state);
            for(i=0; i<=np-1; i++)
            {
                if( state->hasbndl.ptr.p_bool[i] )
                {
                    rhs->rdxl.ptr.p_double[i] = currentmu-(current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i])*currentmult->zxl.ptr.p_double[i];
                }
                if( state->hasbndu.ptr.p_bool[i] )
                {
                    rhs->rdxu.ptr.p_double[i] = currentmu-(state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i])*currentmult->zxu.ptr.p_double[i];
                }
            }
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(np, &rhs->rdxl, _state), _state);
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(np, &rhs->rdxu, _state), _state);
        }
        if( state->hassoftbc )
        {
            ae_assert(!state->hashardbc, "GIPM2: 162459 failed", _state);
            rsetallocv(np, 0.0, &rhs->rdsxl, _state);
            rsetallocv(np, 0.0, &rhs->rdsxu, _state);
            rsetallocv(np, 0.0, &rhs->rdsxlcap, _state);
            rsetallocv(np, 0.0, &rhs->rdsxucap, _state);
            for(i=0; i<=np-1; i++)
            {
                if( state->hasbndl.ptr.p_bool[i] )
                {
                    rhs->rdxl.ptr.p_double[i] = currentmu-(current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*current->sxl.ptr.p_double[i])*currentmult->zxl.ptr.p_double[i];
                    rhs->rdsxl.ptr.p_double[i] = currentmu-current->sxl.ptr.p_double[i]*currentmult->zsxl.ptr.p_double[i];
                    rhs->rdsxlcap.ptr.p_double[i] = currentmu-(state->softbcmsxl.ptr.p_double[i]-current->sxl.ptr.p_double[i])*currentmult->zsxlcap.ptr.p_double[i];
                }
                if( state->hasbndu.ptr.p_bool[i] )
                {
                    rhs->rdxu.ptr.p_double[i] = currentmu-(state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*current->sxu.ptr.p_double[i])*currentmult->zxu.ptr.p_double[i];
                    rhs->rdsxu.ptr.p_double[i] = currentmu-current->sxu.ptr.p_double[i]*currentmult->zsxu.ptr.p_double[i];
                    rhs->rdsxucap.ptr.p_double[i] = currentmu-(state->softbcmsxu.ptr.p_double[i]-current->sxu.ptr.p_double[i])*currentmult->zsxucap.ptr.p_double[i];
                }
            }
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(np, &rhs->rdxl, _state), _state);
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(np, &rhs->rdxu, _state), _state);
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(np, &rhs->rdsxl, _state), _state);
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(np, &rhs->rdsxu, _state), _state);
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(np, &rhs->rdsxlcap, _state), _state);
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(np, &rhs->rdsxucap, _state), _state);
        }
    }
    
    /*
     * FLEX-type constraints: compute Rpsxf, Rpgl, Rpgu, Rdcf, Rdcfgl, Rdcfgu, Rdgl, Rdgu
     */
    rowidx = 1;
    if( state->mflex>0 )
    {
        
        /*
         * P-type RHS
         */
        rsetallocv(state->mflex, 0.0, &rhs->rpsxf, _state);
        raddv(state->mflex, -1.0, &currentmult->zcf, &rhs->rpsxf, _state);
        raddv(state->mflex, 1.0, &currentmult->zcfgl, &rhs->rpsxf, _state);
        raddv(state->mflex, 1.0, &currentmult->zcfgu, &rhs->rpsxf, _state);
        rcopyallocv(state->mflex, &currentmult->zgl, &rhs->rpgl, _state);
        raddv(state->mflex, -1.0, &currentmult->zcfgl, &rhs->rpgl, _state);
        rcopyallocv(state->mflex, &currentmult->zgu, &rhs->rpgu, _state);
        raddv(state->mflex, 1.0, &currentmult->zcfgu, &rhs->rpgu, _state);
        *edualinf = ae_maxreal(*edualinf, rmaxabsv(state->mflex, &rhs->rpsxf, _state), _state);
        *edualinf = ae_maxreal(*edualinf, rmaxabsv(state->mflex, &rhs->rpgl, _state), _state);
        *edualinf = ae_maxreal(*edualinf, rmaxabsv(state->mflex, &rhs->rpgu, _state), _state);
        
        /*
         * D-type RHS
         */
        rsetallocv(state->mflex, 0.0, &rhs->rdcf, _state);
        rsetallocv(state->mflex, 0.0, &rhs->rdcfgl, _state);
        rsetallocv(state->mflex, 0.0, &rhs->rdcfgu, _state);
        rsetallocv(state->mflex, 0.0, &rhs->rdgl, _state);
        rsetallocv(state->mflex, 0.0, &rhs->rdgu, _state);
        if( !state->isprimal )
        {
            for(i=0; i<=state->mflex-1; i++)
            {
                rhs->rdcf.ptr.p_double[i] = -(current->fi.ptr.p_double[rowidx]-current->sxf.ptr.p_double[i])-currentmu*currentmult->zcf.ptr.p_double[i];
                if( state->hasflexcl.ptr.p_bool[i] )
                {
                    rhs->rdcfgl.ptr.p_double[i] = -(current->sxf.ptr.p_double[i]-current->gl.ptr.p_double[i]-state->flexcl.ptr.p_double[i])-currentmu*currentmult->zcfgl.ptr.p_double[i];
                    rhs->rdgl.ptr.p_double[i] = currentmu-current->gl.ptr.p_double[i]*currentmult->zgl.ptr.p_double[i];
                }
                if( state->hasflexcu.ptr.p_bool[i] )
                {
                    rhs->rdcfgu.ptr.p_double[i] = -(current->sxf.ptr.p_double[i]+current->gu.ptr.p_double[i]-state->flexcu.ptr.p_double[i])-currentmu*currentmult->zcfgu.ptr.p_double[i];
                    rhs->rdgu.ptr.p_double[i] = currentmu-current->gu.ptr.p_double[i]*currentmult->zgu.ptr.p_double[i];
                }
                rowidx = rowidx+1;
            }
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(state->mflex, &rhs->rdcf, _state), _state);
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(state->mflex, &rhs->rdcfgl, _state), _state);
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(state->mflex, &rhs->rdcfgu, _state), _state);
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(state->mflex, &rhs->rdgl, _state), _state);
            *ecmplinf = ae_maxreal(*ecmplinf, rmaxabsv(state->mflex, &rhs->rdgu, _state), _state);
        }
    }
    
    /*
     * Other constraints
     */
    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 661231", _state);
    ae_assert(rowidx==1+mtotal, "GIPM2: 355229", _state);
    
    /*
     * Done
     */
    ae_nxpool_recycle(&state->mnx1pool, &tmp, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Compute ECmpl2

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static double gipm2_computeecmpl2(const gipm2state* state,
     const gipm2rhs* rhs,
     ae_state *_state)
{
    ae_int_t np;
    double result;


    np = state->np;
    result = (double)(0);
    if( state->isprimal )
    {
        return result;
    }
    result = result+rdotv2(np, &rhs->rdxl, _state);
    result = result+rdotv2(np, &rhs->rdxu, _state);
    if( state->hassoftbc )
    {
        result = result+rdotv2(np, &rhs->rdsxl, _state);
        result = result+rdotv2(np, &rhs->rdsxu, _state);
        result = result+rdotv2(np, &rhs->rdsxlcap, _state);
        result = result+rdotv2(np, &rhs->rdsxucap, _state);
    }
    if( state->mflex>0 )
    {
        result = result+rdotv2(state->mflex, &rhs->rdcf, _state);
        result = result+rdotv2(state->mflex, &rhs->rdcfgl, _state);
        result = result+rdotv2(state->mflex, &rhs->rdcfgu, _state);
        result = result+rdotv2(state->mflex, &rhs->rdgl, _state);
        result = result+rdotv2(state->mflex, &rhs->rdgu, _state);
    }
    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 409119", _state);
    return result;
}


/*************************************************************************
Compute RHS for a Newton system, additionally saving dual multiplier
estimates into RHS.

Returns inf-norms of raw (unnormalized) primal, dual, complementarity errors.
Also returns normalizing coefficients for errors.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_copyrhs(const gipm2state* state,
     const gipm2rhs* src,
     gipm2rhs* dst,
     ae_state *_state)
{
    ae_int_t mtotal;


    mtotal = state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1;
    rcopyallocv(state->np, &src->rp, &dst->rp, _state);
    rcopyallocv(state->np, &src->rdxl, &dst->rdxl, _state);
    rcopyallocv(state->np, &src->rdxu, &dst->rdxu, _state);
    if( state->hassoftbc )
    {
        rcopyallocv(state->np, &src->rpsxl, &dst->rpsxl, _state);
        rcopyallocv(state->np, &src->rpsxu, &dst->rpsxu, _state);
        rcopyallocv(state->np, &src->rdsxl, &dst->rdsxl, _state);
        rcopyallocv(state->np, &src->rdsxu, &dst->rdsxu, _state);
        rcopyallocv(state->np, &src->rdsxlcap, &dst->rdsxlcap, _state);
        rcopyallocv(state->np, &src->rdsxucap, &dst->rdsxucap, _state);
    }
    if( state->mflex>0 )
    {
        rcopyallocv(state->mflex, &src->rpsxf, &dst->rpsxf, _state);
        rcopyallocv(state->mflex, &src->rpgl, &dst->rpgl, _state);
        rcopyallocv(state->mflex, &src->rpgu, &dst->rpgu, _state);
        rcopyallocv(state->mflex, &src->rdcf, &dst->rdcf, _state);
        rcopyallocv(state->mflex, &src->rdcfgl, &dst->rdcfgl, _state);
        rcopyallocv(state->mflex, &src->rdcfgu, &dst->rdcfgu, _state);
        rcopyallocv(state->mflex, &src->rdgl, &dst->rdgl, _state);
        rcopyallocv(state->mflex, &src->rdgu, &dst->rdgu, _state);
    }
    ae_assert(mtotal-state->mflex==0, "GIPM2: 372016 failed", _state);
}


/*************************************************************************
Compute product of DeltaX and gradient, (deltaX,gradBarr), computed
subject to all primal and primal slack variables present.

Barrier function decrease corresponds to a negative value.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static double gipm2_computedg(const gipm2state* state,
     const gipm2vars* delta,
     const gipm2rhs* rhs,
     ae_state *_state)
{
    ae_int_t mtotal;
    double result;


    mtotal = state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1;
    result = (double)(0);
    result = result-rdotv(state->np, &delta->xp, &rhs->rp, _state);
    if( state->hassoftbc )
    {
        result = result-rdotv(state->np, &delta->sxl, &rhs->rpsxl, _state);
        result = result-rdotv(state->np, &delta->sxu, &rhs->rpsxu, _state);
    }
    if( state->mflex>0 )
    {
        result = result-rdotv(state->mflex, &delta->sxf, &rhs->rpsxf, _state);
        result = result-rdotv(state->mflex, &delta->gl, &rhs->rpgl, _state);
        result = result-rdotv(state->mflex, &delta->gu, &rhs->rpgu, _state);
    }
    ae_assert(mtotal-state->mflex==0, "GIPM2: 262010 failed", _state);
    return result;
}


/*************************************************************************
Compute residual from the candidate solution for the already compute RHS:

    RHS := RHS - [SYSTEM]*delta
    
Inputs include current location, candidate solution Delta and precomputed
products H*Delta.XP, J*Delta.XP, J'*Delta.Y

Returns inf-norm of the residual.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static double gipm2_computeupdateresidual(gipm2state* state,
     const gipm2vars* current,
     const gipm2mult* multlhs,
     double currentmu,
     double currentdiag,
     const gipm2vars* delta,
     const gipm2mult* deltamult,
     /* Real    */ const ae_vector* hdxp,
     /* Real    */ const ae_vector* jdxp,
     /* Real    */ const ae_vector* jtdy,
     gipm2rhs* rhs,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t i;
    double lambdatau;
    ae_int_t cidx;
    double result;


    np = state->np;
    lambdatau = currentdiag+currentmu*gipm2_damptau;
    result = (double)(0);
    
    /*
     * Update RP  := RP  - (Hmod+L+TAU)*d_xp - (J'*d_yp + Jv'*d_yv) + d_zxl - d_zxu
     *
     */
    raddv(np, -1.0, hdxp, &rhs->rp, _state);
    raddv(np, -lambdatau, &delta->xp, &rhs->rp, _state);
    if( state->hashardbc||state->hassoftbc )
    {
        raddv(np, 1.0, &deltamult->zxl, &rhs->rp, _state);
        raddv(np, -1.0, &deltamult->zxu, &rhs->rp, _state);
    }
    raddv(np, 1.0, jtdy, &rhs->rp, _state);
    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 266037", _state);
    result = ae_maxreal(result, rmaxabsv(np, &rhs->rp, _state), _state);
    
    /*
     * Update
     *
     *     RPSXL := RPSXL - L*dsxl + ql*dzxl + dzsxl
     *     RPSXU := RPSXU - L*dsxu + qu*dzxu + dzsxu
     */
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 089003", _state);
        raddv(np, -currentdiag, &delta->sxl, &rhs->rpsxl, _state);
        rmuladdv(np, &state->softbcql, &deltamult->zxl, &rhs->rpsxl, _state);
        raddv(np, 1.0, &deltamult->zsxl, &rhs->rpsxl, _state);
        raddv(np, -1.0, &deltamult->zsxlcap, &rhs->rpsxl, _state);
        raddv(np, -currentdiag, &delta->sxu, &rhs->rpsxu, _state);
        rmuladdv(np, &state->softbcqu, &deltamult->zxu, &rhs->rpsxu, _state);
        raddv(np, 1.0, &deltamult->zsxu, &rhs->rpsxu, _state);
        raddv(np, -1.0, &deltamult->zsxucap, &rhs->rpsxu, _state);
        result = ae_maxreal(result, rmaxabsv(np, &rhs->rpsxl, _state), _state);
        result = ae_maxreal(result, rmaxabsv(np, &rhs->rpsxu, _state), _state);
    }
    
    /*
     * Compute rdxl, rdxu, rpsxl, rpsxu, rdsxl, rdsxu
     */
    if( state->hashardbc )
    {
        ae_assert(!state->hassoftbc, "GIPM2: 075050", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                rhs->rdxl.ptr.p_double[i] = rhs->rdxl.ptr.p_double[i]-multlhs->zxl.ptr.p_double[i]*delta->xp.ptr.p_double[i]-(current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i])*deltamult->zxl.ptr.p_double[i];
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                rhs->rdxu.ptr.p_double[i] = rhs->rdxu.ptr.p_double[i]+multlhs->zxu.ptr.p_double[i]*delta->xp.ptr.p_double[i]-(state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i])*deltamult->zxu.ptr.p_double[i];
            }
        }
        result = ae_maxreal(result, rmaxabsv(np, &rhs->rdxl, _state), _state);
        result = ae_maxreal(result, rmaxabsv(np, &rhs->rdxu, _state), _state);
    }
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 116010", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                rhs->rdxl.ptr.p_double[i] = rhs->rdxl.ptr.p_double[i]-multlhs->zxl.ptr.p_double[i]*(delta->xp.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*delta->sxl.ptr.p_double[i])-(current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*current->sxl.ptr.p_double[i])*deltamult->zxl.ptr.p_double[i];
                rhs->rdsxl.ptr.p_double[i] = rhs->rdsxl.ptr.p_double[i]-multlhs->zsxl.ptr.p_double[i]*delta->sxl.ptr.p_double[i]-current->sxl.ptr.p_double[i]*deltamult->zsxl.ptr.p_double[i];
                rhs->rdsxlcap.ptr.p_double[i] = rhs->rdsxlcap.ptr.p_double[i]+multlhs->zsxlcap.ptr.p_double[i]*delta->sxl.ptr.p_double[i]-(state->softbcmsxl.ptr.p_double[i]-current->sxl.ptr.p_double[i])*deltamult->zsxlcap.ptr.p_double[i];
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                rhs->rdxu.ptr.p_double[i] = rhs->rdxu.ptr.p_double[i]-multlhs->zxu.ptr.p_double[i]*(-delta->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*delta->sxu.ptr.p_double[i])-(state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*current->sxu.ptr.p_double[i])*deltamult->zxu.ptr.p_double[i];
                rhs->rdsxu.ptr.p_double[i] = rhs->rdsxu.ptr.p_double[i]-multlhs->zsxu.ptr.p_double[i]*delta->sxu.ptr.p_double[i]-current->sxu.ptr.p_double[i]*deltamult->zsxu.ptr.p_double[i];
                rhs->rdsxucap.ptr.p_double[i] = rhs->rdsxucap.ptr.p_double[i]+multlhs->zsxucap.ptr.p_double[i]*delta->sxu.ptr.p_double[i]-(state->softbcmsxu.ptr.p_double[i]-current->sxu.ptr.p_double[i])*deltamult->zsxucap.ptr.p_double[i];
            }
        }
        result = ae_maxreal(result, rmaxabsv(np, &rhs->rdxl, _state), _state);
        result = ae_maxreal(result, rmaxabsv(np, &rhs->rdxu, _state), _state);
        result = ae_maxreal(result, rmaxabsv(np, &rhs->rdsxl, _state), _state);
        result = ae_maxreal(result, rmaxabsv(np, &rhs->rdsxu, _state), _state);
        result = ae_maxreal(result, rmaxabsv(np, &rhs->rdsxlcap, _state), _state);
        result = ae_maxreal(result, rmaxabsv(np, &rhs->rdsxucap, _state), _state);
    }
    
    /*
     * FLEX-type constraints: update Rpsxf, Rpgl, Rpgu, Rdcf, Rdcfgl, Rdcfgu, Rdgl, Rdgu
     */
    cidx = 0;
    if( state->mflex>0 )
    {
        raddv(state->mflex, -currentdiag, &delta->sxf, &rhs->rpsxf, _state);
        raddv(state->mflex, -1.0, &deltamult->zcf, &rhs->rpsxf, _state);
        raddv(state->mflex, 1.0, &deltamult->zcfgl, &rhs->rpsxf, _state);
        raddv(state->mflex, 1.0, &deltamult->zcfgu, &rhs->rpsxf, _state);
        raddv(state->mflex, -currentdiag, &delta->gl, &rhs->rpgl, _state);
        raddv(state->mflex, -1.0, &deltamult->zcfgl, &rhs->rpgl, _state);
        raddv(state->mflex, 1.0, &deltamult->zgl, &rhs->rpgl, _state);
        raddv(state->mflex, -currentdiag, &delta->gu, &rhs->rpgu, _state);
        raddv(state->mflex, 1.0, &deltamult->zcfgu, &rhs->rpgu, _state);
        raddv(state->mflex, 1.0, &deltamult->zgu, &rhs->rpgu, _state);
        for(i=0; i<=state->mflex-1; i++)
        {
            rhs->rdcf.ptr.p_double[i] = rhs->rdcf.ptr.p_double[i]-jdxp->ptr.p_double[cidx]+delta->sxf.ptr.p_double[i]-currentmu*deltamult->zcf.ptr.p_double[i];
            rhs->rdgl.ptr.p_double[i] = rhs->rdgl.ptr.p_double[i]-multlhs->zgl.ptr.p_double[i]*delta->gl.ptr.p_double[i]-current->gl.ptr.p_double[i]*deltamult->zgl.ptr.p_double[i];
            rhs->rdgu.ptr.p_double[i] = rhs->rdgu.ptr.p_double[i]-multlhs->zgu.ptr.p_double[i]*delta->gu.ptr.p_double[i]-current->gu.ptr.p_double[i]*deltamult->zgu.ptr.p_double[i];
            rhs->rdcfgl.ptr.p_double[i] = rhs->rdcfgl.ptr.p_double[i]-state->maskflexcl.ptr.p_double[i]*delta->sxf.ptr.p_double[i]+delta->gl.ptr.p_double[i]-currentmu*deltamult->zcfgl.ptr.p_double[i];
            rhs->rdcfgu.ptr.p_double[i] = rhs->rdcfgu.ptr.p_double[i]-state->maskflexcu.ptr.p_double[i]*delta->sxf.ptr.p_double[i]-delta->gu.ptr.p_double[i]-currentmu*deltamult->zcfgu.ptr.p_double[i];
            cidx = cidx+1;
        }
        result = ae_maxreal(result, rmaxabsv(state->mflex, &rhs->rpsxf, _state), _state);
        result = ae_maxreal(result, rmaxabsv(state->mflex, &rhs->rpgl, _state), _state);
        result = ae_maxreal(result, rmaxabsv(state->mflex, &rhs->rpgu, _state), _state);
        result = ae_maxreal(result, rmaxabsv(state->mflex, &rhs->rdcf, _state), _state);
        result = ae_maxreal(result, rmaxabsv(state->mflex, &rhs->rdcfgl, _state), _state);
        result = ae_maxreal(result, rmaxabsv(state->mflex, &rhs->rdcfgu, _state), _state);
        result = ae_maxreal(result, rmaxabsv(state->mflex, &rhs->rdgl, _state), _state);
        result = ae_maxreal(result, rmaxabsv(state->mflex, &rhs->rdgu, _state), _state);
    }
    
    /*
     * Other constraints
     */
    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 661231", _state);
    return result;
}


/*************************************************************************
Transforms nonsymmetric primal Newton system into a symmetric condensed form

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_condenselhs(const gipm2state* state,
     const gipm2vars* current,
     const gipm2mult* currentmult,
     double currentmu,
     double currentdiag,
     gipm2condensedsystem* c,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t mtotal;
    ae_int_t i;
    double vxl;
    double vxu;
    double sxlcap;
    double sxucap;
    double tmpsx0;
    double tmpsx1;
    double tmpsx2;
    double tmpsx3;
    double tmpsx4;
    double tmpsx5;
    double tmpsx8;
    double tmpsx9;
    double tmpcf11;
    double tmpcfl;
    double tmpcf0;
    double tmpcf4;
    double tmpcfu;
    double tmpcf1;
    double tmpcf5;
    ae_int_t fidx;
    ae_int_t d1idx;


    mtotal = state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1;
    np = state->np;
    fidx = 1;
    d1idx = 0;
    
    /*
     * Initial D0 and D1
     */
    rsetallocv(np, currentdiag+currentmu*gipm2_damptau, &c->d0, _state);
    rsetallocv(mtotal, 0.0, &c->d1, _state);
    
    /*
     * Condense box constraints
     */
    if( state->hashardbc )
    {
        ae_assert(!state->hassoftbc, "GIPM2: 080041", _state);
        for(i=0; i<=np-1; i++)
        {
            vxl = current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i];
            vxu = state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i];
            c->d0.ptr.p_double[i] = c->d0.ptr.p_double[i]+currentmult->zxl.ptr.p_double[i]/vxl+currentmult->zxu.ptr.p_double[i]/vxu;
        }
    }
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 080041", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                vxl = current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*current->sxl.ptr.p_double[i];
                sxlcap = state->softbcmsxl.ptr.p_double[i]-current->sxl.ptr.p_double[i];
                tmpsx0 = (double)1/current->sxl.ptr.p_double[i];
                tmpsx2 = (double)1/sxlcap;
                tmpsx4 = (double)1/(currentdiag+currentmult->zsxl.ptr.p_double[i]*tmpsx0+currentmult->zsxlcap.ptr.p_double[i]*tmpsx2);
                tmpsx8 = (double)1/(currentmult->zxl.ptr.p_double[i]*state->softbcql.ptr.p_double[i]*state->softbcql.ptr.p_double[i]*tmpsx4+vxl);
                c->d0.ptr.p_double[i] = c->d0.ptr.p_double[i]+currentmult->zxl.ptr.p_double[i]*tmpsx8;
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                vxu = state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*current->sxu.ptr.p_double[i];
                sxucap = state->softbcmsxu.ptr.p_double[i]-current->sxu.ptr.p_double[i];
                tmpsx1 = (double)1/current->sxu.ptr.p_double[i];
                tmpsx3 = (double)1/sxucap;
                tmpsx5 = (double)1/(currentdiag+currentmult->zsxu.ptr.p_double[i]*tmpsx1+currentmult->zsxucap.ptr.p_double[i]*tmpsx3);
                tmpsx9 = (double)1/(currentmult->zxu.ptr.p_double[i]*state->softbcqu.ptr.p_double[i]*state->softbcqu.ptr.p_double[i]*tmpsx5+vxu);
                c->d0.ptr.p_double[i] = c->d0.ptr.p_double[i]+currentmult->zxu.ptr.p_double[i]*tmpsx9;
            }
        }
    }
    
    /*
     * Condense FLEX constraints
     */
    ae_assert(fidx==1&&d1idx==0, "GIPM2: 388206", _state);
    for(i=0; i<=state->mflex-1; i++)
    {
        tmpcf11 = currentdiag;
        if( state->hasflexcl.ptr.p_bool[i] )
        {
            tmpcfl = (double)1/current->gl.ptr.p_double[i];
            tmpcf0 = currentdiag+currentmult->zgl.ptr.p_double[i]*tmpcfl;
            tmpcf4 = (double)1/((double)1+currentmu*tmpcf0);
            tmpcf11 = tmpcf11+tmpcf0*tmpcf4;
        }
        if( state->hasflexcu.ptr.p_bool[i] )
        {
            tmpcfu = (double)1/current->gu.ptr.p_double[i];
            tmpcf1 = currentdiag+currentmult->zgu.ptr.p_double[i]*tmpcfu;
            tmpcf5 = (double)1/((double)1+currentmu*tmpcf1);
            tmpcf11 = tmpcf11+tmpcf1*tmpcf5;
        }
        tmpcf11 = (double)1/tmpcf11;
        c->d1.ptr.p_double[d1idx] = -(currentmu+tmpcf11);
        fidx = fidx+1;
        d1idx = d1idx+1;
    }
    
    /*
     * Other constraints
     */
    ae_assert(mtotal-state->mflex==0, "GIPM2: 969325", _state);
}


/*************************************************************************
Condense only right-hand-side using already condensed left hand side.

  -- ALGLIB --
     Copyright 07.05.2025 by Bochkanov Sergey
*************************************************************************/
static void gipm2_condenserhs(const gipm2state* state,
     const gipm2vars* currentvars,
     const gipm2mult* currentmult,
     const gipm2rhs* rhs,
     double currentmu,
     double currentdiag,
     gipm2condensedsystem* c,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t mtotal;
    ae_int_t i;
    double vxl;
    double vxu;
    double sxlcap;
    double sxucap;
    double tmpsx0;
    double tmpsx1;
    double tmpsx2;
    double tmpsx3;
    double tmpsx4;
    double tmpsx5;
    double tmpsx6;
    double tmpsx7;
    double tmpsx8;
    double tmpsx9;
    double tmpsx10;
    double tmpsx11;
    double tmpcfl;
    double tmpcfu;
    double tmpcf0;
    double tmpcf1;
    double tmpcf2;
    double tmpcf3;
    double tmpcf4;
    double tmpcf5;
    double tmpcf6;
    double tmpcf7;
    double tmpcf8;
    double tmpcf9;
    double tmpcf10;
    double tmpcf11;
    ae_int_t fidx;
    ae_int_t wyidx;


    np = state->np;
    mtotal = state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1;
    fidx = 1;
    wyidx = 0;
    
    /*
     * Initialize
     */
    rcopyallocv(np, &rhs->rp, &c->wx, _state);
    rsetallocv(mtotal, 0.0, &c->wy, _state);
    
    /*
     * Box constraints
     */
    if( state->hashardbc )
    {
        ae_assert(!state->hassoftbc, "GIPM2: 040045", _state);
        for(i=0; i<=np-1; i++)
        {
            vxl = currentvars->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i];
            vxu = state->finitebndu.ptr.p_double[i]-currentvars->xp.ptr.p_double[i];
            c->wx.ptr.p_double[i] = c->wx.ptr.p_double[i]+rhs->rdxl.ptr.p_double[i]/vxl-rhs->rdxu.ptr.p_double[i]/vxu;
        }
    }
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 193353", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                vxl = currentvars->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*currentvars->sxl.ptr.p_double[i];
                sxlcap = state->softbcmsxl.ptr.p_double[i]-currentvars->sxl.ptr.p_double[i];
                tmpsx0 = (double)1/currentvars->sxl.ptr.p_double[i];
                tmpsx2 = (double)1/sxlcap;
                tmpsx4 = (double)1/(currentdiag+currentmult->zsxl.ptr.p_double[i]*tmpsx0+currentmult->zsxlcap.ptr.p_double[i]*tmpsx2);
                tmpsx6 = rhs->rpsxl.ptr.p_double[i]+rhs->rdsxl.ptr.p_double[i]*tmpsx0-rhs->rdsxlcap.ptr.p_double[i]*tmpsx2;
                tmpsx8 = (double)1/(currentmult->zxl.ptr.p_double[i]*state->softbcql.ptr.p_double[i]*tmpsx4*state->softbcql.ptr.p_double[i]+vxl);
                tmpsx10 = rhs->rdxl.ptr.p_double[i]-currentmult->zxl.ptr.p_double[i]*state->softbcql.ptr.p_double[i]*tmpsx4*tmpsx6;
                c->wx.ptr.p_double[i] = c->wx.ptr.p_double[i]+tmpsx8*tmpsx10;
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                vxu = state->finitebndu.ptr.p_double[i]-currentvars->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*currentvars->sxu.ptr.p_double[i];
                sxucap = state->softbcmsxu.ptr.p_double[i]-currentvars->sxu.ptr.p_double[i];
                tmpsx1 = (double)1/currentvars->sxu.ptr.p_double[i];
                tmpsx3 = (double)1/sxucap;
                tmpsx5 = (double)1/(currentdiag+currentmult->zsxu.ptr.p_double[i]*tmpsx1+currentmult->zsxucap.ptr.p_double[i]*tmpsx3);
                tmpsx7 = rhs->rpsxu.ptr.p_double[i]+rhs->rdsxu.ptr.p_double[i]*tmpsx1-rhs->rdsxucap.ptr.p_double[i]*tmpsx3;
                tmpsx9 = (double)1/(currentmult->zxu.ptr.p_double[i]*state->softbcqu.ptr.p_double[i]*tmpsx5*state->softbcqu.ptr.p_double[i]+vxu);
                tmpsx11 = rhs->rdxu.ptr.p_double[i]-currentmult->zxu.ptr.p_double[i]*state->softbcqu.ptr.p_double[i]*tmpsx5*tmpsx7;
                c->wx.ptr.p_double[i] = c->wx.ptr.p_double[i]-tmpsx9*tmpsx11;
            }
        }
    }
    
    /*
     * Condense FLEX-type constraints
     */
    if( state->mflex>0 )
    {
        ae_assert(fidx==1&&wyidx==0, "GIPM2: 509247", _state);
        for(i=0; i<=state->mflex-1; i++)
        {
            tmpcf10 = rhs->rpsxf.ptr.p_double[i];
            tmpcf11 = currentdiag;
            if( state->hasflexcl.ptr.p_bool[i] )
            {
                tmpcfl = (double)1/currentvars->gl.ptr.p_double[i];
                tmpcf0 = currentdiag+currentmult->zgl.ptr.p_double[i]*tmpcfl;
                tmpcf2 = rhs->rpgl.ptr.p_double[i]+rhs->rdgl.ptr.p_double[i]*tmpcfl;
                tmpcf4 = (double)1/((double)1+currentmu*tmpcf0);
                tmpcf6 = rhs->rdcfgl.ptr.p_double[i]-currentmu*tmpcf2;
                tmpcf8 = tmpcf6*tmpcf4;
                tmpcf10 = tmpcf10+tmpcf2+tmpcf0*tmpcf8;
                tmpcf11 = tmpcf11+tmpcf0*tmpcf4;
            }
            if( state->hasflexcu.ptr.p_bool[i] )
            {
                tmpcfu = (double)1/currentvars->gu.ptr.p_double[i];
                tmpcf1 = currentdiag+currentmult->zgu.ptr.p_double[i]*tmpcfu;
                tmpcf3 = rhs->rpgu.ptr.p_double[i]+rhs->rdgu.ptr.p_double[i]*tmpcfu;
                tmpcf5 = (double)1/((double)1+currentmu*tmpcf1);
                tmpcf7 = rhs->rdcfgu.ptr.p_double[i]+currentmu*tmpcf3;
                tmpcf9 = tmpcf7*tmpcf5;
                tmpcf10 = tmpcf10-tmpcf3+tmpcf1*tmpcf9;
                tmpcf11 = tmpcf11+tmpcf1*tmpcf5;
            }
            tmpcf11 = (double)1/tmpcf11;
            c->wy.ptr.p_double[wyidx] = rhs->rdcf.ptr.p_double[i]+tmpcf11*tmpcf10;
            wyidx = wyidx+1;
            fidx = fidx+1;
        }
    }
    
    /*
     * Other constraints
     */
    ae_assert(mtotal-state->mflex==0, "GIPM2: 226005", _state);
}


/*************************************************************************
Unpack reply to Request=1002 into a solution to the original primal-dual system.
Returns maximum over step components;

Delta must be properly allocated GIPMVars; however, initial values of
its variables are ignored.

  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_unpacksolution(const gipm2state* state,
     const gipm2vars* currentvars,
     const gipm2mult* multlhs,
     double currentmu,
     double currentdiag,
     const gipm2rhs* rhs,
     gipm2condensedsystem* c,
     /* Real    */ const ae_vector* replysol,
     gipm2vars* delta,
     gipm2mult* deltamult,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t mtotal;
    ae_int_t i;
    double vxl;
    double vxu;
    double sxlcap;
    double sxucap;
    double tmpsx0;
    double tmpsx1;
    double tmpsx2;
    double tmpsx3;
    double tmpsx4;
    double tmpsx5;
    double tmpsx6;
    double tmpsx7;
    double tmpsx8;
    double tmpsx9;
    double tmpsx10;
    double tmpsx11;
    double tmpcfl;
    double tmpcfu;
    double tmpcf0;
    double tmpcf1;
    double tmpcf2;
    double tmpcf3;
    double tmpcf4;
    double tmpcf5;
    double tmpcf6;
    double tmpcf7;
    double tmpcf8;
    double tmpcf9;
    double tmpcf10;
    double tmpcf11;
    ae_int_t sidx;


    np = state->np;
    mtotal = state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1;
    sidx = np;
    
    /*
     * Init XP
     */
    rcopyvx(np, &state->replysol, 0, &delta->xp, 0, _state);
    
    /*
     * Handle box constraints
     */
    if( state->hashardbc )
    {
        ae_assert(!state->hassoftbc, "GIPM2: 283018", _state);
        for(i=0; i<=np-1; i++)
        {
            deltamult->zxl.ptr.p_double[i] = 0.0;
            deltamult->zxu.ptr.p_double[i] = 0.0;
            if( state->hasbndl.ptr.p_bool[i] )
            {
                deltamult->zxl.ptr.p_double[i] = (rhs->rdxl.ptr.p_double[i]-multlhs->zxl.ptr.p_double[i]*delta->xp.ptr.p_double[i])/(currentvars->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i]);
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                deltamult->zxu.ptr.p_double[i] = (rhs->rdxu.ptr.p_double[i]+multlhs->zxu.ptr.p_double[i]*delta->xp.ptr.p_double[i])/(state->finitebndu.ptr.p_double[i]-currentvars->xp.ptr.p_double[i]);
            }
        }
    }
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 297019", _state);
        for(i=0; i<=np-1; i++)
        {
            delta->sxl.ptr.p_double[i] = (double)(0);
            delta->sxu.ptr.p_double[i] = (double)(0);
            deltamult->zxl.ptr.p_double[i] = (double)(0);
            deltamult->zsxl.ptr.p_double[i] = (double)(0);
            deltamult->zsxlcap.ptr.p_double[i] = (double)(0);
            deltamult->zxu.ptr.p_double[i] = (double)(0);
            deltamult->zsxu.ptr.p_double[i] = (double)(0);
            deltamult->zsxucap.ptr.p_double[i] = (double)(0);
            if( state->hasbndl.ptr.p_bool[i] )
            {
                vxl = currentvars->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*currentvars->sxl.ptr.p_double[i];
                sxlcap = state->softbcmsxl.ptr.p_double[i]-currentvars->sxl.ptr.p_double[i];
                tmpsx0 = (double)1/currentvars->sxl.ptr.p_double[i];
                tmpsx2 = (double)1/sxlcap;
                tmpsx4 = (double)1/(currentdiag+multlhs->zsxl.ptr.p_double[i]*tmpsx0+multlhs->zsxlcap.ptr.p_double[i]*tmpsx2);
                tmpsx6 = rhs->rpsxl.ptr.p_double[i]+rhs->rdsxl.ptr.p_double[i]*tmpsx0-rhs->rdsxlcap.ptr.p_double[i]*tmpsx2;
                tmpsx8 = (double)1/(multlhs->zxl.ptr.p_double[i]*state->softbcql.ptr.p_double[i]*tmpsx4*state->softbcql.ptr.p_double[i]+vxl);
                tmpsx10 = rhs->rdxl.ptr.p_double[i]-multlhs->zxl.ptr.p_double[i]*state->softbcql.ptr.p_double[i]*tmpsx4*tmpsx6;
                deltamult->zxl.ptr.p_double[i] = tmpsx8*(tmpsx10-multlhs->zxl.ptr.p_double[i]*delta->xp.ptr.p_double[i]);
                delta->sxl.ptr.p_double[i] = tmpsx4*(tmpsx6+state->softbcql.ptr.p_double[i]*deltamult->zxl.ptr.p_double[i]);
                deltamult->zsxl.ptr.p_double[i] = tmpsx0*(rhs->rdsxl.ptr.p_double[i]-multlhs->zsxl.ptr.p_double[i]*delta->sxl.ptr.p_double[i]);
                deltamult->zsxlcap.ptr.p_double[i] = tmpsx2*(rhs->rdsxlcap.ptr.p_double[i]+multlhs->zsxlcap.ptr.p_double[i]*delta->sxl.ptr.p_double[i]);
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                vxu = state->finitebndu.ptr.p_double[i]-currentvars->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*currentvars->sxu.ptr.p_double[i];
                sxucap = state->softbcmsxu.ptr.p_double[i]-currentvars->sxu.ptr.p_double[i];
                tmpsx1 = (double)1/currentvars->sxu.ptr.p_double[i];
                tmpsx3 = (double)1/sxucap;
                tmpsx5 = (double)1/(currentdiag+multlhs->zsxu.ptr.p_double[i]*tmpsx1+multlhs->zsxucap.ptr.p_double[i]*tmpsx3);
                tmpsx7 = rhs->rpsxu.ptr.p_double[i]+rhs->rdsxu.ptr.p_double[i]*tmpsx1-rhs->rdsxucap.ptr.p_double[i]*tmpsx3;
                tmpsx9 = (double)1/(multlhs->zxu.ptr.p_double[i]*state->softbcqu.ptr.p_double[i]*tmpsx5*state->softbcqu.ptr.p_double[i]+vxu);
                tmpsx11 = rhs->rdxu.ptr.p_double[i]-multlhs->zxu.ptr.p_double[i]*state->softbcqu.ptr.p_double[i]*tmpsx5*tmpsx7;
                deltamult->zxu.ptr.p_double[i] = tmpsx9*(tmpsx11+multlhs->zxu.ptr.p_double[i]*delta->xp.ptr.p_double[i]);
                delta->sxu.ptr.p_double[i] = tmpsx5*(tmpsx7+state->softbcqu.ptr.p_double[i]*deltamult->zxu.ptr.p_double[i]);
                deltamult->zsxu.ptr.p_double[i] = tmpsx1*(rhs->rdsxu.ptr.p_double[i]-multlhs->zsxu.ptr.p_double[i]*delta->sxu.ptr.p_double[i]);
                deltamult->zsxucap.ptr.p_double[i] = tmpsx3*(rhs->rdsxucap.ptr.p_double[i]+multlhs->zsxucap.ptr.p_double[i]*delta->sxu.ptr.p_double[i]);
            }
        }
    }
    
    /*
     * Handle FLEX-type constraints
     */
    if( state->mflex>0 )
    {
        ae_assert(sidx==np, "GIPM2: 657310", _state);
        for(i=0; i<=state->mflex-1; i++)
        {
            deltamult->zcf.ptr.p_double[i] = (double)(0);
            delta->sxf.ptr.p_double[i] = (double)(0);
            delta->gl.ptr.p_double[i] = (double)(0);
            deltamult->zgl.ptr.p_double[i] = (double)(0);
            deltamult->zcfgl.ptr.p_double[i] = (double)(0);
            delta->gu.ptr.p_double[i] = (double)(0);
            deltamult->zgu.ptr.p_double[i] = (double)(0);
            deltamult->zcfgu.ptr.p_double[i] = (double)(0);
            tmpcf10 = rhs->rpsxf.ptr.p_double[i];
            tmpcf11 = currentdiag;
            if( state->hasflexcl.ptr.p_bool[i] )
            {
                tmpcfl = (double)1/currentvars->gl.ptr.p_double[i];
                tmpcf0 = currentdiag+multlhs->zgl.ptr.p_double[i]*tmpcfl;
                tmpcf2 = rhs->rpgl.ptr.p_double[i]+rhs->rdgl.ptr.p_double[i]*tmpcfl;
                tmpcf4 = (double)1/((double)1+currentmu*tmpcf0);
                tmpcf6 = rhs->rdcfgl.ptr.p_double[i]-currentmu*tmpcf2;
                tmpcf8 = tmpcf6*tmpcf4;
                tmpcf10 = tmpcf10+tmpcf2+tmpcf0*tmpcf8;
                tmpcf11 = tmpcf11+tmpcf0*tmpcf4;
            }
            if( state->hasflexcu.ptr.p_bool[i] )
            {
                tmpcfu = (double)1/currentvars->gu.ptr.p_double[i];
                tmpcf1 = currentdiag+multlhs->zgu.ptr.p_double[i]*tmpcfu;
                tmpcf3 = rhs->rpgu.ptr.p_double[i]+rhs->rdgu.ptr.p_double[i]*tmpcfu;
                tmpcf5 = (double)1/((double)1+currentmu*tmpcf1);
                tmpcf7 = rhs->rdcfgu.ptr.p_double[i]+currentmu*tmpcf3;
                tmpcf9 = tmpcf7*tmpcf5;
                tmpcf10 = tmpcf10-tmpcf3+tmpcf1*tmpcf9;
                tmpcf11 = tmpcf11+tmpcf1*tmpcf5;
            }
            tmpcf11 = (double)1/tmpcf11;
            deltamult->zcf.ptr.p_double[i] = -replysol->ptr.p_double[sidx];
            delta->sxf.ptr.p_double[i] = tmpcf11*tmpcf10-tmpcf11*deltamult->zcf.ptr.p_double[i];
            if( state->hasflexcl.ptr.p_bool[i] )
            {
                delta->gl.ptr.p_double[i] = -tmpcf8+tmpcf4*delta->sxf.ptr.p_double[i];
                deltamult->zgl.ptr.p_double[i] = (rhs->rdgl.ptr.p_double[i]-multlhs->zgl.ptr.p_double[i]*delta->gl.ptr.p_double[i])*tmpcfl;
                deltamult->zcfgl.ptr.p_double[i] = tmpcf2-tmpcf0*delta->gl.ptr.p_double[i];
            }
            if( state->hasflexcu.ptr.p_bool[i] )
            {
                delta->gu.ptr.p_double[i] = tmpcf9-tmpcf5*delta->sxf.ptr.p_double[i];
                deltamult->zgu.ptr.p_double[i] = (rhs->rdgu.ptr.p_double[i]-multlhs->zgu.ptr.p_double[i]*delta->gu.ptr.p_double[i])*tmpcfu;
                deltamult->zcfgu.ptr.p_double[i] = -tmpcf3+tmpcf1*delta->gu.ptr.p_double[i];
            }
            sidx = sidx+1;
        }
    }
    
    /*
     * Handle other constraints
     */
    ae_assert(mtotal-state->mflex==0, "GIPM2: 102051", _state);
}


/*************************************************************************
Merit function V0
    
  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static double gipm2_meritfunction(const gipm2state* state,
     const gipm2vars* current,
     double currentmu,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t i;
    ae_int_t fidx;
    double barrierf;
    double penaltyf;
    double result;


    np = state->np;
    barrierf = (double)(0);
    penaltyf = (double)(0);
    fidx = 1;
    
    /*
     * Compute fmod(xp,mu) and barrier terms with respect to primal variables XP and slacks SXL/SXU
     */
    result = current->fi.ptr.p_double[0]+0.5*currentmu*gipm2_damptau*rdotv2(np, &current->xp, _state);
    if( state->hashardbc )
    {
        ae_assert(!state->hassoftbc, "GIPM2: 264349", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                barrierf = barrierf-ae_log(current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i], _state);
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                barrierf = barrierf-ae_log(state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i], _state);
            }
        }
    }
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 429016", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                barrierf = barrierf-ae_log(current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*current->sxl.ptr.p_double[i], _state);
                barrierf = barrierf-ae_log(current->sxl.ptr.p_double[i], _state)-ae_log(state->softbcmsxl.ptr.p_double[i]-current->sxl.ptr.p_double[i], _state);
                result = result+state->pxl.ptr.p_double[i]*current->sxl.ptr.p_double[i];
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                barrierf = barrierf-ae_log(state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*current->sxu.ptr.p_double[i], _state);
                barrierf = barrierf-ae_log(current->sxu.ptr.p_double[i], _state)-ae_log(state->softbcmsxu.ptr.p_double[i]-current->sxu.ptr.p_double[i], _state);
                result = result+state->pxu.ptr.p_double[i]*current->sxu.ptr.p_double[i];
            }
        }
    }
    
    /*
     * FLEX-type constraints
     */
    ae_assert(fidx==1, "GIPM2: 828102 failed", _state);
    for(i=0; i<=state->mflex-1; i++)
    {
        penaltyf = penaltyf+ae_sqr(current->fi.ptr.p_double[fidx]-current->sxf.ptr.p_double[i], _state);
        if( state->hasflexcl.ptr.p_bool[i] )
        {
            penaltyf = penaltyf+ae_sqr(current->sxf.ptr.p_double[i]-current->gl.ptr.p_double[i]-state->flexcl.ptr.p_double[i], _state);
            barrierf = barrierf-ae_log(current->gl.ptr.p_double[i], _state);
        }
        if( state->hasflexcu.ptr.p_bool[i] )
        {
            penaltyf = penaltyf+ae_sqr(current->sxf.ptr.p_double[i]+current->gu.ptr.p_double[i]-state->flexcu.ptr.p_double[i], _state);
            barrierf = barrierf-ae_log(current->gu.ptr.p_double[i], _state);
        }
        fidx = fidx+1;
    }
    
    /*
     * Other constraints
     */
    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 263349", _state);
    
    /*
     * Done
     */
    result = result+barrierf*currentmu+penaltyf/((double)2*currentmu);
    return result;
}


/*************************************************************************
Merit function V0
    
  -- ALGLIB --
     Copyright 11.05.2024 by Bochkanov Sergey
*************************************************************************/
static double gipm2_meritfunctionpd(const gipm2state* state,
     const gipm2vars* current,
     const gipm2mult* currentmult,
     double currentmu,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t i;
    ae_int_t fidx;
    double barrierf;
    double penaltyf;
    double v;
    double vv;
    double invmu;
    double result;


    ae_assert(!state->isprimal, "GIPM2: 151843 failed", _state);
    np = state->np;
    barrierf = (double)(0);
    penaltyf = (double)(0);
    fidx = 1;
    invmu = (double)1/currentmu;
    
    /*
     * Compute fmod(xp,mu) and barrier terms with respect to primal variables XP and slacks SXL/SXU
     */
    result = current->fi.ptr.p_double[0]+0.5*currentmu*gipm2_damptau*rdotv2(np, &current->xp, _state);
    if( state->hashardbc )
    {
        ae_assert(!state->hassoftbc, "GIPM2: 264349", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                v = current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i];
                vv = v*currentmult->zxl.ptr.p_double[i]*invmu;
                barrierf = barrierf-ae_log(v, _state)-gipm2_dualmeritnu*(ae_log(vv, _state)+(double)1-vv);
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                v = state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i];
                vv = v*currentmult->zxu.ptr.p_double[i]*invmu;
                barrierf = barrierf-ae_log(v, _state)-gipm2_dualmeritnu*(ae_log(vv, _state)+(double)1-vv);
            }
        }
    }
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 429016", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                v = current->xp.ptr.p_double[i]-state->finitebndl.ptr.p_double[i]+state->softbcql.ptr.p_double[i]*current->sxl.ptr.p_double[i];
                vv = v*currentmult->zxl.ptr.p_double[i]*invmu;
                barrierf = barrierf-ae_log(v, _state)-gipm2_dualmeritnu*(ae_log(vv, _state)+(double)1-vv);
                v = current->sxl.ptr.p_double[i];
                vv = v*currentmult->zsxl.ptr.p_double[i]*invmu;
                barrierf = barrierf-ae_log(v, _state)-gipm2_dualmeritnu*(ae_log(vv, _state)+(double)1-vv);
                v = state->softbcmsxl.ptr.p_double[i]-current->sxl.ptr.p_double[i];
                vv = v*currentmult->zsxlcap.ptr.p_double[i]*invmu;
                barrierf = barrierf-ae_log(v, _state)-gipm2_dualmeritnu*(ae_log(vv, _state)+(double)1-vv);
                result = result+state->pxl.ptr.p_double[i]*current->sxl.ptr.p_double[i];
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                v = state->finitebndu.ptr.p_double[i]-current->xp.ptr.p_double[i]+state->softbcqu.ptr.p_double[i]*current->sxu.ptr.p_double[i];
                vv = v*currentmult->zxu.ptr.p_double[i]*invmu;
                barrierf = barrierf-ae_log(v, _state)-gipm2_dualmeritnu*(ae_log(vv, _state)+(double)1-vv);
                v = current->sxu.ptr.p_double[i];
                vv = v*currentmult->zsxu.ptr.p_double[i]*invmu;
                barrierf = barrierf-ae_log(v, _state)-gipm2_dualmeritnu*(ae_log(vv, _state)+(double)1-vv);
                v = state->softbcmsxu.ptr.p_double[i]-current->sxu.ptr.p_double[i];
                vv = v*currentmult->zsxucap.ptr.p_double[i]*invmu;
                barrierf = barrierf-ae_log(v, _state)-gipm2_dualmeritnu*(ae_log(vv, _state)+(double)1-vv);
                result = result+state->pxu.ptr.p_double[i]*current->sxu.ptr.p_double[i];
            }
        }
    }
    
    /*
     * FLEX-type constraints
     */
    ae_assert(fidx==1, "GIPM2: 828102 failed", _state);
    for(i=0; i<=state->mflex-1; i++)
    {
        v = current->fi.ptr.p_double[fidx]-current->sxf.ptr.p_double[i];
        vv = v+currentmu*currentmult->zcf.ptr.p_double[i];
        penaltyf = penaltyf+v*v+gipm2_dualmeritnu*vv*vv;
        if( state->hasflexcl.ptr.p_bool[i] )
        {
            v = current->sxf.ptr.p_double[i]-current->gl.ptr.p_double[i]-state->flexcl.ptr.p_double[i];
            vv = v+currentmu*currentmult->zcfgl.ptr.p_double[i];
            penaltyf = penaltyf+v*v+gipm2_dualmeritnu*vv*vv;
            v = current->gl.ptr.p_double[i];
            vv = v*currentmult->zgl.ptr.p_double[i]*invmu;
            barrierf = barrierf-ae_log(v, _state)-gipm2_dualmeritnu*(ae_log(vv, _state)+(double)1-vv);
        }
        if( state->hasflexcu.ptr.p_bool[i] )
        {
            v = current->sxf.ptr.p_double[i]+current->gu.ptr.p_double[i]-state->flexcu.ptr.p_double[i];
            vv = v+currentmu*currentmult->zcfgu.ptr.p_double[i];
            penaltyf = penaltyf+v*v+gipm2_dualmeritnu*vv*vv;
            v = current->gu.ptr.p_double[i];
            vv = v*currentmult->zgu.ptr.p_double[i]*invmu;
            barrierf = barrierf-ae_log(v, _state)-gipm2_dualmeritnu*(ae_log(vv, _state)+(double)1-vv);
        }
        fidx = fidx+1;
    }
    
    /*
     * Other constraints
     */
    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 263349", _state);
    
    /*
     * Done
     */
    result = result+barrierf*currentmu+penaltyf/((double)2*currentmu);
    return result;
}


/*************************************************************************
Compute safeguard BndLP/UP from FiniteBndLX/UX.

  -- ALGLIB --
     Copyright 11.02.2025 by Bochkanov Sergey
*************************************************************************/
static void gipm2_safeboundsfromfinitetightened(gipm2state* state,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t i;
    double vmid;
    double v;


    if( !state->hashardbc )
    {
        return;
    }
    np = state->np;
    rallocv(np, &state->safeguardbndlp, _state);
    rallocv(np, &state->safeguardbndup, _state);
    v = (double)10*ae_machineepsilon;
    for(i=0; i<=np-1; i++)
    {
        
        /*
         * Safeguard bounds on primal variables and check interval width
         */
        vmid = 0.5*(state->finitebndl.ptr.p_double[i]+state->finitebndu.ptr.p_double[i]);
        state->safeguardbndlp.ptr.p_double[i] = ae_minreal(state->finitebndl.ptr.p_double[i]+ae_maxreal(ae_fabs(state->finitebndl.ptr.p_double[i], _state), 1.0, _state)*v, vmid, _state);
        state->safeguardbndup.ptr.p_double[i] = ae_maxreal(state->finitebndu.ptr.p_double[i]-ae_maxreal(ae_fabs(state->finitebndu.ptr.p_double[i], _state), 1.0, _state)*v, vmid, _state);
        ae_assert(ae_fp_greater_eq(state->safeguardbndup.ptr.p_double[i],state->safeguardbndlp.ptr.p_double[i]), "GIPM2: hard bounds on primal variables are too tight, bad problem preprocessing", _state);
    }
}


/*************************************************************************
Decrease or increase relaxation penalties

  -- ALGLIB --
     Copyright 25.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_tunepenalties(gipm2state* state,
     const gipm2mult* currentmult,
     ae_state *_state)
{
    ae_int_t np;
    ae_int_t mtotal;
    ae_int_t i;
    ae_int_t increased;
    ae_int_t decreased;
    double v;
    double rlxsafetymed;


    np = state->np;
    mtotal = state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1;
    increased = 0;
    decreased = 0;
    rlxsafetymed = ae_sqrt(gipm2_rlxsafetylo*gipm2_rlxsafetyhi, _state);
    
    /*
     * Handle box constraints
     */
    if( state->hassoftbc )
    {
        ae_assert(!state->hashardbc, "GIPM2: 197214", _state);
        ae_assert(currentmult->hassoftbc, "GIPM2: 197215", _state);
        for(i=0; i<=np-1; i++)
        {
            if( state->hasbndl.ptr.p_bool[i] )
            {
                v = currentmult->zxl.ptr.p_double[i]*state->softbcql.ptr.p_double[i];
                if( ae_fp_greater(v,gipm2_rlxsafetyhi*state->pxl.ptr.p_double[i]) )
                {
                    state->pxl.ptr.p_double[i] = state->pxl.ptr.p_double[i]*(double)10;
                    increased = increased+1;
                    continue;
                }
                if( ae_fp_less(ae_maxreal(v, (double)(1), _state),gipm2_rlxsafetylo*state->pxl.ptr.p_double[i]) )
                {
                    state->pxl.ptr.p_double[i] = v/rlxsafetymed;
                    decreased = decreased+1;
                    continue;
                }
            }
            if( state->hasbndu.ptr.p_bool[i] )
            {
                v = currentmult->zxu.ptr.p_double[i]*state->softbcqu.ptr.p_double[i];
                if( ae_fp_greater(v,gipm2_rlxsafetyhi*state->pxu.ptr.p_double[i]) )
                {
                    state->pxu.ptr.p_double[i] = state->pxu.ptr.p_double[i]*(double)10;
                    increased = increased+1;
                    continue;
                }
                if( ae_fp_less(ae_maxreal(v, (double)(1), _state),gipm2_rlxsafetylo*state->pxu.ptr.p_double[i]) )
                {
                    state->pxu.ptr.p_double[i] = v/rlxsafetymed;
                    decreased = decreased+1;
                    continue;
                }
            }
        }
    }
    
    /*
     * Handle non-box constraints
     */
    ae_assert(mtotal-state->mflex==0, "199213", _state);
    
    /*
     * Trace
     */
    if( state->dotrace&&increased+decreased>0 )
    {
        ae_trace("> updating penalties, %0d increased, %0d decreased\n",
            (int)(increased),
            (int)(decreased));
    }
}


/*************************************************************************
Logic for tightenting/relaxation of a lower bound. Returns new bound value
(can be the same as original one)

PARAMETERS:
    VX              variable or constraint value; can be below lower
                    bound if allowed by soft constraints
    VXNrm           inf-norm of the variable/constraint vector
    WrkBndL         working version of lower bound, potentially tighter than
                    TrueBndL, but not lower
    TrueBndL        true (fixed) lower bound
    NTightened,
    NRelaxed        counters, updated on change

  -- ALGLIB --
     Copyright 25.05.2024 by Bochkanov Sergey
*************************************************************************/
static double gipm2_tgtrlxbndl(double vx,
     double vxnrm,
     double wrkbndl,
     double truebndl,
     ae_int_t* ntightened,
     ae_int_t* nrelaxed,
     ae_state *_state)
{
    double vtgt;
    double vrlx;
    double vmid;
    double result;


    vtgt = ae_maxreal(vx-vxnrm*gipm2_tightendist, truebndl, _state);
    vrlx = ae_maxreal(vx-vxnrm*gipm2_relaxdist, truebndl, _state);
    vmid = ae_maxreal(vx-vxnrm*gipm2_middist, truebndl, _state);
    result = wrkbndl;
    if( ae_fp_less(result,vtgt) )
    {
        result = vmid;
        *ntightened = *ntightened+1;
    }
    if( ae_fp_greater(result,vrlx) )
    {
        result = vmid;
        *nrelaxed = *nrelaxed+1;
    }
    return result;
}


/*************************************************************************
Logic for tightenting/relaxation of an upper bound. Returns new bound value
(can be the same as original one)

PARAMETERS:
    VX              variable or constraint value; can be above upper
                    bound if allowed by soft constraints
    VXNrm           inf-norm of the varible/constraint vector
    WrkBndU         working version of the bound, potentially tighter than
                    TrueBndU, but not above
    TrueBndU        true (fixed) upper bound
    NTightened,
    NRelaxed        counters, updated on change

  -- ALGLIB --
     Copyright 25.05.2024 by Bochkanov Sergey
*************************************************************************/
static double gipm2_tgtrlxbndu(double vx,
     double vxnrm,
     double wrkbndu,
     double truebndu,
     ae_int_t* ntightened,
     ae_int_t* nrelaxed,
     ae_state *_state)
{
    double vtgt;
    double vrlx;
    double vmid;
    double result;


    vtgt = ae_minreal(vx+vxnrm*gipm2_tightendist, truebndu, _state);
    vrlx = ae_minreal(vx+vxnrm*gipm2_relaxdist, truebndu, _state);
    vmid = ae_minreal(vx+vxnrm*gipm2_middist, truebndu, _state);
    result = wrkbndu;
    if( ae_fp_greater(result,vtgt) )
    {
        result = vmid;
        *ntightened = *ntightened+1;
    }
    if( ae_fp_less(result,vrlx) )
    {
        result = vmid;
        *nrelaxed = *nrelaxed+1;
    }
    return result;
}


/*************************************************************************
Tightens/relaxes bounds, recomputes safeguarded BndLP/UP if necessary.

  -- ALGLIB --
     Copyright 25.05.2024 by Bochkanov Sergey
*************************************************************************/
static void gipm2_tightenrelaxbounds(gipm2state* state, ae_state *_state)
{
    ae_int_t np;
    ae_int_t i;
    ae_int_t ntightened;
    ae_int_t nrelaxed;
    double xpnrm;
    double vx;
    double vnrm;


    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 510240", _state);
    ae_assert(ae_fp_less(gipm2_relaxdist,gipm2_tightendist), "GIPM: integrity check 1821 failed", _state);
    np = state->np;
    xpnrm = ae_maxreal(rmaxabsv(np, &state->current.xp, _state), (double)(1), _state);
    ntightened = 0;
    nrelaxed = 0;
    for(i=0; i<=np-1; i++)
    {
        
        /*
         * Variable value and distance scaling factor
         */
        vx = state->current.xp.ptr.p_double[i];
        vnrm = xpnrm;
        if( state->hasbndl.ptr.p_bool[i] )
        {
            state->finitebndl.ptr.p_double[i] = gipm2_tgtrlxbndl(vx, vnrm, state->finitebndl.ptr.p_double[i], state->hardbndl.ptr.p_double[i], &ntightened, &nrelaxed, _state);
        }
        if( state->hasbndu.ptr.p_bool[i] )
        {
            state->finitebndu.ptr.p_double[i] = gipm2_tgtrlxbndu(vx, vnrm, state->finitebndu.ptr.p_double[i], state->hardbndu.ptr.p_double[i], &ntightened, &nrelaxed, _state);
        }
    }
    gipm2_safeboundsfromfinitetightened(state, _state);
    if( state->dotrace&&nrelaxed+ntightened>0 )
    {
        ae_trace("> %0d bounds tightened, %0d bounds relaxed\n",
            (int)(ntightened),
            (int)(nrelaxed));
    }
}


/*************************************************************************
Update box/nonbox error estimates
*************************************************************************/
static void gipm2_updateerrorestimates(gipm2state* state,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t fidx;
    double vx;
    double v;


    state->repbcerr = (double)(0);
    state->repbcidx = -1;
    state->repnlcerr = (double)(0);
    state->repnlcidx = -1;
    state->repsclerr = (double)(0);
    if( state->hashardbc||state->hassoftbc )
    {
        for(i=0; i<=state->np-1; i++)
        {
            vx = state->current.xp.ptr.p_double[i];
            v = state->finitebndl.ptr.p_double[i]-vx;
            if( ae_fp_greater(v,state->repbcerr) )
            {
                state->repbcerr = v;
                state->repbcidx = i;
            }
            v = vx-state->finitebndu.ptr.p_double[i];
            if( ae_fp_greater(v,state->repbcerr) )
            {
                state->repbcerr = v;
                state->repbcidx = i;
            }
        }
    }
    
    /*
     * Other constraints
     */
    fidx = 1;
    for(i=0; i<=state->mflex-1; i++)
    {
        if( state->hasflexcl.ptr.p_bool[i] )
        {
            v = ae_maxreal(state->flexcl.ptr.p_double[i]-state->current.fi.ptr.p_double[fidx], 0.0, _state);
            if( ae_fp_greater(v,state->repnlcerr) )
            {
                state->repnlcerr = v;
                state->repnlcidx = fidx-1;
            }
        }
        if( state->hasflexcu.ptr.p_bool[i] )
        {
            v = ae_maxreal(state->current.fi.ptr.p_double[fidx]-state->flexcu.ptr.p_double[i], 0.0, _state);
            if( ae_fp_greater(v,state->repnlcerr) )
            {
                state->repnlcerr = v;
                state->repnlcidx = fidx-1;
            }
        }
        fidx = fidx+1;
    }
    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 885251", _state);
    
    /*
     * Done
     */
    state->repsclerr = rmax3(state->repsclerr, state->repbcerr, state->repnlcerr, _state);
}


/*************************************************************************
Send X
*************************************************************************/
static void gipm2_sendx(gipm2state* state,
     /* Real    */ const ae_vector* xs,
     ae_state *_state)
{


    ae_assert(state->meq+state->mrlxeq+state->m1+state->mrlx1==0, "GIPM2: 866202", _state);
    rcopyv(state->np, xs, &state->querydata, _state);
    if( state->hashardbc )
    {
        rmergemaxv(state->np, &state->safeguardbndlp, &state->querydata, _state);
        rmergeminv(state->np, &state->safeguardbndup, &state->querydata, _state);
    }
}


/*************************************************************************
Send Lagrangian with terms corresponding to box constraints omitted
*************************************************************************/
static void gipm2_sendlagrangianinfo(gipm2state* state,
     gipm2vars* current,
     gipm2mult* curmult,
     gipm2vars* cand,
     gipm2mult* candmult,
     ae_bool usebestmult,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t np;
    ae_int_t mtotal;
    ae_int_t rowoffs;
    ae_int_t qdoffs;
    ae_int_t lagnlcoffs;
    ae_vector tmp;
    ae_vector tmp2;

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

    np = state->np;
    mtotal = state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1;
    rallocv(4*np+mtotal, &state->querydata, _state);
    
    /*
     * Retrieve temporary array and fill with multipliers
     */
    ae_nxpool_retrieve(&state->mnx1pool, &tmp, _state);
    ae_nxpool_retrieve(&state->mnx1pool, &tmp2, _state);
    ae_assert(tmp.cnt>=mtotal+1&&tmp2.cnt>=mtotal+1, "GIPM2: 167028 failed", _state);
    tmp.ptr.p_double[0] = (double)(1);
    tmp2.ptr.p_double[0] = (double)(1);
    lagnlcoffs = 0;
    rowoffs = 1;
    qdoffs = np;
    for(i=0; i<=state->mflex-1; i++)
    {
        if( usebestmult )
        {
            tmp.ptr.p_double[rowoffs] = -state->bestlagnlc.ptr.p_double[lagnlcoffs];
            tmp2.ptr.p_double[rowoffs] = -state->bestlagnlc.ptr.p_double[lagnlcoffs];
        }
        else
        {
            tmp.ptr.p_double[rowoffs] = -curmult->zcf.ptr.p_double[i];
            tmp2.ptr.p_double[rowoffs] = -candmult->zcf.ptr.p_double[i];
        }
        rowoffs = rowoffs+1;
        qdoffs = qdoffs+1;
        lagnlcoffs = lagnlcoffs+1;
    }
    ae_assert(mtotal-state->mflex==0, "GIPM2: integrity check 734626 failed", _state);
    
    /*
     * Generate QueryData
     */
    rcopyvx(np, &current->xp, 0, &state->querydata, 0, _state);
    rcopyvx(np, &cand->xp, 0, &state->querydata, np, _state);
    sparsegemv(&current->jac, 1.0, 1, &tmp, 0, 0.0, &state->querydata, 2*np, _state);
    sparsegemv(&cand->jac, 1.0, 1, &tmp2, 0, 0.0, &state->querydata, 3*np, _state);
    rcopyvx(mtotal, &tmp2, 1, &state->querydata, 4*np, _state);
    
    /*
     * Recycle
     */
    ae_nxpool_recycle(&state->mnx1pool, &tmp2, _state);
    ae_nxpool_recycle(&state->mnx1pool, &tmp, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Fills preallocated LagBC and LagNLC with values computed from multipliers
*************************************************************************/
static void gipm2_lagcoeffsfrommultipliers(const gipm2mult* mult,
     /* Real    */ ae_vector* lagbc,
     /* Real    */ ae_vector* lagnlc,
     ae_state *_state)
{
    ae_int_t mtotal;
    ae_int_t offs;


    mtotal = mult->mflex+mult->meq+mult->mrlxeq+mult->m1+mult->mrlx1;
    ae_assert(lagbc->cnt>=mult->np&&lagnlc->cnt>=mtotal, "GIPM2: 990225 failed", _state);
    
    /*
     * Box constraints
     */
    if( mult->hashardbc||mult->hassoftbc )
    {
        rcopyv(mult->np, &mult->zxu, lagbc, _state);
        raddv(mult->np, -1.0, &mult->zxl, lagbc, _state);
    }
    else
    {
        rsetv(mult->np, 0.0, lagbc, _state);
    }
    
    /*
     * Nonlinear constraints
     */
    offs = 0;
    if( mult->mflex>0 )
    {
        rcopyvx(mult->mflex, &mult->zcf, 0, lagnlc, offs, _state);
        offs = offs+mult->mflex;
    }
    ae_assert(offs==mtotal, "GIPM2: 015228 failed", _state);
}


/*************************************************************************
Retrieves F-vector and scaled Jacobian, copies them to VFJ,
applying reordering permutation if needed.

Returns True on success, False on failure (when F or J are not finite numbers).
*************************************************************************/
static ae_bool gipm2_retrievefij(const gipm2state* state,
     gipm2vars* vfj,
     ae_state *_state)
{
    ae_int_t mtotal;
    ae_bool result;


    mtotal = state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1;
    
    /*
     * Retrieve function vector and Jacobian
     */
    rcopyv(1+mtotal, &state->replyfi, &vfj->fi, _state);
    sparsecopybuf(&state->replysj, &vfj->jac, _state);
    
    /*
     * Check finiteness
     */
    result = ae_isfinite(rdotv2(1+mtotal, &vfj->fi, _state), _state)&&ae_isfinite(rdotv2(vfj->jac.ridx.ptr.p_int[1+mtotal], &vfj->jac.vals, _state), _state);
    return result;
}


/*************************************************************************
Initialize memory for nonmonotonic merit function optimization

  -- ALGLIB --
     Copyright 25.02.2025 by Bochkanov Sergey
*************************************************************************/
static void gipm2_nonmonotonicinit(gipm2state* state,
     ae_int_t memlen,
     double merit0,
     ae_state *_state)
{


    ae_assert(memlen>=0, "GIPM2: integrity check 157435 failed", _state);
    state->nonmonotonicmemlen = memlen;
    state->nonmonotonicpos = 0;
    rsetallocv(memlen+1, merit0, &state->nonmonotonicmerit, _state);
    state->nonmonotonicmerit.ptr.p_double[memlen] = merit0;
}


/*************************************************************************
Push new value to the nonmonotonic memory

  -- ALGLIB --
     Copyright 25.02.2025 by Bochkanov Sergey
*************************************************************************/
static void gipm2_nonmonotonicpush(gipm2state* state,
     double meritv,
     ae_state *_state)
{


    state->nonmonotonicmerit.ptr.p_double[state->nonmonotonicpos] = meritv;
    state->nonmonotonicpos = (state->nonmonotonicpos+1)%(state->nonmonotonicmemlen+1);
}


/*************************************************************************
Returns nonmonotonic correction that should be added to MeritV

  -- ALGLIB --
     Copyright 25.02.2025 by Bochkanov Sergey
*************************************************************************/
static double gipm2_nonmonotonicgetcorr(gipm2state* state,
     double meritv,
     ae_state *_state)
{
    ae_int_t i;
    double mx;
    double result;


    mx = meritv;
    for(i=0; i<=state->nonmonotonicmemlen; i++)
    {
        mx = ae_maxreal(mx, state->nonmonotonicmerit.ptr.p_double[i], _state);
    }
    result = ae_maxreal(mx-meritv, 0.0, _state);
    return result;
}


/*************************************************************************
Apply function rescaling to the current point

  -- ALGLIB --
     Copyright 25.02.2025 by Bochkanov Sergey
*************************************************************************/
static void gipm2_rescalecurrentbest(gipm2state* state,
     /* Real    */ const ae_vector* rescaleby,
     ae_bool rescalebest,
     ae_state *_state)
{
    ae_int_t mtotal;
    ae_int_t rescaleoffs;
    ae_int_t i;
    double v;


    mtotal = state->mflex+state->meq+state->mrlxeq+state->m1+state->mrlx1;
    
    /*
     * Rescale primal variables and function vector/Jacobian
     */
    rmergemulv(1+mtotal, rescaleby, &state->current.fi, _state);
    sparsemultiplyrowsby(&state->current.jac, rescaleby, _state);
    if( rescalebest )
    {
        rmergemulv(1+mtotal, rescaleby, &state->best.fi, _state);
        sparsemultiplyrowsby(&state->best.jac, rescaleby, _state);
    }
    
    /*
     * Rescale best Lagrange multipliers
     */
    if( rescalebest )
    {
        rmulv(state->np, rescaleby->ptr.p_double[0], &state->bestlagbc, _state);
        rmulv(mtotal, rescaleby->ptr.p_double[0], &state->bestlagnlc, _state);
        for(i=0; i<=mtotal-1; i++)
        {
            state->bestlagnlc.ptr.p_double[i] = state->bestlagnlc.ptr.p_double[i]/rescaleby->ptr.p_double[1+i];
        }
    }
    
    /*
     * Rescale FLEX-type constraints
     */
    rescaleoffs = 1;
    for(i=0; i<=state->mflex-1; i++)
    {
        v = rescaleby->ptr.p_double[rescaleoffs];
        state->flexcl.ptr.p_double[i] = state->flexcl.ptr.p_double[i]*v;
        state->flexcu.ptr.p_double[i] = state->flexcu.ptr.p_double[i]*v;
        state->current.sxf.ptr.p_double[i] = state->current.sxf.ptr.p_double[i]*v;
        if( rescalebest )
        {
            state->best.sxf.ptr.p_double[i] = state->best.sxf.ptr.p_double[i]*v;
        }
        if( state->hasflexcl.ptr.p_bool[i] )
        {
            state->current.gl.ptr.p_double[i] = state->current.gl.ptr.p_double[i]*v;
            if( rescalebest )
            {
                state->best.gl.ptr.p_double[i] = state->best.gl.ptr.p_double[i]*v;
            }
        }
        if( state->hasflexcu.ptr.p_bool[i] )
        {
            state->current.gu.ptr.p_double[i] = state->current.gu.ptr.p_double[i]*v;
            if( rescalebest )
            {
                state->best.gu.ptr.p_double[i] = state->best.gu.ptr.p_double[i]*v;
            }
        }
        rescaleoffs = rescaleoffs+1;
    }
    ae_assert(rescaleoffs==mtotal+1, "GIPM: 780055", _state);
}


/*************************************************************************
Compute aggregate of primal, dual and complementarity errors (now it is max).

  -- ALGLIB --
     Copyright 25.02.2025 by Bochkanov Sergey
*************************************************************************/
static double gipm2_aggregateerr(double p,
     double d,
     double c,
     ae_state *_state)
{
    double result;


    result = rmax3(p, d, c, _state);
    return result;
}


void _gipm2vars_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gipm2vars *p = (gipm2vars*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->xp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sxl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sxu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->sxf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->jac, _state, make_automatic);
}


void _gipm2vars_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gipm2vars       *dst = (gipm2vars*)_dst;
    const gipm2vars *src = (const gipm2vars*)_src;
    dst->hashardbc = src->hashardbc;
    dst->hassoftbc = src->hassoftbc;
    dst->np = src->np;
    dst->mflex = src->mflex;
    dst->meq = src->meq;
    dst->mrlxeq = src->mrlxeq;
    dst->m1 = src->m1;
    dst->mrlx1 = src->mrlx1;
    ae_vector_init_copy(&dst->xp, &src->xp, _state, make_automatic);
    ae_vector_init_copy(&dst->sxl, &src->sxl, _state, make_automatic);
    ae_vector_init_copy(&dst->sxu, &src->sxu, _state, make_automatic);
    ae_vector_init_copy(&dst->sxf, &src->sxf, _state, make_automatic);
    ae_vector_init_copy(&dst->gl, &src->gl, _state, make_automatic);
    ae_vector_init_copy(&dst->gu, &src->gu, _state, make_automatic);
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    _sparsematrix_init_copy(&dst->jac, &src->jac, _state, make_automatic);
}


void _gipm2vars_clear(void* _p)
{
    gipm2vars *p = (gipm2vars*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->xp);
    ae_vector_clear(&p->sxl);
    ae_vector_clear(&p->sxu);
    ae_vector_clear(&p->sxf);
    ae_vector_clear(&p->gl);
    ae_vector_clear(&p->gu);
    ae_vector_clear(&p->fi);
    _sparsematrix_clear(&p->jac);
}


void _gipm2vars_destroy(void* _p)
{
    gipm2vars *p = (gipm2vars*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->xp);
    ae_vector_destroy(&p->sxl);
    ae_vector_destroy(&p->sxu);
    ae_vector_destroy(&p->sxf);
    ae_vector_destroy(&p->gl);
    ae_vector_destroy(&p->gu);
    ae_vector_destroy(&p->fi);
    _sparsematrix_destroy(&p->jac);
}


void _gipm2mult_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gipm2mult *p = (gipm2mult*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->zxl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zxu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zsxl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zsxu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zsxlcap, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zsxucap, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zcf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zgl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zgu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zcfgl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->zcfgu, 0, DT_REAL, _state, make_automatic);
}


void _gipm2mult_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gipm2mult       *dst = (gipm2mult*)_dst;
    const gipm2mult *src = (const gipm2mult*)_src;
    dst->hashardbc = src->hashardbc;
    dst->hassoftbc = src->hassoftbc;
    dst->np = src->np;
    dst->mflex = src->mflex;
    dst->meq = src->meq;
    dst->mrlxeq = src->mrlxeq;
    dst->m1 = src->m1;
    dst->mrlx1 = src->mrlx1;
    ae_vector_init_copy(&dst->zxl, &src->zxl, _state, make_automatic);
    ae_vector_init_copy(&dst->zxu, &src->zxu, _state, make_automatic);
    ae_vector_init_copy(&dst->zsxl, &src->zsxl, _state, make_automatic);
    ae_vector_init_copy(&dst->zsxu, &src->zsxu, _state, make_automatic);
    ae_vector_init_copy(&dst->zsxlcap, &src->zsxlcap, _state, make_automatic);
    ae_vector_init_copy(&dst->zsxucap, &src->zsxucap, _state, make_automatic);
    ae_vector_init_copy(&dst->zcf, &src->zcf, _state, make_automatic);
    ae_vector_init_copy(&dst->zgl, &src->zgl, _state, make_automatic);
    ae_vector_init_copy(&dst->zgu, &src->zgu, _state, make_automatic);
    ae_vector_init_copy(&dst->zcfgl, &src->zcfgl, _state, make_automatic);
    ae_vector_init_copy(&dst->zcfgu, &src->zcfgu, _state, make_automatic);
}


void _gipm2mult_clear(void* _p)
{
    gipm2mult *p = (gipm2mult*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->zxl);
    ae_vector_clear(&p->zxu);
    ae_vector_clear(&p->zsxl);
    ae_vector_clear(&p->zsxu);
    ae_vector_clear(&p->zsxlcap);
    ae_vector_clear(&p->zsxucap);
    ae_vector_clear(&p->zcf);
    ae_vector_clear(&p->zgl);
    ae_vector_clear(&p->zgu);
    ae_vector_clear(&p->zcfgl);
    ae_vector_clear(&p->zcfgu);
}


void _gipm2mult_destroy(void* _p)
{
    gipm2mult *p = (gipm2mult*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->zxl);
    ae_vector_destroy(&p->zxu);
    ae_vector_destroy(&p->zsxl);
    ae_vector_destroy(&p->zsxu);
    ae_vector_destroy(&p->zsxlcap);
    ae_vector_destroy(&p->zsxucap);
    ae_vector_destroy(&p->zcf);
    ae_vector_destroy(&p->zgl);
    ae_vector_destroy(&p->zgu);
    ae_vector_destroy(&p->zcfgl);
    ae_vector_destroy(&p->zcfgu);
}


void _gipm2rhs_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gipm2rhs *p = (gipm2rhs*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->rp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rpsxl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rpsxu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rdxl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rdxu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rdsxl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rdsxu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rdsxlcap, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rdsxucap, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rpsxf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rpgl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rpgu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rdcf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rdcfgl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rdcfgu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rdgl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rdgu, 0, DT_REAL, _state, make_automatic);
}


void _gipm2rhs_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gipm2rhs       *dst = (gipm2rhs*)_dst;
    const gipm2rhs *src = (const gipm2rhs*)_src;
    ae_vector_init_copy(&dst->rp, &src->rp, _state, make_automatic);
    ae_vector_init_copy(&dst->rpsxl, &src->rpsxl, _state, make_automatic);
    ae_vector_init_copy(&dst->rpsxu, &src->rpsxu, _state, make_automatic);
    ae_vector_init_copy(&dst->rdxl, &src->rdxl, _state, make_automatic);
    ae_vector_init_copy(&dst->rdxu, &src->rdxu, _state, make_automatic);
    ae_vector_init_copy(&dst->rdsxl, &src->rdsxl, _state, make_automatic);
    ae_vector_init_copy(&dst->rdsxu, &src->rdsxu, _state, make_automatic);
    ae_vector_init_copy(&dst->rdsxlcap, &src->rdsxlcap, _state, make_automatic);
    ae_vector_init_copy(&dst->rdsxucap, &src->rdsxucap, _state, make_automatic);
    ae_vector_init_copy(&dst->rpsxf, &src->rpsxf, _state, make_automatic);
    ae_vector_init_copy(&dst->rpgl, &src->rpgl, _state, make_automatic);
    ae_vector_init_copy(&dst->rpgu, &src->rpgu, _state, make_automatic);
    ae_vector_init_copy(&dst->rdcf, &src->rdcf, _state, make_automatic);
    ae_vector_init_copy(&dst->rdcfgl, &src->rdcfgl, _state, make_automatic);
    ae_vector_init_copy(&dst->rdcfgu, &src->rdcfgu, _state, make_automatic);
    ae_vector_init_copy(&dst->rdgl, &src->rdgl, _state, make_automatic);
    ae_vector_init_copy(&dst->rdgu, &src->rdgu, _state, make_automatic);
}


void _gipm2rhs_clear(void* _p)
{
    gipm2rhs *p = (gipm2rhs*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->rp);
    ae_vector_clear(&p->rpsxl);
    ae_vector_clear(&p->rpsxu);
    ae_vector_clear(&p->rdxl);
    ae_vector_clear(&p->rdxu);
    ae_vector_clear(&p->rdsxl);
    ae_vector_clear(&p->rdsxu);
    ae_vector_clear(&p->rdsxlcap);
    ae_vector_clear(&p->rdsxucap);
    ae_vector_clear(&p->rpsxf);
    ae_vector_clear(&p->rpgl);
    ae_vector_clear(&p->rpgu);
    ae_vector_clear(&p->rdcf);
    ae_vector_clear(&p->rdcfgl);
    ae_vector_clear(&p->rdcfgu);
    ae_vector_clear(&p->rdgl);
    ae_vector_clear(&p->rdgu);
}


void _gipm2rhs_destroy(void* _p)
{
    gipm2rhs *p = (gipm2rhs*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->rp);
    ae_vector_destroy(&p->rpsxl);
    ae_vector_destroy(&p->rpsxu);
    ae_vector_destroy(&p->rdxl);
    ae_vector_destroy(&p->rdxu);
    ae_vector_destroy(&p->rdsxl);
    ae_vector_destroy(&p->rdsxu);
    ae_vector_destroy(&p->rdsxlcap);
    ae_vector_destroy(&p->rdsxucap);
    ae_vector_destroy(&p->rpsxf);
    ae_vector_destroy(&p->rpgl);
    ae_vector_destroy(&p->rpgu);
    ae_vector_destroy(&p->rdcf);
    ae_vector_destroy(&p->rdcfgl);
    ae_vector_destroy(&p->rdcfgu);
    ae_vector_destroy(&p->rdgl);
    ae_vector_destroy(&p->rdgu);
}


void _gipm2condensedsystem_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gipm2condensedsystem *p = (gipm2condensedsystem*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->wx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->wy, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d1, 0, DT_REAL, _state, make_automatic);
}


void _gipm2condensedsystem_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gipm2condensedsystem       *dst = (gipm2condensedsystem*)_dst;
    const gipm2condensedsystem *src = (const gipm2condensedsystem*)_src;
    ae_vector_init_copy(&dst->wx, &src->wx, _state, make_automatic);
    ae_vector_init_copy(&dst->wy, &src->wy, _state, make_automatic);
    ae_vector_init_copy(&dst->d0, &src->d0, _state, make_automatic);
    ae_vector_init_copy(&dst->d1, &src->d1, _state, make_automatic);
}


void _gipm2condensedsystem_clear(void* _p)
{
    gipm2condensedsystem *p = (gipm2condensedsystem*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->wx);
    ae_vector_clear(&p->wy);
    ae_vector_clear(&p->d0);
    ae_vector_clear(&p->d1);
}


void _gipm2condensedsystem_destroy(void* _p)
{
    gipm2condensedsystem *p = (gipm2condensedsystem*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->wx);
    ae_vector_destroy(&p->wy);
    ae_vector_destroy(&p->d0);
    ae_vector_destroy(&p->d1);
}


void _gipm2state_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    gipm2state *p = (gipm2state*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hardbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hardbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->softbcql, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->softbcqu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->softbcmsxl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->softbcmsxu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->callerfscales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitebndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitebndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->safeguardbndlp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->safeguardbndup, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->elp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->eup, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->flexcl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->flexcu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->maskflexcl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->maskflexcu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasflexcl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasflexcu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->reportx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->querydata, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyfi, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->replysj, _state, make_automatic);
    ae_vector_init(&p->replysol, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyprod, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyv, 0, DT_REAL, _state, make_automatic);
    _gipm2vars_init(&p->current, _state, make_automatic);
    _gipm2mult_init(&p->currentmult, _state, make_automatic);
    _gipm2mult_init(&p->delayedmult, _state, make_automatic);
    _gipm2vars_init(&p->best, _state, make_automatic);
    ae_vector_init(&p->bestlagbc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bestlagnlc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->pxl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->pxu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nonmonotonicmerit, 0, DT_REAL, _state, make_automatic);
    _gipm2vars_init(&p->cand, _state, make_automatic);
    _gipm2vars_init(&p->delta, _state, make_automatic);
    _gipm2vars_init(&p->corr, _state, make_automatic);
    _gipm2mult_init(&p->deltamult, _state, make_automatic);
    _gipm2mult_init(&p->corrmult, _state, make_automatic);
    _gipm2condensedsystem_init(&p->condensed, _state, make_automatic);
    _gipm2rhs_init(&p->rhs, _state, make_automatic);
    _gipm2rhs_init(&p->tmprhs, _state, make_automatic);
    ae_vector_init(&p->tmphdxp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpjdxp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpjtdy, 0, DT_REAL, _state, make_automatic);
    ae_nxpool_init(&p->mnx1pool, DT_REAL, _state, make_automatic);
    _gipm2mult_init(&p->candmult, _state, make_automatic);
    _gipm2rhs_init(&p->candrhs, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
}


void _gipm2state_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    gipm2state       *dst = (gipm2state*)_dst;
    const gipm2state *src = (const gipm2state*)_src;
    dst->np = src->np;
    dst->mflex = src->mflex;
    dst->meq = src->meq;
    dst->mrlxeq = src->mrlxeq;
    dst->m1 = src->m1;
    dst->mrlx1 = src->mrlx1;
    dst->hashardbc = src->hashardbc;
    dst->hassoftbc = src->hassoftbc;
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->hardbndl, &src->hardbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hardbndu, &src->hardbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->softbcql, &src->softbcql, _state, make_automatic);
    ae_vector_init_copy(&dst->softbcqu, &src->softbcqu, _state, make_automatic);
    ae_vector_init_copy(&dst->softbcmsxl, &src->softbcmsxl, _state, make_automatic);
    ae_vector_init_copy(&dst->softbcmsxu, &src->softbcmsxu, _state, make_automatic);
    dst->mudependent = src->mudependent;
    dst->isprimal = src->isprimal;
    ae_vector_init_copy(&dst->callerfscales, &src->callerfscales, _state, make_automatic);
    dst->multupdatedelay = src->multupdatedelay;
    ae_vector_init_copy(&dst->finitebndl, &src->finitebndl, _state, make_automatic);
    ae_vector_init_copy(&dst->finitebndu, &src->finitebndu, _state, make_automatic);
    ae_vector_init_copy(&dst->safeguardbndlp, &src->safeguardbndlp, _state, make_automatic);
    ae_vector_init_copy(&dst->safeguardbndup, &src->safeguardbndup, _state, make_automatic);
    ae_vector_init_copy(&dst->elp, &src->elp, _state, make_automatic);
    ae_vector_init_copy(&dst->eup, &src->eup, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->flexcl, &src->flexcl, _state, make_automatic);
    ae_vector_init_copy(&dst->flexcu, &src->flexcu, _state, make_automatic);
    ae_vector_init_copy(&dst->maskflexcl, &src->maskflexcl, _state, make_automatic);
    ae_vector_init_copy(&dst->maskflexcu, &src->maskflexcu, _state, make_automatic);
    ae_vector_init_copy(&dst->hasflexcl, &src->hasflexcl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasflexcu, &src->hasflexcu, _state, make_automatic);
    dst->muepsfactor = src->muepsfactor;
    dst->mudecaylinear = src->mudecaylinear;
    dst->mudecaypower = src->mudecaypower;
    dst->eps = src->eps;
    dst->maxits = src->maxits;
    dst->maxcheckpointstagnationits = src->maxcheckpointstagnationits;
    dst->requesttype = src->requesttype;
    ae_vector_init_copy(&dst->reportx, &src->reportx, _state, make_automatic);
    dst->reportf = src->reportf;
    ae_vector_init_copy(&dst->querydata, &src->querydata, _state, make_automatic);
    ae_vector_init_copy(&dst->replyfi, &src->replyfi, _state, make_automatic);
    _sparsematrix_init_copy(&dst->replysj, &src->replysj, _state, make_automatic);
    ae_vector_init_copy(&dst->replysol, &src->replysol, _state, make_automatic);
    ae_vector_init_copy(&dst->replyprod, &src->replyprod, _state, make_automatic);
    ae_vector_init_copy(&dst->replyv, &src->replyv, _state, make_automatic);
    dst->replywasrescale = src->replywasrescale;
    dst->hasjac = src->hasjac;
    dst->factsuccess = src->factsuccess;
    dst->dotrace = src->dotrace;
    dst->dotracelaconic = src->dotracelaconic;
    _gipm2vars_init_copy(&dst->current, &src->current, _state, make_automatic);
    _gipm2mult_init_copy(&dst->currentmult, &src->currentmult, _state, make_automatic);
    _gipm2mult_init_copy(&dst->delayedmult, &src->delayedmult, _state, make_automatic);
    dst->hasbest = src->hasbest;
    _gipm2vars_init_copy(&dst->best, &src->best, _state, make_automatic);
    ae_vector_init_copy(&dst->bestlagbc, &src->bestlagbc, _state, make_automatic);
    ae_vector_init_copy(&dst->bestlagnlc, &src->bestlagnlc, _state, make_automatic);
    dst->besterr = src->besterr;
    dst->fstagnationcnt = src->fstagnationcnt;
    dst->rhsstagnationcounter = src->rhsstagnationcounter;
    dst->goodinnerits = src->goodinnerits;
    dst->currentmu = src->currentmu;
    dst->prevmu = src->prevmu;
    dst->mumin = src->mumin;
    dst->curbigval = src->curbigval;
    dst->curbiggrowth = src->curbiggrowth;
    dst->lambdav = src->lambdav;
    dst->lambdadecay = src->lambdadecay;
    dst->lambdafound = src->lambdafound;
    dst->subsequentfactfailures = src->subsequentfactfailures;
    dst->firstouteriteration = src->firstouteriteration;
    dst->infinitiesdetected = src->infinitiesdetected;
    dst->linesearchalwayssuccessful = src->linesearchalwayssuccessful;
    dst->failedtorecoverfrominfinities = src->failedtorecoverfrominfinities;
    dst->unboundednesssuspected = src->unboundednesssuspected;
    dst->rejectedwithineps = src->rejectedwithineps;
    dst->lastprim = src->lastprim;
    dst->lastdual = src->lastdual;
    dst->lastcmpl = src->lastcmpl;
    ae_vector_init_copy(&dst->pxl, &src->pxl, _state, make_automatic);
    ae_vector_init_copy(&dst->pxu, &src->pxu, _state, make_automatic);
    dst->inneriterationidx = src->inneriterationidx;
    dst->hessiandecay = src->hessiandecay;
    dst->nonmonotonicmemlen = src->nonmonotonicmemlen;
    dst->nonmonotonicpos = src->nonmonotonicpos;
    ae_vector_init_copy(&dst->nonmonotonicmerit, &src->nonmonotonicmerit, _state, make_automatic);
    dst->nrmx0 = src->nrmx0;
    dst->absf0 = src->absf0;
    dst->dxpnrm = src->dxpnrm;
    _gipm2vars_init_copy(&dst->cand, &src->cand, _state, make_automatic);
    _gipm2vars_init_copy(&dst->delta, &src->delta, _state, make_automatic);
    _gipm2vars_init_copy(&dst->corr, &src->corr, _state, make_automatic);
    _gipm2mult_init_copy(&dst->deltamult, &src->deltamult, _state, make_automatic);
    _gipm2mult_init_copy(&dst->corrmult, &src->corrmult, _state, make_automatic);
    _gipm2condensedsystem_init_copy(&dst->condensed, &src->condensed, _state, make_automatic);
    _gipm2rhs_init_copy(&dst->rhs, &src->rhs, _state, make_automatic);
    _gipm2rhs_init_copy(&dst->tmprhs, &src->tmprhs, _state, make_automatic);
    ae_vector_init_copy(&dst->tmphdxp, &src->tmphdxp, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpjdxp, &src->tmpjdxp, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpjtdy, &src->tmpjtdy, _state, make_automatic);
    ae_nxpool_init_copy(&dst->mnx1pool, &src->mnx1pool, _state, make_automatic);
    _gipm2mult_init_copy(&dst->candmult, &src->candmult, _state, make_automatic);
    _gipm2rhs_init_copy(&dst->candrhs, &src->candrhs, _state, make_automatic);
    dst->repiterationscount = src->repiterationscount;
    dst->repterminationtype = src->repterminationtype;
    dst->repbcerr = src->repbcerr;
    dst->repbcidx = src->repbcidx;
    dst->repnlcerr = src->repnlcerr;
    dst->repnlcidx = src->repnlcidx;
    dst->repsclerr = src->repsclerr;
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
}


void _gipm2state_clear(void* _p)
{
    gipm2state *p = (gipm2state*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->hardbndl);
    ae_vector_clear(&p->hardbndu);
    ae_vector_clear(&p->softbcql);
    ae_vector_clear(&p->softbcqu);
    ae_vector_clear(&p->softbcmsxl);
    ae_vector_clear(&p->softbcmsxu);
    ae_vector_clear(&p->callerfscales);
    ae_vector_clear(&p->finitebndl);
    ae_vector_clear(&p->finitebndu);
    ae_vector_clear(&p->safeguardbndlp);
    ae_vector_clear(&p->safeguardbndup);
    ae_vector_clear(&p->elp);
    ae_vector_clear(&p->eup);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->flexcl);
    ae_vector_clear(&p->flexcu);
    ae_vector_clear(&p->maskflexcl);
    ae_vector_clear(&p->maskflexcu);
    ae_vector_clear(&p->hasflexcl);
    ae_vector_clear(&p->hasflexcu);
    ae_vector_clear(&p->reportx);
    ae_vector_clear(&p->querydata);
    ae_vector_clear(&p->replyfi);
    _sparsematrix_clear(&p->replysj);
    ae_vector_clear(&p->replysol);
    ae_vector_clear(&p->replyprod);
    ae_vector_clear(&p->replyv);
    _gipm2vars_clear(&p->current);
    _gipm2mult_clear(&p->currentmult);
    _gipm2mult_clear(&p->delayedmult);
    _gipm2vars_clear(&p->best);
    ae_vector_clear(&p->bestlagbc);
    ae_vector_clear(&p->bestlagnlc);
    ae_vector_clear(&p->pxl);
    ae_vector_clear(&p->pxu);
    ae_vector_clear(&p->nonmonotonicmerit);
    _gipm2vars_clear(&p->cand);
    _gipm2vars_clear(&p->delta);
    _gipm2vars_clear(&p->corr);
    _gipm2mult_clear(&p->deltamult);
    _gipm2mult_clear(&p->corrmult);
    _gipm2condensedsystem_clear(&p->condensed);
    _gipm2rhs_clear(&p->rhs);
    _gipm2rhs_clear(&p->tmprhs);
    ae_vector_clear(&p->tmphdxp);
    ae_vector_clear(&p->tmpjdxp);
    ae_vector_clear(&p->tmpjtdy);
    ae_nxpool_clear(&p->mnx1pool);
    _gipm2mult_clear(&p->candmult);
    _gipm2rhs_clear(&p->candrhs);
    _rcommstate_clear(&p->rstate);
}


void _gipm2state_destroy(void* _p)
{
    gipm2state *p = (gipm2state*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->hardbndl);
    ae_vector_destroy(&p->hardbndu);
    ae_vector_destroy(&p->softbcql);
    ae_vector_destroy(&p->softbcqu);
    ae_vector_destroy(&p->softbcmsxl);
    ae_vector_destroy(&p->softbcmsxu);
    ae_vector_destroy(&p->callerfscales);
    ae_vector_destroy(&p->finitebndl);
    ae_vector_destroy(&p->finitebndu);
    ae_vector_destroy(&p->safeguardbndlp);
    ae_vector_destroy(&p->safeguardbndup);
    ae_vector_destroy(&p->elp);
    ae_vector_destroy(&p->eup);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->flexcl);
    ae_vector_destroy(&p->flexcu);
    ae_vector_destroy(&p->maskflexcl);
    ae_vector_destroy(&p->maskflexcu);
    ae_vector_destroy(&p->hasflexcl);
    ae_vector_destroy(&p->hasflexcu);
    ae_vector_destroy(&p->reportx);
    ae_vector_destroy(&p->querydata);
    ae_vector_destroy(&p->replyfi);
    _sparsematrix_destroy(&p->replysj);
    ae_vector_destroy(&p->replysol);
    ae_vector_destroy(&p->replyprod);
    ae_vector_destroy(&p->replyv);
    _gipm2vars_destroy(&p->current);
    _gipm2mult_destroy(&p->currentmult);
    _gipm2mult_destroy(&p->delayedmult);
    _gipm2vars_destroy(&p->best);
    ae_vector_destroy(&p->bestlagbc);
    ae_vector_destroy(&p->bestlagnlc);
    ae_vector_destroy(&p->pxl);
    ae_vector_destroy(&p->pxu);
    ae_vector_destroy(&p->nonmonotonicmerit);
    _gipm2vars_destroy(&p->cand);
    _gipm2vars_destroy(&p->delta);
    _gipm2vars_destroy(&p->corr);
    _gipm2mult_destroy(&p->deltamult);
    _gipm2mult_destroy(&p->corrmult);
    _gipm2condensedsystem_destroy(&p->condensed);
    _gipm2rhs_destroy(&p->rhs);
    _gipm2rhs_destroy(&p->tmprhs);
    ae_vector_destroy(&p->tmphdxp);
    ae_vector_destroy(&p->tmpjdxp);
    ae_vector_destroy(&p->tmpjtdy);
    ae_nxpool_destroy(&p->mnx1pool);
    _gipm2mult_destroy(&p->candmult);
    _gipm2rhs_destroy(&p->candrhs);
    _rcommstate_destroy(&p->rstate);
}


#endif
#if defined(AE_COMPILE_NLCGIPM2) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
Initialize GQPIPM structure.

X0      -   initial point; must be:
            * strictly feasible wrt non-equality box constraints
            * non-strictly feasible wrt equality box constraints
            * strictly feasible wrt conic constraints, if present
            Failure to satisfy these properties will lead to an  exception
            generated either during  initialization  or  during  the  very
            first iteration.

  -- ALGLIB --
     Copyright 11.02.2025 by Bochkanov Sergey
*************************************************************************/
void nlpgipm2initbuf(/* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     /* Real    */ const ae_vector* s,
     /* Real    */ const ae_vector* x0,
     ae_int_t n,
     const nlpstoppingcriteria* criteria,
     ae_bool isdense,
     nlpgipm2state* state,
     ae_state *_state)
{
    ae_int_t i;
    double fixtol;
    double v;


    ae_assert(!isdense, "NLPGIPM2: 225301", _state);
    fixtol = 0.01*ae_sqrt(ae_machineepsilon, _state);
    state->isdense = isdense;
    state->isconvex = ae_false;
    state->n = n;
    state->mlc = 0;
    state->mnlc = 0;
    state->mxx = 0;
    state->hasbc = ae_false;
    state->hasfixed = ae_false;
    rcopyallocv(n, s, &state->s, _state);
    rsetallocv(n, _state->v_neginf, &state->scaledbndl, _state);
    rsetallocv(n, _state->v_posinf, &state->scaledbndu, _state);
    rsetallocv(n, -ae_sqrt(ae_maxrealnumber, _state), &state->finitescaledbndl, _state);
    rsetallocv(n, ae_sqrt(ae_maxrealnumber, _state), &state->finitescaledbndu, _state);
    rsetallocv(n, -ae_sqrt(ae_maxrealnumber, _state), &state->finiterawbndl, _state);
    rsetallocv(n, ae_sqrt(ae_maxrealnumber, _state), &state->finiterawbndu, _state);
    bsetallocv(n, ae_false, &state->isfixed, _state);
    rsetallocv(n, 1.0, &state->nonfixedmask, _state);
    rallocv(n, &state->scaledx0, _state);
    fixtol = ae_sqrt(ae_machineepsilon, _state);
    for(i=0; i<=n-1; i++)
    {
        state->scaledx0.ptr.p_double[i] = x0->ptr.p_double[i]/s->ptr.p_double[i];
        if( (ae_isfinite(bndl->ptr.p_double[i], _state)&&ae_isfinite(bndu->ptr.p_double[i], _state))&&ae_fp_less(bndu->ptr.p_double[i]-bndl->ptr.p_double[i],fixtol*s->ptr.p_double[i]) )
        {
            v = 0.5*(bndl->ptr.p_double[i]+bndu->ptr.p_double[i])/s->ptr.p_double[i];
            state->scaledbndl.ptr.p_double[i] = v;
            state->scaledbndu.ptr.p_double[i] = v;
            state->finitescaledbndl.ptr.p_double[i] = v;
            state->finitescaledbndu.ptr.p_double[i] = v;
            state->finiterawbndl.ptr.p_double[i] = bndl->ptr.p_double[i];
            state->finiterawbndu.ptr.p_double[i] = bndu->ptr.p_double[i];
            state->hasbc = ae_true;
            state->hasfixed = ae_true;
            state->isfixed.ptr.p_bool[i] = ae_true;
            state->nonfixedmask.ptr.p_double[i] = 0.0;
            continue;
        }
        if( ae_isfinite(bndl->ptr.p_double[i], _state) )
        {
            state->scaledbndl.ptr.p_double[i] = bndl->ptr.p_double[i]/s->ptr.p_double[i];
            state->finitescaledbndl.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i];
            state->finiterawbndl.ptr.p_double[i] = bndl->ptr.p_double[i];
            state->hasbc = ae_true;
        }
        if( ae_isfinite(bndu->ptr.p_double[i], _state) )
        {
            state->scaledbndu.ptr.p_double[i] = bndu->ptr.p_double[i]/s->ptr.p_double[i];
            state->finitescaledbndu.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i];
            state->finiterawbndu.ptr.p_double[i] = bndu->ptr.p_double[i];
            state->hasbc = ae_true;
        }
    }
    state->maxits = critgetmaxits(criteria, _state);
    state->eps = critgetepswithdefault(criteria, 1.0E-7, _state);
    state->repterminationtype = 0;
    state->repiterationscount = 0;
    rcopyallocv(n, x0, &state->xs, _state);
    state->dolaconictrace = ae_is_trace_enabled("GIPM2.LACONIC");
    state->dotrace = ae_is_trace_enabled("GIPM2")&&!state->dolaconictrace;
    ae_vector_set_length(&state->rstate.ia, 0+1, _state);
    ae_vector_set_length(&state->rstate.ra, 0+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Set constraints

INPUT PARAMETERS:
    State               -   instance initialized with one of the initialization
                            functions
    SparseA             -   sparse constraint matrix, CRS format
    CL, CU              -   lower and upper bounds for constraints
                            -INF <= CL[I] <= CU[I] <= +INF.
    M                   -   constraints count

  -- ALGLIB --
     Copyright 25.02.2025 by Bochkanov Sergey
*************************************************************************/
void nlpgipm2setlinearconstraints(nlpgipm2state* state,
     const sparsematrix* sparsea,
     /* Real    */ const ae_vector* cl,
     /* Real    */ const ae_vector* cu,
     /* Integer */ const ae_vector* lcsrcidx,
     ae_int_t m,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;


    n = state->n;
    ae_assert(m>=0, "NLPGIPM2SetLinearConstraints: M<0", _state);
    ae_assert(m==0||((sparsea->matrixtype==1&&sparsea->m==m)&&sparsea->n==n), "NLPGIPM2SetLinearConstraints: constraint matrix has incorrect size", _state);
    ae_assert(cl->cnt>=m, "NLPGIPM2SetLinearConstraints: CL is too short!", _state);
    ae_assert(cu->cnt>=m, "NLPGIPM2SetLinearConstraints: CU is too short!", _state);
    state->mlc = m;
    if( m>0 )
    {
        sparsecopytocrsbuf(sparsea, &state->sparselc, _state);
        rallocv(m, &state->lcbndl, _state);
        rallocv(m, &state->lcbndu, _state);
        for(i=0; i<=m-1; i++)
        {
            ae_assert(ae_isfinite(cl->ptr.p_double[i], _state)||ae_isneginf(cl->ptr.p_double[i], _state), "NLPGIPM2SetLinearConstraints: CL is not finite number or -INF", _state);
            ae_assert(ae_isfinite(cu->ptr.p_double[i], _state)||ae_isposinf(cu->ptr.p_double[i], _state), "NLPGIPM2SetLinearConstraints: CU is not finite number or +INF", _state);
            state->lcbndl.ptr.p_double[i] = cl->ptr.p_double[i];
            state->lcbndu.ptr.p_double[i] = cu->ptr.p_double[i];
        }
        icopyallocv(m, lcsrcidx, &state->lcsrcidx, _state);
        rsetallocv(n, 0.0, &state->tmp0, _state);
        scaleshiftsparselcinplace(&state->s, &state->tmp0, n, &state->sparselc, m, &state->lcbndl, &state->lcbndu, _state);
        normalizesparselcinplace(&state->sparselc, m, &state->lcbndl, &state->lcbndu, n, ae_true, &state->lcscales, ae_true, _state);
    }
}


/*************************************************************************
Set nonlinear constraints

INPUT PARAMETERS:
    State               -   instance initialized with one of the initialization
                            functions
    NL, NU              -   lower and upper bounds for constraints
                            -INF <= NL[I] <= NU[I] <= +INF.
    NNLC                -   constraints count

  -- ALGLIB --
     Copyright 25.02.2025 by Bochkanov Sergey
*************************************************************************/
void nlpgipm2setnonlinearconstraints(nlpgipm2state* state,
     /* Real    */ const ae_vector* nl,
     /* Real    */ const ae_vector* nu,
     ae_int_t nnlc,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(nnlc>=0, "NLPGIPM2SetNonlinearConstraints: NNLC<0", _state);
    ae_assert(nl->cnt>=nnlc, "NLPGIPM2SetNonlinearConstraints: CL is too short!", _state);
    ae_assert(nu->cnt>=nnlc, "NLPGIPM2SetNonlinearConstraints: CU is too short!", _state);
    state->mnlc = nnlc;
    if( nnlc>0 )
    {
        rallocv(nnlc, &state->rawnl, _state);
        rallocv(nnlc, &state->rawnu, _state);
        for(i=0; i<=nnlc-1; i++)
        {
            ae_assert(ae_isfinite(nl->ptr.p_double[i], _state)||ae_isneginf(nl->ptr.p_double[i], _state), "NLPGIPM2SetNonlinearConstraints: NL is not finite number or -INF", _state);
            ae_assert(ae_isfinite(nu->ptr.p_double[i], _state)||ae_isposinf(nu->ptr.p_double[i], _state), "NLPGIPM2SetNonlinearConstraints: NU is not finite number or +INF", _state);
            state->rawnl.ptr.p_double[i] = nl->ptr.p_double[i];
            state->rawnu.ptr.p_double[i] = nu->ptr.p_double[i];
        }
    }
}


/*************************************************************************
This function performs actual processing for GIPM2 algorithm.

  -- ALGLIB --
     Copyright 01.02.2025 by Bochkanov Sergey
*************************************************************************/
ae_bool nlpgipm2iteration(nlpgipm2state* state,
     smoothnessmonitor* smonitor,
     ae_bool userterminationneeded,
     ae_state *_state)
{
    ae_int_t i;
    double v;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        i = state->rstate.ia.ptr.p_int[0];
        v = state->rstate.ra.ptr.p_double[0];
    }
    else
    {
        i = 359;
        v = -58.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    
    /*
     * Routine body
     */
    state->needfisj = ae_false;
    state->xupdated = ae_false;
    rallocv(state->n, &state->x, _state);
    ae_assert(state->mxx==0, "GIPM2: integrity check 377132 failed", _state);
    rallocv(1+state->mnlc+state->mlc, &state->replyfi, _state);
    state->haslast = ae_false;
    state->hasbase = ae_false;
    state->hasprevbase = ae_false;
    state->hasbest = ae_false;
    state->hasbaseh = ae_false;
    state->factorized = ae_false;
    state->hassparsityanalyzed = ae_false;
    stimerinit(&state->timertotal, _state);
    stimerinit(&state->timerfactorize, _state);
    stimerinit(&state->timersolve, _state);
    stimerinit(&state->timercallback, _state);
    stimerinit(&state->timerreport, _state);
    stimerinit(&state->timeranalyze, _state);
    stimerinit(&state->timermv, _state);
    stimerinit(&state->timerrescale, _state);
    stimerstart(&state->timertotal, _state);
    nlcgipm2_autoscaleinit(state, _state);
    gipm2initbuf(&state->scaledx0, state->n, state->eps, state->maxits, &state->solver, _state);
    gipm2setprofilequasinewton(&state->solver, ae_false, _state);
    if( state->hasbc )
    {
        gipm2setsoftbc(&state->solver, &state->scaledbndl, &state->isfixed, &state->scaledbndu, &state->isfixed, _state);
    }
    ae_assert(state->mxx==0, "GIPM2: integrity check 342033 failed", _state);
    if( state->mnlc+state->mlc>0 )
    {
        rallocv(state->mnlc+state->mlc, &state->wrkccbndl, _state);
        rallocv(state->mnlc+state->mlc, &state->wrkccbndu, _state);
        rcopyvx(state->mnlc, &state->rawnl, 0, &state->wrkccbndl, 0, _state);
        rcopyvx(state->mnlc, &state->rawnu, 0, &state->wrkccbndu, 0, _state);
        rcopyvx(state->mlc, &state->lcbndl, 0, &state->wrkccbndl, state->mnlc, _state);
        rcopyvx(state->mlc, &state->lcbndu, 0, &state->wrkccbndu, state->mnlc, _state);
        gipm2setflexnlc(&state->solver, &state->wrkccbndl, &state->wrkccbndu, state->mnlc+state->mlc, _state);
    }
    ae_assert(!state->isdense, "NLCGIPM2: 455319", _state);
    state->hessmemlen = ae_minint(state->n, nlcgipm2_xbfgsmemlen, _state);
    hessianinitlowranksr1(&state->hess, state->n, state->hessmemlen, ae_maxreal(state->eps, ae_sqrt(ae_machineepsilon, _state), _state), nlcgipm2_xbfgsmaxhess, _state);
    state->hess.mincrv = 0.0;
    if( state->dotrace )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("//  NONLINEAR GIPM2 SOLVER STARTED                                                                //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("N             = %6d\n",
            (int)(state->n));
        ae_trace("MLC           = %6d\n",
            (int)(state->mlc));
        ae_trace("MNLC          = %6d\n",
            (int)(state->mnlc));
    }
lbl_4:
    if( !gipm2iteration(&state->solver, smonitor, userterminationneeded, _state) )
    {
        goto lbl_5;
    }
    if( state->solver.requesttype==-1 )
    {
        goto lbl_4;
    }
    if( state->solver.requesttype!=1 )
    {
        goto lbl_6;
    }
    ae_assert(state->mxx==0, "GIPM2: integrity check 395132 failed", _state);
    rcopyv(state->n, &state->solver.querydata, &state->x, _state);
    rmergemaxv(state->n, &state->finitescaledbndl, &state->x, _state);
    rmergeminv(state->n, &state->finitescaledbndu, &state->x, _state);
    rcopyallocv(state->n, &state->x, &state->xproj, _state);
    state->needfisj = ae_true;
    if( state->dotrace )
    {
        stimerstart(&state->timercallback, _state);
    }
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    if( state->dotrace )
    {
        stimerstop(&state->timercallback, _state);
    }
    state->needfisj = ae_false;
    if( state->mlc>0 )
    {
        sparsegemv(&state->sparselc, 1.0, 0, &state->xproj, 0, 0.0, &state->replyfi, 1+state->mnlc+state->mxx, _state);
        sparseappendmatrix(&state->replysj, &state->sparselc, _state);
    }
    v = state->replyfi.ptr.p_double[0];
    ae_assert(state->mxx==0, "GIPM2: integrity check 395133 failed", _state);
    rmergemulv(1+state->mnlc+state->mxx+state->mlc, &state->invfscales, &state->replyfi, _state);
    sparsemultiplyrowscolsby(&state->replysj, &state->invfscales, &state->nonfixedmask, _state);
    rcopyv(1+state->mnlc+state->mxx+state->mlc, &state->replyfi, &state->solver.replyfi, _state);
    sparsecopybuf(&state->replysj, &state->solver.replysj, _state);
    nlcgipm2_savepointaslast(state, v, _state);
    goto lbl_4;
lbl_6:
    if( state->solver.requesttype==999 )
    {
        hessianmultiplyby(&state->hess, state->solver.querydata.ptr.p_double[0], _state);
        goto lbl_4;
    }
    if( state->solver.requesttype!=1000 )
    {
        goto lbl_8;
    }
    ae_assert(state->mxx==0, "GIPM2: integrity check 480643 failed", _state);
    nlcgipm2_lastpointasbase(state, &state->solver.querydata, _state);
    ae_assert(state->mxx==0, "NLCGIPM2: integrity check 395132 failed", _state);
    rcopyv(state->n, &state->basex, &state->x, _state);
    rmergemaxv(state->n, &state->finitescaledbndl, &state->x, _state);
    rmergeminv(state->n, &state->finitescaledbndu, &state->x, _state);
    state->reportf = state->baserawf0;
    state->xupdated = ae_true;
    if( state->dotrace )
    {
        stimerstart(&state->timerreport, _state);
    }
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    if( state->dotrace )
    {
        stimerstop(&state->timerreport, _state);
    }
    state->xupdated = ae_false;
    goto lbl_4;
    goto lbl_4;
lbl_8:
    if( state->solver.requesttype==1001 )
    {
        if( state->dotrace )
        {
            stimerstart(&state->timeranalyze, _state);
        }
        nlcgipm2_generatesparsesys(state, &state->basesj, _state);
        if( state->dotrace )
        {
            stimerstop(&state->timeranalyze, _state);
        }
        if( state->dotrace )
        {
            stimerstart(&state->timerfactorize, _state);
        }
        nlcgipm2_factorizematrix(state, _state);
        if( state->dotrace )
        {
            stimerstop(&state->timerfactorize, _state);
        }
        goto lbl_4;
    }
    if( state->solver.requesttype==1002 )
    {
        if( state->dotrace )
        {
            stimerstart(&state->timersolve, _state);
        }
        nlcgipm2_solvesystem(state, _state);
        if( state->dotrace )
        {
            stimerstop(&state->timersolve, _state);
        }
        goto lbl_4;
    }
    if( state->solver.requesttype==1003 )
    {
        if( state->dotrace )
        {
            stimerstart(&state->timermv, _state);
        }
        nlcgipm2_matrixvector(state, _state);
        if( state->dotrace )
        {
            stimerstop(&state->timermv, _state);
        }
        goto lbl_4;
    }
    if( state->solver.requesttype==1004 )
    {
        if( state->dotrace )
        {
            stimerstart(&state->timerrescale, _state);
        }
        nlcgipm2_autoscaletargetconstraints(state, _state);
        if( state->dotrace )
        {
            stimerstop(&state->timerrescale, _state);
        }
        goto lbl_4;
    }
    if( state->solver.requesttype==1005 )
    {
        rcopyv(1+state->mnlc+state->mxx+state->mlc, &state->fscales, &state->solver.replyv, _state);
        goto lbl_4;
    }
    if( state->solver.requesttype==1006 )
    {
        nlcgipm2_basepointasbest(state, _state);
        goto lbl_4;
    }
    if( state->solver.requesttype!=1007 )
    {
        goto lbl_10;
    }
    nlcgipm2_restorebestpoint(state, _state);
    ae_assert(state->mxx==0, "NLCGIPM2: integrity check 395132 failed", _state);
    rcopyv(state->n, &state->basex, &state->x, _state);
    rmergemaxv(state->n, &state->finitescaledbndl, &state->x, _state);
    rmergeminv(state->n, &state->finitescaledbndu, &state->x, _state);
    state->reportf = state->baserawf0;
    state->xupdated = ae_true;
    if( state->dotrace )
    {
        stimerstart(&state->timerreport, _state);
    }
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    if( state->dotrace )
    {
        stimerstop(&state->timerreport, _state);
    }
    state->xupdated = ae_false;
    goto lbl_4;
lbl_10:
    if( state->solver.requesttype==1008 )
    {
        ae_assert(state->mxx==0, "GIPM2: integrity check 760054 failed", _state);
        if( state->dotrace )
        {
            ae_trace("> updating Hessian model\n");
        }
        rallocv(state->n, &state->tmp0, _state);
        rallocv(state->n, &state->tmp1, _state);
        rallocv(state->n, &state->tmp2, _state);
        rallocv(state->n, &state->tmp3, _state);
        rcopyvx(state->n, &state->solver.querydata, 0*state->n, &state->tmp0, 0, _state);
        rcopyvx(state->n, &state->solver.querydata, 1*state->n, &state->tmp1, 0, _state);
        rcopyvx(state->n, &state->solver.querydata, 2*state->n, &state->tmp2, 0, _state);
        rcopyvx(state->n, &state->solver.querydata, 3*state->n, &state->tmp3, 0, _state);
        hessianupdatev2(&state->hess, &state->tmp0, &state->tmp2, &state->tmp1, &state->tmp3, 2, ae_false, state->dotrace, 2, _state);
        goto lbl_4;
    }
    ae_assert(ae_false, "NLPGIPM2: request not implemented", _state);
    goto lbl_4;
lbl_5:
    ae_assert(state->mxx==0, "GIPM2: integrity check 430320 failed", _state);
    gipm2results(&state->solver, &state->xs, &state->replagbc, &state->tmp0, &state->repterminationtype, _state);
    ae_assert(state->mxx==0, "GIPM2: integrity check 395132 failed", _state);
    rcopyv(state->n, &state->xs, &state->x, _state);
    rmergemaxv(state->n, &state->finitescaledbndl, &state->x, _state);
    rmergeminv(state->n, &state->finitescaledbndu, &state->x, _state);
    rcopyallocv(state->n, &state->x, &state->xproj, _state);
    state->needfisj = ae_true;
    if( state->dotrace )
    {
        stimerstart(&state->timercallback, _state);
    }
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    if( state->dotrace )
    {
        stimerstop(&state->timercallback, _state);
    }
    state->needfisj = ae_false;
    if( state->mlc>0 )
    {
        sparsegemv(&state->sparselc, 1.0, 0, &state->xproj, 0, 0.0, &state->replyfi, 1+state->mnlc+state->mxx, _state);
        sparseappendmatrix(&state->replysj, &state->sparselc, _state);
    }
    ae_assert(state->mxx==0, "GIPM2: integrity check 395133 failed", _state);
    rmergemulv(1+state->mnlc+state->mxx+state->mlc, &state->invfscales, &state->replyfi, _state);
    sparsemultiplyrowsby(&state->replysj, &state->invfscales, _state);
    if( state->hasfixed )
    {
        rallocv(1+state->mnlc+state->mxx+state->mlc, &state->tmp1, _state);
        state->tmp1.ptr.p_double[0] = (double)(1);
        rcopyvx(state->mnlc+state->mxx+state->mlc, &state->tmp0, 0, &state->tmp1, 1, _state);
        rmulvx(state->mnlc+state->mxx+state->mlc, -1.0, &state->tmp1, 1, _state);
        ae_assert(state->mxx==0, "GIPM2: integrity check 804136 failed", _state);
        rcopyallocv(state->n, &state->replagbc, &state->tmp2, _state);
        sparsegemv(&state->replysj, 1.0, 1, &state->tmp1, 0, 1.0, &state->tmp2, 0, _state);
        for(i=0; i<=state->n-1; i++)
        {
            if( state->isfixed.ptr.p_bool[i] )
            {
                state->replagbc.ptr.p_double[i] = state->replagbc.ptr.p_double[i]-state->tmp2.ptr.p_double[i];
            }
        }
    }
    rmulv(state->n, state->fscales.ptr.p_double[0], &state->replagbc, _state);
    rmergedivv(state->n, &state->s, &state->replagbc, _state);
    rallocv(state->mlc, &state->replaglc, _state);
    for(i=0; i<=state->mlc-1; i++)
    {
        state->replaglc.ptr.p_double[i] = state->tmp0.ptr.p_double[state->mnlc+state->mxx+i]*(-state->fscales.ptr.p_double[0])/(state->fscales.ptr.p_double[1+state->mnlc+state->mxx+i]*state->lcscales.ptr.p_double[i]);
    }
    rallocv(state->mnlc, &state->replagnlc, _state);
    for(i=0; i<=state->mnlc-1; i++)
    {
        state->replagnlc.ptr.p_double[i] = state->tmp0.ptr.p_double[state->mxx+i]*(-state->fscales.ptr.p_double[0])/state->fscales.ptr.p_double[1+state->mxx+i];
    }
    state->repbcerr = state->solver.repbcerr;
    state->repbcidx = state->solver.repbcidx;
    state->repsclerr = state->solver.repsclerr;
    checklc2violation(&state->sparselc, &state->lcbndl, &state->lcbndu, &state->lcsrcidx, state->mlc, &state->xs, &state->replcerr, &state->replcidx, _state);
    unscaleandchecknlc2violation(&state->replyfi, &state->fscales, &state->rawnl, &state->rawnu, state->mnlc, &state->repnlcerr, &state->repnlcidx, _state);
    state->repiterationscount = state->solver.repiterationscount;
    stimerstop(&state->timertotal, _state);
    if( state->solver.dotrace )
    {
        ae_trace("total time:     %10.1f ms\n",
            (double)(stimergetms(&state->timertotal, _state)));
        ae_trace("* callback:     %10.1f ms\n",
            (double)(stimergetms(&state->timercallback, _state)));
        ae_trace("* report:       %10.1f ms\n",
            (double)(stimergetms(&state->timerreport, _state)));
        ae_trace("* analyze:      %10.1f ms\n",
            (double)(stimergetms(&state->timeranalyze, _state)));
        ae_trace("* factorize:    %10.1f ms\n",
            (double)(stimergetms(&state->timerfactorize, _state)));
        ae_trace("* solve:        %10.1f ms\n",
            (double)(stimergetms(&state->timersolve, _state)));
        ae_trace("* gemv:         %10.1f ms\n",
            (double)(stimergetms(&state->timermv, _state)));
        ae_trace("* rescale:      %10.1f ms\n",
            (double)(stimergetms(&state->timerrescale, _state)));
    }
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = i;
    state->rstate.ra.ptr.p_double[0] = v;
    return result;
}


/*************************************************************************
Handle compute-and-remember request

  -- ALGLIB --
     Copyright 07.02.2025 by Bochkanov Sergey
*************************************************************************/
static void nlcgipm2_savepointaslast(nlpgipm2state* state,
     double rawf0,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(state->mxx==0, "GIPM2: integrity check 403144 failed", _state);
    rcopyallocv(n, &state->solver.querydata, &state->laztx, _state);
    rcopyallocv(1+state->mnlc+state->mlc, &state->replyfi, &state->laztfi, _state);
    sparsecopybuf(&state->replysj, &state->laztsj, _state);
    state->laztrawf0 = rawf0;
    state->haslast = ae_true;
}


/*************************************************************************
Handle compute-and-remember request

  -- ALGLIB --
     Copyright 07.02.2025 by Bochkanov Sergey
*************************************************************************/
static void nlcgipm2_lastpointasbase(nlpgipm2state* state,
     /* Real    */ const ae_vector* querydata,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(state->haslast, "NLPGIPM2: integrity check 468318 failed", _state);
    ae_assert(state->mxx==0, "GIPM2: integrity check 403144 failed", _state);
    if( state->hasbase )
    {
        rcopyallocv(n, &state->basex, &state->prevbasex, _state);
        rcopyallocv(1+state->mnlc+state->mlc, &state->basefi, &state->prevbasefi, _state);
        sparsecopybuf(&state->basesj, &state->prevbasesj, _state);
        state->prevbaserawf0 = state->baserawf0;
        state->hasprevbase = ae_true;
    }
    rcopyallocv(n, &state->laztx, &state->basex, _state);
    rcopyallocv(1+state->mnlc+state->mlc, &state->laztfi, &state->basefi, _state);
    sparsecopybuf(&state->laztsj, &state->basesj, _state);
    state->baserawf0 = state->laztrawf0;
    state->hasbase = ae_true;
    state->hasbaseh = ae_false;
}


/*************************************************************************
Save base point as best

  -- ALGLIB --
     Copyright 07.02.2025 by Bochkanov Sergey
*************************************************************************/
static void nlcgipm2_basepointasbest(nlpgipm2state* state,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(state->hasbase, "NLPGIPM2: integrity check 141338 failed", _state);
    ae_assert(state->mxx==0, "GIPM2: integrity check 142338 failed", _state);
    rcopyallocv(n, &state->basex, &state->bestx, _state);
    rcopyallocv(1+state->mnlc+state->mlc, &state->basefi, &state->bestfi, _state);
    sparsecopybuf(&state->basesj, &state->bestsj, _state);
    state->bestrawf0 = state->baserawf0;
    state->hasbest = ae_true;
}


/*************************************************************************

  -- ALGLIB --
     Copyright 07.02.2025 by Bochkanov Sergey
*************************************************************************/
static void nlcgipm2_restorebestpoint(nlpgipm2state* state,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    ae_assert(state->hasbest, "NLPGIPM2: integrity check 141338 failed", _state);
    ae_assert(state->mxx==0, "GIPM2: integrity check 142338 failed", _state);
    rcopyallocv(n, &state->bestx, &state->basex, _state);
    rcopyallocv(1+state->mnlc+state->mlc, &state->bestfi, &state->basefi, _state);
    sparsecopybuf(&state->bestsj, &state->basesj, _state);
    state->baserawf0 = state->bestrawf0;
    state->hasbase = ae_true;
}


/*************************************************************************
Handle factorization request

  -- ALGLIB --
     Copyright 07.02.2025 by Bochkanov Sergey
*************************************************************************/
static void nlcgipm2_factorizematrix(nlpgipm2state* state,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t offs;
    ae_int_t mtotal;
    ae_int_t jj;
    ae_int_t j0;
    ae_int_t j1;


    ae_assert(state->hasbase, "NLPGIPM2: integrity check 453018 failed (no base point)", _state);
    n = state->n;
    mtotal = state->mnlc+state->mxx+state->mlc;
    ae_assert(!state->isdense, "NLPGIPM2: integrity check 017312 failed", _state);
    hessiangetlowrankstabilized(&state->hess, &state->basehsr1diag, &state->basehsr1corrc, &state->basehsr1corrd, &state->basehrank, _state);
    state->hasbaseh = ae_true;
    ae_assert(!state->isdense, "NLPGIPM2: integrity check 017313 failed", _state);
    ae_assert(state->hassparsityanalyzed, "NLPGIPM2: integrity check 018312 failed (sparsity pattern was not analyzed)", _state);
    rsetv(state->sparsesys.ridx.ptr.p_int[n+mtotal+state->hessmemlen], 0.0, &state->sparsesys.vals, _state);
    for(i=0; i<=n-1; i++)
    {
        state->sparsesys.vals.ptr.p_double[i] = state->basehsr1diag.ptr.p_double[i]+state->solver.querydata.ptr.p_double[i];
    }
    for(i=0; i<=mtotal-1; i++)
    {
        offs = state->sparsesys.ridx.ptr.p_int[n+i];
        j0 = state->lastanalyzedjac.ridx.ptr.p_int[1+i];
        j1 = state->lastanalyzedjac.ridx.ptr.p_int[1+i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            state->sparsesys.vals.ptr.p_double[offs] = state->lastanalyzedjac.vals.ptr.p_double[jj];
            offs = offs+1;
        }
        state->sparsesys.vals.ptr.p_double[offs] = state->solver.querydata.ptr.p_double[n+i];
        offs = offs+1;
        ae_assert(offs==state->sparsesys.ridx.ptr.p_int[n+i+1], "GIPM2: integrity check 066230 failed, LastAnalyzedJac sparsity pattern is inconsistent with the what was analyzed", _state);
    }
    for(i=0; i<=state->basehrank-1; i++)
    {
        ae_assert(state->sparsesys.uidx.ptr.p_int[n+mtotal+i]-state->sparsesys.ridx.ptr.p_int[n+mtotal+i]==n+1, "NLCGIPM2: 049020", _state);
        offs = state->sparsesys.ridx.ptr.p_int[n+mtotal+i];
        for(j=0; j<=n-1; j++)
        {
            state->sparsesys.vals.ptr.p_double[offs+j] = state->basehsr1corrc.ptr.pp_double[i][j];
        }
        state->sparsesys.vals.ptr.p_double[offs+n] = -state->basehsr1corrd.ptr.p_double[i];
    }
    for(i=state->basehrank; i<=state->hessmemlen-1; i++)
    {
        ae_assert(state->sparsesys.didx.ptr.p_int[n+mtotal+i]<state->sparsesys.uidx.ptr.p_int[n+mtotal+i], "NLCGIPM2: 065212", _state);
        state->sparsesys.vals.ptr.p_double[state->sparsesys.didx.ptr.p_int[n+mtotal+i]] = 1.0;
    }
    ballocv(n+mtotal+state->hessmemlen, &state->tmpb, _state);
    for(i=0; i<=n-1; i++)
    {
        state->tmpb.ptr.p_bool[i] = ae_true;
    }
    for(i=n; i<=mtotal+n-1; i++)
    {
        state->tmpb.ptr.p_bool[i] = ae_false;
    }
    for(i=mtotal+n; i<=state->hessmemlen+mtotal+n-1; i++)
    {
        state->tmpb.ptr.p_bool[i] = ae_fp_greater_eq(state->sparsesys.vals.ptr.p_double[state->sparsesys.didx.ptr.p_int[i]],0.0);
    }
    spsymmcontrolsign(&state->analysis, &state->tmpb, 0.0, _state);
    spsymmreload(&state->analysis, &state->sparsesys, _state);
    state->factorized = spsymmfactorize(&state->analysis, _state);
    state->solver.factsuccess = state->factorized;
    if( !state->factorized )
    {
        return;
    }
    if( state->solver.dotrace )
    {
        ae_trace("> Hessian factorized, |stabilized.diagonal|=%0.2e\n",
            (double)(rmaxabsv(n, &state->basehsr1diag, _state)));
    }
    state->solver.factsuccess = state->factorized;
}


/*************************************************************************
Handle solve request

  -- ALGLIB --
     Copyright 25.02.2025 by Bochkanov Sergey
*************************************************************************/
static void nlcgipm2_solvesystem(nlpgipm2state* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t mtotal;


    n = state->n;
    mtotal = state->mnlc+state->mxx+state->mlc;
    ae_assert(state->factorized, "NLPGIPM2: integrity check 577120 failed", _state);
    rsetallocv(n+mtotal+state->hessmemlen, 0.0, &state->tmp0, _state);
    rcopyv(n+mtotal, &state->solver.querydata, &state->tmp0, _state);
    spsymmsolve(&state->analysis, &state->tmp0, _state);
    rcopyv(n+mtotal, &state->tmp0, &state->solver.replysol, _state);
}


/*************************************************************************
Handle matrix-vector product request

  -- ALGLIB --
     Copyright 25.02.2025 by Bochkanov Sergey
*************************************************************************/
static void nlcgipm2_matrixvector(nlpgipm2state* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t mtotal;


    n = state->n;
    mtotal = state->mnlc+state->mxx+state->mlc;
    ae_assert(state->hasbase&&state->hasbaseh, "NLPGIPM2: integrity check 4322 failed", _state);
    ae_assert(!state->isdense, "NLCGIPM2: integrity check 217017 failed", _state);
    rallocv(ae_maxint(n, mtotal+1, _state), &state->tmp0, _state);
    rallocv(ae_maxint(n, mtotal+1, _state), &state->tmp1, _state);
    rallocv(ae_maxint(n, mtotal+1, _state), &state->tmp2, _state);
    rcopyvx(n, &state->solver.querydata, 0, &state->tmp0, 0, _state);
    rcopyv(n, &state->tmp0, &state->tmp1, _state);
    rmergemulv(n, &state->basehsr1diag, &state->tmp1, _state);
    if( state->basehrank>0 )
    {
        ae_assert(state->basehrank<=n, "NLCGIPM2: integrity check 235014 failed", _state);
        rgemv(state->basehrank, n, 1.0, &state->basehsr1corrc, 0, &state->tmp0, 0.0, &state->tmp2, _state);
        rmergemulv(state->basehrank, &state->basehsr1corrd, &state->tmp2, _state);
        rgemv(n, state->basehrank, 1.0, &state->basehsr1corrc, 1, &state->tmp2, 1.0, &state->tmp1, _state);
    }
    rcopyvx(n, &state->tmp1, 0, &state->solver.replyprod, 0, _state);
    rsetv(1+mtotal, 0.0, &state->tmp1, _state);
    ae_assert(state->mxx==0, "NLPGIPM2: integrity check 272335 failed", _state);
    sparsegemv(&state->basesj, 1.0, 0, &state->solver.querydata, 0, 0.0, &state->tmp1, 0, _state);
    rcopyvx(mtotal, &state->tmp1, 1, &state->solver.replyprod, n, _state);
    rsetv(n, 0.0, &state->tmp1, _state);
    ae_assert(state->mxx==0, "NLPGIPM2: integrity check 277336 failed", _state);
    state->tmp2.ptr.p_double[0] = (double)(0);
    rcopyvx(mtotal, &state->solver.querydata, n, &state->tmp2, 1, _state);
    sparsegemv(&state->basesj, 1.0, 1, &state->tmp2, 0, 1.0, &state->tmp1, 0, _state);
    rcopyvx(n, &state->tmp1, 0, &state->solver.replyprod, n+mtotal, _state);
}


/*************************************************************************
Initialize autoscaler state

  -- ALGLIB --
     Copyright 23.02.2025 by Bochkanov Sergey
*************************************************************************/
static void nlcgipm2_autoscaleinit(nlpgipm2state* state, ae_state *_state)
{
    ae_int_t mtotal;


    mtotal = state->mnlc+state->mxx+state->mlc;
    rsetallocv(1+mtotal, 1.0, &state->fscales, _state);
    rsetallocv(1+mtotal, 1.0, &state->invfscales, _state);
    rsetallocv(1+mtotal, 0.0, &state->autoscalerhessnorms, _state);
    state->firstscaling = ae_true;
    state->fbigscale = 5.0;
    state->fsmallscale = 0.1;
}


/*************************************************************************
Rescale target/constraints if gradients deviate from 1.0 too much.

Sets State.Solver.ReplyWasRescale to False or True; if the latter, also
sets State.Solver.ReplyV to multipliers applied to objective/constraints.

  -- ALGLIB --
     Copyright 23.02.2025 by Bochkanov Sergey
*************************************************************************/
static void nlcgipm2_autoscaletargetconstraints(nlpgipm2state* state,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t mtotal;
    ae_int_t i;
    ae_int_t k;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t jj;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t kk;
    double ss;
    double sy;
    double nrm;
    double gnrm2;
    double v;
    double multiplyby;
    double setscaleto;
    double minss;
    ae_bool bigscaletriggered;
    ae_bool smallscaletriggered;


    ae_assert(state->hasbase, "NLPGIPM2: autoscale request was issued before base point was marked", _state);
    n = state->n;
    mtotal = state->mnlc+state->mxx+state->mlc;
    minss = ae_sqr(1.0E-4, _state);
    ss = (double)(0);
    if( state->hasprevbase )
    {
        for(i=0; i<=n-1; i++)
        {
            ss = ss+ae_sqr(state->basex.ptr.p_double[i]-state->prevbasex.ptr.p_double[i], _state);
        }
    }
    state->solver.replywasrescale = ae_false;
    bigscaletriggered = ae_false;
    smallscaletriggered = ae_false;
    for(i=0; i<=mtotal; i++)
    {
        j0 = state->basesj.ridx.ptr.p_int[i];
        j1 = state->basesj.ridx.ptr.p_int[i+1]-1;
        gnrm2 = (double)(0);
        for(jj=j0; jj<=j1; jj++)
        {
            v = state->basesj.vals.ptr.p_double[jj];
            gnrm2 = gnrm2+v*v;
        }
        gnrm2 = ae_sqrt(gnrm2, _state);
        nrm = gnrm2;
        if( state->hasprevbase&&ae_fp_greater_eq(ss,minss) )
        {
            k0 = state->prevbasesj.ridx.ptr.p_int[i];
            k1 = state->prevbasesj.ridx.ptr.p_int[i+1]-1;
            jj = j0;
            kk = k0;
            sy = (double)(0);
            while(jj<=j1&&kk<=k1)
            {
                if( state->basesj.idx.ptr.p_int[jj]==state->prevbasesj.idx.ptr.p_int[kk] )
                {
                    k = state->basesj.idx.ptr.p_int[jj];
                    sy = sy+(state->basex.ptr.p_double[k]-state->prevbasex.ptr.p_double[k])*(state->basesj.vals.ptr.p_double[jj]-state->prevbasesj.vals.ptr.p_double[kk]);
                    jj = jj+1;
                    kk = kk+1;
                    continue;
                }
                if( state->basesj.idx.ptr.p_int[jj]<state->prevbasesj.idx.ptr.p_int[kk] )
                {
                    k = state->basesj.idx.ptr.p_int[jj];
                    sy = sy+(state->basex.ptr.p_double[k]-state->prevbasex.ptr.p_double[k])*(state->basesj.vals.ptr.p_double[jj]-(double)0);
                    jj = jj+1;
                }
                else
                {
                    k = state->prevbasesj.idx.ptr.p_int[kk];
                    sy = sy+(state->basex.ptr.p_double[k]-state->prevbasex.ptr.p_double[k])*((double)0-state->prevbasesj.vals.ptr.p_double[kk]);
                    kk = kk+1;
                }
            }
            while(jj<=j1)
            {
                k = state->basesj.idx.ptr.p_int[jj];
                sy = sy+(state->basex.ptr.p_double[k]-state->prevbasex.ptr.p_double[k])*(state->basesj.vals.ptr.p_double[jj]-(double)0);
                jj = jj+1;
            }
            while(kk<=k1)
            {
                k = state->prevbasesj.idx.ptr.p_int[kk];
                sy = sy+(state->basex.ptr.p_double[k]-state->prevbasex.ptr.p_double[k])*((double)0-state->prevbasesj.vals.ptr.p_double[kk]);
                kk = kk+1;
            }
            state->autoscalerhessnorms.ptr.p_double[i] = nlcgipm2_autoscalerhessdecay*state->autoscalerhessnorms.ptr.p_double[i]+((double)1-nlcgipm2_autoscalerhessdecay)*ae_fabs(sy/ss, _state);
        }
        nrm = ae_maxreal(nrm, state->autoscalerhessnorms.ptr.p_double[i]/nlcgipm2_maxhessnrm, _state);
        multiplyby = 1.0;
        setscaleto = state->fscales.ptr.p_double[i];
        if( ae_fp_greater_eq(nrm,state->fbigscale) )
        {
            v = nrm;
            if( !state->firstscaling )
            {
                v = ae_minreal(v, nlcgipm2_maxrescale, _state);
            }
            multiplyby = (double)1/v;
            setscaleto = state->fscales.ptr.p_double[i]*v;
            bigscaletriggered = ae_true;
        }
        if( ae_fp_less_eq(nrm,state->fsmallscale)&&ae_fp_greater(state->fscales.ptr.p_double[i],1.0) )
        {
            v = nrm;
            if( !state->firstscaling )
            {
                v = ae_maxreal(v, nlcgipm2_minrescale, _state);
            }
            if( ae_fp_greater(state->fscales.ptr.p_double[i]*v,(double)(1)) )
            {
                multiplyby = (double)1/v;
                setscaleto = state->fscales.ptr.p_double[i]*v;
            }
            else
            {
                multiplyby = state->fscales.ptr.p_double[i];
                setscaleto = 1.0;
            }
            smallscaletriggered = ae_true;
        }
        state->solver.replyv.ptr.p_double[i] = multiplyby;
        if( ae_fp_neq(multiplyby,1.0) )
        {
            state->fscales.ptr.p_double[i] = setscaleto;
            state->invfscales.ptr.p_double[i] = (double)1/setscaleto;
            state->autoscalerhessnorms.ptr.p_double[i] = state->autoscalerhessnorms.ptr.p_double[i]*multiplyby;
            if( state->haslast )
            {
                nlcgipm2_applyrescale(&state->laztfi, &state->laztsj, i, multiplyby, _state);
            }
            if( state->hasbase )
            {
                nlcgipm2_applyrescale(&state->basefi, &state->basesj, i, multiplyby, _state);
            }
            if( state->hasprevbase )
            {
                nlcgipm2_applyrescale(&state->prevbasefi, &state->prevbasesj, i, multiplyby, _state);
            }
            if( i>=1 )
            {
                ae_assert(state->mxx==0, "NLPGIPM2: 956327", _state);
                if( ae_isfinite(state->wrkccbndl.ptr.p_double[i-1], _state) )
                {
                    state->wrkccbndl.ptr.p_double[i-1] = state->wrkccbndl.ptr.p_double[i-1]*multiplyby;
                }
                if( ae_isfinite(state->wrkccbndu.ptr.p_double[i-1], _state) )
                {
                    state->wrkccbndu.ptr.p_double[i-1] = state->wrkccbndu.ptr.p_double[i-1]*multiplyby;
                }
            }
            state->solver.replywasrescale = ae_true;
        }
    }
    if( smallscaletriggered )
    {
        state->fsmallscale = state->fsmallscale*nlcgipm2_smallscaledecrease;
    }
    state->firstscaling = ae_false;
    touchboolean(&bigscaletriggered, _state);
}


/*************************************************************************
Rescale specific component of a function vector and a corresponding Jacobian
row.

  -- ALGLIB --
     Copyright 23.02.2025 by Bochkanov Sergey
*************************************************************************/
static void nlcgipm2_applyrescale(/* Real    */ ae_vector* fi,
     sparsematrix* sj,
     ae_int_t idx,
     double multiplyby,
     ae_state *_state)
{
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t jj;


    fi->ptr.p_double[idx] = fi->ptr.p_double[idx]*multiplyby;
    j0 = sj->ridx.ptr.p_int[idx];
    j1 = sj->ridx.ptr.p_int[idx+1]-1;
    for(jj=j0; jj<=j1; jj++)
    {
        sj->vals.ptr.p_double[jj] = sj->vals.ptr.p_double[jj]*multiplyby;
    }
}


/*************************************************************************
Analyze sparsity pattern of KKT system and generate SparseSys; recompute
if anything has changed.

  -- ALGLIB --
     Copyright 23.02.2025 by Bochkanov Sergey
*************************************************************************/
static void nlcgipm2_generatesparsesys(nlpgipm2state* state,
     const sparsematrix* upcomingsj,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t mtotal;
    ae_int_t i;
    ae_int_t j;
    ae_int_t offs;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t jj;
    ae_int_t facttype;


    n = state->n;
    mtotal = state->mnlc+state->mxx+state->mlc;
    ae_assert((sparseiscrs(upcomingsj, _state)&&upcomingsj->m==mtotal+1)&&upcomingsj->n==n, "NLCGIPM2: 484120", _state);
    if( state->hassparsityanalyzed&&sparsetrygatherclear(&state->lastanalyzedjac, upcomingsj, _state) )
    {
        return;
    }
    if( state->hassparsityanalyzed )
    {
        sparsemergepatterns(&state->lastanalyzedjac, upcomingsj, &state->tmpsparse, _state);
        sparsecopybuf(&state->tmpsparse, &state->lastanalyzedjac, _state);
        if( !sparsetrygatherclear(&state->lastanalyzedjac, upcomingsj, _state) )
        {
            ae_assert(ae_false, "NLCGIPM2: integrity check 765614 failed", _state);
        }
    }
    else
    {
        sparsecopybuf(upcomingsj, &state->lastanalyzedjac, _state);
    }
    state->sparsesys.m = n+mtotal+state->hessmemlen;
    state->sparsesys.n = n+mtotal+state->hessmemlen;
    iallocv(n+state->hessmemlen+mtotal+1, &state->sparsesys.ridx, _state);
    state->sparsesys.ridx.ptr.p_int[0] = 0;
    offs = 0;
    igrowv(offs+n, &state->sparsesys.idx, _state);
    rgrowv(offs+n, &state->sparsesys.vals, _state);
    for(i=0; i<=n-1; i++)
    {
        state->sparsesys.idx.ptr.p_int[offs] = i;
        state->sparsesys.vals.ptr.p_double[offs] = 0.0;
        offs = offs+1;
        state->sparsesys.ridx.ptr.p_int[i+1] = offs;
    }
    for(i=0; i<=mtotal-1; i++)
    {
        igrowv(offs+n+1, &state->sparsesys.idx, _state);
        rgrowv(offs+n+1, &state->sparsesys.vals, _state);
        j0 = state->lastanalyzedjac.ridx.ptr.p_int[1+i];
        j1 = state->lastanalyzedjac.ridx.ptr.p_int[1+i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            state->sparsesys.idx.ptr.p_int[offs] = state->lastanalyzedjac.idx.ptr.p_int[jj];
            state->sparsesys.vals.ptr.p_double[offs] = 1.0;
            offs = offs+1;
        }
        state->sparsesys.idx.ptr.p_int[offs] = n+i;
        state->sparsesys.vals.ptr.p_double[offs] = 1.0;
        offs = offs+1;
        state->sparsesys.ridx.ptr.p_int[n+i+1] = offs;
    }
    igrowv(offs+state->hessmemlen*(n+1), &state->sparsesys.idx, _state);
    rgrowv(offs+state->hessmemlen*(n+1), &state->sparsesys.vals, _state);
    for(i=0; i<=state->hessmemlen-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            state->sparsesys.idx.ptr.p_int[offs] = j;
            state->sparsesys.vals.ptr.p_double[offs] = 0.0;
            offs = offs+1;
        }
        state->sparsesys.idx.ptr.p_int[offs] = n+mtotal+i;
        state->sparsesys.vals.ptr.p_double[offs] = 0.0;
        offs = offs+1;
        state->sparsesys.ridx.ptr.p_int[n+mtotal+i+1] = offs;
    }
    sparsecreatecrsinplace(&state->sparsesys, _state);
    facttype = 21;
    iallocv(n+mtotal+state->hessmemlen, &state->priorities, _state);
    for(i=0; i<=n-1; i++)
    {
        state->priorities.ptr.p_int[i] = 0;
    }
    for(i=n; i<=mtotal+n-1; i++)
    {
        state->priorities.ptr.p_int[i] = 0;
    }
    for(i=mtotal+n; i<=state->hessmemlen+mtotal+n-1; i++)
    {
        state->priorities.ptr.p_int[i] = 2;
    }
    if( !spsymmanalyze(&state->sparsesys, &state->priorities, nlcgipm2_mupromote, 0, facttype, 3, 1, &state->analysis, _state) )
    {
        ae_assert(ae_false, "NLCGIPM2: critical integrity check failed, symbolically degenerate KKT system encountered", _state);
    }
    state->hassparsityanalyzed = ae_true;
}


void _nlpgipm2state_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    nlpgipm2state *p = (nlpgipm2state*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scaledx0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scaledbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scaledbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitescaledbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finitescaledbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finiterawbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->finiterawbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->isfixed, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->nonfixedmask, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyfi, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->replysj, _state, make_automatic);
    _xbfgshessian_init(&p->hess, _state, make_automatic);
    ae_vector_init(&p->laztx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->laztfi, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->laztsj, _state, make_automatic);
    ae_vector_init(&p->basex, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->basefi, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->basesj, _state, make_automatic);
    ae_vector_init(&p->prevbasex, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->prevbasefi, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->prevbasesj, _state, make_automatic);
    ae_vector_init(&p->bestx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bestfi, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->bestsj, _state, make_automatic);
    ae_vector_init(&p->basehsr1diag, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->basehsr1corrd, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->basehsr1corrc, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xproj, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->deltaproj, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->priorities, 0, DT_INT, _state, make_automatic);
    _spcholanalysis_init(&p->analysis, _state, make_automatic);
    _sparsematrix_init(&p->sparsesys, _state, make_automatic);
    _sparsematrix_init(&p->lastanalyzedjac, _state, make_automatic);
    ae_vector_init(&p->fscales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->invfscales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->autoscalerhessnorms, 0, DT_REAL, _state, make_automatic);
    _gipm2state_init(&p->solver, _state, make_automatic);
    _sparsematrix_init(&p->sparselc, _state, make_automatic);
    ae_vector_init(&p->lcbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lcbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lcsrcidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->lcscales, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawnl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawnu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->wrkccbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->wrkccbndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xs, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replagbc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replaglc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replagnlc, 0, DT_REAL, _state, make_automatic);
    _stimer_init(&p->timertotal, _state, make_automatic);
    _stimer_init(&p->timerfactorize, _state, make_automatic);
    _stimer_init(&p->timersolve, _state, make_automatic);
    _stimer_init(&p->timercallback, _state, make_automatic);
    _stimer_init(&p->timerreport, _state, make_automatic);
    _stimer_init(&p->timeranalyze, _state, make_automatic);
    _stimer_init(&p->timermv, _state, make_automatic);
    _stimer_init(&p->timerrescale, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp3, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpb, 0, DT_BOOL, _state, make_automatic);
    _sparsematrix_init(&p->tmpsparse, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
}


void _nlpgipm2state_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    nlpgipm2state       *dst = (nlpgipm2state*)_dst;
    const nlpgipm2state *src = (const nlpgipm2state*)_src;
    dst->isdense = src->isdense;
    dst->isconvex = src->isconvex;
    dst->n = src->n;
    dst->mlc = src->mlc;
    dst->mnlc = src->mnlc;
    dst->mxx = src->mxx;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledx0, &src->scaledx0, _state, make_automatic);
    dst->hasbc = src->hasbc;
    dst->hasfixed = src->hasfixed;
    ae_vector_init_copy(&dst->scaledbndl, &src->scaledbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledbndu, &src->scaledbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->finitescaledbndl, &src->finitescaledbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->finitescaledbndu, &src->finitescaledbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->finiterawbndl, &src->finiterawbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->finiterawbndu, &src->finiterawbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->isfixed, &src->isfixed, _state, make_automatic);
    ae_vector_init_copy(&dst->nonfixedmask, &src->nonfixedmask, _state, make_automatic);
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    ae_vector_init_copy(&dst->replyfi, &src->replyfi, _state, make_automatic);
    _sparsematrix_init_copy(&dst->replysj, &src->replysj, _state, make_automatic);
    dst->reportf = src->reportf;
    dst->needfisj = src->needfisj;
    dst->xupdated = src->xupdated;
    _xbfgshessian_init_copy(&dst->hess, &src->hess, _state, make_automatic);
    ae_vector_init_copy(&dst->laztx, &src->laztx, _state, make_automatic);
    ae_vector_init_copy(&dst->laztfi, &src->laztfi, _state, make_automatic);
    _sparsematrix_init_copy(&dst->laztsj, &src->laztsj, _state, make_automatic);
    dst->laztrawf0 = src->laztrawf0;
    ae_vector_init_copy(&dst->basex, &src->basex, _state, make_automatic);
    ae_vector_init_copy(&dst->basefi, &src->basefi, _state, make_automatic);
    _sparsematrix_init_copy(&dst->basesj, &src->basesj, _state, make_automatic);
    dst->baserawf0 = src->baserawf0;
    ae_vector_init_copy(&dst->prevbasex, &src->prevbasex, _state, make_automatic);
    ae_vector_init_copy(&dst->prevbasefi, &src->prevbasefi, _state, make_automatic);
    _sparsematrix_init_copy(&dst->prevbasesj, &src->prevbasesj, _state, make_automatic);
    dst->prevbaserawf0 = src->prevbaserawf0;
    ae_vector_init_copy(&dst->bestx, &src->bestx, _state, make_automatic);
    ae_vector_init_copy(&dst->bestfi, &src->bestfi, _state, make_automatic);
    _sparsematrix_init_copy(&dst->bestsj, &src->bestsj, _state, make_automatic);
    dst->bestrawf0 = src->bestrawf0;
    dst->hessmemlen = src->hessmemlen;
    ae_vector_init_copy(&dst->basehsr1diag, &src->basehsr1diag, _state, make_automatic);
    ae_vector_init_copy(&dst->basehsr1corrd, &src->basehsr1corrd, _state, make_automatic);
    ae_matrix_init_copy(&dst->basehsr1corrc, &src->basehsr1corrc, _state, make_automatic);
    dst->basehrank = src->basehrank;
    dst->hasbaseh = src->hasbaseh;
    dst->haslast = src->haslast;
    dst->hasbase = src->hasbase;
    dst->hasprevbase = src->hasprevbase;
    dst->hasbest = src->hasbest;
    ae_vector_init_copy(&dst->xproj, &src->xproj, _state, make_automatic);
    ae_vector_init_copy(&dst->deltaproj, &src->deltaproj, _state, make_automatic);
    ae_vector_init_copy(&dst->priorities, &src->priorities, _state, make_automatic);
    _spcholanalysis_init_copy(&dst->analysis, &src->analysis, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsesys, &src->sparsesys, _state, make_automatic);
    _sparsematrix_init_copy(&dst->lastanalyzedjac, &src->lastanalyzedjac, _state, make_automatic);
    dst->hassparsityanalyzed = src->hassparsityanalyzed;
    dst->factorized = src->factorized;
    ae_vector_init_copy(&dst->fscales, &src->fscales, _state, make_automatic);
    ae_vector_init_copy(&dst->invfscales, &src->invfscales, _state, make_automatic);
    dst->fbigscale = src->fbigscale;
    dst->fsmallscale = src->fsmallscale;
    dst->firstscaling = src->firstscaling;
    ae_vector_init_copy(&dst->autoscalerhessnorms, &src->autoscalerhessnorms, _state, make_automatic);
    _gipm2state_init_copy(&dst->solver, &src->solver, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparselc, &src->sparselc, _state, make_automatic);
    ae_vector_init_copy(&dst->lcbndl, &src->lcbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->lcbndu, &src->lcbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->lcsrcidx, &src->lcsrcidx, _state, make_automatic);
    ae_vector_init_copy(&dst->lcscales, &src->lcscales, _state, make_automatic);
    ae_vector_init_copy(&dst->rawnl, &src->rawnl, _state, make_automatic);
    ae_vector_init_copy(&dst->rawnu, &src->rawnu, _state, make_automatic);
    ae_vector_init_copy(&dst->wrkccbndl, &src->wrkccbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->wrkccbndu, &src->wrkccbndu, _state, make_automatic);
    dst->eps = src->eps;
    dst->maxits = src->maxits;
    dst->repiterationscount = src->repiterationscount;
    dst->repterminationtype = src->repterminationtype;
    dst->repbcerr = src->repbcerr;
    dst->repbcidx = src->repbcidx;
    dst->replcerr = src->replcerr;
    dst->replcidx = src->replcidx;
    dst->repnlcerr = src->repnlcerr;
    dst->repnlcidx = src->repnlcidx;
    dst->repsclerr = src->repsclerr;
    ae_vector_init_copy(&dst->xs, &src->xs, _state, make_automatic);
    ae_vector_init_copy(&dst->replagbc, &src->replagbc, _state, make_automatic);
    ae_vector_init_copy(&dst->replaglc, &src->replaglc, _state, make_automatic);
    ae_vector_init_copy(&dst->replagnlc, &src->replagnlc, _state, make_automatic);
    dst->dotrace = src->dotrace;
    dst->dolaconictrace = src->dolaconictrace;
    _stimer_init_copy(&dst->timertotal, &src->timertotal, _state, make_automatic);
    _stimer_init_copy(&dst->timerfactorize, &src->timerfactorize, _state, make_automatic);
    _stimer_init_copy(&dst->timersolve, &src->timersolve, _state, make_automatic);
    _stimer_init_copy(&dst->timercallback, &src->timercallback, _state, make_automatic);
    _stimer_init_copy(&dst->timerreport, &src->timerreport, _state, make_automatic);
    _stimer_init_copy(&dst->timeranalyze, &src->timeranalyze, _state, make_automatic);
    _stimer_init_copy(&dst->timermv, &src->timermv, _state, make_automatic);
    _stimer_init_copy(&dst->timerrescale, &src->timerrescale, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp2, &src->tmp2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp3, &src->tmp3, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpb, &src->tmpb, _state, make_automatic);
    _sparsematrix_init_copy(&dst->tmpsparse, &src->tmpsparse, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
}


void _nlpgipm2state_clear(void* _p)
{
    nlpgipm2state *p = (nlpgipm2state*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->scaledx0);
    ae_vector_clear(&p->scaledbndl);
    ae_vector_clear(&p->scaledbndu);
    ae_vector_clear(&p->finitescaledbndl);
    ae_vector_clear(&p->finitescaledbndu);
    ae_vector_clear(&p->finiterawbndl);
    ae_vector_clear(&p->finiterawbndu);
    ae_vector_clear(&p->isfixed);
    ae_vector_clear(&p->nonfixedmask);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->replyfi);
    _sparsematrix_clear(&p->replysj);
    _xbfgshessian_clear(&p->hess);
    ae_vector_clear(&p->laztx);
    ae_vector_clear(&p->laztfi);
    _sparsematrix_clear(&p->laztsj);
    ae_vector_clear(&p->basex);
    ae_vector_clear(&p->basefi);
    _sparsematrix_clear(&p->basesj);
    ae_vector_clear(&p->prevbasex);
    ae_vector_clear(&p->prevbasefi);
    _sparsematrix_clear(&p->prevbasesj);
    ae_vector_clear(&p->bestx);
    ae_vector_clear(&p->bestfi);
    _sparsematrix_clear(&p->bestsj);
    ae_vector_clear(&p->basehsr1diag);
    ae_vector_clear(&p->basehsr1corrd);
    ae_matrix_clear(&p->basehsr1corrc);
    ae_vector_clear(&p->xproj);
    ae_vector_clear(&p->deltaproj);
    ae_vector_clear(&p->priorities);
    _spcholanalysis_clear(&p->analysis);
    _sparsematrix_clear(&p->sparsesys);
    _sparsematrix_clear(&p->lastanalyzedjac);
    ae_vector_clear(&p->fscales);
    ae_vector_clear(&p->invfscales);
    ae_vector_clear(&p->autoscalerhessnorms);
    _gipm2state_clear(&p->solver);
    _sparsematrix_clear(&p->sparselc);
    ae_vector_clear(&p->lcbndl);
    ae_vector_clear(&p->lcbndu);
    ae_vector_clear(&p->lcsrcidx);
    ae_vector_clear(&p->lcscales);
    ae_vector_clear(&p->rawnl);
    ae_vector_clear(&p->rawnu);
    ae_vector_clear(&p->wrkccbndl);
    ae_vector_clear(&p->wrkccbndu);
    ae_vector_clear(&p->xs);
    ae_vector_clear(&p->replagbc);
    ae_vector_clear(&p->replaglc);
    ae_vector_clear(&p->replagnlc);
    _stimer_clear(&p->timertotal);
    _stimer_clear(&p->timerfactorize);
    _stimer_clear(&p->timersolve);
    _stimer_clear(&p->timercallback);
    _stimer_clear(&p->timerreport);
    _stimer_clear(&p->timeranalyze);
    _stimer_clear(&p->timermv);
    _stimer_clear(&p->timerrescale);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmp1);
    ae_vector_clear(&p->tmp2);
    ae_vector_clear(&p->tmp3);
    ae_vector_clear(&p->tmpb);
    _sparsematrix_clear(&p->tmpsparse);
    _rcommstate_clear(&p->rstate);
}


void _nlpgipm2state_destroy(void* _p)
{
    nlpgipm2state *p = (nlpgipm2state*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->scaledx0);
    ae_vector_destroy(&p->scaledbndl);
    ae_vector_destroy(&p->scaledbndu);
    ae_vector_destroy(&p->finitescaledbndl);
    ae_vector_destroy(&p->finitescaledbndu);
    ae_vector_destroy(&p->finiterawbndl);
    ae_vector_destroy(&p->finiterawbndu);
    ae_vector_destroy(&p->isfixed);
    ae_vector_destroy(&p->nonfixedmask);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->replyfi);
    _sparsematrix_destroy(&p->replysj);
    _xbfgshessian_destroy(&p->hess);
    ae_vector_destroy(&p->laztx);
    ae_vector_destroy(&p->laztfi);
    _sparsematrix_destroy(&p->laztsj);
    ae_vector_destroy(&p->basex);
    ae_vector_destroy(&p->basefi);
    _sparsematrix_destroy(&p->basesj);
    ae_vector_destroy(&p->prevbasex);
    ae_vector_destroy(&p->prevbasefi);
    _sparsematrix_destroy(&p->prevbasesj);
    ae_vector_destroy(&p->bestx);
    ae_vector_destroy(&p->bestfi);
    _sparsematrix_destroy(&p->bestsj);
    ae_vector_destroy(&p->basehsr1diag);
    ae_vector_destroy(&p->basehsr1corrd);
    ae_matrix_destroy(&p->basehsr1corrc);
    ae_vector_destroy(&p->xproj);
    ae_vector_destroy(&p->deltaproj);
    ae_vector_destroy(&p->priorities);
    _spcholanalysis_destroy(&p->analysis);
    _sparsematrix_destroy(&p->sparsesys);
    _sparsematrix_destroy(&p->lastanalyzedjac);
    ae_vector_destroy(&p->fscales);
    ae_vector_destroy(&p->invfscales);
    ae_vector_destroy(&p->autoscalerhessnorms);
    _gipm2state_destroy(&p->solver);
    _sparsematrix_destroy(&p->sparselc);
    ae_vector_destroy(&p->lcbndl);
    ae_vector_destroy(&p->lcbndu);
    ae_vector_destroy(&p->lcsrcidx);
    ae_vector_destroy(&p->lcscales);
    ae_vector_destroy(&p->rawnl);
    ae_vector_destroy(&p->rawnu);
    ae_vector_destroy(&p->wrkccbndl);
    ae_vector_destroy(&p->wrkccbndu);
    ae_vector_destroy(&p->xs);
    ae_vector_destroy(&p->replagbc);
    ae_vector_destroy(&p->replaglc);
    ae_vector_destroy(&p->replagnlc);
    _stimer_destroy(&p->timertotal);
    _stimer_destroy(&p->timerfactorize);
    _stimer_destroy(&p->timersolve);
    _stimer_destroy(&p->timercallback);
    _stimer_destroy(&p->timerreport);
    _stimer_destroy(&p->timeranalyze);
    _stimer_destroy(&p->timermv);
    _stimer_destroy(&p->timerrescale);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmp1);
    ae_vector_destroy(&p->tmp2);
    ae_vector_destroy(&p->tmp3);
    ae_vector_destroy(&p->tmpb);
    _sparsematrix_destroy(&p->tmpsparse);
    _rcommstate_destroy(&p->rstate);
}


#endif
#if defined(AE_COMPILE_MINBLEIC) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
                     BOUND CONSTRAINED OPTIMIZATION
       WITH ADDITIONAL LINEAR EQUALITY AND INEQUALITY CONSTRAINTS

DESCRIPTION:
The  subroutine  minimizes  function   F(x)  of N arguments subject to any
combination of:
* bound constraints
* linear inequality constraints
* linear equality constraints

REQUIREMENTS:
* user must provide function value and gradient
* starting point X0 must be feasible or
  not too far away from the feasible set
* grad(f) must be Lipschitz continuous on a level set:
  L = { x : f(x)<=f(x0) }
* function must be defined everywhere on the feasible set F

USAGE:

Constrained optimization if far more complex than the unconstrained one.
Here we give very brief outline of the BLEIC optimizer. We strongly recommend
you to read examples in the ALGLIB Reference Manual and to read ALGLIB User Guide
on optimization, which is available at http://www.alglib.net/optimization/

1. User initializes algorithm state with MinBLEICCreate() call

2. USer adds boundary and/or linear constraints by calling
   MinBLEICSetBC() and MinBLEICSetLC() functions.

3. User sets stopping conditions with MinBLEICSetCond().

4. User calls MinBLEICOptimize() function which takes algorithm  state and
   pointer (delegate, etc.) to callback function which calculates F/G.

5. User calls MinBLEICResults() to get solution

6. Optionally user may call MinBLEICRestartFrom() to solve another problem
   with same N but another starting point.
   MinBLEICRestartFrom() allows to reuse already initialized structure.

NOTE: if you have box-only constraints (no  general  linear  constraints),
      then MinBC optimizer can be better option. It uses  special,  faster
      constraint activation method, which performs better on problems with
      multiple constraints active at the solution.
      
      On small-scale problems performance of MinBC is similar to  that  of
      MinBLEIC, but on large-scale ones (hundreds and thousands of  active
      constraints) it can be several times faster than MinBLEIC.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size ofX
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleiccreate(ae_int_t n,
     /* Real    */ const ae_vector* x,
     minbleicstate* state,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix c;
    ae_vector ct;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    _minbleicstate_clear(state);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);

    ae_assert(n>=1, "MinBLEICCreate: N<1", _state);
    ae_assert(x->cnt>=n, "MinBLEICCreate: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinBLEICCreate: X contains infinite or NaN values!", _state);
    minbleic_minbleicinitinternal(n, x, 0.0, state, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
The subroutine is finite difference variant of MinBLEICCreate().  It  uses
finite differences in order to differentiate target function.

Description below contains information which is specific to  this function
only. We recommend to read comments on MinBLEICCreate() in  order  to  get
more information about creation of BLEIC optimizer.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[0..N-1].
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinBLEICSetScale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large truncation  errors, while too small
   step will result in too large numerical  errors.  1.0E-6  can  be  good
   value to start with.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is  less  robust and precise. CG needs exact gradient values. Imprecise
   gradient may slow  down  convergence, especially  on  highly  nonlinear
   problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
void minbleiccreatef(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minbleicstate* state,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix c;
    ae_vector ct;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    _minbleicstate_clear(state);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);

    ae_assert(n>=1, "MinBLEICCreateF: N<1", _state);
    ae_assert(x->cnt>=n, "MinBLEICCreateF: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinBLEICCreateF: X contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(diffstep, _state), "MinBLEICCreateF: DiffStep is infinite or NaN!", _state);
    ae_assert(ae_fp_greater(diffstep,(double)(0)), "MinBLEICCreateF: DiffStep is non-positive!", _state);
    minbleic_minbleicinitinternal(n, x, diffstep, state, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
This function sets boundary constraints for BLEIC optimizer.

Boundary constraints are inactive by default (after initial creation).
They are preserved after algorithm restart with MinBLEICRestartFrom().

NOTE: if you have box-only constraints (no  general  linear  constraints),
      then MinBC optimizer can be better option. It uses  special,  faster
      constraint activation method, which performs better on problems with
      multiple constraints active at the solution.
      
      On small-scale problems performance of MinBC is similar to  that  of
      MinBLEIC, but on large-scale ones (hundreds and thousands of  active
      constraints) it can be several times faster than MinBLEIC.
      
INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF.

NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

NOTE 2: this solver has following useful properties:
* bound constraints are always satisfied exactly
* function is evaluated only INSIDE area specified by  bound  constraints,
  even  when  numerical  differentiation is used (algorithm adjusts  nodes
  according to boundary constraints)

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetbc(minbleicstate* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->nmain;
    ae_assert(bndl->cnt>=n, "MinBLEICSetBC: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=n, "MinBLEICSetBC: Length(BndU)<N", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "MinBLEICSetBC: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "MinBLEICSetBC: BndL contains NAN or -INF", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
    }
    sassetbc(&state->sas, bndl, bndu, _state);
}


/*************************************************************************
This function sets linear constraints for BLEIC optimizer.

Linear constraints are inactive by default (after initial creation).
They are preserved after algorithm restart with MinBLEICRestartFrom().

INPUT PARAMETERS:
    State   -   structure previously allocated with MinBLEICCreate call.
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

NOTE 1: linear (non-bound) constraints are satisfied only approximately:
* there always exists some minor violation (about Epsilon in magnitude)
  due to rounding errors
* numerical differentiation, if used, may  lead  to  function  evaluations
  outside  of the feasible  area,   because   algorithm  does  NOT  change
  numerical differentiation formula according to linear constraints.
If you want constraints to be  satisfied  exactly, try to reformulate your
problem  in  such  manner  that  all constraints will become boundary ones
(this kind of constraints is always satisfied exactly, both in  the  final
solution and in all intermediate points).

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetlc(minbleicstate* state,
     /* Real    */ const ae_matrix* c,
     /* Integer */ const ae_vector* ct,
     ae_int_t k,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    double v;


    n = state->nmain;
    
    /*
     * First, check for errors in the inputs
     */
    ae_assert(k>=0, "MinBLEICSetLC: K<0", _state);
    ae_assert(c->cols>=n+1||k==0, "MinBLEICSetLC: Cols(C)<N+1", _state);
    ae_assert(c->rows>=k, "MinBLEICSetLC: Rows(C)<K", _state);
    ae_assert(ct->cnt>=k, "MinBLEICSetLC: Length(CT)<K", _state);
    ae_assert(apservisfinitematrix(c, k, n+1, _state), "MinBLEICSetLC: C contains infinite or NaN values!", _state);
    
    /*
     * Handle zero K
     */
    if( k==0 )
    {
        state->nec = 0;
        state->nic = 0;
        sassetlc(&state->sas, c, ct, 0, _state);
        return;
    }
    
    /*
     * Equality constraints are stored first, in the upper
     * NEC rows of State.CLEIC matrix. Inequality constraints
     * are stored in the next NIC rows.
     *
     * NOTE: we convert inequality constraints to the form
     * A*x<=b before copying them.
     */
    rmatrixsetlengthatleast(&state->cleic, k, n+1, _state);
    state->nec = 0;
    state->nic = 0;
    for(i=0; i<=k-1; i++)
    {
        if( ct->ptr.p_int[i]==0 )
        {
            ae_v_move(&state->cleic.ptr.pp_double[state->nec][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
            state->nec = state->nec+1;
        }
    }
    for(i=0; i<=k-1; i++)
    {
        if( ct->ptr.p_int[i]!=0 )
        {
            if( ct->ptr.p_int[i]>0 )
            {
                ae_v_moveneg(&state->cleic.ptr.pp_double[state->nec+state->nic][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
            }
            else
            {
                ae_v_move(&state->cleic.ptr.pp_double[state->nec+state->nic][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
            }
            state->nic = state->nic+1;
        }
    }
    
    /*
     * Normalize rows of State.CLEIC: each row must have unit norm.
     * Norm is calculated using first N elements (i.e. right part is
     * not counted when we calculate norm).
     */
    for(i=0; i<=k-1; i++)
    {
        v = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            v = v+ae_sqr(state->cleic.ptr.pp_double[i][j], _state);
        }
        if( ae_fp_eq(v,(double)(0)) )
        {
            continue;
        }
        v = (double)1/ae_sqrt(v, _state);
        ae_v_muld(&state->cleic.ptr.pp_double[i][0], 1, ae_v_len(0,n), v);
    }
    sassetlc(&state->sas, c, ct, k, _state);
}


/*************************************************************************
This function sets stopping conditions for the optimizer.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsG    -   >=0
                The  subroutine  finishes  its  work   if   the  condition
                |v|<EpsG is satisfied, where:
                * |.| means Euclidian norm
                * v - scaled gradient vector, v[i]=g[i]*s[i]
                * g - gradient
                * s - scaling coefficients set by MinBLEICSetScale()
    EpsF    -   >=0
                The  subroutine  finishes  its work if on k+1-th iteration
                the  condition  |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1}
                is satisfied.
    EpsX    -   >=0
                The subroutine finishes its work if  on  k+1-th  iteration
                the condition |v|<=EpsX is fulfilled, where:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - step vector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinBLEICSetScale()
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead
to automatic stopping criterion selection.

NOTE: when SetCond() called with non-zero MaxIts, BLEIC solver may perform
      slightly more than MaxIts iterations. I.e., MaxIts  sets  non-strict
      limit on iterations count.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetcond(minbleicstate* state,
     double epsg,
     double epsf,
     double epsx,
     ae_int_t maxits,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsg, _state), "MinBLEICSetCond: EpsG is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsg,(double)(0)), "MinBLEICSetCond: negative EpsG", _state);
    ae_assert(ae_isfinite(epsf, _state), "MinBLEICSetCond: EpsF is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsf,(double)(0)), "MinBLEICSetCond: negative EpsF", _state);
    ae_assert(ae_isfinite(epsx, _state), "MinBLEICSetCond: EpsX is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "MinBLEICSetCond: negative EpsX", _state);
    ae_assert(maxits>=0, "MinBLEICSetCond: negative MaxIts!", _state);
    if( ((ae_fp_eq(epsg,(double)(0))&&ae_fp_eq(epsf,(double)(0)))&&ae_fp_eq(epsx,(double)(0)))&&maxits==0 )
    {
        epsx = 1.0E-6;
    }
    state->epsg = epsg;
    state->epsf = epsf;
    state->epsx = epsx;
    state->maxits = maxits;
}


/*************************************************************************
This function sets scaling coefficients for BLEIC optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Scaling is also used by finite difference variant of the optimizer  - step
along I-th axis is equal to DiffStep*S[I].

In  most  optimizers  (and  in  the  BLEIC  too)  scaling is NOT a form of
preconditioning. It just  affects  stopping  conditions.  You  should  set
preconditioner  by  separate  call  to  one  of  the  MinBLEICSetPrec...()
functions.

There is a special  preconditioning  mode, however,  which  uses   scaling
coefficients to form diagonal preconditioning matrix. You  can  turn  this
mode on, if you want.   But  you should understand that scaling is not the
same thing as preconditioning - these are two different, although  related
forms of tuning solver.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minbleicsetscale(minbleicstate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(s->cnt>=state->nmain, "MinBLEICSetScale: Length(S)<N", _state);
    for(i=0; i<=state->nmain-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "MinBLEICSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "MinBLEICSetScale: S contains zero elements", _state);
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
    sassetscale(&state->sas, s, _state);
}


/*************************************************************************
Modification of the preconditioner: preconditioning is turned off.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetprecdefault(minbleicstate* state, ae_state *_state)
{


    state->prectype = 0;
}


/*************************************************************************
Modification  of  the  preconditioner:  diagonal of approximate Hessian is
used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    D       -   diagonal of the approximate Hessian, array[0..N-1],
                (if larger, only leading N elements are used).

NOTE 1: D[i] should be positive. Exception will be thrown otherwise.

NOTE 2: you should pass diagonal of approximate Hessian - NOT ITS INVERSE.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetprecdiag(minbleicstate* state,
     /* Real    */ const ae_vector* d,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(d->cnt>=state->nmain, "MinBLEICSetPrecDiag: D is too short", _state);
    for(i=0; i<=state->nmain-1; i++)
    {
        ae_assert(ae_isfinite(d->ptr.p_double[i], _state), "MinBLEICSetPrecDiag: D contains infinite or NAN elements", _state);
        ae_assert(ae_fp_greater(d->ptr.p_double[i],(double)(0)), "MinBLEICSetPrecDiag: D contains non-positive elements", _state);
    }
    rvectorsetlengthatleast(&state->diagh, state->nmain, _state);
    state->prectype = 2;
    for(i=0; i<=state->nmain-1; i++)
    {
        state->diagh.ptr.p_double[i] = d->ptr.p_double[i];
    }
}


/*************************************************************************
Modification of the preconditioner: scale-based diagonal preconditioning.

This preconditioning mode can be useful when you  don't  have  approximate
diagonal of Hessian, but you know that your  variables  are  badly  scaled
(for  example,  one  variable is in [1,10], and another in [1000,100000]),
and most part of the ill-conditioning comes from different scales of vars.

In this case simple  scale-based  preconditioner,  with H[i] = 1/(s[i]^2),
can greatly improve convergence.

IMPRTANT: you should set scale of your variables  with  MinBLEICSetScale()
call  (before  or after MinBLEICSetPrecScale() call). Without knowledge of
the scale of your variables scale-based preconditioner will be  just  unit
matrix.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetprecscale(minbleicstate* state, ae_state *_state)
{


    state->prectype = 3;
}


/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinBLEICOptimize().

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetxrep(minbleicstate* state,
     ae_bool needxrep,
     ae_state *_state)
{


    state->xrep = needxrep;
}


/*************************************************************************
This function turns on/off line search reports.
These reports are described in more details in developer-only  comments on
MinBLEICState object.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedDRep-   whether line search reports are needed or not

This function is intended for private use only. Turning it on artificially
may cause program failure.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetdrep(minbleicstate* state,
     ae_bool needdrep,
     ae_state *_state)
{


    state->drep = needdrep;
}


/*************************************************************************
This function sets maximum step length

IMPORTANT: this feature is hard to combine with preconditioning. You can't
set upper limit on step length, when you solve optimization  problem  with
linear (non-boundary) constraints AND preconditioner turned on.

When  non-boundary  constraints  are  present,  you  have to either a) use
preconditioner, or b) use upper limit on step length.  YOU CAN'T USE BOTH!
In this case algorithm will terminate with appropriate error code.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    StpMax  -   maximum step length, >=0. Set StpMax to 0.0,  if you don't
                want to limit step length.

Use this subroutine when you optimize target function which contains exp()
or  other  fast  growing  functions,  and optimization algorithm makes too
large  steps  which  lead   to overflow. This function allows us to reject
steps  that  are  too  large  (and  therefore  expose  us  to the possible
overflow) without actually calculating function value at the x+stp*d.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetstpmax(minbleicstate* state,
     double stpmax,
     ae_state *_state)
{


    ae_assert(ae_isfinite(stpmax, _state), "MinBLEICSetStpMax: StpMax is not finite!", _state);
    ae_assert(ae_fp_greater_eq(stpmax,(double)(0)), "MinBLEICSetStpMax: StpMax<0!", _state);
    state->stpmax = stpmax;
}


/*************************************************************************
NOTES:

1. This function has two different implementations: one which  uses  exact
   (analytical) user-supplied gradient,  and one which uses function value
   only  and  numerically  differentiates  function  in  order  to  obtain
   gradient.

   Depending  on  the  specific  function  used to create optimizer object
   (either  MinBLEICCreate() for analytical gradient or  MinBLEICCreateF()
   for numerical differentiation) you should choose appropriate variant of
   MinBLEICOptimize() - one  which  accepts  function  AND gradient or one
   which accepts function ONLY.

   Be careful to choose variant of MinBLEICOptimize() which corresponds to
   your optimization scheme! Table below lists different  combinations  of
   callback (function/gradient) passed to MinBLEICOptimize()  and specific
   function used to create optimizer.


                     |         USER PASSED TO MinBLEICOptimize()
   CREATED WITH      |  function only   |  function and gradient
   ------------------------------------------------------------
   MinBLEICCreateF() |     work                FAIL
   MinBLEICCreate()  |     FAIL                work

   Here "FAIL" denotes inappropriate combinations  of  optimizer  creation
   function  and  MinBLEICOptimize()  version.   Attemps   to   use   such
   combination (for  example,  to  create optimizer with MinBLEICCreateF()
   and  to  pass  gradient information to MinBLEICOptimize()) will lead to
   exception being thrown. Either  you  did  not pass gradient when it WAS
   needed or you passed gradient when it was NOT needed.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
ae_bool minbleiciteration(minbleicstate* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    double v;
    double vv;
    double v0;
    ae_bool b;
    ae_int_t mcinfo;
    ae_int_t actstatus;
    ae_int_t itidx;
    double penalty;
    double ginit;
    double gdecay;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        n = state->rstate.ia.ptr.p_int[0];
        m = state->rstate.ia.ptr.p_int[1];
        i = state->rstate.ia.ptr.p_int[2];
        j = state->rstate.ia.ptr.p_int[3];
        mcinfo = state->rstate.ia.ptr.p_int[4];
        actstatus = state->rstate.ia.ptr.p_int[5];
        itidx = state->rstate.ia.ptr.p_int[6];
        b = state->rstate.ba.ptr.p_bool[0];
        v = state->rstate.ra.ptr.p_double[0];
        vv = state->rstate.ra.ptr.p_double[1];
        v0 = state->rstate.ra.ptr.p_double[2];
        penalty = state->rstate.ra.ptr.p_double[3];
        ginit = state->rstate.ra.ptr.p_double[4];
        gdecay = state->rstate.ra.ptr.p_double[5];
    }
    else
    {
        n = 359;
        m = -58;
        i = -919;
        j = -909;
        mcinfo = 81;
        actstatus = 255;
        itidx = 74;
        b = ae_false;
        v = 809.0;
        vv = 205.0;
        v0 = -838.0;
        penalty = 939.0;
        ginit = -526.0;
        gdecay = 763.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    if( state->rstate.stage==6 )
    {
        goto lbl_6;
    }
    if( state->rstate.stage==7 )
    {
        goto lbl_7;
    }
    if( state->rstate.stage==8 )
    {
        goto lbl_8;
    }
    if( state->rstate.stage==9 )
    {
        goto lbl_9;
    }
    if( state->rstate.stage==10 )
    {
        goto lbl_10;
    }
    if( state->rstate.stage==11 )
    {
        goto lbl_11;
    }
    if( state->rstate.stage==12 )
    {
        goto lbl_12;
    }
    if( state->rstate.stage==13 )
    {
        goto lbl_13;
    }
    if( state->rstate.stage==14 )
    {
        goto lbl_14;
    }
    if( state->rstate.stage==15 )
    {
        goto lbl_15;
    }
    if( state->rstate.stage==16 )
    {
        goto lbl_16;
    }
    if( state->rstate.stage==17 )
    {
        goto lbl_17;
    }
    if( state->rstate.stage==18 )
    {
        goto lbl_18;
    }
    if( state->rstate.stage==19 )
    {
        goto lbl_19;
    }
    if( state->rstate.stage==20 )
    {
        goto lbl_20;
    }
    if( state->rstate.stage==21 )
    {
        goto lbl_21;
    }
    
    /*
     * Routine body
     */
    
    /*
     * Algorithm parameters:
     * * M          number of L-BFGS corrections.
     *              This coefficient remains fixed during iterations.
     * * GDecay     desired decrease of constrained gradient during L-BFGS iterations.
     *              This coefficient is decreased after each L-BFGS round until
     *              it reaches minimum decay.
     */
    m = ae_minint(5, state->nmain, _state);
    gdecay = minbleic_initialdecay;
    
    /*
     * Init
     */
    n = state->nmain;
    state->steepestdescentstep = ae_false;
    state->userterminationneeded = ae_false;
    state->repterminationtype = 0;
    state->repinneriterationscount = 0;
    state->repouteriterationscount = 0;
    state->repnfev = 0;
    state->repvaridx = -1;
    state->repdebugeqerr = 0.0;
    state->repdebugfs = _state->v_nan;
    state->repdebugff = _state->v_nan;
    state->repdebugdx = _state->v_nan;
    if( ae_fp_neq(state->stpmax,(double)(0))&&state->prectype!=0 )
    {
        state->repterminationtype = -10;
        result = ae_false;
        return result;
    }
    rmatrixsetlengthatleast(&state->bufyk, m+1, n, _state);
    rmatrixsetlengthatleast(&state->bufsk, m+1, n, _state);
    rvectorsetlengthatleast(&state->bufrho, m, _state);
    rvectorsetlengthatleast(&state->buftheta, m, _state);
    rvectorsetlengthatleast(&state->tmp0, n, _state);
    smoothnessmonitorinit(&state->smonitor, &state->s, n, 1, state->smoothnessguardlevel>0, _state);
    for(i=0; i<=n-1; i++)
    {
        state->lastscaleused.ptr.p_double[i] = state->s.ptr.p_double[i];
        state->invs.ptr.p_double[i] = (double)1/state->s.ptr.p_double[i];
    }
    
    /*
     *  Check analytic derivative
     */
    minbleic_clearrequestfields(state, _state);
    if( !(ae_fp_eq(state->diffstep,(double)(0))&&ae_fp_greater(state->teststep,(double)(0))) )
    {
        goto lbl_22;
    }
lbl_24:
    if( !smoothnessmonitorcheckgradientatx0(&state->smonitor, &state->xstart, &state->s, &state->bndl, &state->bndu, ae_true, state->teststep, _state) )
    {
        goto lbl_25;
    }
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = state->smonitor.x.ptr.p_double[i];
    }
    state->needfg = ae_true;
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    state->needfg = ae_false;
    state->smonitor.fi.ptr.p_double[0] = state->f;
    for(i=0; i<=n-1; i++)
    {
        state->smonitor.j.ptr.pp_double[0][i] = state->g.ptr.p_double[i];
    }
    goto lbl_24;
lbl_25:
lbl_22:
    
    /*
     * Fill TmpPrec with current preconditioner
     */
    rvectorsetlengthatleast(&state->tmpprec, n, _state);
    for(i=0; i<=n-1; i++)
    {
        if( state->prectype==2 )
        {
            state->tmpprec.ptr.p_double[i] = state->diagh.ptr.p_double[i];
            continue;
        }
        if( state->prectype==3 )
        {
            state->tmpprec.ptr.p_double[i] = (double)1/ae_sqr(state->s.ptr.p_double[i], _state);
            continue;
        }
        state->tmpprec.ptr.p_double[i] = (double)(1);
    }
    sassetprecdiag(&state->sas, &state->tmpprec, _state);
    
    /*
     * Start optimization
     */
    if( !sasstartoptimization(&state->sas, &state->xstart, _state) )
    {
        state->repterminationtype = -3;
        result = ae_false;
        return result;
    }
    
    /*
     * Main cycle of BLEIC-PG algorithm
     */
    state->repterminationtype = 0;
    state->lastgoodstep = (double)(0);
    state->lastscaledgoodstep = (double)(0);
    state->maxscaledgrad = (double)(0);
    state->nonmonotoniccnt = ae_round(1.5*(double)(n+state->nic), _state)+5;
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->sas.xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minbleic_clearrequestfields(state, _state);
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_26;
    }
    state->needfg = ae_true;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->needfg = ae_false;
    goto lbl_27;
lbl_26:
    state->needf = ae_true;
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->needf = ae_false;
lbl_27:
    state->fc = state->f;
    trimprepare(state->f, &state->trimthreshold, _state);
    state->repnfev = state->repnfev+1;
    if( !state->xrep )
    {
        goto lbl_28;
    }
    
    /*
     * Report current point
     */
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->sas.xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->f = state->fc;
    state->xupdated = ae_true;
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    state->xupdated = ae_false;
lbl_28:
    if( state->userterminationneeded )
    {
        
        /*
         * User requested termination
         */
        sasstopoptimization(&state->sas, _state);
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
lbl_30:
    if( ae_false )
    {
        goto lbl_31;
    }
    
    /*
     * Preparations
     *
     * (a) calculate unconstrained gradient
     * (b) determine initial active set
     * (c) update MaxScaledGrad
     * (d) check F/G for NAN/INF, abnormally terminate algorithm if needed
     */
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->sas.xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minbleic_clearrequestfields(state, _state);
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_32;
    }
    
    /*
     * Analytic gradient
     */
    state->needfg = ae_true;
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    state->needfg = ae_false;
    goto lbl_33;
lbl_32:
    
    /*
     * Numerical differentiation
     */
    state->needf = ae_true;
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    state->fbase = state->f;
    i = 0;
lbl_34:
    if( i>n-1 )
    {
        goto lbl_36;
    }
    v = state->x.ptr.p_double[i];
    b = ae_false;
    if( state->hasbndl.ptr.p_bool[i] )
    {
        b = b||ae_fp_less(v-state->diffstep*state->s.ptr.p_double[i],state->bndl.ptr.p_double[i]);
    }
    if( state->hasbndu.ptr.p_bool[i] )
    {
        b = b||ae_fp_greater(v+state->diffstep*state->s.ptr.p_double[i],state->bndu.ptr.p_double[i]);
    }
    if( b )
    {
        goto lbl_37;
    }
    state->x.ptr.p_double[i] = v-state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 6;
    goto lbl_rcomm;
lbl_6:
    state->fm2 = state->f;
    state->x.ptr.p_double[i] = v-0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 7;
    goto lbl_rcomm;
lbl_7:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = v+0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 8;
    goto lbl_rcomm;
lbl_8:
    state->fp1 = state->f;
    state->x.ptr.p_double[i] = v+state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 9;
    goto lbl_rcomm;
lbl_9:
    state->fp2 = state->f;
    state->g.ptr.p_double[i] = ((double)8*(state->fp1-state->fm1)-(state->fp2-state->fm2))/((double)6*state->diffstep*state->s.ptr.p_double[i]);
    goto lbl_38;
lbl_37:
    state->xm1 = v-state->diffstep*state->s.ptr.p_double[i];
    state->xp1 = v+state->diffstep*state->s.ptr.p_double[i];
    if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less(state->xm1,state->bndl.ptr.p_double[i]) )
    {
        state->xm1 = state->bndl.ptr.p_double[i];
    }
    if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater(state->xp1,state->bndu.ptr.p_double[i]) )
    {
        state->xp1 = state->bndu.ptr.p_double[i];
    }
    state->x.ptr.p_double[i] = state->xm1;
    state->rstate.stage = 10;
    goto lbl_rcomm;
lbl_10:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = state->xp1;
    state->rstate.stage = 11;
    goto lbl_rcomm;
lbl_11:
    state->fp1 = state->f;
    if( ae_fp_neq(state->xm1,state->xp1) )
    {
        state->g.ptr.p_double[i] = (state->fp1-state->fm1)/(state->xp1-state->xm1);
    }
    else
    {
        state->g.ptr.p_double[i] = (double)(0);
    }
lbl_38:
    state->x.ptr.p_double[i] = v;
    i = i+1;
    goto lbl_34;
lbl_36:
    state->f = state->fbase;
    state->needf = ae_false;
lbl_33:
    state->fc = state->f;
    ae_v_move(&state->ugc.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->cgc.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    sasreactivateconstraintsprec(&state->sas, &state->ugc, _state);
    sasconstraineddirection(&state->sas, &state->cgc, _state);
    ginit = 0.0;
    for(i=0; i<=n-1; i++)
    {
        ginit = ginit+ae_sqr(state->cgc.ptr.p_double[i]*state->s.ptr.p_double[i], _state);
    }
    ginit = ae_sqrt(ginit, _state);
    state->maxscaledgrad = ae_maxreal(state->maxscaledgrad, ginit, _state);
    if( !ae_isfinite(ginit, _state)||!ae_isfinite(state->fc, _state) )
    {
        
        /*
         * Abnormal termination - infinities in function/gradient
         */
        sasstopoptimization(&state->sas, _state);
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    if( state->userterminationneeded )
    {
        
        /*
         * User requested termination
         */
        sasstopoptimization(&state->sas, _state);
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    
    /*
     * LBFGS stage:
     * * during LBFGS iterations we activate new constraints, but never
     *   deactivate already active ones.
     * * we perform at most N iterations of LBFGS before re-evaluating
     *   active set and restarting LBFGS.
     * * first iteration of LBFGS is a special - it is performed with
     *   minimum set of active constraints, algorithm termination can
     *   be performed only at this state. We call this iteration
     *  "steepest descent step".
     *
     * About termination:
     * * LBFGS iterations can be terminated because of two reasons:
     *   * "termination" - non-zero termination code in RepTerminationType,
     *     which means that optimization is done
     *   * "restart" - zero RepTerminationType, which means that we
     *     have to re-evaluate active set and resume LBFGS stage.
     * * one more option is "refresh" - to continue LBFGS iterations,
     *   but with all BFGS updates (Sk/Yk pairs) being dropped;
     *   it happens after changes in active set
     */
    state->bufsize = 0;
    state->steepestdescentstep = ae_true;
    itidx = -1;
lbl_39:
    if( itidx>=n-1 )
    {
        goto lbl_40;
    }
    
    /*
     * Increment iterations counter
     *
     * NOTE: we have strong reasons to use such complex scheme
     *       instead of just for() loop - this counter may be
     *       decreased at some occasions to perform "restart"
     *       of an iteration.
     */
    itidx = itidx+1;
    
    /*
     * At the beginning of each iteration:
     * * SAS.XC stores current point
     * * FC stores current function value
     * * UGC stores current unconstrained gradient
     * * CGC stores current constrained gradient
     * * D stores constrained step direction (calculated at this block)
     *
     *
     * Check gradient-based stopping criteria
     *
     * This stopping condition is tested only for step which is the
     * first step of LBFGS (subsequent steps may accumulate active
     * constraints thus they should NOT be used for stopping - gradient
     * may be small when constrained, but these constraints may be
     * deactivated by the subsequent steps)
     */
    if( state->steepestdescentstep&&ae_fp_less_eq(sasscaledconstrainednorm(&state->sas, &state->ugc, _state),state->epsg) )
    {
        
        /*
         * Gradient is small enough.
         * Optimization is terminated
         */
        state->repterminationtype = 4;
        goto lbl_40;
    }
    
    /*
     * 1. Calculate search direction D according to L-BFGS algorithm
     *    using constrained preconditioner to perform inner multiplication.
     * 2. Evaluate scaled length of direction D; restart LBFGS if D is zero
     *    (it may be possible that we found minimum, but it is also possible
     *    that some constraints need deactivation)
     * 3. If D is non-zero, try to use previous scaled step length as initial estimate for new step.
     */
    ae_v_move(&state->work.ptr.p_double[0], 1, &state->cgc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    for(i=state->bufsize-1; i>=0; i--)
    {
        v = ae_v_dotproduct(&state->bufsk.ptr.pp_double[i][0], 1, &state->work.ptr.p_double[0], 1, ae_v_len(0,n-1));
        state->buftheta.ptr.p_double[i] = v;
        vv = v*state->bufrho.ptr.p_double[i];
        ae_v_subd(&state->work.ptr.p_double[0], 1, &state->bufyk.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), vv);
    }
    sasconstraineddirectionprec(&state->sas, &state->work, _state);
    for(i=0; i<=state->bufsize-1; i++)
    {
        v = ae_v_dotproduct(&state->bufyk.ptr.pp_double[i][0], 1, &state->work.ptr.p_double[0], 1, ae_v_len(0,n-1));
        vv = state->bufrho.ptr.p_double[i]*(-v+state->buftheta.ptr.p_double[i]);
        ae_v_addd(&state->work.ptr.p_double[0], 1, &state->bufsk.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), vv);
    }
    sasconstraineddirection(&state->sas, &state->work, _state);
    ae_v_moveneg(&state->d.ptr.p_double[0], 1, &state->work.ptr.p_double[0], 1, ae_v_len(0,n-1));
    v = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr(state->d.ptr.p_double[i]/state->s.ptr.p_double[i], _state);
    }
    v = ae_sqrt(v, _state);
    if( ae_fp_eq(v,(double)(0)) )
    {
        
        /*
         * Search direction is zero.
         * If we perform "steepest descent step", algorithm is terminated.
         * Otherwise we just restart LBFGS.
         */
        if( state->steepestdescentstep )
        {
            state->repterminationtype = 4;
        }
        goto lbl_40;
    }
    ae_assert(ae_fp_greater(v,(double)(0)), "MinBLEIC: internal error", _state);
    if( ae_fp_greater(state->lastscaledgoodstep,(double)(0))&&ae_fp_greater(v,(double)(0)) )
    {
        state->stp = state->lastscaledgoodstep/v;
    }
    else
    {
        state->stp = 1.0/v;
    }
    
    /*
     * Calculate bound on step length.
     * Step direction is stored
     */
    sasexploredirection(&state->sas, &state->d, &state->curstpmax, &state->cidx, &state->cval, _state);
    state->activationstep = state->curstpmax;
    if( state->cidx>=0&&ae_fp_eq(state->activationstep,(double)(0)) )
    {
        
        /*
         * We are exactly at the boundary, immediate activation
         * of constraint is required. LBFGS stage is continued
         * with "refreshed" model.
         *
         * ! IMPORTANT: we do not clear SteepestDescent flag here,
         * !            it is very important for correct stopping
         * !            of algorithm.
         *
         * ! IMPORTANT: we decrease iteration counter in order to
         *              preserve computational budget for iterations.
         */
        sasimmediateactivation(&state->sas, state->cidx, state->cval, _state);
        state->bufsize = 0;
        itidx = itidx-1;
        goto lbl_39;
    }
    if( ae_fp_greater(state->stpmax,(double)(0)) )
    {
        v = ae_v_dotproduct(&state->d.ptr.p_double[0], 1, &state->d.ptr.p_double[0], 1, ae_v_len(0,n-1));
        v = ae_sqrt(v, _state);
        if( ae_fp_greater(v,(double)(0)) )
        {
            state->curstpmax = ae_minreal(state->curstpmax, state->stpmax/v, _state);
        }
    }
    
    /*
     * Report beginning of line search (if requested by caller).
     * See description of the MinBLEICState for more information
     * about fields accessible to caller.
     *
     * Caller may do following:
     * * change State.Stp and load better initial estimate of
     *   the step length.
     * Caller may not terminate algorithm.
     */
    if( !state->drep )
    {
        goto lbl_41;
    }
    minbleic_clearrequestfields(state, _state);
    state->lsstart = ae_true;
    state->boundedstep = state->cidx>=0;
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->sas.xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->rstate.stage = 12;
    goto lbl_rcomm;
lbl_12:
    state->lsstart = ae_false;
lbl_41:
    
    /*
     * Minimize F(x+alpha*d)
     */
    ae_v_move(&state->xn.ptr.p_double[0], 1, &state->sas.xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->cgn.ptr.p_double[0], 1, &state->cgc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->ugn.ptr.p_double[0], 1, &state->ugc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->fn = state->fc;
    state->mcstage = 0;
    smoothnessmonitorstartlinesearch1u(&state->smonitor, &state->s, &state->invs, &state->xn, state->fn, &state->ugn, state->repinneriterationscount, -1, _state);
    mcsrch(n, &state->xn, &state->fn, &state->ugn, &state->d, &state->stp, state->curstpmax, minbleic_gtol, &mcinfo, &state->nfev, &state->work, &state->lstate, &state->mcstage, _state);
lbl_43:
    if( state->mcstage==0 )
    {
        goto lbl_44;
    }
    
    /*
     * Perform correction (constraints are enforced)
     * Copy XN to X
     */
    sascorrection(&state->sas, &state->xn, &penalty, _state);
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = state->xn.ptr.p_double[i];
    }
    
    /*
     * Gradient, either user-provided or numerical differentiation
     */
    minbleic_clearrequestfields(state, _state);
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_45;
    }
    
    /*
     * Analytic gradient
     */
    state->needfg = ae_true;
    state->rstate.stage = 13;
    goto lbl_rcomm;
lbl_13:
    state->needfg = ae_false;
    state->repnfev = state->repnfev+1;
    goto lbl_46;
lbl_45:
    
    /*
     * Numerical differentiation
     */
    state->needf = ae_true;
    state->rstate.stage = 14;
    goto lbl_rcomm;
lbl_14:
    state->fbase = state->f;
    i = 0;
lbl_47:
    if( i>n-1 )
    {
        goto lbl_49;
    }
    v = state->x.ptr.p_double[i];
    b = ae_false;
    if( state->hasbndl.ptr.p_bool[i] )
    {
        b = b||ae_fp_less(v-state->diffstep*state->s.ptr.p_double[i],state->bndl.ptr.p_double[i]);
    }
    if( state->hasbndu.ptr.p_bool[i] )
    {
        b = b||ae_fp_greater(v+state->diffstep*state->s.ptr.p_double[i],state->bndu.ptr.p_double[i]);
    }
    if( b )
    {
        goto lbl_50;
    }
    state->x.ptr.p_double[i] = v-state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 15;
    goto lbl_rcomm;
lbl_15:
    state->fm2 = state->f;
    state->x.ptr.p_double[i] = v-0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 16;
    goto lbl_rcomm;
lbl_16:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = v+0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 17;
    goto lbl_rcomm;
lbl_17:
    state->fp1 = state->f;
    state->x.ptr.p_double[i] = v+state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 18;
    goto lbl_rcomm;
lbl_18:
    state->fp2 = state->f;
    state->g.ptr.p_double[i] = ((double)8*(state->fp1-state->fm1)-(state->fp2-state->fm2))/((double)6*state->diffstep*state->s.ptr.p_double[i]);
    state->repnfev = state->repnfev+4;
    goto lbl_51;
lbl_50:
    state->xm1 = v-state->diffstep*state->s.ptr.p_double[i];
    state->xp1 = v+state->diffstep*state->s.ptr.p_double[i];
    if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less(state->xm1,state->bndl.ptr.p_double[i]) )
    {
        state->xm1 = state->bndl.ptr.p_double[i];
    }
    if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater(state->xp1,state->bndu.ptr.p_double[i]) )
    {
        state->xp1 = state->bndu.ptr.p_double[i];
    }
    state->x.ptr.p_double[i] = state->xm1;
    state->rstate.stage = 19;
    goto lbl_rcomm;
lbl_19:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = state->xp1;
    state->rstate.stage = 20;
    goto lbl_rcomm;
lbl_20:
    state->fp1 = state->f;
    if( ae_fp_neq(state->xm1,state->xp1) )
    {
        state->g.ptr.p_double[i] = (state->fp1-state->fm1)/(state->xp1-state->xm1);
    }
    else
    {
        state->g.ptr.p_double[i] = (double)(0);
    }
    state->repnfev = state->repnfev+2;
lbl_51:
    state->x.ptr.p_double[i] = v;
    i = i+1;
    goto lbl_47;
lbl_49:
    state->f = state->fbase;
    state->needf = ae_false;
lbl_46:
    
    /*
     * Back to MCSRCH
     *
     * NOTE: penalty term from correction is added to FN in order
     *       to penalize increase in infeasibility.
     */
    smoothnessmonitorenqueuepoint1u(&state->smonitor, &state->s, &state->invs, &state->d, state->stp, &state->x, state->f, &state->g, _state);
    state->fn = state->f+minbleic_penaltyfactor*state->maxscaledgrad*penalty;
    ae_v_move(&state->cgn.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->ugn.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    sasconstraineddirection(&state->sas, &state->cgn, _state);
    trimfunction(&state->fn, &state->cgn, n, state->trimthreshold, _state);
    mcsrch(n, &state->xn, &state->fn, &state->ugn, &state->d, &state->stp, state->curstpmax, minbleic_gtol, &mcinfo, &state->nfev, &state->work, &state->lstate, &state->mcstage, _state);
    goto lbl_43;
lbl_44:
    ae_v_moveneg(&state->bufsk.ptr.pp_double[state->bufsize][0], 1, &state->sas.xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_moveneg(&state->bufyk.ptr.pp_double[state->bufsize][0], 1, &state->cgc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_add(&state->bufsk.ptr.pp_double[state->bufsize][0], 1, &state->xn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_add(&state->bufyk.ptr.pp_double[state->bufsize][0], 1, &state->cgn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    smoothnessmonitorfinalizelinesearch(&state->smonitor, _state);
    
    /*
     * Check for presence of NAN/INF in function/gradient
     */
    v = state->fn;
    for(i=0; i<=n-1; i++)
    {
        v = 0.1*v+state->ugn.ptr.p_double[i];
    }
    if( !ae_isfinite(v, _state) )
    {
        
        /*
         * Abnormal termination - infinities in function/gradient
         */
        state->repterminationtype = -8;
        goto lbl_40;
    }
    
    /*
     * Handle possible failure of the line search or request for termination
     */
    if( mcinfo!=1&&mcinfo!=5 )
    {
        
        /*
         * We can not find step which decreases function value. We have
         * two possibilities:
         * (a) numerical properties of the function do not allow us to
         *     find good step.
         * (b) we are close to activation of some constraint, and it is
         *     so close that step which activates it leads to change in
         *     target function which is smaller than numerical noise.
         *
         * Optimization algorithm must be able to handle case (b), because
         * inability to handle it will cause failure when algorithm
         * started very close to boundary of the feasible area.
         *
         * In order to correctly handle such cases we allow limited amount
         * of small steps which increase function value.
         */
        v = 0.0;
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_sqr(state->d.ptr.p_double[i]*state->curstpmax/state->s.ptr.p_double[i], _state);
        }
        v = ae_sqrt(v, _state);
        b = ae_false;
        if( (state->cidx>=0&&ae_fp_less_eq(v,minbleic_maxnonmonotoniclen))&&state->nonmonotoniccnt>0 )
        {
            
            /*
             * We try to enforce non-monotonic step:
             * * Stp    := CurStpMax
             * * MCINFO := 5
             * * XN     := XC+CurStpMax*D
             * * non-monotonic counter is decreased
             *
             * NOTE: UGN/CGN are not updated because step is so short that we assume that
             *       GN is approximately equal to GC.
             *
             * NOTE: prior to enforcing such step we check that it does not increase infeasibility
             *       of constraints beyond tolerable level
             */
            v = state->curstpmax;
            ae_v_move(&state->tmp0.ptr.p_double[0], 1, &state->sas.xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
            ae_v_addd(&state->tmp0.ptr.p_double[0], 1, &state->d.ptr.p_double[0], 1, ae_v_len(0,n-1), v);
            state->stp = state->curstpmax;
            mcinfo = 5;
            ae_v_move(&state->xn.ptr.p_double[0], 1, &state->tmp0.ptr.p_double[0], 1, ae_v_len(0,n-1));
            state->nonmonotoniccnt = state->nonmonotoniccnt-1;
            b = ae_true;
        }
        if( !b )
        {
            
            /*
             * Numerical properties of the function do not allow
             * us to solve problem. Here we have two possibilities:
             * * if it is "steepest descent" step, we can terminate
             *   algorithm because we are close to minimum
             * * if it is NOT "steepest descent" step, we should restart
             *   LBFGS iterations.
             */
            if( state->steepestdescentstep )
            {
                
                /*
                 * Algorithm is terminated
                 */
                state->repterminationtype = 7;
                goto lbl_40;
            }
            else
            {
                
                /*
                 * Re-evaluate active set and restart LBFGS
                 */
                goto lbl_40;
            }
        }
    }
    if( state->userterminationneeded )
    {
        goto lbl_40;
    }
    
    /*
     * Current point is updated:
     * * move XC/FC/GC to XP/FP/GP
     * * change current point remembered by SAS structure
     * * move XN/FN/GN to XC/FC/GC
     * * report current point and update iterations counter
     * * if MCINFO=1, push new pair SK/YK to LBFGS buffer
     */
    state->fp = state->fc;
    ae_v_move(&state->xp.ptr.p_double[0], 1, &state->sas.xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->fc = state->fn;
    ae_v_move(&state->cgc.ptr.p_double[0], 1, &state->cgn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->ugc.ptr.p_double[0], 1, &state->ugn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    actstatus = sasmoveto(&state->sas, &state->xn, state->cidx>=0&&ae_fp_greater_eq(state->stp,state->activationstep), state->cidx, state->cval, _state);
    if( !state->xrep )
    {
        goto lbl_52;
    }
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->sas.xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minbleic_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 21;
    goto lbl_rcomm;
lbl_21:
    state->xupdated = ae_false;
lbl_52:
    state->repinneriterationscount = state->repinneriterationscount+1;
    if( mcinfo==1 )
    {
        
        /*
         * Accept new LBFGS update given by Sk,Yk
         */
        if( state->bufsize==m )
        {
            
            /*
             * Buffer is full, shift contents by one row
             */
            for(i=0; i<=state->bufsize-1; i++)
            {
                ae_v_move(&state->bufsk.ptr.pp_double[i][0], 1, &state->bufsk.ptr.pp_double[i+1][0], 1, ae_v_len(0,n-1));
                ae_v_move(&state->bufyk.ptr.pp_double[i][0], 1, &state->bufyk.ptr.pp_double[i+1][0], 1, ae_v_len(0,n-1));
            }
            for(i=0; i<=state->bufsize-2; i++)
            {
                state->bufrho.ptr.p_double[i] = state->bufrho.ptr.p_double[i+1];
                state->buftheta.ptr.p_double[i] = state->buftheta.ptr.p_double[i+1];
            }
        }
        else
        {
            
            /*
             * Buffer is not full, increase buffer size by 1
             */
            state->bufsize = state->bufsize+1;
        }
        v = ae_v_dotproduct(&state->bufyk.ptr.pp_double[state->bufsize-1][0], 1, &state->bufsk.ptr.pp_double[state->bufsize-1][0], 1, ae_v_len(0,n-1));
        vv = ae_v_dotproduct(&state->bufyk.ptr.pp_double[state->bufsize-1][0], 1, &state->bufyk.ptr.pp_double[state->bufsize-1][0], 1, ae_v_len(0,n-1));
        if( ae_fp_eq(v,(double)(0))||ae_fp_eq(vv,(double)(0)) )
        {
            
            /*
             * Strange internal error in LBFGS - either YK=0
             * (which should not have been) or (SK,YK)=0 (again,
             * unexpected). It should not take place because
             * MCINFO=1, which signals "good" step. But just
             * to be sure we have special branch of code which
             * restarts LBFGS
             */
            goto lbl_40;
        }
        state->bufrho.ptr.p_double[state->bufsize-1] = (double)1/v;
        ae_assert(state->bufsize<=m, "MinBLEIC: internal error", _state);
        
        /*
         * Update length of the good step
         */
        v = (double)(0);
        vv = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_sqr((state->sas.xc.ptr.p_double[i]-state->xp.ptr.p_double[i])/state->s.ptr.p_double[i], _state);
            vv = vv+ae_sqr(state->sas.xc.ptr.p_double[i]-state->xp.ptr.p_double[i], _state);
        }
        state->lastgoodstep = ae_sqrt(vv, _state);
        minbleic_updateestimateofgoodstep(&state->lastscaledgoodstep, ae_sqrt(v, _state), _state);
    }
    
    /*
     * Check stopping criteria
     *
     * Step size and function-based stopping criteria are tested only
     * for step which satisfies Wolfe conditions and is the first step of
     * LBFGS (subsequent steps may accumulate active constraints thus
     * they should NOT be used for stopping; step size or function change
     * may be small when constrained, but these constraints may be
     * deactivated by the subsequent steps).
     *
     * MaxIts-based stopping condition is checked for all kinds of steps.
     */
    if( mcinfo==1&&state->steepestdescentstep )
    {
        
        /*
         * Step is small enough
         */
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_sqr((state->sas.xc.ptr.p_double[i]-state->xp.ptr.p_double[i])/state->s.ptr.p_double[i], _state);
        }
        v = ae_sqrt(v, _state);
        if( ae_fp_less_eq(v,state->epsx) )
        {
            state->repterminationtype = 2;
            goto lbl_40;
        }
        
        /*
         * Function change is small enough
         */
        if( ae_fp_less_eq(ae_fabs(state->fp-state->fc, _state),state->epsf*ae_maxreal(ae_fabs(state->fc, _state), ae_maxreal(ae_fabs(state->fp, _state), 1.0, _state), _state)) )
        {
            state->repterminationtype = 1;
            goto lbl_40;
        }
    }
    if( state->maxits>0&&state->repinneriterationscount>=state->maxits )
    {
        state->repterminationtype = 5;
        goto lbl_40;
    }
    
    /*
     * Clear "steepest descent" flag.
     */
    state->steepestdescentstep = ae_false;
    
    /*
     * Smooth reset (LBFGS memory model is refreshed) or hard restart:
     * * LBFGS model is refreshed, if line search was performed with activation of constraints
     * * algorithm is restarted if scaled gradient decreased below GDecay
     */
    if( actstatus>=0 )
    {
        state->bufsize = 0;
        goto lbl_39;
    }
    v = 0.0;
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr(state->cgc.ptr.p_double[i]*state->s.ptr.p_double[i], _state);
    }
    if( ae_fp_less(ae_sqrt(v, _state),gdecay*ginit) )
    {
        goto lbl_40;
    }
    goto lbl_39;
lbl_40:
    if( state->userterminationneeded )
    {
        
        /*
         * User requested termination
         */
        state->repterminationtype = 8;
        goto lbl_31;
    }
    if( state->repterminationtype!=0 )
    {
        
        /*
         * Algorithm terminated
         */
        goto lbl_31;
    }
    
    /*
     * Decrease decay coefficient. Subsequent L-BFGS stages will
     * have more stringent stopping criteria.
     */
    gdecay = ae_maxreal(gdecay*minbleic_decaycorrection, minbleic_mindecay, _state);
    goto lbl_30;
lbl_31:
    sasstopoptimization(&state->sas, _state);
    state->repouteriterationscount = 1;
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = n;
    state->rstate.ia.ptr.p_int[1] = m;
    state->rstate.ia.ptr.p_int[2] = i;
    state->rstate.ia.ptr.p_int[3] = j;
    state->rstate.ia.ptr.p_int[4] = mcinfo;
    state->rstate.ia.ptr.p_int[5] = actstatus;
    state->rstate.ia.ptr.p_int[6] = itidx;
    state->rstate.ba.ptr.p_bool[0] = b;
    state->rstate.ra.ptr.p_double[0] = v;
    state->rstate.ra.ptr.p_double[1] = vv;
    state->rstate.ra.ptr.p_double[2] = v0;
    state->rstate.ra.ptr.p_double[3] = penalty;
    state->rstate.ra.ptr.p_double[4] = ginit;
    state->rstate.ra.ptr.p_double[5] = gdecay;
    return result;
}


/*************************************************************************
This  function  activates/deactivates verification  of  the  user-supplied
analytic gradient.

Upon  activation  of  this  option  OptGuard  integrity  checker  performs
numerical differentiation of your target function  at  the  initial  point
(note: future versions may also perform check  at  the  final  point)  and
compares numerical gradient with analytic one provided by you.

If difference is too large, an error flag is set and optimization  session
continues. After optimization session is over, you can retrieve the report
which  stores  both  gradients  and  specific  components  highlighted  as
suspicious by the OptGuard.

The primary OptGuard report can be retrieved with minbleicoptguardresults().

IMPORTANT: gradient check is a high-overhead option which  will  cost  you
           about 3*N additional function evaluations. In many cases it may
           cost as much as the rest of the optimization session.
           
           YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO
           CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY.

NOTE: unlike previous incarnation of the gradient checking code,  OptGuard
      does NOT interrupt optimization even if it discovers bad gradient.
      
INPUT PARAMETERS:
    State       -   structure used to store algorithm state
    TestStep    -   verification step used for numerical differentiation:
                    * TestStep=0 turns verification off
                    * TestStep>0 activates verification
                    You should carefully choose TestStep. Value  which  is
                    too large (so large that  function  behavior  is  non-
                    cubic at this scale) will lead  to  false  alarms. Too
                    short step will result in rounding  errors  dominating
                    numerical derivative.
                    
                    You may use different step for different parameters by
                    means of setting scale with minbleicsetscale().

=== EXPLANATION ==========================================================                    

In order to verify gradient algorithm performs following steps:
  * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i],
    where X[i] is i-th component of the initial point and S[i] is a  scale
    of i-th parameter
  * F(X) is evaluated at these trial points
  * we perform one more evaluation in the middle point of the interval
  * we  build  cubic  model using function values and derivatives at trial
    points and we compare its prediction with actual value in  the  middle
    point

  -- ALGLIB --
     Copyright 15.06.2014 by Bochkanov Sergey
*************************************************************************/
void minbleicoptguardgradient(minbleicstate* state,
     double teststep,
     ae_state *_state)
{


    ae_assert(ae_isfinite(teststep, _state), "MinBLEICOptGuardGradient: TestStep contains NaN or INF", _state);
    ae_assert(ae_fp_greater_eq(teststep,(double)(0)), "MinBLEICOptGuardGradient: invalid argument TestStep(TestStep<0)", _state);
    state->teststep = teststep;
}


/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0)
b) nonsmooth     target function (non-C1)

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.
      
      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.
      
      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.
      
      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.
  
=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbleicoptguardsmoothness(minbleicstate* state,
     ae_int_t level,
     ae_state *_state)
{


    ae_assert(level==0||level==1, "MinBLEICOptGuardSmoothness: unexpected value of level parameter", _state);
    state->smoothnessguardlevel = level;
}


/*************************************************************************
Results of OptGuard integrity check, should be called  after  optimization
session is over.

=== PRIMARY REPORT =======================================================

OptGuard performs several checks which are intended to catch common errors
in the implementation of nonlinear function/gradient:
* incorrect analytic gradient
* discontinuous (non-C0) target functions (constraints)
* nonsmooth     (non-C1) target functions (constraints)

Each of these checks is activated with appropriate function:
* minbleicoptguardgradient() for gradient verification
* minbleicoptguardsmoothness() for C0/C1 checks

Following flags are set when these errors are suspected:
* rep.badgradsuspected, and additionally:
  * rep.badgradvidx for specific variable (gradient element) suspected
  * rep.badgradxbase, a point where gradient is tested
  * rep.badgraduser, user-provided gradient  (stored  as  2D  matrix  with
    single row in order to make  report  structure  compatible  with  more
    complex optimizers like MinNLC or MinLM)
  * rep.badgradnum,   reference    gradient    obtained    via   numerical
    differentiation (stored as  2D matrix with single row in order to make
    report structure compatible with more complex optimizers  like  MinNLC
    or MinLM)
* rep.nonc0suspected
* rep.nonc1suspected

=== ADDITIONAL REPORTS/LOGS ==============================================
    
Several different tests are performed to catch C0/C1 errors, you can  find
out specific test signaled error by looking to:
* rep.nonc0test0positive, for non-C0 test #0
* rep.nonc1test0positive, for non-C1 test #0
* rep.nonc1test1positive, for non-C1 test #1

Additional information (including line search logs)  can  be  obtained  by
means of:
* minbleicoptguardnonc1test0results()
* minbleicoptguardnonc1test1results()
which return detailed error reports, specific points where discontinuities
were found, and so on.

==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    rep     -   generic OptGuard report;  more  detailed  reports  can  be
                retrieved with other functions.

NOTE: false negatives (nonsmooth problems are not identified as  nonsmooth
      ones) are possible although unlikely.
      
      The reason  is  that  you  need  to  make several evaluations around
      nonsmoothness  in  order  to  accumulate  enough  information  about
      function curvature. Say, if you start right from the nonsmooth point,
      optimizer simply won't get enough data to understand what  is  going
      wrong before it terminates due to abrupt changes in the  derivative.
      It is also  possible  that  "unlucky"  step  will  move  us  to  the
      termination too quickly.
      
      Our current approach is to have less than 0.1%  false  negatives  in
      our test examples  (measured  with  multiple  restarts  from  random
      points), and to have exactly 0% false positives.
   
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbleicoptguardresults(minbleicstate* state,
     optguardreport* rep,
     ae_state *_state)
{

    _optguardreport_clear(rep);

    smoothnessmonitorexportreport(&state->smonitor, rep, _state);
}


/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #0

Nonsmoothness (non-C1) test #0 studies  function  values  (not  gradient!)
obtained during line searches and monitors  behavior  of  the  directional
derivative estimate.

This test is less powerful than test #1, but it does  not  depend  on  the
gradient values and thus it is more robust against artifacts introduced by
numerical differentiation.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], f[] - arrays of length CNT which store step lengths and  function
  values at these points; f[i] is evaluated in x0+stp[i]*d.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
  
==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #0 "strong" report
    lngrep  -   C1 test #0 "long" report
    
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbleicoptguardnonc1test0results(const minbleicstate* state,
     optguardnonc1test0report* strrep,
     optguardnonc1test0report* lngrep,
     ae_state *_state)
{

    _optguardnonc1test0report_clear(strrep);
    _optguardnonc1test0report_clear(lngrep);

    smoothnessmonitorexportc1test0report(&state->smonitor.nonc1test0strrep, &state->lastscaleused, strrep, _state);
    smoothnessmonitorexportc1test0report(&state->smonitor.nonc1test0lngrep, &state->lastscaleused, lngrep, _state);
}


/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #1

Nonsmoothness (non-C1)  test  #1  studies  individual  components  of  the
gradient computed during line search.

When precise analytic gradient is provided this test is more powerful than
test #0  which  works  with  function  values  and  ignores  user-provided
gradient.  However,  test  #0  becomes  more   powerful   when   numerical
differentiation is employed (in such cases test #1 detects  higher  levels
of numerical noise and becomes too conservative).

This test also tells specific components of the gradient which violate  C1
continuity, which makes it more informative than #0, which just tells that
continuity is violated.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* vidx - is an index of the variable in [0,N) with nonsmooth derivative
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], g[] - arrays of length CNT which store step lengths and  gradient
  values at these points; g[i] is evaluated in  x0+stp[i]*d  and  contains
  vidx-th component of the gradient.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
  
==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #1 "strong" report
    lngrep  -   C1 test #1 "long" report
    
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbleicoptguardnonc1test1results(minbleicstate* state,
     optguardnonc1test1report* strrep,
     optguardnonc1test1report* lngrep,
     ae_state *_state)
{

    _optguardnonc1test1report_clear(strrep);
    _optguardnonc1test1report_clear(lngrep);

    smoothnessmonitorexportc1test1report(&state->smonitor.nonc1test1strrep, &state->lastscaleused, strrep, _state);
    smoothnessmonitorexportc1test1report(&state->smonitor.nonc1test1lngrep, &state->lastscaleused, lngrep, _state);
}


/*************************************************************************
BLEIC results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report. You should check Rep.TerminationType
                in  order  to  distinguish  successful  termination  from
                unsuccessful one:
                * -8    internal integrity control  detected  infinite or
                        NAN   values   in   function/gradient.   Abnormal
                        termination signalled.
                * -3   inconsistent constraints. Feasible point is
                       either nonexistent or too hard to find. Try to
                       restart optimizer with better initial approximation
                *  1   relative function improvement is no more than EpsF.
                *  2   scaled step is no more than EpsX.
                *  4   scaled gradient norm is no more than EpsG.
                *  5   MaxIts steps was taken
                *  8   terminated by user who called minbleicrequesttermination().
                       X contains point which was "current accepted"  when
                       termination request was submitted.
                More information about fields of this  structure  can  be
                found in the comments on MinBLEICReport datatype.
   
  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicresults(const minbleicstate* state,
     /* Real    */ ae_vector* x,
     minbleicreport* rep,
     ae_state *_state)
{

    ae_vector_clear(x);
    _minbleicreport_clear(rep);

    minbleicresultsbuf(state, x, rep, _state);
}


/*************************************************************************
BLEIC results

Buffered implementation of MinBLEICResults() which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicresultsbuf(const minbleicstate* state,
     /* Real    */ ae_vector* x,
     minbleicreport* rep,
     ae_state *_state)
{
    ae_int_t i;


    if( x->cnt<state->nmain )
    {
        ae_vector_set_length(x, state->nmain, _state);
    }
    rep->iterationscount = state->repinneriterationscount;
    rep->inneriterationscount = state->repinneriterationscount;
    rep->outeriterationscount = state->repouteriterationscount;
    rep->nfev = state->repnfev;
    rep->varidx = state->repvaridx;
    rep->terminationtype = state->repterminationtype;
    if( state->repterminationtype>0 )
    {
        ae_v_move(&x->ptr.p_double[0], 1, &state->sas.xc.ptr.p_double[0], 1, ae_v_len(0,state->nmain-1));
    }
    else
    {
        for(i=0; i<=state->nmain-1; i++)
        {
            x->ptr.p_double[i] = _state->v_nan;
        }
    }
    rep->debugeqerr = state->repdebugeqerr;
    rep->debugfs = state->repdebugfs;
    rep->debugff = state->repdebugff;
    rep->debugdx = state->repdebugdx;
    rep->debugfeasqpits = state->repdebugfeasqpits;
    rep->debugfeasgpaits = state->repdebugfeasgpaits;
}


/*************************************************************************
This subroutine restarts algorithm from new point.
All optimization parameters (including constraints) are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have  same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinBLEICCreate call.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicrestartfrom(minbleicstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;


    n = state->nmain;
    
    /*
     * First, check for errors in the inputs
     */
    ae_assert(x->cnt>=n, "MinBLEICRestartFrom: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinBLEICRestartFrom: X contains infinite or NaN values!", _state);
    
    /*
     * Set XC
     */
    ae_v_move(&state->xstart.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
    
    /*
     * prepare RComm facilities
     */
    ae_vector_set_length(&state->rstate.ia, 6+1, _state);
    ae_vector_set_length(&state->rstate.ba, 0+1, _state);
    ae_vector_set_length(&state->rstate.ra, 5+1, _state);
    state->rstate.stage = -1;
    minbleic_clearrequestfields(state, _state);
    sasstopoptimization(&state->sas, _state);
}


/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.
      
NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void minbleicrequesttermination(minbleicstate* state, ae_state *_state)
{


    state->userterminationneeded = ae_true;
}


/*************************************************************************
This subroutine finalizes internal structures after emergency  termination
from State.LSStart report (see comments on MinBLEICState for more information).

INPUT PARAMETERS:
    State   -   structure after exit from LSStart report

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicemergencytermination(minbleicstate* state, ae_state *_state)
{


    sasstopoptimization(&state->sas, _state);
}


/*************************************************************************
Set V1 reverse communication protocol
*************************************************************************/
void minbleicsetprotocolv1(minbleicstate* state, ae_state *_state)
{


    state->protocolversion = 1;
    ae_vector_set_length(&state->rstate.ia, 6+1, _state);
    ae_vector_set_length(&state->rstate.ba, 0+1, _state);
    ae_vector_set_length(&state->rstate.ra, 5+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Clears request fileds (to be sure that we don't forget to clear something)
*************************************************************************/
static void minbleic_clearrequestfields(minbleicstate* state,
     ae_state *_state)
{


    state->needf = ae_false;
    state->needfg = ae_false;
    state->xupdated = ae_false;
    state->lsstart = ae_false;
}


/*************************************************************************
Internal initialization subroutine
*************************************************************************/
static void minbleic_minbleicinitinternal(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minbleicstate* state,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_matrix c;
    ae_vector ct;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);

    
    /*
     * Initialize
     */
    state->protocolversion = 1;
    state->teststep = (double)(0);
    state->smoothnessguardlevel = 0;
    smoothnessmonitorinit(&state->smonitor, &state->s, 0, 0, ae_false, _state);
    state->nmain = n;
    state->diffstep = diffstep;
    sasinit(n, &state->sas, _state);
    ae_vector_set_length(&state->bndl, n, _state);
    ae_vector_set_length(&state->hasbndl, n, _state);
    ae_vector_set_length(&state->bndu, n, _state);
    ae_vector_set_length(&state->hasbndu, n, _state);
    ae_vector_set_length(&state->xstart, n, _state);
    ae_vector_set_length(&state->cgc, n, _state);
    ae_vector_set_length(&state->ugc, n, _state);
    ae_vector_set_length(&state->xn, n, _state);
    ae_vector_set_length(&state->cgn, n, _state);
    ae_vector_set_length(&state->ugn, n, _state);
    ae_vector_set_length(&state->xp, n, _state);
    ae_vector_set_length(&state->d, n, _state);
    ae_vector_set_length(&state->s, n, _state);
    ae_vector_set_length(&state->invs, n, _state);
    ae_vector_set_length(&state->lastscaleused, n, _state);
    ae_vector_set_length(&state->x, n, _state);
    ae_vector_set_length(&state->g, n, _state);
    ae_vector_set_length(&state->work, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->bndl.ptr.p_double[i] = _state->v_neginf;
        state->hasbndl.ptr.p_bool[i] = ae_false;
        state->bndu.ptr.p_double[i] = _state->v_posinf;
        state->hasbndu.ptr.p_bool[i] = ae_false;
        state->s.ptr.p_double[i] = 1.0;
        state->invs.ptr.p_double[i] = 1.0;
        state->lastscaleused.ptr.p_double[i] = 1.0;
    }
    minbleicsetlc(state, &c, &ct, 0, _state);
    minbleicsetcond(state, 0.0, 0.0, 0.0, 0, _state);
    minbleicsetxrep(state, ae_false, _state);
    minbleicsetdrep(state, ae_false, _state);
    minbleicsetstpmax(state, 0.0, _state);
    minbleicsetprecdefault(state, _state);
    minbleicrestartfrom(state, x, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
This subroutine updates estimate of the good step length given:
1) previous estimate
2) new length of the good step

It makes sure that estimate does not change too rapidly - ratio of new and
old estimates will be at least 0.01, at most 100.0

In case previous estimate of good step is zero (no estimate), new estimate
is used unconditionally.

  -- ALGLIB --
     Copyright 16.01.2013 by Bochkanov Sergey
*************************************************************************/
static void minbleic_updateestimateofgoodstep(double* estimate,
     double newstep,
     ae_state *_state)
{


    if( ae_fp_eq(*estimate,(double)(0)) )
    {
        *estimate = newstep;
        return;
    }
    if( ae_fp_less(newstep,*estimate*0.01) )
    {
        *estimate = *estimate*0.01;
        return;
    }
    if( ae_fp_greater(newstep,*estimate*(double)100) )
    {
        *estimate = *estimate*(double)100;
        return;
    }
    *estimate = newstep;
}


void _minbleicstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minbleicstate *p = (minbleicstate*)_p;
    ae_touch_ptr((void*)p);
    _sactiveset_init(&p->sas, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagh, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    ae_vector_init(&p->ugc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cgc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ugn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cgn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->cleic, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xstart, 0, DT_REAL, _state, make_automatic);
    _snnlssolver_init(&p->solver, _state, make_automatic);
    ae_vector_init(&p->tmpprec, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->work, 0, DT_REAL, _state, make_automatic);
    _linminstate_init(&p->lstate, _state, make_automatic);
    ae_matrix_init(&p->bufyk, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->bufsk, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bufrho, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->buftheta, 0, DT_REAL, _state, make_automatic);
    _smoothnessmonitor_init(&p->smonitor, _state, make_automatic);
    ae_vector_init(&p->lastscaleused, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->invs, 0, DT_REAL, _state, make_automatic);
}


void _minbleicstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minbleicstate       *dst = (minbleicstate*)_dst;
    const minbleicstate *src = (const minbleicstate*)_src;
    dst->nmain = src->nmain;
    dst->nslack = src->nslack;
    dst->epsg = src->epsg;
    dst->epsf = src->epsf;
    dst->epsx = src->epsx;
    dst->maxits = src->maxits;
    dst->xrep = src->xrep;
    dst->drep = src->drep;
    dst->stpmax = src->stpmax;
    dst->diffstep = src->diffstep;
    _sactiveset_init_copy(&dst->sas, &src->sas, _state, make_automatic);
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    dst->prectype = src->prectype;
    ae_vector_init_copy(&dst->diagh, &src->diagh, _state, make_automatic);
    dst->protocolversion = src->protocolversion;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    dst->f = src->f;
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    dst->needf = src->needf;
    dst->needfg = src->needfg;
    dst->xupdated = src->xupdated;
    dst->lsstart = src->lsstart;
    dst->steepestdescentstep = src->steepestdescentstep;
    dst->boundedstep = src->boundedstep;
    dst->userterminationneeded = src->userterminationneeded;
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    ae_vector_init_copy(&dst->ugc, &src->ugc, _state, make_automatic);
    ae_vector_init_copy(&dst->cgc, &src->cgc, _state, make_automatic);
    ae_vector_init_copy(&dst->xn, &src->xn, _state, make_automatic);
    ae_vector_init_copy(&dst->ugn, &src->ugn, _state, make_automatic);
    ae_vector_init_copy(&dst->cgn, &src->cgn, _state, make_automatic);
    ae_vector_init_copy(&dst->xp, &src->xp, _state, make_automatic);
    dst->fc = src->fc;
    dst->fn = src->fn;
    dst->fp = src->fp;
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    ae_matrix_init_copy(&dst->cleic, &src->cleic, _state, make_automatic);
    dst->nec = src->nec;
    dst->nic = src->nic;
    dst->lastgoodstep = src->lastgoodstep;
    dst->lastscaledgoodstep = src->lastscaledgoodstep;
    dst->maxscaledgrad = src->maxscaledgrad;
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    dst->repinneriterationscount = src->repinneriterationscount;
    dst->repouteriterationscount = src->repouteriterationscount;
    dst->repnfev = src->repnfev;
    dst->repvaridx = src->repvaridx;
    dst->repterminationtype = src->repterminationtype;
    dst->repdebugeqerr = src->repdebugeqerr;
    dst->repdebugfs = src->repdebugfs;
    dst->repdebugff = src->repdebugff;
    dst->repdebugdx = src->repdebugdx;
    dst->repdebugfeasqpits = src->repdebugfeasqpits;
    dst->repdebugfeasgpaits = src->repdebugfeasgpaits;
    ae_vector_init_copy(&dst->xstart, &src->xstart, _state, make_automatic);
    _snnlssolver_init_copy(&dst->solver, &src->solver, _state, make_automatic);
    dst->fbase = src->fbase;
    dst->fm2 = src->fm2;
    dst->fm1 = src->fm1;
    dst->fp1 = src->fp1;
    dst->fp2 = src->fp2;
    dst->xm1 = src->xm1;
    dst->xp1 = src->xp1;
    dst->gm1 = src->gm1;
    dst->gp1 = src->gp1;
    dst->cidx = src->cidx;
    dst->cval = src->cval;
    ae_vector_init_copy(&dst->tmpprec, &src->tmpprec, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    dst->nfev = src->nfev;
    dst->mcstage = src->mcstage;
    dst->stp = src->stp;
    dst->curstpmax = src->curstpmax;
    dst->activationstep = src->activationstep;
    ae_vector_init_copy(&dst->work, &src->work, _state, make_automatic);
    _linminstate_init_copy(&dst->lstate, &src->lstate, _state, make_automatic);
    dst->trimthreshold = src->trimthreshold;
    dst->nonmonotoniccnt = src->nonmonotoniccnt;
    ae_matrix_init_copy(&dst->bufyk, &src->bufyk, _state, make_automatic);
    ae_matrix_init_copy(&dst->bufsk, &src->bufsk, _state, make_automatic);
    ae_vector_init_copy(&dst->bufrho, &src->bufrho, _state, make_automatic);
    ae_vector_init_copy(&dst->buftheta, &src->buftheta, _state, make_automatic);
    dst->bufsize = src->bufsize;
    dst->teststep = src->teststep;
    dst->smoothnessguardlevel = src->smoothnessguardlevel;
    _smoothnessmonitor_init_copy(&dst->smonitor, &src->smonitor, _state, make_automatic);
    ae_vector_init_copy(&dst->lastscaleused, &src->lastscaleused, _state, make_automatic);
    ae_vector_init_copy(&dst->invs, &src->invs, _state, make_automatic);
}


void _minbleicstate_clear(void* _p)
{
    minbleicstate *p = (minbleicstate*)_p;
    ae_touch_ptr((void*)p);
    _sactiveset_clear(&p->sas);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->diagh);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->g);
    _rcommstate_clear(&p->rstate);
    ae_vector_clear(&p->ugc);
    ae_vector_clear(&p->cgc);
    ae_vector_clear(&p->xn);
    ae_vector_clear(&p->ugn);
    ae_vector_clear(&p->cgn);
    ae_vector_clear(&p->xp);
    ae_vector_clear(&p->d);
    ae_matrix_clear(&p->cleic);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->xstart);
    _snnlssolver_clear(&p->solver);
    ae_vector_clear(&p->tmpprec);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->work);
    _linminstate_clear(&p->lstate);
    ae_matrix_clear(&p->bufyk);
    ae_matrix_clear(&p->bufsk);
    ae_vector_clear(&p->bufrho);
    ae_vector_clear(&p->buftheta);
    _smoothnessmonitor_clear(&p->smonitor);
    ae_vector_clear(&p->lastscaleused);
    ae_vector_clear(&p->invs);
}


void _minbleicstate_destroy(void* _p)
{
    minbleicstate *p = (minbleicstate*)_p;
    ae_touch_ptr((void*)p);
    _sactiveset_destroy(&p->sas);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->diagh);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->g);
    _rcommstate_destroy(&p->rstate);
    ae_vector_destroy(&p->ugc);
    ae_vector_destroy(&p->cgc);
    ae_vector_destroy(&p->xn);
    ae_vector_destroy(&p->ugn);
    ae_vector_destroy(&p->cgn);
    ae_vector_destroy(&p->xp);
    ae_vector_destroy(&p->d);
    ae_matrix_destroy(&p->cleic);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->xstart);
    _snnlssolver_destroy(&p->solver);
    ae_vector_destroy(&p->tmpprec);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->work);
    _linminstate_destroy(&p->lstate);
    ae_matrix_destroy(&p->bufyk);
    ae_matrix_destroy(&p->bufsk);
    ae_vector_destroy(&p->bufrho);
    ae_vector_destroy(&p->buftheta);
    _smoothnessmonitor_destroy(&p->smonitor);
    ae_vector_destroy(&p->lastscaleused);
    ae_vector_destroy(&p->invs);
}


void _minbleicreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minbleicreport *p = (minbleicreport*)_p;
    ae_touch_ptr((void*)p);
}


void _minbleicreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minbleicreport       *dst = (minbleicreport*)_dst;
    const minbleicreport *src = (const minbleicreport*)_src;
    dst->iterationscount = src->iterationscount;
    dst->nfev = src->nfev;
    dst->varidx = src->varidx;
    dst->terminationtype = src->terminationtype;
    dst->debugeqerr = src->debugeqerr;
    dst->debugfs = src->debugfs;
    dst->debugff = src->debugff;
    dst->debugdx = src->debugdx;
    dst->debugfeasqpits = src->debugfeasqpits;
    dst->debugfeasgpaits = src->debugfeasgpaits;
    dst->inneriterationscount = src->inneriterationscount;
    dst->outeriterationscount = src->outeriterationscount;
}


void _minbleicreport_clear(void* _p)
{
    minbleicreport *p = (minbleicreport*)_p;
    ae_touch_ptr((void*)p);
}


void _minbleicreport_destroy(void* _p)
{
    minbleicreport *p = (minbleicreport*)_p;
    ae_touch_ptr((void*)p);
}


#endif
#if defined(AE_COMPILE_MINNLC) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
                  NONLINEARLY  CONSTRAINED  OPTIMIZATION

DESCRIPTION:
The  subroutine  minimizes a function  F(x)  of N arguments subject to the
any combination of the:
* box constraints
* linear equality/inequality/range constraints CL<=Ax<=CU
* nonlinear equality/inequality/range constraints HL<=Hi(x)<=HU

REQUIREMENTS:
* F(), H() are continuously differentiable on the  feasible  set  and  its
  neighborhood
* starting point X0, which can be infeasible

USAGE:

Here we give the very brief outline  of  the MinNLC optimizer. We strongly
recommend you to study examples in the ALGLIB Reference Manual and to read
ALGLIB User Guide: https://www.alglib.net/nonlinear-programming/

1. The user initializes the solver with minnlccreate() or minnlccreatef(),
   depending on the specific solver chosen:
   * minnlccreate() for solvers capable of handling the  problem  'as is',
     without relying  on numerical differentiation (SQP/GIPM2 for problems
     with analytic derivatives, ORBIT for derivative-free problems)
   * minnlccreatef(), when a numerical differentiation is used to  provide
     derivatives to a smooth derivative-based solver (SQP or GIPM2).
   
   In the current release the following solvers can be used:
   
   * sparse large-scale filter-based SQP solver, recommended  for problems
     of any size (from several  variables  to  hundreds  of  thousands  of
     variables). Good at warm-starts. Activated with the minnlcsetalgosqp()
     function.
   
   * sparse large-scale nonlinear nonconvex interior point method (GIPM2),
     recommended  for  problems  of  any  size  (from several variables to
     hundreds of thousands of variables).  Has  lower  iteration  overhead
     than SQP, but is worse at using good initial  points  (it  will  need
     at  least  tens  of  iterations even when started from the solution).
     Activated with minnlcsetalgogipm2() function.
     
   * dense SQP-BFGS solver, recommended  for small-scale problems  (up  to
     several hundreds of variables). Requires  less  function  evaluations
     than SQP, but has more expensive iteration.
     Activated with minnlcsetalgosqpbfgs() function.
   
   * ORBIT, a model-based derivative  free  solver  that  uses  local  RBF
     models to optimize expensive objectives.  This  solver  is  activated
     with minnlcsetalgoorbit() function.
     
   * several other solvers, including legacy ones

2. [optional] user activates OptGuard  integrity checker  which  tries  to
   detect possible errors in the user-supplied callbacks:
   * discontinuity/nonsmoothness of the target/nonlinear constraints
   * errors in the analytic gradient provided by user
   This feature is essential for early prototyping stages because it helps
   to catch common coding and problem statement errors.
   OptGuard can be activated with following functions (one per each  check
   performed):
   * minnlcoptguardsmoothness()
   * minnlcoptguardgradient()

3. User adds boundary and/or linear and/or nonlinear constraints by  means
   of calling one of the following functions:
   a) minnlcsetbc() for boundary constraints
   b) minnlcsetlc2() for sparse two-sided linear constraints,
      minnlcsetlc2dense() for dense two-sided linear constraints,
      minnlcsetlc2mixed() for mixed sparse/dense two-sided linear constraints
    * minqpaddlc2dense()  to add one dense row to the dense constraint submatrix
    * minqpaddlc2()       to add one sparse row to the sparse constraint submatrix
    * minqpaddlc2sparsefromdense() to add one sparse row (passed as a dense array) to the sparse constraint submatrix
   c) minnlcsetnlc2() for nonlinear constraints
   You may combine (a), (b) and (c) in one optimization problem.
   
4. User sets scale of the variables with minnlcsetscale() function. It  is
   VERY important to set  scale  of  the  variables,  because  nonlinearly
   constrained problems are hard to solve when variables are badly scaled.
   Knowing  variable   scales   helps   to  check  stopping  criteria  and
   precondition the solver.

5. User sets stopping conditions with minnlcsetcond3() or minnlcsetcond().
   If NLC solver uses inner/outer  iteration  layout,  this  function sets
   stopping conditions for INNER iterations.
   
6. Finally, user calls minnlcoptimize()  function  which  takes  algorithm
   state and pointer (delegate, etc.) to callback function which calculates
   F/G/H.

7. User calls  minnlcresults()  to  get  solution;  additionally  you  can
   retrieve OptGuard report with minnlcoptguardresults(), and get detailed
   report about purported errors in the target function with:
   * minnlcoptguardnonc1test0results()
   * minnlcoptguardnonc1test1results()

8. Optionally user may call minnlcrestartfrom() to solve  another  problem
   with same N but another starting point. minnlcrestartfrom()  allows  to
   reuse already initialized structure.


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size ofX
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlccreate(ae_int_t n,
     /* Real    */ const ae_vector* x,
     minnlcstate* state,
     ae_state *_state)
{

    _minnlcstate_clear(state);

    ae_assert(n>=1, "MinNLCCreate: N<1", _state);
    ae_assert(x->cnt>=n, "MinNLCCreate: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinNLCCreate: X contains infinite or NaN values", _state);
    minnlc_minnlcinitinternal(n, x, 0.0, state, _state);
}


/*************************************************************************
Buffered version of minnlccreate() which reuses already  allocated  memory
as much as possible.

  -- ALGLIB --
     Copyright 06.10.2024 by Bochkanov Sergey
*************************************************************************/
void minnlccreatebuf(ae_int_t n,
     /* Real    */ const ae_vector* x,
     minnlcstate* state,
     ae_state *_state)
{


    ae_assert(n>=1, "MinNLCCreateBuf: N<1", _state);
    ae_assert(x->cnt>=n, "MinNLCCreateBuf: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinNLCCreateBuf: X contains infinite or NaN values", _state);
    minnlc_minnlcinitinternal(n, x, 0.0, state, _state);
}


/*************************************************************************
This subroutine is a finite  difference variant of MinNLCCreate(). It uses
finite differences in order to differentiate target function.

This function is relevant when you want to solve a derivative-free problem
using a solver that relies on gradient info being available (e.g.  SQP  or
GIPM2). Do not use it for derivative-free solvers  like  ORBIT,  the  algo
will generate an exception during optimization.

Description below contains information which is specific to this  function
only. We recommend to read comments on MinNLCCreate() in order to get more
information about creation of the NLC optimizer.

CALLBACK PARALLELISM

The MINNLC optimizer supports parallel parallel  numerical differentiation
('callback parallelism'). This feature, which  is  present  in  commercial
ALGLIB  editions,  greatly   accelerates   optimization   with   numerical
differentiation of an expensive target functions.

Callback parallelism is usually  beneficial  when  computing  a  numerical
gradient requires more than several  milliseconds.  In this case  the  job
of computing individual gradient components can be split between  multiple
threads. Even inexpensive targets can benefit  from  parallelism,  if  you
have many variables.

ALGLIB Reference Manual, 'Working with commercial  version' section, tells
how to activate callback parallelism for your programming language.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size ofX
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.
    DiffStep-   differentiation step, >0.
                By default, a 5-point formula is used  (actually,  only  4
                function values per variable are used because  the central
                one has zero coefficient due to symmetry; that's why  this
                formula is often called a 4-point one). It can be  changed
                with minnlcsetnumdiff() function.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

NOTES:

1. the differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinNLCSetScale() call.
   
2. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large TRUNCATION  errors, while too small
   step will result in too large NUMERICAL  errors.  1.0E-4  can  be  good
   value to start from.
   
3. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs ~N function evaluations. This function will work  for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   
   We should also say that code which relies on numerical  differentiation
   is  less   robust   and  precise.  Imprecise  gradient  may  slow  down
   convergence, especially  on  highly  nonlinear  problems  or  near  the
   solution.
   
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlccreatef(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minnlcstate* state,
     ae_state *_state)
{

    _minnlcstate_clear(state);

    ae_assert(n>=1, "MinNLCCreateF: N<1", _state);
    ae_assert(x->cnt>=n, "MinNLCCreateF: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinNLCCreateF: X contains infinite or NaN values", _state);
    ae_assert(ae_isfinite(diffstep, _state), "MinNLCCreateF: DiffStep is infinite or NaN!", _state);
    ae_assert(ae_fp_greater(diffstep,(double)(0)), "MinNLCCreateF: DiffStep is non-positive!", _state);
    minnlc_minnlcinitinternal(n, x, diffstep, state, _state);
}


/*************************************************************************
Buffered version of minnlccreatef() which reuses already allocated memory
as much as possible.

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlccreatefbuf(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minnlcstate* state,
     ae_state *_state)
{


    ae_assert(n>=1, "MinNLCCreateFBuf: N<1", _state);
    ae_assert(x->cnt>=n, "MinNLCCreateFBuf: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinNLCCreateFBuf: X contains infinite or NaN values", _state);
    ae_assert(ae_isfinite(diffstep, _state), "MinNLCCreateFBuf: DiffStep is infinite or NaN!", _state);
    ae_assert(ae_fp_greater(diffstep,(double)(0)), "MinNLCCreateFBuf: DiffStep is non-positive!", _state);
    minnlc_minnlcinitinternal(n, x, diffstep, state, _state);
}


/*************************************************************************
This function sets boundary constraints for NLC optimizer.

Boundary constraints are inactive by  default  (after  initial  creation).
They are preserved after algorithm restart with  MinNLCRestartFrom().

You may combine boundary constraints with  general  linear ones - and with
nonlinear ones! Boundary constraints are  handled  more  efficiently  than
other types.  Thus,  if  your  problem  has  mixed  constraints,  you  may
explicitly specify some of them as boundary and save some time/space.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF.

NOTE 1:  it is possible to specify  BndL[i]=BndU[i].  In  this  case  I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

NOTE 2:  when you solve your problem  with  augmented  Lagrangian  solver,
         boundary constraints are  satisfied  only  approximately!  It  is
         possible   that  algorithm  will  evaluate  function  outside  of
         feasible area!

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcsetbc(minnlcstate* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_assert(bndl->cnt>=n, "MinNLCSetBC: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=n, "MinNLCSetBC: Length(BndU)<N", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "MinNLCSetBC: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "MinNLCSetBC: BndL contains NAN or -INF", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function sets linear constraints for MinNLC optimizer.

Linear constraints are inactive by default (after initial creation).  They
are preserved after algorithm restart with MinNLCRestartFrom().

You may combine linear constraints with boundary ones - and with nonlinear
ones! If your problem has mixed constraints, you  may  explicitly  specify
some of them as linear. It  may  help  optimizer   to   handle  them  more
efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinNLCCreate call.
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

NOTE 1: when you solve your problem  with  augmented  Lagrangian   solver,
        linear constraints are  satisfied  only   approximately!   It   is
        possible   that  algorithm  will  evaluate  function  outside   of
        feasible area!

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcsetlc(minnlcstate* state,
     /* Real    */ const ae_matrix* c,
     /* Integer */ const ae_vector* ct,
     ae_int_t k,
     ae_state *_state)
{


    ae_assert(k>=0, "MinNLCSetLC: K<0", _state);
    ae_assert(c->cols>=state->n+1||k==0, "MinNLCSetLC: Cols(C)<N+1", _state);
    ae_assert(c->rows>=k, "MinNLCSetLC: Rows(C)<K", _state);
    ae_assert(ct->cnt>=k, "MinNLCSetLC: Length(CT)<K", _state);
    ae_assert(apservisfinitematrix(c, k, state->n+1, _state), "MinNLCSetLC: C contains infinite or NaN values!", _state);
    xlcsetlcmixed(&state->xlc, &state->tmps1, ct, 0, c, ct, k, _state);
}


/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU with dense
constraint matrix A.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 15.04.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcsetlc2dense(minnlcstate* state,
     /* Real    */ const ae_matrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t k,
     ae_state *_state)
{


    xlcsetlc2mixed(&state->xlc, &state->tmps1, 0, a, k, al, au, _state);
}


/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
a sparse constraining matrix A. Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    A       -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 15.04.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcsetlc2(minnlcstate* state,
     const sparsematrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t k,
     ae_state *_state)
{


    xlcsetlc2mixed(&state->xlc, a, k, &state->tmpj1, 0, al, au, _state);
}


/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
a mixed constraining matrix A including a sparse part (first SparseK rows)
and a dense part (last DenseK rows). Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

This function may be useful if constraint matrix includes large number  of
both types of rows - dense and sparse. If you have just a few sparse rows,
you  may  represent  them  in  dense  format  without losing  performance.
Similarly, if you have just a few dense rows, you may store them in sparse
format with almost same performance.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    SparseA -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    SparseK -   number of sparse constraints, SparseK>=0
    DenseA  -   linear constraints, array[K,N], set of dense constraints.
                Each row of A represents one general linear constraint.
    DenseK  -   number of dense constraints, DenseK>=0
    AL, AU  -   lower and upper bounds, array[SparseK+DenseK], with former
                SparseK elements corresponding to sparse constraints,  and
                latter DenseK elements corresponding to dense constraints;
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 15.04.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcsetlc2mixed(minnlcstate* state,
     const sparsematrix* sparsea,
     ae_int_t ksparse,
     /* Real    */ const ae_matrix* densea,
     ae_int_t kdense,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_state *_state)
{


    xlcsetlc2mixed(&state->xlc, sparsea, ksparse, densea, kdense, al, au, _state);
}


/*************************************************************************
This function appends a two-sided linear constraint AL <= A*x <= AU to the
matrix of dense constraints.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    A       -   linear constraint coefficient, array[N], right side is NOT
                included.
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint Ai*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint Ai*x<=AU
                * AU=+INF  => one-sided constraint AL<=Ai*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 15.04.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcaddlc2dense(minnlcstate* state,
     /* Real    */ const ae_vector* a,
     double al,
     double au,
     ae_state *_state)
{


    xlcaddlc2dense(&state->xlc, a, al, au, _state);
}


/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present sparse constraints.

Constraint is passed in compressed format: as list of non-zero entries  of
coefficient vector A. Such approach is more efficient than  dense  storage
for highly sparse constraint vectors.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    IdxA    -   array[NNZ], indexes of non-zero elements of A:
                * can be unsorted
                * can include duplicate indexes (corresponding entries  of
                  ValA[] will be summed)
    ValA    -   array[NNZ], values of non-zero elements of A
    NNZ     -   number of non-zero coefficients in A
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcaddlc2(minnlcstate* state,
     /* Integer */ const ae_vector* idxa,
     /* Real    */ const ae_vector* vala,
     ae_int_t nnz,
     double al,
     double au,
     ae_state *_state)
{


    xlcaddlc2(&state->xlc, idxa, vala, nnz, al, au, _state);
}


/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present sparse constraints.

Constraint vector A is  passed  as  a  dense  array  which  is  internally
sparsified by this function.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnlccreate() call.
    DA      -   array[N], constraint vector
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcaddlc2sparsefromdense(minnlcstate* state,
     /* Real    */ const ae_vector* da,
     double al,
     double au,
     ae_state *_state)
{


    xlcaddlc2sparsefromdense(&state->xlc, da, al, au, _state);
}


/*************************************************************************
This function sets nonlinear constraints for MinNLC optimizer.

It sets constraints of the form

    Ci(x)=0 for i=0..NLEC-1
    Ci(x)<=0 for i=NLEC..NLEC+NLIC-1

See MinNLCSetNLC2() for a modern function which allows greater flexibility
in the constraint specification.

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcsetnlc(minnlcstate* state,
     ae_int_t nlec,
     ae_int_t nlic,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(nlec>=0, "MinNLCSetNLC: NLEC<0", _state);
    ae_assert(nlic>=0, "MinNLCSetNLC: NLIC<0", _state);
    state->nnlc = nlec+nlic;
    rallocv(state->nnlc, &state->nl, _state);
    rallocv(state->nnlc, &state->nu, _state);
    for(i=0; i<=nlec-1; i++)
    {
        state->nl.ptr.p_double[i] = (double)(0);
        state->nu.ptr.p_double[i] = (double)(0);
    }
    for(i=nlec; i<=nlec+nlic-1; i++)
    {
        state->nl.ptr.p_double[i] = _state->v_neginf;
        state->nu.ptr.p_double[i] = (double)(0);
    }
}


/*************************************************************************
This function sets two-sided nonlinear constraints for MinNLC optimizer.

In fact, this function sets  only  constraints  COUNT  and  their  BOUNDS.
Constraints  themselves  (constraint  functions)   are   passed   to   the
MinNLCOptimize() method as callbacks.

MinNLCOptimize() method accepts a user-defined vector function F[] and its
Jacobian J[], where:
* first element of F[] and first row of J[] correspond to the target
* subsequent NNLC components of F[] (and rows of J[]) correspond  to  two-
  sided nonlinear constraints NL<=C(x)<=NU, where
  * NL[i]=NU[i] => I-th row is an equality constraint Ci(x)=NL
  * NL[i]<NU[i] => I-th tow is a  two-sided constraint NL[i]<=Ci(x)<=NU[i]
  * NL[i]=-INF  => I-th row is an one-sided constraint Ci(x)<=NU[i]
  * NU[i]=+INF  => I-th row is an one-sided constraint NL[i]<=Ci(x)
  * NL[i]=-INF, NU[i]=+INF => constraint is ignored

NOTE: you may combine nonlinear constraints with linear/boundary ones.  If
      your problem has mixed constraints, you  may explicitly specify some
      of them as linear or box ones.
      It helps optimizer to handle them more efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinNLCCreate call.
    NL      -   array[NNLC], lower bounds, can contain -INF
    NU      -   array[NNLC], lower bounds, can contain +INF
    NNLC    -   constraints count, NNLC>=0

NOTE 1: nonlinear constraints are satisfied only  approximately!   It   is
        possible that the algorithm will evaluate the function  outside of
        the feasible area!
        
NOTE 2: algorithm scales variables  according  to the scale  specified by
        MinNLCSetScale()  function,  so it can handle problems with badly
        scaled variables (as long as we KNOW their scales).
           
        However,  there  is  no  way  to  automatically  scale   nonlinear
        constraints. Inappropriate scaling  of nonlinear  constraints  may
        ruin convergence. Solving problem with  constraint  "1000*G0(x)=0"
        is NOT the same as solving it with constraint "0.001*G0(x)=0".
           
        It means that YOU are  the  one who is responsible for the correct
        scaling of the nonlinear constraints Gi(x) and Hi(x). We recommend
        you to scale nonlinear constraints in such a way that the Jacobian
        rows have approximately unit magnitude  (for  problems  with  unit
        scale) or have magnitude approximately equal to 1/S[i] (where S is
        a scale set by MinNLCSetScale() function).

  -- ALGLIB --
     Copyright 23.09.2023 by Bochkanov Sergey
*************************************************************************/
void minnlcsetnlc2(minnlcstate* state,
     /* Real    */ const ae_vector* nl,
     /* Real    */ const ae_vector* nu,
     ae_int_t nnlc,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(nnlc>=0, "MinNLCSetNLC2: NNLC<0", _state);
    ae_assert(nl->cnt>=nnlc, "MinNLCSetNLC2: Length(NL)<NNLC", _state);
    ae_assert(nu->cnt>=nnlc, "MinNLCSetNLC2: Length(NU)<NNLC", _state);
    state->nnlc = nnlc;
    rallocv(nnlc, &state->nl, _state);
    rallocv(nnlc, &state->nu, _state);
    for(i=0; i<=nnlc-1; i++)
    {
        ae_assert(ae_isfinite(nl->ptr.p_double[i], _state)||ae_isneginf(nl->ptr.p_double[i], _state), "MinNLCSetNLC2: NL[i] is +INF or NAN", _state);
        ae_assert(ae_isfinite(nu->ptr.p_double[i], _state)||ae_isposinf(nu->ptr.p_double[i], _state), "MinNLCSetNLC2: NU[i] is -INF or NAN", _state);
        state->nl.ptr.p_double[i] = nl->ptr.p_double[i];
        state->nu.ptr.p_double[i] = nu->ptr.p_double[i];
    }
}


/*************************************************************************
This function sets specific finite  difference  formula  to  be  used  for
numerical differentiation.

It works only for optimizers created  with  minnlccreatef()  function;  in
other cases it has no effect.

INPUT PARAMETERS:
    State       -   structure previously allocated with MinNLCCreateF call.
    FormulaType -   formula type:
                    * 5 for a 5-point formula (actually, only 4 values per
                      variable  are  used, ones at x+h, x+h/2,  x-h/2  and
                      x-h; the central  one has  zero  multiplier  due  to
                      symmetry). The most precise and the  most  expensive
                      option, chosen by default
                    * 3 for a 3-point formula, which is also  known  as  a
                      symmetric difference quotient (the formula  actually
                      uses only two function values per variable:  at  x+h
                      and x-h). A   good  compromise  for  medium-accuracy
                      setups
                    * 2 for a forward (or backward, depending  on variable
                      bounds)  finite   difference  (f(x+h)-f(x))/h.  This
                      formula has the lowest accuracy. However, it  is  4x
                      faster than the 5-point formula and 2x  faster  than
                      the 3-point one because, in addition to the  central
                      value f(x), it needs only  one  additional  function
                      evaluation per variable.
    

  -- ALGLIB --
     Copyright 03.12.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcsetnumdiff(minnlcstate* state,
     ae_int_t formulatype,
     ae_state *_state)
{


    ae_assert((formulatype==2||formulatype==3)||formulatype==5, "MinNLCSetNumDiff: unexpected formula type", _state);
    state->formulatype = formulatype;
}


/*************************************************************************
This function sets stopping conditions for the optimizer.

This  function allows to set  iterations  limit  and  step-based  stopping
conditions. If you want the solver to stop upon having a small  change  in
the target, use minnlcsetcond3() function.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Eps     -   >=0, specific meaning depends on the algorithm:
                * for GIPM2 - stop when primal/dual/compl errors are less
                  than Eps
                * for SQP-based solvers - stop when the scaled trust region
                  radius is less than Eps
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic
selection of the stopping condition.

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcsetcond(minnlcstate* state,
     double eps,
     ae_int_t maxits,
     ae_state *_state)
{


    ae_assert(ae_isfinite(eps, _state), "MinNLCSetCond: Eps is not finite number", _state);
    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "MinNLCSetCond: negative Eps", _state);
    ae_assert(maxits>=0, "MinNLCSetCond: negative MaxIts!", _state);
    critsetcondv1(&state->criteria, 0.0, eps, maxits, _state);
}


/*************************************************************************
This function sets stopping conditions for the optimizer.

This function allows to set three types of stopping conditions:
* iterations limit
* stopping upon performing a short step (depending on the specific  solver
  being used  it may stop as soon as the first short step was made, or
  only after performing several sequential short steps)
* stopping upon having a small change in  the  target  (depending  on  the
  specific solver being used it may stop as soon as the  first  step  with
  small change in the target was made, or only  after  performing  several
  sequential steps)

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsF    -   >=0
                The optimizer will stop as soon as the following condition
                is met:
                    
                    |f_scl(k+1)-f_scl(k)| <= max(|f_scl(k+1)|,|f_scl(k)|,1)
                    
                where f_scl is an internally used by the optimizer rescaled
                target (ALGLIB optimizers usually apply rescaling in order
                to normalize target and constraints).
    Eps     -   >=0, specific meaning depends on the algorithm:
                * for GIPM2 - stop when primal/dual/compl errors are less
                  than Eps
                * for SQP-based solvers - stop when the scaled trust region
                  radius is less than Eps
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsF, EpsX=0 and MaxIts=0 (simultaneously) will lead to the
automatic selection of the stopping condition.

  -- ALGLIB --
     Copyright 21.09.2023 by Bochkanov Sergey
*************************************************************************/
void minnlcsetcond3(minnlcstate* state,
     double epsf,
     double eps,
     ae_int_t maxits,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsf, _state), "MinNLCSetCond3: EpsF is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsf,(double)(0)), "MinNLCSetCond3: negative EpsF", _state);
    ae_assert(ae_isfinite(eps, _state), "MinNLCSetCond3: Eps is not finite number", _state);
    ae_assert(ae_fp_greater_eq(eps,(double)(0)), "MinNLCSetCond3: negative Eps", _state);
    ae_assert(maxits>=0, "MinNLCSetCond3: negative MaxIts!", _state);
    critsetcondv1(&state->criteria, epsf, eps, maxits, _state);
}


/*************************************************************************
This function sets scaling coefficients for NLC optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison  with  tolerances).  Scales
are also used by the finite difference variant of the optimizer - the step
along I-th axis is equal to DiffStep*S[I]. Finally,  variable  scales  are
used for preconditioning (i.e. to speed up the solver).

The scale of the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 06.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcsetscale(minnlcstate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(s->cnt>=state->n, "MinNLCSetScale: Length(S)<N", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "MinNLCSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "MinNLCSetScale: S contains zero elements", _state);
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function sets maximum step length (after scaling of step vector  with
respect to variable scales specified by minnlcsetscale() call).

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    StpMax  -   maximum step length, >=0. Set StpMax to 0.0 (default),  if
                you don't want to limit step length.

Use this subroutine when you optimize target function which contains exp()
or  other  fast  growing  functions,  and optimization algorithm makes too
large  steps  which  leads  to overflow. This function allows us to reject
steps  that  are  too  large  (and  therefore  expose  us  to the possible
overflow) without actually calculating function value at the x+stp*d.

NOTE: different solvers employed by MinNLC  optimizer  may  use  different
      norms for the step.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minnlcsetstpmax(minnlcstate* state, double stpmax, ae_state *_state)
{


    ae_assert(ae_isfinite(stpmax, _state), "MinNLCSetStpMax: StpMax is not finite!", _state);
    ae_assert(ae_fp_greater_eq(stpmax,(double)(0)), "MinNLCSetStpMax: StpMax<0!", _state);
    state->stpmax = stpmax;
}


/*************************************************************************
This function tells MinNLC unit to use the large-scale augmented Lagrangian
algorithm for nonlinearly constrained optimization.

This  algorithm  is  a  significant  refactoring  of  one  described in "A
Modified Barrier-Augmented  Lagrangian Method for Constrained Minimization
(1999)" by D.GOLDFARB,  R.POLYAK,  K. SCHEINBERG,  I.YUZEFOVICH  with  the
following additions:
* improved sparsity support
* improved handling of large-scale problems with the low rank  LBFGS-based
  sparse preconditioner
* automatic selection of the penalty parameter Rho

AUL solver can be significantly faster than SQP on easy  problems  due  to
cheaper iterations, although it needs more function evaluations. On large-
scale sparse problems one iteration of the AUL solver usually  costs  tens
times less than one iteration of the SQP solver.

However, the SQP solver is more robust than the AUL. In particular, it  is
much better at constraint enforcement and will  never escape feasible area
after constraints were successfully enforced.  It  also  needs  much  less
target function evaluations.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    MaxOuterIts-upper limit on outer iterations count:
                * MaxOuterIts=0 means that the solver  will  automatically
                  choose an upper limit. Recommended value.
                * MaxOuterIts>1 means that the AUL solver will performs at
                  most specified number of outer iterations

  -- ALGLIB --
     Copyright 22.09.2023 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgoaul2(minnlcstate* state,
     ae_int_t maxouterits,
     ae_state *_state)
{


    ae_assert(maxouterits>=0, "MinNLCSetAlgoAUL2: negative MaxOuterIts", _state);
    state->aulitscnt = maxouterits;
    state->solvertype = 0;
}


/*************************************************************************
This function selects a legacy solver: an L1 merit function based SQP with
the sparse l-BFGS update.

It is recommended to use either SQP or SQP-BFGS solvers  instead  of  this
one.  These  solvers  use  filters  to  provide  much  faster  and  robust
convergence.
> 

  -- ALGLIB --
     Copyright 02.12.2019 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgosl1qp(minnlcstate* state, ae_state *_state)
{


    state->solvertype = 2;
}


/*************************************************************************
This function selects a legacy solver: an L1 merit function based SQP with
the dense BFGS update.

It is recommended to use either SQP or SQP-BFGS solvers  instead  of  this
one.  These  solvers  use  filters  to  provide  much  faster  and  robust
convergence.

  -- ALGLIB --
     Copyright 02.12.2019 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgosl1qpbfgs(minnlcstate* state, ae_state *_state)
{


    state->solvertype = 3;
}


/*************************************************************************
This function selects large-scale  sparse  filter-based  SQP  solver,  the
most robust solver in ALGLIB, a recommended option.

This algorithm is scalable to problems with millions of variables and  can
efficiently handle sparsity of constraints.

The convergence is proved for the following case:
* function and constraints are continuously differentiable (C1 class)

This algorithm has the following nice properties:
* no parameters to tune
* no convexity requirements for target function or constraints
* the initial point can be infeasible
* the algorithm respects box constraints in all  intermediate  points  (it
  does not even evaluate the target outside of the box constrained area)
* once linear constraints are enforced, the algorithm will not violate them
* no such guarantees can be provided for nonlinear constraints,  but  once
  nonlinear  constraints  are  enforced,  the algorithm will try to respect
  them as much as possible
* numerical differentiation does  not  violate  box  constraints  (although
  general linear and nonlinear ones can be violated during differentiation)

IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes significant amounts of
           time when solving large-scale problems).  It  can  also  use  a
           performance backend (e.g. Intel PARDISO  or  another  platform-
           specific library) to accelerate sparse factorization.
           
           Specific speed-up due to parallelism  and  performance  backend
           usage heavily depends on the sparsity pattern  of  constraints.
           For some  problem  types  performance  backends  provide  great
           speed-up. For other ones,  ALGLIB's  own  sparse  factorization
           code is the preferred option.
           
           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

===== TRACING SQP SOLVER =================================================

SQP solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'SQP'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
                  It also prints OptGuard  integrity  checker  report when
                  nonsmoothness of target/constraints is suspected.
* 'SQP.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'SQP'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format
* 'SQP.PROBING' - to let algorithm insert additional function  evaluations
                  before line search  in  order  to  build  human-readable
                  chart of the raw  Lagrangian  (~40  additional  function
                  evaluations is performed for  each  line  search).  This
                  symbol  also  implicitly  defines  'SQP'  and  activates
                  OptGuard integrity checker which detects continuity  and
                  smoothness violations. An OptGuard log is printed at the
                  end of the file.

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related   overhead.  Specifying  'SQP.PROBING'  adds   even  larger
overhead due to additional function evaluations being performed.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("SQP,SQP.PROBING,PREC.F6", "path/to/trace.log")
> 

  -- ALGLIB --
     Copyright 02.12.2023 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgosqp(minnlcstate* state, ae_state *_state)
{


    state->solvertype = 4;
}


/*************************************************************************
This function selects ORBIT solver, a model-based  derivative-free  solver
for minimization of expensive derivative-free functions.

The ORBIT algorithm by Wild and Shoemaker (2013) is an algorithm that uses
objective function values to build a smooth  RBF  model  (f=r^3)  that  is
minimized over a trust region in order to  identify  step  direction.  The
algorithm saves and reuses function values at all previously known points.

ALGLIB added to the original algorithm the following modifications:
* box, linear and nonlinear constraints
* improved tolerance to noise in the objective/constraints

Its  intended  area  of  application  is  a  low-accuracy  minimization of
expensive objectives with no  gradient  available.  It  is  expected  that
additional overhead of building and minimizing an RBF model is  negligible
when compared with the objective evaluation cost. Iteration overhead grows
as O(N^3), so this solver is recommended for problems with N below 100.

This algorithm has the following nice properties:
* no parameters to tune
* no convexity requirements for target function or constraints
* the initial point can be infeasible
* the algorithm respects box constraints in all  intermediate  points  (it
  does not even evaluate the target outside of the box constrained area)
* once linear and nonlinear constraints are enforced, the  algorithm  will
  try to respect them as much as possible.

When compared with SQP solver, ORBIT:
* is much faster than the finite-difference  based  serial  SQP  at  early
  stages of optimization, being able to achieve 0.1-0.01 relative accuracy
  about 4x-10x faster than SQP solver
* has slower asymptotic convergence on ill-conditioned problems, sometimes
  being unable to reduce error in objective or constraints below  1E-5  in
  a reasonable amount of time
* has  no  obvious  benefits  over  SQP  with  analytic gradient or highly
  parallelized (more than 10 cores) finite-difference SQP
  
NOTE: whilst technically this algorithm supports callback parallelism,  in
      practice it can't efficiently utilize parallel resources because  it
      issues requests for objective/constraints in  an  inherently  serial
      manner.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    
    Rad0    -   initial  sampling   radius  (multiplied  by   per-variable
                scales),  Rad0>=0,  zero  value  means  automatic   radius
                selection.
                An ideal  value  is  large  enough  to  allow  significant
                progress by making a Rad0-sized step, but  not  too  large
                (so  that  initial  linear  model  well  approximates  the
                objective).
                Recommended values: 0.1 or 1  (assuming  properly   chosen
                variable scales.
                The solver can tolerate inappropriately  chosen  Rad0,  at
                the expense of additional function evaluations  needed  to
                adjust it.
                
    MaxNFEV -   MaxNFEV>=0,  with   zero  value  meaning  no  limit.  This
                parameter allows to control computational budget (measured
                in  function  evaluations).
                
                It provides somewhat finer control than  MaxIts  parameter
                of minnlcsetcond(), which controls the maximum  amount  of
                iterations performed by the algorithm, with  one iteration
                usually needing more than one function evaluation.
                
                The  solver  does  not  stop  immediately  after  reaching
                MaxNFEV evaluations, but  will  stop  shortly  after  that
                (usually within N+1 evaluations, often within 1-2).

  -- ALGLIB --
     Copyright 02.10.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgoorbit(minnlcstate* state,
     double rad0,
     ae_int_t maxnfev,
     ae_state *_state)
{


    ae_assert(ae_isfinite(rad0, _state), "MinNLCSetAlgoORBIT: Rad0 is INF/NAN", _state);
    ae_assert(ae_fp_greater_eq(rad0,(double)(0)), "MinNLCSetAlgoORBIT: Rad0<0", _state);
    ae_assert(maxnfev>=0, "MinNLCSetAlgoORBIT: MaxNFEV<0", _state);
    state->solvertype = 6;
    state->orbitrad0 = rad0;
    state->orbitmaxnfev = maxnfev;
}


/*************************************************************************
This function selects a SQP solver specialized on low-dimensional problems
- dense filter-based SQP-BFGS solver.

This algorithm uses a dense quadratic model of the  target  and  solves  a
dense QP subproblem at each step. Thus, it has difficulties scaling beyond
several hundreds of variables.  However,  it  usually  needs  the smallest
number of the target evaluations - sometimes  up  to  30%  less  than  the
sparse large-scale filter-based SQP.

The convergence is proved for the following case:
* function and constraints are continuously differentiable (C1 class)

This algorithm has the following nice properties:
* no parameters to tune
* no convexity requirements for target function or constraints
* the initial point can be infeasible
* the algorithm respects box constraints in all  intermediate  points  (it
  does not even evaluate the target outside of the box constrained area)
* once linear constraints are enforced, the algorithm will not violate them
* no such guarantees can be provided for nonlinear constraints,  but  once
  nonlinear  constraints  are  enforced,  the algorithm will try to respect
  them as much as possible
* numerical differentiation does  not  violate  box  constraints  (although
  general linear and nonlinear ones can be violated during differentiation)


IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes most time when solving
           large-scale problems). It can also use  a  performance  backend
           (e.g.  Intel  MKL  or  another  platform-specific  library)  to
           accelerate dense factorization.
           
           Dense Cholesky factorization is a well  studied  and  optimized
           algorithm. ALGLIB includes  a  well  optimized  implementation;
           however, using a hardware vendor-provided  performance  library
           usually results in a better performance.
           
           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    
===== TRACING SQP SOLVER =================================================

SQP solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'SQP'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
                  It also prints OptGuard  integrity  checker  report when
                  nonsmoothness of target/constraints is suspected.
* 'SQP.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'SQP'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format
* 'SQP.PROBING' - to let algorithm insert additional function  evaluations
                  before line search  in  order  to  build  human-readable
                  chart of the raw  Lagrangian  (~40  additional  function
                  evaluations is performed for  each  line  search).  This
                  symbol  also  implicitly  defines  'SQP'  and  activates
                  OptGuard integrity checker which detects continuity  and
                  smoothness violations. An OptGuard log is printed at the
                  end of the file.

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related   overhead.  Specifying  'SQP.PROBING'  adds   even  larger
overhead due to additional function evaluations being performed.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("SQP,SQP.PROBING,PREC.F6", "path/to/trace.log")
> 

  -- ALGLIB --
     Copyright 02.12.2023 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgosqpbfgs(minnlcstate* state, ae_state *_state)
{


    state->solvertype = 5;
}


/*************************************************************************
This function selects  large-scale sparse interior point solver (GIPM2).

This  algorithm  is  scalable  to  problems  with hundreds of thousands of
variables and can efficiently handle sparsity of constraints.

When compared to SQP, the following differences can be noted:
* GIPM2 has lower iteration overhead than SQP
* GIPM2 violates all non-box constraints (linear  and  nonlinear, equality
  and inequality ones) until it finally converges. Contrary to  that,  SQP
  respects linear constraints  after  initial  enforcement,  and  tends to
  closely follow nonlinear ones.
* as all interior point methods, GIPM2 is  bad  at  hot  starts.  Even  if
  started from solution, it will perform at least 30-60 iterations (first,
  it  will  move  away from the solution to find a centered initial point,
  then it will converge back along the central path).
  
The shorty summary is that SQP wins when starting close to  the  solution,
and GIPM2 tends to win on large-scale  sparse  problems  where  constraint
factorization adds significant linear algebra overhead.

The convergence is proved for the following case:
* function and constraints are continuously differentiable (C1 class)

This algorithm has the following nice properties:
* no parameters to tune
* no convexity requirements for target function or constraints
* the initial point can be infeasible
* the algorithm respects box constraints in all  intermediate  points  (it
  does not even evaluate the target outside of the box constrained area)
* numerical differentiation does  not  violate  box  constraints  (although
  general linear and nonlinear ones can be violated during differentiation)


IMPORTANT: the commercial edition of ALGLIB can parallelize  factorization
           phase of this function (this phase takes significant amounts of
           time when solving large-scale problems).  It  can  also  use  a
           performance backend (e.g. Intel PARDISO  or  another  platform-
           specific library) to accelerate sparse factorization.
           
           Specific speed-up due to parallelism  and  performance  backend
           usage heavily depends on the sparsity pattern  of  constraints.
           For some  problem  types  performance  backends  provide  great
           speed-up. For other ones,  ALGLIB's  own  sparse  factorization
           code is the preferred option.
           
           See the ALGLIB Reference Manual for more information on how  to
           activate parallelism and backend support.
           

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

===== TRACING GIPM2 SOLVER ===============================================

GIPM2  solver  supports  advanced  tracing  capabilities.  You  can  trace
algorithm output by specifying following trace symbols  (case-insensitive)
by means of trace_file() call:
* 'GIPM2'       - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related   overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("GIPM2,PREC.F6", "path/to/trace.log")
> 

  -- ALGLIB --
     Copyright 02.02.2025 by Bochkanov Sergey
*************************************************************************/
void minnlcsetalgogipm2(minnlcstate* state, ae_state *_state)
{


    state->solvertype = 8;
}


/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinNLCOptimize().

NOTE: algorithm passes two parameters to rep() callback  -  current  point
      and penalized function value at current point. Important -  function
      value which is returned is NOT function being minimized. It  is  sum
      of the value of the function being minimized - and penalty term.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minnlcsetxrep(minnlcstate* state, ae_bool needxrep, ae_state *_state)
{


    state->xrep = needxrep;
}


/*************************************************************************

CALLBACK PARALLELISM

The MINNLC optimizer supports parallel parallel  numerical differentiation
('callback parallelism'). This feature, which  is  present  in  commercial
ALGLIB  editions,  greatly   accelerates   optimization   with   numerical
differentiation of an expensive target functions.

Callback parallelism is usually  beneficial  when  computing  a  numerical
gradient requires more than several  milliseconds.  In this case  the  job
of computing individual gradient components can be split between  multiple
threads. Even inexpensive targets can benefit  from  parallelism,  if  you
have many variables.

ALGLIB Reference Manual, 'Working with commercial  version' section, tells
how to activate callback parallelism for your programming language.

NOTES:

1. This function has two different implementations:
   * one which accepts objective/constraints values and their gradients
   * and one which accepts only objective/constraints values
   
   The former function should be called  when  using  solvers  relying  on
   analytic gradients (SQP, GIPM2, etc). The latter one  is  intended  for
   derivative-free NLP solvers like ORBIT and, additionally, for numerical
   differentiation based versions of SQP/GIPM2/etc.
   
   Be careful to choose variant of MinNLCOptimize()  which  corresponds to
   your optimization scheme! Table below lists different  combinations  of
   callback (function/gradient) passed to MinNLCOptimize()   and  specific
   function used to create optimizer.


                                            |  PASSED TO MinNLCOptimize()
                                            |              |              
       PROBLEM SETUP                        |  values only |  values
                                            |              | and gradients
   -----------------------------------------------------------------------
   MinNLCCreate()+SQP/GIPM2/AUL             |      FAILS          +
   MinNLCCreate()+ORBIT                     |        +          FAILS
   MinNLCCreateF()+SQP/GIPM2/AUL            |        +          FAILS
   MinNLCCreateF()+ORBIT                    |      FAILS        FAILS

   Here "FAILS" denotes inappropriate combinations  of  optimizer creation
   function  and  MinNLCOptimize()  version.   Any attempts   to  use such
   combination will lead to exception. Either  you  did  not pass gradient
   when it WAS needed or you passed gradient when it was NOT needed.

  -- ALGLIB --
     Copyright 06.05.2025 by Bochkanov Sergey
*************************************************************************/
ae_bool minnlciteration(minnlcstate* state, ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t j0;
    ae_int_t j1;
    ae_int_t jj;
    ae_int_t n;
    ae_int_t nnlc;
    ae_int_t offs;
    double vleft;
    double vright;
    double v;
    ae_bool b;
    ae_int_t originalrequest;
    ae_int_t originalquerysize;
    ae_bool densejac;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        i = state->rstate.ia.ptr.p_int[0];
        j = state->rstate.ia.ptr.p_int[1];
        k = state->rstate.ia.ptr.p_int[2];
        j0 = state->rstate.ia.ptr.p_int[3];
        j1 = state->rstate.ia.ptr.p_int[4];
        jj = state->rstate.ia.ptr.p_int[5];
        n = state->rstate.ia.ptr.p_int[6];
        nnlc = state->rstate.ia.ptr.p_int[7];
        offs = state->rstate.ia.ptr.p_int[8];
        originalrequest = state->rstate.ia.ptr.p_int[9];
        originalquerysize = state->rstate.ia.ptr.p_int[10];
        b = state->rstate.ba.ptr.p_bool[0];
        densejac = state->rstate.ba.ptr.p_bool[1];
        vleft = state->rstate.ra.ptr.p_double[0];
        vright = state->rstate.ra.ptr.p_double[1];
        v = state->rstate.ra.ptr.p_double[2];
    }
    else
    {
        i = 359;
        j = -58;
        k = -919;
        j0 = -909;
        j1 = 81;
        jj = 255;
        n = 74;
        nnlc = -788;
        offs = 809;
        originalrequest = 205;
        originalquerysize = -838;
        b = ae_true;
        densejac = ae_false;
        vleft = 763.0;
        vright = -541.0;
        v = -698.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    if( state->rstate.stage==6 )
    {
        goto lbl_6;
    }
    if( state->rstate.stage==7 )
    {
        goto lbl_7;
    }
    if( state->rstate.stage==8 )
    {
        goto lbl_8;
    }
    if( state->rstate.stage==9 )
    {
        goto lbl_9;
    }
    if( state->rstate.stage==10 )
    {
        goto lbl_10;
    }
    if( state->rstate.stage==11 )
    {
        goto lbl_11;
    }
    if( state->rstate.stage==12 )
    {
        goto lbl_12;
    }
    if( state->rstate.stage==13 )
    {
        goto lbl_13;
    }
    if( state->rstate.stage==14 )
    {
        goto lbl_14;
    }
    if( state->rstate.stage==15 )
    {
        goto lbl_15;
    }
    if( state->rstate.stage==16 )
    {
        goto lbl_16;
    }
    if( state->rstate.stage==17 )
    {
        goto lbl_17;
    }
    if( state->rstate.stage==18 )
    {
        goto lbl_18;
    }
    if( state->rstate.stage==19 )
    {
        goto lbl_19;
    }
    
    /*
     * Routine body
     */
    
    /*
     * Init
     */
    state->userterminationneeded = ae_false;
    state->repf = ae_maxrealnumber;
    state->repterminationtype = 0;
    state->repinneriterationscount = 0;
    state->repouteriterationscount = 0;
    state->repnfev = 0;
    state->repdbgphase0its = 0;
    state->repbcerr = (double)(0);
    state->repbcidx = -1;
    state->replcerr = (double)(0);
    state->replcidx = -1;
    state->repnlcerr = (double)(0);
    state->repnlcidx = -1;
    state->repsclfeaserr = 0.0;
    rsetallocv(state->n, 0.0, &state->replagbc, _state);
    rsetallocv(state->n, 0.0, &state->replagbcnz, _state);
    rsetallocv(state->xlc.nsparse+state->xlc.ndense, 0.0, &state->replaglc, _state);
    rsetallocv(state->nnlc, 0.0, &state->replagnlc, _state);
    n = state->n;
    nnlc = state->nnlc;
    minnlc_clearrequestfields(state, _state);
    ae_assert(state->smoothnessguardlevel==0||state->smoothnessguardlevel==1, "MinNLCIteration: integrity check failed", _state);
    b = state->smoothnessguardlevel>0;
    b = b||(state->solvertype==0&&(ae_is_trace_enabled("AUL.PROBING")||ae_is_trace_enabled("AUL.PROBINGONFAILURE")));
    b = b||((((state->solvertype==2||state->solvertype==3)||state->solvertype==4)||state->solvertype==5)&&(ae_is_trace_enabled("SQP.PROBING")||ae_is_trace_enabled("SQP.PROBINGONFAILURE")));
    smoothnessmonitorinit(&state->smonitor, &state->s, n, 1+nnlc, b, _state);
    for(i=0; i<=n-1; i++)
    {
        state->lastscaleused.ptr.p_double[i] = state->s.ptr.p_double[i];
    }
    
    /*
     * Allocate buffers, as mandated by the V2 protocol
     */
    if( state->protocolversion==1 )
    {
        if( state->fi.cnt!=1+state->nnlc )
        {
            ae_vector_set_length(&state->fi, 1+state->nnlc, _state);
        }
        if( state->j.rows!=1+state->nnlc||state->j.cols!=state->n )
        {
            ae_matrix_set_length(&state->j, 1+state->nnlc, state->n, _state);
        }
    }
    if( state->protocolversion==2 )
    {
        if( ae_fp_eq(state->diffstep,(double)(0)) )
        {
            rallocv(n, &state->querydata, _state);
        }
        else
        {
            rallocv(n+2*3*n, &state->querydata, _state);
        }
        rallocv(1+nnlc, &state->replyfi, _state);
        rallocv(1+nnlc, &state->tmpf1, _state);
        rallocv(n, &state->tmpg1, _state);
        rallocv(n, &state->tmpx1, _state);
    }
    
    /*
     * Check correctness of box constraints
     */
    for(i=0; i<=n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i] )
        {
            if( ae_fp_greater(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                state->repterminationtype = -3;
                state->repbcerr = state->bndl.ptr.p_double[i]-state->bndu.ptr.p_double[i];
                state->repbcidx = i;
                result = ae_false;
                return result;
            }
        }
    }
    
    /*
     * Test gradient
     */
    if( !(ae_fp_eq(state->diffstep,(double)(0))&&ae_fp_greater(state->teststep,(double)(0))) )
    {
        goto lbl_20;
    }
lbl_22:
    if( !smoothnessmonitorcheckgradientatx0(&state->smonitor, &state->xstart, &state->s, &state->bndl, &state->bndu, ae_true, state->teststep, _state) )
    {
        goto lbl_23;
    }
    if( state->protocolversion!=2 )
    {
        goto lbl_24;
    }
    if( !state->issuesparserequests )
    {
        goto lbl_26;
    }
    state->requesttype = 1;
    state->querysize = 1;
    state->queryfuncs = 1+nnlc;
    state->queryvars = n;
    state->querydim = 0;
    rcopyv(n, &state->smonitor.x, &state->querydata, _state);
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    ae_assert(sparsegetnrows(&state->replysj, _state)==state->querysize*state->queryfuncs, "MINNLC: integrity check 0106 failed", _state);
    ae_assert(sparsegetncols(&state->replysj, _state)==state->queryvars, "MINNLC: integrity check 0107 failed", _state);
    for(i=0; i<=nnlc; i++)
    {
        state->smonitor.fi.ptr.p_double[i] = state->replyfi.ptr.p_double[i];
        rsetr(n, 0.0, &state->smonitor.j, i, _state);
        j0 = state->replysj.ridx.ptr.p_int[i];
        j1 = state->replysj.ridx.ptr.p_int[i+1]-1;
        for(jj=j0; jj<=j1; jj++)
        {
            state->smonitor.j.ptr.pp_double[i][state->replysj.idx.ptr.p_int[jj]] = state->replysj.vals.ptr.p_double[jj];
        }
    }
    goto lbl_27;
lbl_26:
    state->requesttype = 2;
    state->querysize = 1;
    state->queryfuncs = 1+nnlc;
    state->queryvars = n;
    state->querydim = 0;
    rcopyv(n, &state->smonitor.x, &state->querydata, _state);
    rallocv(n*(1+nnlc), &state->replydj, _state);
    rallocm(1+nnlc, n, &state->tmpj1, _state);
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    for(i=0; i<=nnlc; i++)
    {
        state->smonitor.fi.ptr.p_double[i] = state->replyfi.ptr.p_double[i];
        for(k=0; k<=n-1; k++)
        {
            state->smonitor.j.ptr.pp_double[i][k] = state->replydj.ptr.p_double[i*n+k];
        }
    }
lbl_27:
    goto lbl_25;
lbl_24:
    ae_assert(state->protocolversion==1, "MINNLC: integrity check 0748 failed", _state);
    ae_assert(!state->issuesparserequests, "MINNLC: integrity check 0749 failed", _state);
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = state->smonitor.x.ptr.p_double[i];
    }
    state->needfij = ae_true;
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->needfij = ae_false;
    for(i=0; i<=nnlc; i++)
    {
        state->smonitor.fi.ptr.p_double[i] = state->fi.ptr.p_double[i];
        for(k=0; k<=n-1; k++)
        {
            state->smonitor.j.ptr.pp_double[i][k] = state->j.ptr.pp_double[i][k];
        }
    }
lbl_25:
    goto lbl_22;
lbl_23:
lbl_20:
    
    /*
     * Initialization phase
     */
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        rvectorsetlengthatleast(&state->xbase, n, _state);
        rvectorsetlengthatleast(&state->fbase, 1+nnlc, _state);
        rvectorsetlengthatleast(&state->fm2, 1+nnlc, _state);
        rvectorsetlengthatleast(&state->fm1, 1+nnlc, _state);
        rvectorsetlengthatleast(&state->fp1, 1+nnlc, _state);
        rvectorsetlengthatleast(&state->fp2, 1+nnlc, _state);
    }
    b = ae_false;
    if( state->solvertype==0 )
    {
        
        /*
         * Initialize AUL solver
         */
        if( !(state->aulsolverstate!=NULL) )
        {
            state->aulsolverstate = (minaulstate*)ae_malloc(sizeof(minaulstate), _state); /* note: using state->aulsolverstate as a temporary prior to assigning its value to _state->aulsolverstate */
            memset(state->aulsolverstate, 0, sizeof(minaulstate));
            _minaulstate_init(state->aulsolverstate, _state, ae_false);
            ae_smart_ptr_assign(&state->_aulsolverstate, state->aulsolverstate, ae_true, ae_true, (ae_int_t)sizeof(minaulstate), _minaulstate_init_copy, _minaulstate_destroy);
        }
        xlcconverttosparse(&state->xlc, _state);
        minaulinitbuf(&state->bndl, &state->bndu, &state->s, &state->xstart, n, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, &state->xlc.lcsrcidx, state->xlc.nsparse+state->xlc.ndense, &state->nl, &state->nu, nnlc, &state->criteria, state->aulitscnt, state->aulsolverstate, _state);
        b = ae_true;
    }
    if( state->solvertype==2||state->solvertype==3 )
    {
        
        /*
         * Initialize SQP solver
         */
        if( !(state->sqpsolverstate!=NULL) )
        {
            state->sqpsolverstate = (minsqpstate*)ae_malloc(sizeof(minsqpstate), _state); /* note: using state->sqpsolverstate as a temporary prior to assigning its value to _state->sqpsolverstate */
            memset(state->sqpsolverstate, 0, sizeof(minsqpstate));
            _minsqpstate_init(state->sqpsolverstate, _state, ae_false);
            ae_smart_ptr_assign(&state->_sqpsolverstate, state->sqpsolverstate, ae_true, ae_true, (ae_int_t)sizeof(minsqpstate), _minsqpstate_init_copy, _minsqpstate_destroy);
        }
        xlcconverttoold(&state->xlc, _state);
        converttwosidednlctoonesidedold(&state->nl, &state->nu, nnlc, &state->nlcidx, &state->nlcmul, &state->nlcadd, &state->nlcnlec, &state->nlcnlic, _state);
        minsqpinitbuf(&state->bndl, &state->bndu, &state->s, &state->xstart, n, &state->xlc.cleic, &state->xlc.lcsrcidx, state->xlc.nec, state->xlc.nic, state->nlcnlec, state->nlcnlic, &state->criteria, state->solvertype==3, state->sqpsolverstate, _state);
        b = ae_true;
    }
    if( state->solvertype==4||state->solvertype==5 )
    {
        
        /*
         * Initialize FSQP solver
         */
        if( !(state->fsqpsolverstate!=NULL) )
        {
            state->fsqpsolverstate = (minfsqpstate*)ae_malloc(sizeof(minfsqpstate), _state); /* note: using state->fsqpsolverstate as a temporary prior to assigning its value to _state->fsqpsolverstate */
            memset(state->fsqpsolverstate, 0, sizeof(minfsqpstate));
            _minfsqpstate_init(state->fsqpsolverstate, _state, ae_false);
            ae_smart_ptr_assign(&state->_fsqpsolverstate, state->fsqpsolverstate, ae_true, ae_true, (ae_int_t)sizeof(minfsqpstate), _minfsqpstate_init_copy, _minfsqpstate_destroy);
        }
        xlcconverttosparse(&state->xlc, _state);
        minfsqpinitbuf(&state->bndl, &state->bndu, &state->s, &state->xstart, n, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, &state->xlc.lcsrcidx, state->xlc.nsparse+state->xlc.ndense, &state->nl, &state->nu, nnlc, &state->criteria, state->solvertype==5, state->fsqpsolverstate, _state);
        if( state->fsqpadditsforctol>0 )
        {
            minfsqpsetadditsforctol(state->fsqpsolverstate, state->fsqpadditsforctol, state->fsqpctol, _state);
        }
        b = ae_true;
    }
    if( state->solvertype!=6 )
    {
        goto lbl_28;
    }
    
    /*
     * Initialize ORBIT solver.
     *
     * Remove fixed variables (most derivative-free solvers do not allow fixed variables
     * because it overcomplicates geometry improvement code).
     */
    if( !(state->orbitsolver!=NULL) )
    {
        state->orbitsolver = (dfgmstate*)ae_malloc(sizeof(dfgmstate), _state); /* note: using state->orbitsolver as a temporary prior to assigning its value to _state->orbitsolver */
        memset(state->orbitsolver, 0, sizeof(dfgmstate));
        _dfgmstate_init(state->orbitsolver, _state, ae_false);
        ae_smart_ptr_assign(&state->_orbitsolver, state->orbitsolver, ae_true, ae_true, (ae_int_t)sizeof(dfgmstate), _dfgmstate_init_copy, _dfgmstate_destroy);
    }
    iallocv(n, &state->idxraw2red, _state);
    rallocv(n, &state->redbl, _state);
    rallocv(n, &state->redbu, _state);
    rallocv(n, &state->reds, _state);
    rallocv(n, &state->redx0, _state);
    state->nreduced = 0;
    for(i=0; i<=n-1; i++)
    {
        if( ae_isfinite(state->bndl.ptr.p_double[i], _state)&&ae_isfinite(state->bndu.ptr.p_double[i], _state) )
        {
            if( ae_fp_eq(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
            {
                state->idxraw2red.ptr.p_int[i] = -1;
                continue;
            }
        }
        state->redx0.ptr.p_double[state->nreduced] = state->xstart.ptr.p_double[i];
        state->reds.ptr.p_double[state->nreduced] = state->s.ptr.p_double[i];
        state->redbl.ptr.p_double[state->nreduced] = state->bndl.ptr.p_double[i];
        state->redbu.ptr.p_double[state->nreduced] = state->bndu.ptr.p_double[i];
        state->idxraw2red.ptr.p_int[i] = state->nreduced;
        state->nreduced = state->nreduced+1;
    }
    if( state->nreduced!=0 )
    {
        goto lbl_30;
    }
    
    /*
     * All variables are fixed, quick exit
     */
    state->repbcerr = 0.0;
    state->repbcidx = -1;
    state->replcerr = 0.0;
    state->replcidx = -1;
    if( state->xlc.nsparse+state->xlc.ndense>0 )
    {
        xlcconverttosparse(&state->xlc, _state);
        checklc2violation(&state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, &state->xlc.lcsrcidx, state->xlc.nsparse+state->xlc.ndense, &state->bndl, &state->replcerr, &state->replcidx, _state);
    }
    if( state->protocolversion!=2 )
    {
        goto lbl_32;
    }
    rallocv(1+nnlc, &state->replyfi, _state);
    rcopyallocv(n, &state->bndl, &state->querydata, _state);
    state->requesttype = 4;
    state->querysize = 1;
    state->queryfuncs = 1+nnlc;
    state->queryvars = n;
    state->querydim = 0;
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    goto lbl_33;
lbl_32:
    ae_assert(state->protocolversion==1, "MINNLC: integrity check 849631 failed", _state);
    rcopyv(n, &state->bndl, &state->x, _state);
    state->needfi = ae_true;
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    state->needfi = ae_false;
    rcopyallocv(1+nnlc, &state->fi, &state->replyfi, _state);
lbl_33:
    state->repf = state->replyfi.ptr.p_double[0];
    checknlc2violation(&state->replyfi, &state->nl, &state->nu, nnlc, &state->repnlcerr, &state->repnlcidx, _state);
    state->repsclfeaserr = rmaxabs3(state->repbcerr, state->replcerr, state->repnlcerr, _state);
    rsetv(n, 0.0, &state->replagbc, _state);
    rsetv(n, 0.0, &state->replagbcnz, _state);
    rsetv(state->xlc.nsparse+state->xlc.ndense, 0.0, &state->replaglc, _state);
    rsetv(nnlc, 0.0, &state->replagnlc, _state);
    state->repterminationtype = 2;
    rcopyv(n, &state->bndl, &state->xc, _state);
    result = ae_false;
    return result;
lbl_30:
    if( state->xlc.nsparse+state->xlc.ndense>0 )
    {
        xlcconverttosparse(&state->xlc, _state);
        iallocv(state->xlc.nsparse+state->xlc.ndense+1, &state->tmpi, _state);
        state->tmpi.ptr.p_int[0] = 0;
        for(i=0; i<=state->xlc.nsparse+state->xlc.ndense-1; i++)
        {
            k = state->tmpi.ptr.p_int[i];
            v = 0.0;
            for(j=state->xlc.effsparsea.ridx.ptr.p_int[i]; j<=state->xlc.effsparsea.ridx.ptr.p_int[i+1]-1; j++)
            {
                if( state->idxraw2red.ptr.p_int[state->xlc.effsparsea.idx.ptr.p_int[j]]<0 )
                {
                    v = v+state->bndl.ptr.p_double[state->xlc.effsparsea.idx.ptr.p_int[j]]*state->xlc.effsparsea.vals.ptr.p_double[j];
                    continue;
                }
                state->xlc.effsparsea.idx.ptr.p_int[k] = state->idxraw2red.ptr.p_int[state->xlc.effsparsea.idx.ptr.p_int[j]];
                state->xlc.effsparsea.vals.ptr.p_double[k] = state->xlc.effsparsea.vals.ptr.p_double[j];
                k = k+1;
            }
            if( ae_isfinite(state->xlc.effal.ptr.p_double[i], _state) )
            {
                state->xlc.effal.ptr.p_double[i] = state->xlc.effal.ptr.p_double[i]-v;
            }
            if( ae_isfinite(state->xlc.effau.ptr.p_double[i], _state) )
            {
                state->xlc.effau.ptr.p_double[i] = state->xlc.effau.ptr.p_double[i]-v;
            }
            state->tmpi.ptr.p_int[i+1] = k;
        }
        icopyv(state->xlc.nsparse+state->xlc.ndense+1, &state->tmpi, &state->xlc.effsparsea.ridx, _state);
        state->xlc.effsparsea.ninitialized = state->xlc.effsparsea.ridx.ptr.p_int[state->xlc.nsparse+state->xlc.ndense];
        sparseinitduidx(&state->xlc.effsparsea, _state);
        state->xlc.effsparsea.n = state->nreduced;
    }
    dfgminitbuf(&state->redbl, &state->redbu, &state->reds, &state->redx0, state->nreduced, 1, ae_false, 2, &state->criteria, 0, state->orbitrad0, state->orbitmaxnfev, state->orbitsolver, _state);
    dfgmsetlc2(state->orbitsolver, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, state->xlc.nsparse+state->xlc.ndense, _state);
    dfgmsetnlc2(state->orbitsolver, &state->nl, &state->nu, nnlc, _state);
    b = ae_true;
lbl_28:
    if( state->solvertype==7||state->solvertype==8 )
    {
        
        /*
         * Initialize GIPM2 solver
         */
        if( !(state->gipm2solver!=NULL) )
        {
            state->gipm2solver = (nlpgipm2state*)ae_malloc(sizeof(nlpgipm2state), _state); /* note: using state->gipm2solver as a temporary prior to assigning its value to _state->gipm2solver */
            memset(state->gipm2solver, 0, sizeof(nlpgipm2state));
            _nlpgipm2state_init(state->gipm2solver, _state, ae_false);
            ae_smart_ptr_assign(&state->_gipm2solver, state->gipm2solver, ae_true, ae_true, (ae_int_t)sizeof(nlpgipm2state), _nlpgipm2state_init_copy, _nlpgipm2state_destroy);
        }
        xlcconverttosparse(&state->xlc, _state);
        nlpgipm2initbuf(&state->bndl, &state->bndu, &state->s, &state->xstart, n, &state->criteria, state->solvertype==5, state->gipm2solver, _state);
        nlpgipm2setlinearconstraints(state->gipm2solver, &state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, &state->xlc.lcsrcidx, state->xlc.nsparse+state->xlc.ndense, _state);
        nlpgipm2setnonlinearconstraints(state->gipm2solver, &state->nl, &state->nu, state->nnlc, _state);
        b = ae_true;
    }
    ae_assert(b, "MINNLC: integrity check 7046 failed", _state);
    
    /*
     * Iteration phase
     */
lbl_34:
    if( ae_false )
    {
        goto lbl_35;
    }
    
    /*
     * Run iteration function and load evaluation point
     */
    minnlc_clearrequestfields(state, _state);
    originalrequest = 0;
    originalquerysize = 0;
    densejac = ae_true;
    b = ae_false;
    if( state->solvertype==0 )
    {
        
        /*
         * AUL solver
         */
        if( !minauliteration(state->aulsolverstate, &state->smonitor, state->userterminationneeded, _state) )
        {
            goto lbl_35;
        }
        if( state->aulsolverstate->needsj )
        {
            
            /*
             * Request dense analytic Jacobian
             */
            if( state->protocolversion==2 )
            {
                minnlc_unscale(state, &state->aulsolverstate->x, &state->aulsolverstate->scaledbndl, &state->aulsolverstate->scaledbndu, &state->querydata, _state);
            }
            else
            {
                ae_assert(state->protocolversion==1, "MINNLC: integrity check 0312 failed", _state);
                minnlc_unscale(state, &state->aulsolverstate->x, &state->aulsolverstate->scaledbndl, &state->aulsolverstate->scaledbndu, &state->x, _state);
            }
            originalrequest = 2;
            originalquerysize = 1;
            b = ae_true;
        }
        if( state->aulsolverstate->xupdated )
        {
            if( state->protocolversion==2 )
            {
                rallocv(n, &state->reportx, _state);
                minnlc_unscale(state, &state->aulsolverstate->x, &state->aulsolverstate->scaledbndl, &state->aulsolverstate->scaledbndu, &state->reportx, _state);
                state->reportf = state->aulsolverstate->f;
            }
            else
            {
                ae_assert(state->protocolversion==1, "MINNLC: integrity check 0941 failed", _state);
                minnlc_unscale(state, &state->aulsolverstate->x, &state->aulsolverstate->scaledbndl, &state->aulsolverstate->scaledbndu, &state->x, _state);
                state->f = state->aulsolverstate->f;
            }
            state->repf = state->aulsolverstate->f;
            originalrequest = -1;
            b = ae_true;
        }
    }
    if( state->solvertype==2||state->solvertype==3 )
    {
        
        /*
         * SQP solver
         */
        if( !minsqpiteration(state->sqpsolverstate, &state->smonitor, state->userterminationneeded, _state) )
        {
            goto lbl_35;
        }
        if( state->sqpsolverstate->needfij )
        {
            if( state->protocolversion==2 )
            {
                minnlc_unscale(state, &state->sqpsolverstate->x, &state->sqpsolverstate->scaledbndl, &state->sqpsolverstate->scaledbndu, &state->querydata, _state);
            }
            else
            {
                ae_assert(state->protocolversion==1, "MINNLC: integrity check 7919 failed", _state);
                minnlc_unscale(state, &state->sqpsolverstate->x, &state->sqpsolverstate->scaledbndl, &state->sqpsolverstate->scaledbndu, &state->x, _state);
            }
            originalrequest = 2;
            originalquerysize = 1;
            b = ae_true;
        }
        if( state->sqpsolverstate->xupdated )
        {
            if( state->protocolversion==2 )
            {
                rallocv(n, &state->reportx, _state);
                minnlc_unscale(state, &state->sqpsolverstate->x, &state->sqpsolverstate->scaledbndl, &state->sqpsolverstate->scaledbndu, &state->reportx, _state);
                state->reportf = state->sqpsolverstate->f;
            }
            else
            {
                ae_assert(state->protocolversion==1, "MINNLC: integrity check 9420 failed", _state);
                minnlc_unscale(state, &state->sqpsolverstate->x, &state->sqpsolverstate->scaledbndl, &state->sqpsolverstate->scaledbndu, &state->x, _state);
                state->f = state->sqpsolverstate->f;
            }
            state->repf = state->sqpsolverstate->f;
            originalrequest = -1;
            b = ae_true;
        }
    }
    if( state->solvertype==4||state->solvertype==5 )
    {
        
        /*
         * FSQP solver
         */
        if( !minfsqpiteration(state->fsqpsolverstate, &state->smonitor, state->userterminationneeded, _state) )
        {
            goto lbl_35;
        }
        if( state->fsqpsolverstate->needfisj )
        {
            if( state->protocolversion==2 )
            {
                minnlc_unscale(state, &state->fsqpsolverstate->x, &state->fsqpsolverstate->scaledbndl, &state->fsqpsolverstate->scaledbndu, &state->querydata, _state);
            }
            else
            {
                ae_assert(state->protocolversion==1, "MINNLC: integrity check 7919 failed", _state);
                minnlc_unscale(state, &state->fsqpsolverstate->x, &state->fsqpsolverstate->scaledbndl, &state->fsqpsolverstate->scaledbndu, &state->x, _state);
            }
            originalrequest = 2;
            originalquerysize = 1;
            b = ae_true;
        }
        if( state->fsqpsolverstate->xupdated )
        {
            if( state->protocolversion==2 )
            {
                rallocv(n, &state->reportx, _state);
                minnlc_unscale(state, &state->fsqpsolverstate->x, &state->fsqpsolverstate->scaledbndl, &state->fsqpsolverstate->scaledbndu, &state->reportx, _state);
                state->reportf = state->fsqpsolverstate->f;
            }
            else
            {
                ae_assert(state->protocolversion==1, "MINNLC: integrity check 9420 failed", _state);
                minnlc_unscale(state, &state->fsqpsolverstate->x, &state->fsqpsolverstate->scaledbndl, &state->fsqpsolverstate->scaledbndu, &state->x, _state);
                state->f = state->fsqpsolverstate->f;
            }
            state->repf = state->fsqpsolverstate->f;
            originalrequest = -1;
            b = ae_true;
        }
    }
    if( state->solvertype==6 )
    {
        
        /*
         * ORBIT solver
         */
        if( !dfgmiteration(state->orbitsolver, state->userterminationneeded, _state) )
        {
            goto lbl_35;
        }
        if( state->orbitsolver->requesttype==4 )
        {
            rallocv(state->orbitsolver->querysize*n, &state->batchquerydata, _state);
            minnlc_unscalebatch(state, &state->orbitsolver->querydata, state->orbitsolver->querysize, &state->idxraw2red, state->nreduced, &state->orbitsolver->scaledbndl, &state->orbitsolver->scaledbndu, &state->batchquerydata, _state);
            originalrequest = 4;
            originalquerysize = state->orbitsolver->querysize;
            b = ae_true;
        }
        if( state->orbitsolver->requesttype==-1 )
        {
            if( state->protocolversion==2 )
            {
                rallocv(n, &state->reportx, _state);
                minnlc_unscalebatch(state, &state->orbitsolver->querydata, 1, &state->idxraw2red, state->nreduced, &state->orbitsolver->scaledbndl, &state->orbitsolver->scaledbndu, &state->reportx, _state);
                state->reportf = state->orbitsolver->reportf;
            }
            else
            {
                ae_assert(state->protocolversion==1, "MINNLC: integrity check 9420 failed", _state);
                minnlc_unscalebatch(state, &state->orbitsolver->querydata, 1, &state->idxraw2red, state->nreduced, &state->orbitsolver->scaledbndl, &state->orbitsolver->scaledbndu, &state->x, _state);
                state->f = state->orbitsolver->reportf;
            }
            state->repf = state->orbitsolver->reportf;
            originalrequest = -1;
            b = ae_true;
        }
    }
    if( state->solvertype==7||state->solvertype==8 )
    {
        
        /*
         * GIPM2 solver
         */
        if( !nlpgipm2iteration(state->gipm2solver, &state->smonitor, state->userterminationneeded, _state) )
        {
            goto lbl_35;
        }
        if( state->gipm2solver->needfisj )
        {
            if( state->protocolversion==2 )
            {
                minnlc_unscale(state, &state->gipm2solver->x, &state->gipm2solver->scaledbndl, &state->gipm2solver->scaledbndu, &state->querydata, _state);
            }
            else
            {
                ae_assert(state->protocolversion==1, "MINNLC: integrity check 368137 failed", _state);
                minnlc_unscale(state, &state->gipm2solver->x, &state->gipm2solver->scaledbndl, &state->gipm2solver->scaledbndu, &state->x, _state);
            }
            originalrequest = 2;
            originalquerysize = 1;
            b = ae_true;
        }
        if( state->gipm2solver->xupdated )
        {
            if( state->protocolversion==2 )
            {
                rallocv(n, &state->reportx, _state);
                minnlc_unscale(state, &state->gipm2solver->x, &state->gipm2solver->scaledbndl, &state->gipm2solver->scaledbndu, &state->reportx, _state);
                state->reportf = state->gipm2solver->reportf;
            }
            else
            {
                ae_assert(state->protocolversion==1, "MINNLC: integrity check 9420 failed", _state);
                minnlc_unscale(state, &state->gipm2solver->x, &state->gipm2solver->scaledbndl, &state->gipm2solver->scaledbndu, &state->x, _state);
                state->f = state->gipm2solver->reportf;
            }
            state->repf = state->gipm2solver->reportf;
            originalrequest = -1;
            b = ae_true;
        }
    }
    ae_assert(b, "MINNLC: integrity check 1848 failed", _state);
    
    /*
     * Process evaluation request, perform numerical differentiation if necessary, offload results back to the optimizer
     */
    b = ae_false;
    if( originalrequest!=-1 )
    {
        goto lbl_36;
    }
    
    /*
     * Report
     */
    if( !state->xrep )
    {
        goto lbl_38;
    }
    if( state->protocolversion!=2 )
    {
        goto lbl_40;
    }
    state->requesttype = -1;
    state->queryvars = n;
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    goto lbl_41;
lbl_40:
    ae_assert(state->protocolversion==1, "MINNLC: integrity check 8214 failed", _state);
    state->xupdated = ae_true;
    state->rstate.stage = 6;
    goto lbl_rcomm;
lbl_6:
    state->xupdated = ae_false;
lbl_41:
lbl_38:
    b = ae_true;
lbl_36:
    if( !(originalrequest==2&&ae_fp_neq(state->diffstep,(double)(0))) )
    {
        goto lbl_42;
    }
    
    /*
     * Jacobian evaluation with numerical differentiation
     */
    ae_assert(originalquerysize==1, "MINNLC: integrity check 023105 failed", _state);
    densejac = ae_true;
    if( state->protocolversion!=2 )
    {
        goto lbl_44;
    }
    
    /*
     * Issue V2 request
     */
    state->requesttype = 5;
    state->querysize = 1;
    state->queryfuncs = 1+nnlc;
    state->queryvars = n;
    state->querydim = 0;
    state->queryformulasize = 2;
    rallocv(n*(1+nnlc), &state->replydj, _state);
    rallocm(1+nnlc, n, &state->tmpj1, _state);
    ae_assert(state->querydata.cnt>=n+state->queryformulasize*3*n, "MINNLC: integrity check 1558 failed", _state);
    rcopyv(n, &state->querydata, &state->xbase, _state);
    rsetvx(state->queryformulasize*3*n, 0.0, &state->querydata, n, _state);
    for(k=0; k<=n-1; k++)
    {
        vleft = state->xbase.ptr.p_double[k]-state->s.ptr.p_double[k]*state->diffstep;
        vright = state->xbase.ptr.p_double[k]+state->s.ptr.p_double[k]*state->diffstep;
        if( (state->hasbndl.ptr.p_bool[k]&&ae_fp_less(vleft,state->bndl.ptr.p_double[k]))||(state->hasbndu.ptr.p_bool[k]&&ae_fp_greater(vright,state->bndu.ptr.p_double[k])) )
        {
            
            /*
             * A full-step formula violates box constraints, use a reduced one
             */
            if( state->hasbndl.ptr.p_bool[k]&&ae_fp_less(vleft,state->bndl.ptr.p_double[k]) )
            {
                vleft = state->bndl.ptr.p_double[k];
            }
            if( state->hasbndu.ptr.p_bool[k]&&ae_fp_greater(vright,state->bndu.ptr.p_double[k]) )
            {
                vright = state->bndu.ptr.p_double[k];
            }
            ae_assert(ae_fp_less_eq(vleft,vright), "MinNLC: integrity check failed", _state);
            if( ae_fp_eq(vleft,vright) )
            {
                
                /*
                 * Fixed variable
                 */
                continue;
            }
            if( state->formulatype==3||state->formulatype==5 )
            {
                
                /*
                 * 5-point and 3-point differences are converted into a nonsymmetric 2-point one
                 */
                state->querydata.ptr.p_double[n+state->queryformulasize*3*k+3+0] = vright;
                state->querydata.ptr.p_double[n+state->queryformulasize*3*k+3+1] = vleft;
                state->querydata.ptr.p_double[n+state->queryformulasize*3*k+3+2] = (double)1/(vright-vleft);
                state->repnfev = state->repnfev+2;
                continue;
            }
            if( state->formulatype==2 )
            {
                
                /*
                 * 2-point forward difference is converted into a forward or a backward one (possibly with reduced step).
                 * The difference with longer step is chosen.
                 */
                if( ae_fp_greater(vright-state->xbase.ptr.p_double[k],state->xbase.ptr.p_double[k]-vleft) )
                {
                    state->querydata.ptr.p_double[n+state->queryformulasize*3*k+3+0] = vright;
                    state->querydata.ptr.p_double[n+state->queryformulasize*3*k+3+1] = state->xbase.ptr.p_double[k];
                    state->querydata.ptr.p_double[n+state->queryformulasize*3*k+3+2] = (double)1/(vright-state->xbase.ptr.p_double[k]);
                }
                else
                {
                    state->querydata.ptr.p_double[n+state->queryformulasize*3*k+3+0] = state->xbase.ptr.p_double[k];
                    state->querydata.ptr.p_double[n+state->queryformulasize*3*k+3+1] = vleft;
                    state->querydata.ptr.p_double[n+state->queryformulasize*3*k+3+2] = (double)1/(state->xbase.ptr.p_double[k]-vleft);
                }
                state->repnfev = state->repnfev+1;
                continue;
            }
            ae_assert(ae_false, "MINNLC: integrity check 214202 failed", _state);
        }
        
        /*
         * A full-step formula does not violate box constraints.
         */
        if( state->formulatype==5 )
        {
            
            /*
             * Use 4-point central formula, converted to a sparsity-friendly representation:
             *
             *     f' = 4/3h*(f(x+0.5h)-f(x-0.5h))-1/6h*(f(x+h)-f(x-h))
             *
             *          [                                            ]
             *        = [ -8*(f(x+0.5h)-f(x-0.5h)) + (f(x+h)-f(x-h)) ]*(-1/6h)
             *          [                                            ]
             */
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+0*3+0] = state->xbase.ptr.p_double[k]+0.5*state->s.ptr.p_double[k]*state->diffstep;
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+0*3+1] = state->xbase.ptr.p_double[k]-0.5*state->s.ptr.p_double[k]*state->diffstep;
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+0*3+2] = (double)(-8);
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+1*3+0] = vright;
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+1*3+1] = vleft;
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+1*3+2] = -(double)1/((double)6*state->diffstep*state->s.ptr.p_double[k]);
            state->repnfev = state->repnfev+4;
            continue;
        }
        if( state->formulatype==3 )
        {
            
            /*
             * Use 2-point central formula, converted to a sparsity-friendly representation:
             *
             *     f' = (f(x+h)-f(x-h))/2h
             */
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+0*3+0] = (double)(0);
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+0*3+1] = (double)(0);
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+0*3+2] = (double)(0);
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+1*3+0] = vright;
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+1*3+1] = vleft;
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+1*3+2] = (double)1/((double)2*state->diffstep*state->s.ptr.p_double[k]);
            state->repnfev = state->repnfev+2;
            continue;
        }
        if( state->formulatype==2 )
        {
            
            /*
             * Use forward finite difference formula, converted to a sparsity-friendly representation:
             *
             *     f' = (f(x+h)-f(x))/h
             */
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+0*3+0] = (double)(0);
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+0*3+1] = (double)(0);
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+0*3+2] = (double)(0);
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+1*3+0] = vright;
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+1*3+1] = state->xbase.ptr.p_double[k];
            state->querydata.ptr.p_double[n+state->queryformulasize*3*k+1*3+2] = (double)1/(state->diffstep*state->s.ptr.p_double[k]);
            state->repnfev = state->repnfev+1;
            continue;
        }
        ae_assert(ae_false, "MINNLC: integrity check 234316 failed", _state);
    }
    state->repnfev = state->repnfev+1;
    rcopyv(n, &state->xbase, &state->x, _state);
    state->rstate.stage = 7;
    goto lbl_rcomm;
lbl_7:
    state->needfi = ae_false;
    goto lbl_45;
lbl_44:
    
    /*
     * Issue V1 request
     */
    ae_assert(state->protocolversion==1, "MINNLC: integrity check 0252 failed", _state);
    ae_assert(state->j.rows==1+nnlc&&state->j.cols==n, "MINNLC: integrity check 4236 failed", _state);
    rsetallocv(n, 0.0, &state->tmpsptolj, _state);
    state->needfi = ae_true;
    rcopyv(n, &state->x, &state->xbase, _state);
    k = 0;
lbl_46:
    if( k>n-1 )
    {
        goto lbl_48;
    }
    vleft = state->xbase.ptr.p_double[k]-state->s.ptr.p_double[k]*state->diffstep;
    vright = state->xbase.ptr.p_double[k]+state->s.ptr.p_double[k]*state->diffstep;
    if( !((state->hasbndl.ptr.p_bool[k]&&vleft<state->bndl.ptr.p_double[k])||(state->hasbndu.ptr.p_bool[k]&&vright>state->bndu.ptr.p_double[k])) )
    {
        goto lbl_49;
    }
    
    /*
     * 4-point formula violates box constraints, use 2-point uncentered one
     */
    if( state->hasbndl.ptr.p_bool[k]&&ae_fp_less(vleft,state->bndl.ptr.p_double[k]) )
    {
        vleft = state->bndl.ptr.p_double[k];
    }
    if( state->hasbndu.ptr.p_bool[k]&&ae_fp_greater(vright,state->bndu.ptr.p_double[k]) )
    {
        vright = state->bndu.ptr.p_double[k];
    }
    ae_assert(ae_fp_less_eq(vleft,vright), "MinNLC: integrity check failed", _state);
    if( ae_fp_eq(vleft,vright) )
    {
        
        /*
         * Fixed variable
         */
        for(i=0; i<=nnlc; i++)
        {
            state->j.ptr.pp_double[i][k] = (double)(0);
        }
        goto lbl_47;
    }
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = vleft;
    state->rstate.stage = 8;
    goto lbl_rcomm;
lbl_8:
    rcopyv(1+nnlc, &state->fi, &state->fm1, _state);
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = vright;
    state->rstate.stage = 9;
    goto lbl_rcomm;
lbl_9:
    rcopyv(1+nnlc, &state->fi, &state->fp1, _state);
    for(i=0; i<=nnlc; i++)
    {
        state->j.ptr.pp_double[i][k] = (state->fp1.ptr.p_double[i]-state->fm1.ptr.p_double[i])/(vright-vleft);
    }
    state->repnfev = state->repnfev+2;
    state->tmpsptolj.ptr.p_double[k] = (double)10*ae_machineepsilon/(vright-vleft);
    goto lbl_50;
lbl_49:
    
    /*
     * Use 4-point central formula
     */
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = state->x.ptr.p_double[k]-state->s.ptr.p_double[k]*state->diffstep;
    state->rstate.stage = 10;
    goto lbl_rcomm;
lbl_10:
    rcopyv(1+nnlc, &state->fi, &state->fm2, _state);
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = state->x.ptr.p_double[k]-0.5*state->s.ptr.p_double[k]*state->diffstep;
    state->rstate.stage = 11;
    goto lbl_rcomm;
lbl_11:
    rcopyv(1+nnlc, &state->fi, &state->fm1, _state);
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = state->x.ptr.p_double[k]+0.5*state->s.ptr.p_double[k]*state->diffstep;
    state->rstate.stage = 12;
    goto lbl_rcomm;
lbl_12:
    rcopyv(1+nnlc, &state->fi, &state->fp1, _state);
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = state->x.ptr.p_double[k]+state->s.ptr.p_double[k]*state->diffstep;
    state->rstate.stage = 13;
    goto lbl_rcomm;
lbl_13:
    rcopyv(1+nnlc, &state->fi, &state->fp2, _state);
    for(i=0; i<=nnlc; i++)
    {
        state->j.ptr.pp_double[i][k] = ((double)8*(state->fp1.ptr.p_double[i]-state->fm1.ptr.p_double[i])-(state->fp2.ptr.p_double[i]-state->fm2.ptr.p_double[i]))/((double)6*state->diffstep*state->s.ptr.p_double[k]);
    }
    state->repnfev = state->repnfev+4;
    state->tmpsptolj.ptr.p_double[k] = ae_fabs((double)10*ae_machineepsilon*(double)4/((double)3*state->diffstep*state->s.ptr.p_double[k]), _state);
lbl_50:
lbl_47:
    k = k+1;
    goto lbl_46;
lbl_48:
    rcopyv(n, &state->xbase, &state->x, _state);
    state->rstate.stage = 14;
    goto lbl_rcomm;
lbl_14:
    state->needfi = ae_false;
    state->repnfev = state->repnfev+1;
    
    /*
     * Sparsify Jacobian by zeroing components whose deviation from zero can be
     * explained by numerical rounding errors.
     *
     * This part is essential for the performance of sparse optimizers which can
     * face severe performance breakdown on Jacobians which became dense due to
     * numerical rounding errors.
     */
    rallocv(1+nnlc, &state->tmpspaf, _state);
    for(i=0; i<=nnlc; i++)
    {
        state->tmpspaf.ptr.p_double[i] = ae_fabs(state->fi.ptr.p_double[i], _state);
    }
    for(i=0; i<=nnlc; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fabs(state->j.ptr.pp_double[i][j], _state)<=state->tmpspaf.ptr.p_double[i]*state->tmpsptolj.ptr.p_double[j] )
            {
                state->j.ptr.pp_double[i][j] = (double)(0);
            }
        }
    }
lbl_45:
    b = ae_true;
lbl_42:
    if( !(originalrequest==2&&ae_fp_eq(state->diffstep,(double)(0))) )
    {
        goto lbl_51;
    }
    
    /*
     * Analytic Jacobian
     */
    ae_assert(originalquerysize==1, "MINNLC: integrity check 198106 failed", _state);
    if( !state->issuesparserequests )
    {
        goto lbl_53;
    }
    
    /*
     * Request sparse Jacobian
     */
    densejac = ae_false;
    ae_assert(state->protocolversion==2, "MINNLC: integrity check 0559 failed", _state);
    state->requesttype = 1;
    state->querysize = 1;
    state->queryfuncs = 1+nnlc;
    state->queryvars = n;
    state->querydim = 0;
    state->replysj.m = -1;
    state->replysj.n = -1;
    state->rstate.stage = 15;
    goto lbl_rcomm;
lbl_15:
    ae_assert(sparsegetnrows(&state->replysj, _state)==state->querysize*state->queryfuncs, "MINNLC: integrity check 1639 failed", _state);
    ae_assert(sparsegetncols(&state->replysj, _state)==state->queryvars, "MINNLC: integrity check 1639 failed", _state);
    goto lbl_54;
lbl_53:
    
    /*
     * Request dense Jacobian
     */
    densejac = ae_true;
    if( state->protocolversion!=2 )
    {
        goto lbl_55;
    }
    state->requesttype = 2;
    state->querysize = 1;
    state->queryfuncs = 1+nnlc;
    state->queryvars = n;
    state->querydim = 0;
    rallocv(n*(1+nnlc), &state->replydj, _state);
    rallocm(1+nnlc, n, &state->tmpj1, _state);
    state->rstate.stage = 16;
    goto lbl_rcomm;
lbl_16:
    goto lbl_56;
lbl_55:
    ae_assert(state->protocolversion==1, "MINNLC: integrity check 8659 failed", _state);
    ae_assert(state->j.rows==1+nnlc&&state->j.cols==n, "MINNLC: integrity check 4236 failed", _state);
    state->needfij = ae_true;
    state->rstate.stage = 17;
    goto lbl_rcomm;
lbl_17:
    state->needfij = ae_false;
lbl_56:
lbl_54:
    state->repnfev = state->repnfev+1;
    b = ae_true;
lbl_51:
    if( originalrequest!=4 )
    {
        goto lbl_57;
    }
    
    /*
     * Batch request for gradient-free objective/constraints
     */
    rallocv(originalquerysize*(1+nnlc), &state->replyfi, _state);
    if( state->protocolversion!=2 )
    {
        goto lbl_59;
    }
    rcopyallocv(n*originalquerysize, &state->batchquerydata, &state->querydata, _state);
    state->requesttype = 4;
    state->querysize = originalquerysize;
    state->queryfuncs = 1+nnlc;
    state->queryvars = n;
    state->querydim = 0;
    state->rstate.stage = 18;
    goto lbl_rcomm;
lbl_18:
    goto lbl_60;
lbl_59:
    ae_assert(state->protocolversion==1, "MINNLC: integrity check 8659 failed", _state);
    i = 0;
lbl_61:
    if( i>originalquerysize-1 )
    {
        goto lbl_63;
    }
    rcopyvx(n, &state->batchquerydata, i*n, &state->x, 0, _state);
    state->needfi = ae_true;
    state->rstate.stage = 19;
    goto lbl_rcomm;
lbl_19:
    state->needfi = ae_false;
    rcopyvx(1+nnlc, &state->fi, 0, &state->replyfi, i*(1+nnlc), _state);
    i = i+1;
    goto lbl_61;
lbl_63:
lbl_60:
    state->repnfev = state->repnfev+originalquerysize;
    b = ae_true;
lbl_57:
    ae_assert(b, "MINNLC: integrity check 6323 failed", _state);
    
    /*
     * Send results back to the optimizer
     */
    ae_assert(((originalrequest==-1||originalrequest==1)||originalrequest==2)||originalrequest==4, "MINNLC: integrity check 6336 failed", _state);
    b = ae_false;
    if( originalrequest==-1 )
    {
        
        /*
         * Do nothing
         */
        b = ae_true;
    }
    if( originalrequest==2 )
    {
        if( state->solvertype==0 )
        {
            
            /*
             * AUL solver
             */
            ae_assert(state->protocolversion==1||state->protocolversion==2, "MINNLC: integrity check 1435 failed", _state);
            if( state->protocolversion==2 )
            {
                rcopyv(1+nnlc, &state->replyfi, &state->aulsolverstate->fi, _state);
            }
            else
            {
                rcopyv(1+nnlc, &state->fi, &state->aulsolverstate->fi, _state);
            }
            if( densejac )
            {
                
                /*
                 * Convert dense Jacobian to sparse and scale
                 */
                state->aulsolverstate->sj.matrixtype = 1;
                state->aulsolverstate->sj.m = 1+nnlc;
                state->aulsolverstate->sj.n = n;
                iallocv(state->aulsolverstate->sj.m+1, &state->aulsolverstate->sj.ridx, _state);
                state->aulsolverstate->sj.ridx.ptr.p_int[0] = 0;
                offs = 0;
                for(i=0; i<=nnlc; i++)
                {
                    igrowv(offs+n, &state->aulsolverstate->sj.idx, _state);
                    rgrowv(offs+n, &state->aulsolverstate->sj.vals, _state);
                    if( state->protocolversion==2 )
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( state->replydj.ptr.p_double[i*n+j]!=0.0 )
                            {
                                state->aulsolverstate->sj.idx.ptr.p_int[offs] = j;
                                state->aulsolverstate->sj.vals.ptr.p_double[offs] = state->replydj.ptr.p_double[i*n+j]*state->s.ptr.p_double[j];
                                offs = offs+1;
                            }
                        }
                    }
                    else
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( state->j.ptr.pp_double[i][j]!=0.0 )
                            {
                                state->aulsolverstate->sj.idx.ptr.p_int[offs] = j;
                                state->aulsolverstate->sj.vals.ptr.p_double[offs] = state->j.ptr.pp_double[i][j]*state->s.ptr.p_double[j];
                                offs = offs+1;
                            }
                        }
                    }
                    state->aulsolverstate->sj.ridx.ptr.p_int[i+1] = offs;
                }
                state->aulsolverstate->sj.ninitialized = offs;
                sparseinitduidx(&state->aulsolverstate->sj, _state);
            }
            else
            {
                
                /*
                 * Copy sparse Jacobian and scale
                 */
                sparsecopytocrsbuf(&state->replysj, &state->aulsolverstate->sj, _state);
                sparsemultiplycolsby(&state->aulsolverstate->sj, &state->s, _state);
            }
            b = ae_true;
        }
        if( state->solvertype==2||state->solvertype==3 )
        {
            
            /*
             * SQP solver
             */
            if( state->protocolversion==2 )
            {
                state->sqpsolverstate->fi.ptr.p_double[0] = state->replyfi.ptr.p_double[0];
                if( !densejac )
                {
                    rsetr(n, 0.0, &state->sqpsolverstate->j, 0, _state);
                    j0 = state->replysj.ridx.ptr.p_int[0];
                    j1 = state->replysj.ridx.ptr.p_int[1]-1;
                    for(jj=j0; jj<=j1; jj++)
                    {
                        state->sqpsolverstate->j.ptr.pp_double[0][state->replysj.idx.ptr.p_int[jj]] = state->replysj.vals.ptr.p_double[jj];
                    }
                }
                else
                {
                    rcopyvr(n, &state->replydj, &state->sqpsolverstate->j, 0, _state);
                }
                rmergemulvr(n, &state->s, &state->sqpsolverstate->j, 0, _state);
                for(i=0; i<=state->nlcnlec+state->nlcnlic-1; i++)
                {
                    state->sqpsolverstate->fi.ptr.p_double[i+1] = state->replyfi.ptr.p_double[1+state->nlcidx.ptr.p_int[i]]*state->nlcmul.ptr.p_double[i]+state->nlcadd.ptr.p_double[i];
                    k = 1+state->nlcidx.ptr.p_int[i];
                    if( !densejac )
                    {
                        rsetr(n, 0.0, &state->sqpsolverstate->j, i+1, _state);
                        j0 = state->replysj.ridx.ptr.p_int[k];
                        j1 = state->replysj.ridx.ptr.p_int[k+1]-1;
                        for(jj=j0; jj<=j1; jj++)
                        {
                            state->sqpsolverstate->j.ptr.pp_double[i+1][state->replysj.idx.ptr.p_int[jj]] = state->replysj.vals.ptr.p_double[jj];
                        }
                    }
                    else
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            state->sqpsolverstate->j.ptr.pp_double[i+1][j] = state->replydj.ptr.p_double[k*n+j];
                        }
                    }
                    rmergemulvr(n, &state->s, &state->sqpsolverstate->j, i+1, _state);
                    rmulr(n, state->nlcmul.ptr.p_double[i], &state->sqpsolverstate->j, i+1, _state);
                }
            }
            else
            {
                ae_assert(state->protocolversion==1, "MINNLC: integrity check 2222 failed", _state);
                state->sqpsolverstate->fi.ptr.p_double[0] = state->fi.ptr.p_double[0];
                rcopyrr(n, &state->j, 0, &state->sqpsolverstate->j, 0, _state);
                rmergemulvr(n, &state->s, &state->sqpsolverstate->j, 0, _state);
                for(i=0; i<=state->nlcnlec+state->nlcnlic-1; i++)
                {
                    state->sqpsolverstate->fi.ptr.p_double[i+1] = state->fi.ptr.p_double[1+state->nlcidx.ptr.p_int[i]]*state->nlcmul.ptr.p_double[i]+state->nlcadd.ptr.p_double[i];
                    rcopyrr(n, &state->j, 1+state->nlcidx.ptr.p_int[i], &state->sqpsolverstate->j, i+1, _state);
                    rmergemulvr(n, &state->s, &state->sqpsolverstate->j, i+1, _state);
                    rmulr(n, state->nlcmul.ptr.p_double[i], &state->sqpsolverstate->j, i+1, _state);
                }
            }
            b = ae_true;
        }
        if( state->solvertype==4||state->solvertype==5 )
        {
            
            /*
             * FSQP solver
             */
            if( state->protocolversion==2 )
            {
                rcopyv(nnlc+1, &state->replyfi, &state->fsqpsolverstate->fi, _state);
                if( densejac )
                {
                    minnlc_crsfromdensev(&state->replydj, state->nnlc+1, n, &state->fsqpsolverstate->sj, _state);
                }
                else
                {
                    sparsecopytocrsbuf(&state->replysj, &state->fsqpsolverstate->sj, _state);
                }
                sparsemultiplycolsby(&state->fsqpsolverstate->sj, &state->s, _state);
            }
            else
            {
                ae_assert(state->protocolversion==1, "MINNLC: integrity check 2222 failed", _state);
                ae_assert(densejac, "MINNLC: integrity check 2223 failed", _state);
                rcopyv(nnlc+1, &state->fi, &state->fsqpsolverstate->fi, _state);
                minnlc_crsfromdense(&state->j, state->nnlc+1, n, &state->fsqpsolverstate->sj, _state);
                sparsemultiplycolsby(&state->fsqpsolverstate->sj, &state->s, _state);
            }
            b = ae_true;
        }
        if( state->solvertype==7||state->solvertype==8 )
        {
            
            /*
             * GIPM2 solver
             */
            if( state->protocolversion==2 )
            {
                rcopyv(nnlc+1, &state->replyfi, &state->gipm2solver->replyfi, _state);
                if( densejac )
                {
                    minnlc_crsfromdensev(&state->replydj, state->nnlc+1, n, &state->gipm2solver->replysj, _state);
                }
                else
                {
                    sparsecopytocrsbuf(&state->replysj, &state->gipm2solver->replysj, _state);
                }
                sparsemultiplycolsby(&state->gipm2solver->replysj, &state->s, _state);
            }
            else
            {
                ae_assert(state->protocolversion==1, "MINNLC: integrity check 2222 failed", _state);
                ae_assert(densejac, "MINNLC: integrity check 2223 failed", _state);
                rcopyv(nnlc+1, &state->fi, &state->gipm2solver->replyfi, _state);
                minnlc_crsfromdense(&state->j, state->nnlc+1, n, &state->gipm2solver->replysj, _state);
                sparsemultiplycolsby(&state->gipm2solver->replysj, &state->s, _state);
            }
            b = ae_true;
        }
    }
    if( originalrequest==4 )
    {
        if( state->solvertype==6 )
        {
            
            /*
             * ORBIT solver
             */
            rcopyv((nnlc+1)*originalquerysize, &state->replyfi, &state->orbitsolver->replyfi, _state);
            b = ae_true;
        }
    }
    ae_assert(b, "MINNLC: integrity check 0038 failed", _state);
    goto lbl_34;
lbl_35:
    
    /*
     * Results phase
     */
    b = ae_false;
    if( state->solvertype==0 )
    {
        
        /*
         * AUL results
         */
        state->repterminationtype = state->aulsolverstate->repterminationtype;
        state->repouteriterationscount = 1;
        state->repinneriterationscount = state->aulsolverstate->repiterationscount;
        state->repbcerr = state->aulsolverstate->repbcerr;
        state->repbcidx = state->aulsolverstate->repbcidx;
        state->replcerr = state->aulsolverstate->replcerr;
        state->replcidx = state->aulsolverstate->replcidx;
        state->repnlcerr = state->aulsolverstate->repnlcerr;
        state->repnlcidx = state->aulsolverstate->repnlcidx;
        state->repsclfeaserr = rmaxabs3(state->repbcerr, state->replcerr, state->repnlcerr, _state);
        minnlc_unscale(state, &state->aulsolverstate->xtruebest.x, &state->aulsolverstate->scaledbndl, &state->aulsolverstate->scaledbndu, &state->xc, _state);
        b = ae_true;
    }
    if( state->solvertype==2||state->solvertype==3 )
    {
        state->repterminationtype = state->sqpsolverstate->repterminationtype;
        state->repouteriterationscount = state->sqpsolverstate->repiterationscount;
        state->repinneriterationscount = state->sqpsolverstate->repiterationscount;
        state->repbcerr = state->sqpsolverstate->repbcerr;
        state->repbcidx = state->sqpsolverstate->repbcidx;
        state->replcerr = state->sqpsolverstate->replcerr;
        state->replcidx = state->sqpsolverstate->replcidx;
        state->repnlcerr = state->sqpsolverstate->repnlcerr;
        state->repnlcidx = state->sqpsolverstate->repnlcidx;
        state->repsclfeaserr = rmaxabs3(state->repbcerr, state->replcerr, state->repnlcerr, _state);
        minnlc_unscale(state, &state->sqpsolverstate->stepk.x, &state->sqpsolverstate->scaledbndl, &state->sqpsolverstate->scaledbndu, &state->xc, _state);
        b = ae_true;
    }
    if( state->solvertype==4||state->solvertype==5 )
    {
        state->repterminationtype = state->fsqpsolverstate->repterminationtype;
        state->repouteriterationscount = state->fsqpsolverstate->repiterationscount;
        state->repinneriterationscount = state->fsqpsolverstate->repiterationscount;
        state->repbcerr = state->fsqpsolverstate->repbcerr;
        state->repbcidx = state->fsqpsolverstate->repbcidx;
        state->replcerr = state->fsqpsolverstate->replcerr;
        state->replcidx = state->fsqpsolverstate->replcidx;
        state->repnlcerr = state->fsqpsolverstate->repnlcerr;
        state->repnlcidx = state->fsqpsolverstate->repnlcidx;
        state->repsclfeaserr = state->fsqpsolverstate->repsclerr;
        minnlc_unscale(state, &state->fsqpsolverstate->stepk.x, &state->fsqpsolverstate->scaledbndl, &state->fsqpsolverstate->scaledbndu, &state->xc, _state);
        rcopyv(state->n, &state->fsqpsolverstate->replagbc, &state->replagbc, _state);
        rcopyv(state->n, &state->fsqpsolverstate->replagbc, &state->replagbcnz, _state);
        for(i=0; i<=state->n-1; i++)
        {
            if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&state->bndl.ptr.p_double[i]==state->bndu.ptr.p_double[i] )
            {
                state->replagbc.ptr.p_double[i] = 0.0;
                if( state->diffstep!=(double)0 )
                {
                    state->replagbcnz.ptr.p_double[i] = 0.0;
                }
            }
        }
        for(i=0; i<=state->xlc.nsparse+state->xlc.ndense-1; i++)
        {
            state->replaglc.ptr.p_double[state->xlc.lcsrcidx.ptr.p_int[i]] = state->replaglc.ptr.p_double[state->xlc.lcsrcidx.ptr.p_int[i]]+state->fsqpsolverstate->replaglc.ptr.p_double[i];
        }
        for(i=0; i<=nnlc-1; i++)
        {
            state->replagnlc.ptr.p_double[i] = state->fsqpsolverstate->replagnlc.ptr.p_double[i];
        }
        b = ae_true;
    }
    if( state->solvertype==6 )
    {
        state->repterminationtype = state->orbitsolver->repterminationtype;
        state->repouteriterationscount = state->orbitsolver->repiterationscount;
        state->repinneriterationscount = state->orbitsolver->repiterationscount;
        state->repbcerr = 0.0;
        state->repbcidx = -1;
        state->replcerr = 0.0;
        state->replcidx = -1;
        minnlc_unscalebatch(state, &state->orbitsolver->xk, 1, &state->idxraw2red, state->nreduced, &state->orbitsolver->scaledbndl, &state->orbitsolver->scaledbndu, &state->xc, _state);
        if( state->xlc.nsparse+state->xlc.ndense>0 )
        {
            xlcconverttosparse(&state->xlc, _state);
            checklc2violation(&state->xlc.effsparsea, &state->xlc.effal, &state->xlc.effau, &state->xlc.lcsrcidx, state->xlc.nsparse+state->xlc.ndense, &state->xc, &state->replcerr, &state->replcidx, _state);
        }
        checknlc2violation(&state->orbitsolver->fkvec, &state->nl, &state->nu, nnlc, &state->repnlcerr, &state->repnlcidx, _state);
        state->repsclfeaserr = rmaxabs3(state->repbcerr, state->replcerr, state->repnlcerr, _state);
        rsetv(state->n, 0.0, &state->replagbc, _state);
        rsetv(state->n, 0.0, &state->replagbcnz, _state);
        rsetv(state->xlc.nsparse+state->xlc.ndense, 0.0, &state->replaglc, _state);
        rsetv(nnlc, 0.0, &state->replagnlc, _state);
        b = ae_true;
    }
    if( state->solvertype==7||state->solvertype==8 )
    {
        state->repterminationtype = state->gipm2solver->repterminationtype;
        state->repouteriterationscount = state->gipm2solver->repiterationscount;
        state->repinneriterationscount = state->gipm2solver->repiterationscount;
        state->repbcerr = state->gipm2solver->repbcerr;
        state->repbcidx = state->gipm2solver->repbcidx;
        state->replcerr = state->gipm2solver->replcerr;
        state->replcidx = state->gipm2solver->replcidx;
        state->repnlcerr = state->gipm2solver->repnlcerr;
        state->repnlcidx = state->gipm2solver->repnlcidx;
        state->repsclfeaserr = state->gipm2solver->repsclerr;
        minnlc_unscale(state, &state->gipm2solver->xs, &state->gipm2solver->scaledbndl, &state->gipm2solver->scaledbndu, &state->xc, _state);
        rcopyv(state->n, &state->gipm2solver->replagbc, &state->replagbc, _state);
        rcopyv(state->n, &state->gipm2solver->replagbc, &state->replagbcnz, _state);
        for(i=0; i<=state->n-1; i++)
        {
            if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&state->bndl.ptr.p_double[i]==state->bndu.ptr.p_double[i] )
            {
                state->replagbc.ptr.p_double[i] = 0.0;
                if( state->diffstep!=(double)0 )
                {
                    state->replagbcnz.ptr.p_double[i] = 0.0;
                }
            }
        }
        for(i=0; i<=state->xlc.nsparse+state->xlc.ndense-1; i++)
        {
            state->replaglc.ptr.p_double[state->xlc.lcsrcidx.ptr.p_int[i]] = state->replaglc.ptr.p_double[state->xlc.lcsrcidx.ptr.p_int[i]]+state->gipm2solver->replaglc.ptr.p_double[i];
        }
        for(i=0; i<=nnlc-1; i++)
        {
            state->replagnlc.ptr.p_double[i] = state->gipm2solver->replagnlc.ptr.p_double[i];
        }
        b = ae_true;
    }
    ae_assert(b, "MINNLC: integrity check 2219 failed", _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = i;
    state->rstate.ia.ptr.p_int[1] = j;
    state->rstate.ia.ptr.p_int[2] = k;
    state->rstate.ia.ptr.p_int[3] = j0;
    state->rstate.ia.ptr.p_int[4] = j1;
    state->rstate.ia.ptr.p_int[5] = jj;
    state->rstate.ia.ptr.p_int[6] = n;
    state->rstate.ia.ptr.p_int[7] = nnlc;
    state->rstate.ia.ptr.p_int[8] = offs;
    state->rstate.ia.ptr.p_int[9] = originalrequest;
    state->rstate.ia.ptr.p_int[10] = originalquerysize;
    state->rstate.ba.ptr.p_bool[0] = b;
    state->rstate.ba.ptr.p_bool[1] = densejac;
    state->rstate.ra.ptr.p_double[0] = vleft;
    state->rstate.ra.ptr.p_double[1] = vright;
    state->rstate.ra.ptr.p_double[2] = v;
    return result;
}


/*************************************************************************
This  function  activates/deactivates verification  of  the  user-supplied
analytic gradient/Jacobian.

Upon  activation  of  this  option  OptGuard  integrity  checker  performs
numerical differentiation of your target  function  (constraints)  at  the
initial point (note: future versions may also perform check  at  the final
point) and compares numerical gradient/Jacobian with analytic one provided
by you.

If difference is too large, an error flag is set and optimization  session
continues. After optimization session is over, you can retrieve the report
which stores both gradients/Jacobians, and specific components highlighted
as suspicious by the OptGuard.

The primary OptGuard report can be retrieved with minnlcoptguardresults().

IMPORTANT: gradient check is a high-overhead option which  will  cost  you
           about 3*N additional function evaluations. In many cases it may
           cost as much as the rest of the optimization session.
           
           YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO
           CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY.

NOTE: unlike previous incarnation of the gradient checking code,  OptGuard
      does NOT interrupt optimization even if it discovers bad gradient.
      
INPUT PARAMETERS:
    State       -   structure used to store algorithm state
    TestStep    -   verification step used for numerical differentiation:
                    * TestStep=0 turns verification off
                    * TestStep>0 activates verification
                    You should carefully choose TestStep. Value  which  is
                    too large (so large that  function  behavior  is  non-
                    cubic at this scale) will lead  to  false  alarms. Too
                    short step will result in rounding  errors  dominating
                    numerical derivative.
                    
                    You may use different step for different parameters by
                    means of setting scale with minnlcsetscale().

=== EXPLANATION ==========================================================                    

In order to verify gradient algorithm performs following steps:
  * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i],
    where X[i] is i-th component of the initial point and S[i] is a  scale
    of i-th parameter
  * F(X) is evaluated at these trial points
  * we perform one more evaluation in the middle point of the interval
  * we  build  cubic  model using function values and derivatives at trial
    points and we compare its prediction with actual value in  the  middle
    point

  -- ALGLIB --
     Copyright 15.06.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcoptguardgradient(minnlcstate* state,
     double teststep,
     ae_state *_state)
{


    ae_assert(ae_isfinite(teststep, _state), "MinNLCOptGuardGradient: TestStep contains NaN or INF", _state);
    ae_assert(ae_fp_greater_eq(teststep,(double)(0)), "MinNLCOptGuardGradient: invalid argument TestStep(TestStep<0)", _state);
    state->teststep = teststep;
}


/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0) and/or constraints
b) nonsmooth     target function (non-C1) and/or constraints

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.
      
      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.
      
      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.
      
      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.
                      This kind of monitoring does not work well with  SQP
                      because SQP solver needs just 1-2 function evaluations
                      per step, which is not enough for OptGuard  to  make
                      any conclusions.
  
=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcoptguardsmoothness(minnlcstate* state,
     ae_int_t level,
     ae_state *_state)
{


    ae_assert(level==0||level==1, "MinNLCOptGuardSmoothness: unexpected value of level parameter", _state);
    state->smoothnessguardlevel = level;
}


/*************************************************************************
Results of OptGuard integrity check, should be called  after  optimization
session is over.

=== PRIMARY REPORT =======================================================

OptGuard performs several checks which are intended to catch common errors
in the implementation of nonlinear function/gradient:
* incorrect analytic gradient
* discontinuous (non-C0) target functions (constraints)
* nonsmooth     (non-C1) target functions (constraints)

Each of these checks is activated with appropriate function:
* minnlcoptguardgradient() for gradient verification
* minnlcoptguardsmoothness() for C0/C1 checks

Following flags are set when these errors are suspected:
* rep.badgradsuspected, and additionally:
  * rep.badgradfidx for specific function (Jacobian row) suspected
  * rep.badgradvidx for specific variable (Jacobian column) suspected
  * rep.badgradxbase, a point where gradient/Jacobian is tested
  * rep.badgraduser, user-provided gradient/Jacobian
  * rep.badgradnum, reference gradient/Jacobian obtained via numerical
    differentiation
* rep.nonc0suspected, and additionally:
  * rep.nonc0fidx - an index of specific function violating C0 continuity
* rep.nonc1suspected, and additionally
  * rep.nonc1fidx - an index of specific function violating C1 continuity
Here function index 0 means  target function, index 1  or  higher  denotes
nonlinear constraints.

=== ADDITIONAL REPORTS/LOGS ==============================================
    
Several different tests are performed to catch C0/C1 errors, you can  find
out specific test signaled error by looking to:
* rep.nonc0test0positive, for non-C0 test #0
* rep.nonc1test0positive, for non-C1 test #0
* rep.nonc1test1positive, for non-C1 test #1

Additional information (including line search logs)  can  be  obtained  by
means of:
* minnlcoptguardnonc1test0results()
* minnlcoptguardnonc1test1results()
which return detailed error reports, specific points where discontinuities
were found, and so on.

==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    rep     -   generic OptGuard report;  more  detailed  reports  can  be
                retrieved with other functions.

NOTE: false negatives (nonsmooth problems are not identified as  nonsmooth
      ones) are possible although unlikely.
      
      The reason  is  that  you  need  to  make several evaluations around
      nonsmoothness  in  order  to  accumulate  enough  information  about
      function curvature. Say, if you start right from the nonsmooth point,
      optimizer simply won't get enough data to understand what  is  going
      wrong before it terminates due to abrupt changes in the  derivative.
      It is also  possible  that  "unlucky"  step  will  move  us  to  the
      termination too quickly.
      
      Our current approach is to have less than 0.1%  false  negatives  in
      our test examples  (measured  with  multiple  restarts  from  random
      points), and to have exactly 0% false positives.
   
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcoptguardresults(minnlcstate* state,
     optguardreport* rep,
     ae_state *_state)
{

    _optguardreport_clear(rep);

    smoothnessmonitorexportreport(&state->smonitor, rep, _state);
}


/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #0

Nonsmoothness (non-C1) test #0 studies  function  values  (not  gradient!)
obtained during line searches and monitors  behavior  of  the  directional
derivative estimate.

This test is less powerful than test #1, but it does  not  depend  on  the
gradient values and thus it is more robust against artifacts introduced by
numerical differentiation.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* fidx - is an index of the function (0 for  target  function, 1 or higher
  for nonlinear constraints) which is suspected of being "non-C1"
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], f[] - arrays of length CNT which store step lengths and  function
  values at these points; f[i] is evaluated in x0+stp[i]*d.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
  
==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #0 "strong" report
    lngrep  -   C1 test #0 "long" report
    
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcoptguardnonc1test0results(const minnlcstate* state,
     optguardnonc1test0report* strrep,
     optguardnonc1test0report* lngrep,
     ae_state *_state)
{

    _optguardnonc1test0report_clear(strrep);
    _optguardnonc1test0report_clear(lngrep);

    smoothnessmonitorexportc1test0report(&state->smonitor.nonc1test0strrep, &state->lastscaleused, strrep, _state);
    smoothnessmonitorexportc1test0report(&state->smonitor.nonc1test0lngrep, &state->lastscaleused, lngrep, _state);
}


/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #1

Nonsmoothness (non-C1)  test  #1  studies  individual  components  of  the
gradient computed during line search.

When precise analytic gradient is provided this test is more powerful than
test #0  which  works  with  function  values  and  ignores  user-provided
gradient.  However,  test  #0  becomes  more   powerful   when   numerical
differentiation is employed (in such cases test #1 detects  higher  levels
of numerical noise and becomes too conservative).

This test also tells specific components of the gradient which violate  C1
continuity, which makes it more informative than #0, which just tells that
continuity is violated.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* fidx - is an index of the function (0 for  target  function, 1 or higher
  for nonlinear constraints) which is suspected of being "non-C1"
* vidx - is an index of the variable in [0,N) with nonsmooth derivative
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], g[] - arrays of length CNT which store step lengths and  gradient
  values at these points; g[i] is evaluated in  x0+stp[i]*d  and  contains
  vidx-th component of the gradient.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
  
==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #1 "strong" report
    lngrep  -   C1 test #1 "long" report
    
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcoptguardnonc1test1results(minnlcstate* state,
     optguardnonc1test1report* strrep,
     optguardnonc1test1report* lngrep,
     ae_state *_state)
{

    _optguardnonc1test1report_clear(strrep);
    _optguardnonc1test1report_clear(lngrep);

    smoothnessmonitorexportc1test1report(&state->smonitor.nonc1test1strrep, &state->lastscaleused, strrep, _state);
    smoothnessmonitorexportc1test1report(&state->smonitor.nonc1test1lngrep, &state->lastscaleused, lngrep, _state);
}


/*************************************************************************
MinNLC results:  the  solution  found,  completion  codes  and  additional
information.

If you activated OptGuard integrity checking functionality and want to get
OptGuard report, it can be retrieved with:
* minnlcoptguardresults() - for a primary report about (a) suspected C0/C1
  continuity violations and (b) errors in the analytic gradient.
* minnlcoptguardnonc1test0results() - for C1 continuity violation test #0,
  detailed line search log
* minnlcoptguardnonc1test1results() - for C1 continuity violation test #1,
  detailed line search log

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report, contains information about completion
                code, constraint violation at the solution and so on.
                
                rep.f contains objective value at the solution.
                
                You   should   check   rep.terminationtype  in  order   to
                distinguish successful termination from unsuccessful one:
                
                === FAILURE CODES ===
                * -8    internal  integrity control  detected  infinite or
                        NAN  values  in  function/gradient,  recovery  was
                        impossible. Abnormal termination signalled.
                * -3    box  constraints are infeasible.
                        Note: infeasibility of  non-box  constraints  does
                              NOT trigger emergency completion;  you  have
                              to examine rep.bcerr/rep.lcerr/rep.nlcerr to
                              detect possibly inconsistent constraints.
                              
                === SUCCESS CODES ===
                *  1   small objective decrease indicates convergence
                *  2   scaled step is no more than EpsX.
                *  5   MaxIts steps were taken.
                *  7   stopping conditions are  too  stringent,  returning
                       the best point so far
                *  8   user   requested    algorithm    termination    via
                       minnlcrequesttermination(), last accepted point  is
                       returned.
                       
                === ADDITIONAL CODES ===
                * +800      if   during  algorithm  execution  the  solver
                            encountered NAN/INF values in  the  target  or
                            constraints but managed to recover by reducing
                            trust region radius, the  solver  returns  one
                            of SUCCESS codes but adds +800 to the code.
                
                Some  solvers  (as  of  ALGLIB 4.05, SQP and GIPM2) return
                Lagrange multipliers in rep.lagbc/lagbcnz,  laglc,  lagnlc
                fields.
                
                More information about fields of this  structure  can  be
                found in the comments on the minnlcreport datatype.
   
  -- ALGLIB --
     Copyright 18.01.2024 by Bochkanov Sergey
*************************************************************************/
void minnlcresults(const minnlcstate* state,
     /* Real    */ ae_vector* x,
     minnlcreport* rep,
     ae_state *_state)
{

    ae_vector_clear(x);
    _minnlcreport_clear(rep);

    minnlcresultsbuf(state, x, rep, _state);
}


/*************************************************************************
NLC results

Buffered implementation of MinNLCResults() which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minnlcresultsbuf(const minnlcstate* state,
     /* Real    */ ae_vector* x,
     minnlcreport* rep,
     ae_state *_state)
{


    rep->f = state->repf;
    rep->iterationscount = state->repinneriterationscount;
    rep->nfev = state->repnfev;
    rep->terminationtype = state->repterminationtype;
    rep->bcerr = state->repbcerr;
    rep->bcidx = state->repbcidx;
    rep->lcerr = state->replcerr;
    rep->lcidx = state->replcidx;
    rep->nlcerr = state->repnlcerr;
    rep->nlcidx = state->repnlcidx;
    rep->sclfeaserr = state->repsclfeaserr;
    rep->dbgphase0its = state->repdbgphase0its;
    if( state->repterminationtype>0 )
    {
        rcopyallocv(state->n, &state->replagbc, &rep->lagbc, _state);
        rcopyallocv(state->n, &state->replagbcnz, &rep->lagbcnz, _state);
        rcopyallocv(state->xlc.ndense+state->xlc.nsparse, &state->replaglc, &rep->laglc, _state);
        rcopyallocv(state->nnlc, &state->replagnlc, &rep->lagnlc, _state);
        rcopyallocv(state->n, &state->xc, x, _state);
    }
    else
    {
        rsetallocv(state->n, 0.0, &rep->lagbc, _state);
        rsetallocv(state->n, 0.0, &rep->lagbcnz, _state);
        rsetallocv(state->xlc.nec+state->xlc.nic, 0.0, &rep->laglc, _state);
        rsetallocv(state->nnlc, 0.0, &rep->lagnlc, _state);
        rsetallocv(state->n, _state->v_nan, x, _state);
    }
}


/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.
      
NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void minnlcrequesttermination(minnlcstate* state, ae_state *_state)
{


    state->userterminationneeded = ae_true;
}


/*************************************************************************
This subroutine restarts algorithm from new point.
All optimization parameters (including constraints) are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have  same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinNLCCreate call.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minnlcrestartfrom(minnlcstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    
    /*
     * First, check for errors in the inputs
     */
    ae_assert(x->cnt>=n, "MinNLCRestartFrom: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinNLCRestartFrom: X contains infinite or NaN values!", _state);
    
    /*
     * Set XC
     */
    ae_v_move(&state->xstart.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
    
    /*
     * prepare RComm facilities
     */
    ae_vector_set_length(&state->rstate.ia, 10+1, _state);
    ae_vector_set_length(&state->rstate.ba, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 2+1, _state);
    state->rstate.stage = -1;
    minnlc_clearrequestfields(state, _state);
}


/*************************************************************************
This function sets the number additional iterations that can be  performed
after reaching MaxIts limit, if the current  point  is  infeasible (scaled
constraint violation is above CTol), but there was a feasible point in the
optimization history.

Up to AddIts iterations will be performed, with iterations being terminated
immediately after reaching CTol level of error.

Only FSQP solver respects this function.

INPUT PARAMETERS:
    State           -   solver state
    AddIts          -   additional iterations, zero to turn off
    CTol            -   scaled constraint violation tolerance. Points with
                        violation below or at CTol are considered feasible
                        
                        Do not use zero value  because  it  is  likely  to
                        reject points with even a tiny violation of
                        constraints.

  -- ALGLIB --
     Copyright 05.03.2018 by Bochkanov Sergey
*************************************************************************/
void minnlcsetfsqpadditsforctol(minnlcstate* state,
     ae_int_t addits,
     double ctol,
     ae_state *_state)
{


    state->fsqpadditsforctol = addits;
    state->fsqpctol = ctol;
}


/*************************************************************************
Set V1 reverse communication protocol
*************************************************************************/
void minnlcsetprotocolv1(minnlcstate* state, ae_state *_state)
{


    state->protocolversion = 1;
    state->issuesparserequests = ae_false;
    ae_vector_set_length(&state->fi, 1+state->nnlc, _state);
    ae_matrix_set_length(&state->j, 1+state->nnlc, state->n, _state);
    ae_vector_set_length(&state->rstate.ia, 10+1, _state);
    ae_vector_set_length(&state->rstate.ba, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 2+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Set V2 reverse communication protocol with dense requests
*************************************************************************/
void minnlcsetprotocolv2(minnlcstate* state, ae_state *_state)
{


    state->protocolversion = 2;
    state->issuesparserequests = ae_false;
    ae_vector_set_length(&state->rstate.ia, 10+1, _state);
    ae_vector_set_length(&state->rstate.ba, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 2+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Set V2 reverse communication protocol with sparse requests
*************************************************************************/
void minnlcsetprotocolv2s(minnlcstate* state, ae_state *_state)
{


    state->protocolversion = 2;
    state->issuesparserequests = ae_true;
    ae_vector_set_length(&state->rstate.ia, 10+1, _state);
    ae_vector_set_length(&state->rstate.ba, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 2+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Clears request fileds (to be sure that we don't forget to clear something)
*************************************************************************/
static void minnlc_clearrequestfields(minnlcstate* state,
     ae_state *_state)
{


    state->needfi = ae_false;
    state->needfij = ae_false;
    state->xupdated = ae_false;
}


/*************************************************************************
Internal initialization subroutine.
Sets default NLC solver with default criteria.
*************************************************************************/
static void minnlc_minnlcinitinternal(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minnlcstate* state,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_matrix c;
    ae_vector ct;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);

    state->protocolversion = 1;
    state->issuesparserequests = ae_false;
    
    /*
     * Smoothness monitor, default init
     */
    state->teststep = (double)(0);
    state->smoothnessguardlevel = 0;
    smoothnessmonitorinit(&state->smonitor, &state->s, 0, 0, ae_false, _state);
    
    /*
     * Initialize other params
     */
    critinitdefault(&state->criteria, _state);
    state->n = n;
    state->diffstep = diffstep;
    state->userterminationneeded = ae_false;
    ae_vector_set_length(&state->bndl, n, _state);
    ae_vector_set_length(&state->hasbndl, n, _state);
    ae_vector_set_length(&state->bndu, n, _state);
    ae_vector_set_length(&state->hasbndu, n, _state);
    ae_vector_set_length(&state->s, n, _state);
    ae_vector_set_length(&state->lastscaleused, n, _state);
    ae_vector_set_length(&state->xstart, n, _state);
    ae_vector_set_length(&state->xc, n, _state);
    ae_vector_set_length(&state->x, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->bndl.ptr.p_double[i] = _state->v_neginf;
        state->hasbndl.ptr.p_bool[i] = ae_false;
        state->bndu.ptr.p_double[i] = _state->v_posinf;
        state->hasbndu.ptr.p_bool[i] = ae_false;
        state->s.ptr.p_double[i] = 1.0;
        state->lastscaleused.ptr.p_double[i] = 1.0;
        state->xstart.ptr.p_double[i] = x->ptr.p_double[i];
        state->xc.ptr.p_double[i] = x->ptr.p_double[i];
    }
    xlcinit(n, &state->xlc, _state);
    minnlcsetnlc(state, 0, 0, _state);
    minnlcsetxrep(state, ae_false, _state);
    minnlcsetalgosqp(state, _state);
    minnlcsetstpmax(state, 0.0, _state);
    minnlcsetnumdiff(state, 5, _state);
    minnlcrestartfrom(state, x, _state);
    
    /*
     * Default values of solver-specific params
     */
    state->fsqpadditsforctol = 0;
    state->fsqpctol = (double)(0);
    ae_frame_leave(_state);
}


/*************************************************************************
Unscales X (converts from scaled variables to original ones), paying special
attention to box constraints (output is always feasible; active constraints
are mapped to active ones).
*************************************************************************/
static void minnlc_unscale(const minnlcstate* state,
     /* Real    */ const ae_vector* xs,
     /* Real    */ const ae_vector* scaledbndl,
     /* Real    */ const ae_vector* scaledbndu,
     /* Real    */ ae_vector* xu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    for(i=0; i<=n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i]&&xs->ptr.p_double[i]<=scaledbndl->ptr.p_double[i] )
        {
            xu->ptr.p_double[i] = state->bndl.ptr.p_double[i];
            continue;
        }
        if( state->hasbndu.ptr.p_bool[i]&&xs->ptr.p_double[i]>=scaledbndu->ptr.p_double[i] )
        {
            xu->ptr.p_double[i] = state->bndu.ptr.p_double[i];
            continue;
        }
        xu->ptr.p_double[i] = xs->ptr.p_double[i]*state->s.ptr.p_double[i];
        if( state->hasbndl.ptr.p_bool[i]&&xu->ptr.p_double[i]<state->bndl.ptr.p_double[i] )
        {
            xu->ptr.p_double[i] = state->bndl.ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i]&&xu->ptr.p_double[i]>state->bndu.ptr.p_double[i] )
        {
            xu->ptr.p_double[i] = state->bndu.ptr.p_double[i];
        }
    }
}


/*************************************************************************
Convert dense Jacobian to a sparse  matrix  without
checks for NAN/INF performed by the similar function from the SPARSE subpackage.
*************************************************************************/
static void minnlc_crsfromdense(/* Real    */ const ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     sparsematrix* s,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t nnz;
    ae_int_t offs;


    ae_assert(m>0, "CreateCRSFromDenseBuf: M<=0", _state);
    ae_assert(n>0, "CreateCRSFromDenseBuf: N<=0", _state);
    ae_assert(a->rows>=m, "CreateCRSFromDenseBuf: rows(A)<M", _state);
    ae_assert(a->cols>=n, "CreateCRSFromDenseBuf: cols(A)<N", _state);
    nnz = 0;
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( a->ptr.pp_double[i][j]!=0.0 )
            {
                nnz = nnz+1;
            }
        }
    }
    s->matrixtype = 1;
    s->ninitialized = nnz;
    s->m = m;
    s->n = n;
    iallocv(m+1, &s->ridx, _state);
    iallocv(nnz, &s->idx, _state);
    rallocv(nnz, &s->vals, _state);
    s->ridx.ptr.p_int[0] = 0;
    offs = 0;
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( a->ptr.pp_double[i][j]!=0.0 )
            {
                s->idx.ptr.p_int[offs] = j;
                s->vals.ptr.p_double[offs] = a->ptr.pp_double[i][j];
                offs = offs+1;
            }
        }
        s->ridx.ptr.p_int[i+1] = offs;
    }
    ae_assert(offs==nnz, "CreateCRSFromDenseBuf: integrity check 6447 failed", _state);
    sparseinitduidx(s, _state);
}


/*************************************************************************
Convert dense Jacobian (packed into a vector) to a sparse  matrix  without
checks for NAN/INF performed by the similar function from the SPARSE subpackage.
*************************************************************************/
static void minnlc_crsfromdensev(/* Real    */ const ae_vector* a,
     ae_int_t m,
     ae_int_t n,
     sparsematrix* s,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t nnz;
    ae_int_t offs;


    ae_assert(m>0, "CreateCRSFromDenseVBuf: M<=0", _state);
    ae_assert(n>0, "CreateCRSFromDenseVBuf: N<=0", _state);
    ae_assert(a->cnt>=m*n, "CreateCRSFromDenseVBuf: length(A)<M*N", _state);
    nnz = 0;
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( a->ptr.p_double[i*n+j]!=0.0 )
            {
                nnz = nnz+1;
            }
        }
    }
    s->matrixtype = 1;
    s->ninitialized = nnz;
    s->m = m;
    s->n = n;
    iallocv(m+1, &s->ridx, _state);
    iallocv(nnz, &s->idx, _state);
    rallocv(nnz, &s->vals, _state);
    s->ridx.ptr.p_int[0] = 0;
    offs = 0;
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( a->ptr.p_double[i*n+j]!=0.0 )
            {
                s->idx.ptr.p_int[offs] = j;
                s->vals.ptr.p_double[offs] = a->ptr.p_double[i*n+j];
                offs = offs+1;
            }
        }
        s->ridx.ptr.p_int[i+1] = offs;
    }
    ae_assert(offs==nnz, "CreateCRSFromDenseVBuf: integrity check 6447 failed", _state);
    sparseinitduidx(s, _state);
}


/*************************************************************************
Unscales X (converts from scaled variables to original ones), paying special
attention to box constraints (output is always feasible; active constraints
are mapped to active ones).

Can handle batch requests. Can handle problems with some variables being
fixed and removed from consideration.

Parameters:
    State           solver
    XS              array[NReduced*BatchSize] - variables after scaling and
                    removal of fixed vars
    idxRaw2Red      array[NRaw], maps original variable indexes to ones
                    after removal of fixed vars. Contains -1 for fixed vars.
    scaledBndL,
    ScaledBndU      array[NReduced], lower/upper bounds after removal of
                    the fixed vars
                    
output:
    XU              array[NRaw*BatchSize], vars after unscaling and
                    substitution of the fixed vars
*************************************************************************/
static void minnlc_unscalebatch(const minnlcstate* state,
     /* Real    */ const ae_vector* xs,
     ae_int_t batchsize,
     /* Integer */ const ae_vector* idxraw2red,
     ae_int_t nreduced,
     /* Real    */ const ae_vector* scaledbndl,
     /* Real    */ const ae_vector* scaledbndu,
     /* Real    */ ae_vector* xu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t jr;
    ae_int_t nraw;
    double vs;


    nraw = state->n;
    if( nreduced==nraw )
    {
        
        /*
         * No fixed vars, simple code
         */
        for(k=0; k<=batchsize-1; k++)
        {
            for(j=0; j<=nraw-1; j++)
            {
                i = k*nraw+j;
                vs = xs->ptr.p_double[i];
                if( state->hasbndl.ptr.p_bool[j]&&vs<=scaledbndl->ptr.p_double[j] )
                {
                    xu->ptr.p_double[i] = state->bndl.ptr.p_double[j];
                    continue;
                }
                if( state->hasbndu.ptr.p_bool[j]&&vs>=scaledbndu->ptr.p_double[j] )
                {
                    xu->ptr.p_double[i] = state->bndu.ptr.p_double[j];
                    continue;
                }
                xu->ptr.p_double[i] = vs*state->s.ptr.p_double[j];
                if( state->hasbndl.ptr.p_bool[j]&&xu->ptr.p_double[i]<state->bndl.ptr.p_double[j] )
                {
                    xu->ptr.p_double[i] = state->bndl.ptr.p_double[j];
                }
                if( state->hasbndu.ptr.p_bool[j]&&xu->ptr.p_double[i]>state->bndu.ptr.p_double[j] )
                {
                    xu->ptr.p_double[i] = state->bndu.ptr.p_double[j];
                }
            }
        }
    }
    else
    {
        
        /*
         * Some vars are fixed
         */
        for(k=0; k<=batchsize-1; k++)
        {
            for(j=0; j<=nraw-1; j++)
            {
                i = k*nraw+j;
                jr = idxraw2red->ptr.p_int[j];
                if( jr<0 )
                {
                    xu->ptr.p_double[i] = state->bndl.ptr.p_double[j];
                    continue;
                }
                vs = xs->ptr.p_double[k*nreduced+jr];
                if( state->hasbndl.ptr.p_bool[j]&&vs<=scaledbndl->ptr.p_double[jr] )
                {
                    xu->ptr.p_double[i] = state->bndl.ptr.p_double[j];
                    continue;
                }
                if( state->hasbndu.ptr.p_bool[j]&&vs>=scaledbndu->ptr.p_double[jr] )
                {
                    xu->ptr.p_double[i] = state->bndu.ptr.p_double[j];
                    continue;
                }
                xu->ptr.p_double[i] = vs*state->s.ptr.p_double[j];
                if( state->hasbndl.ptr.p_bool[j]&&xu->ptr.p_double[i]<state->bndl.ptr.p_double[j] )
                {
                    xu->ptr.p_double[i] = state->bndl.ptr.p_double[j];
                }
                if( state->hasbndu.ptr.p_bool[j]&&xu->ptr.p_double[i]>state->bndu.ptr.p_double[j] )
                {
                    xu->ptr.p_double[i] = state->bndu.ptr.p_double[j];
                }
            }
        }
    }
}


void _minnlcstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minnlcstate *p = (minnlcstate*)_p;
    ae_touch_ptr((void*)p);
    _nlpstoppingcriteria_init(&p->criteria, _state, make_automatic);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    _xlinearconstraints_init(&p->xlc, _state, make_automatic);
    ae_vector_init(&p->nl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->j, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->reportx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->querydata, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replyfi, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replydj, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->replysj, _state, make_automatic);
    ae_vector_init(&p->tmpx1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpc1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpf1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpg1, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpj1, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->tmps1, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    _rcommstate_init(&p->rstateaul, _state, make_automatic);
    ae_vector_init(&p->scaledbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scaledbndu, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->scaledcleic, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xstart, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xbase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fbase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dfbase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fm2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fm1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fp2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->batchquerydata, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpspaf, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpsptolj, 0, DT_REAL, _state, make_automatic);
    ae_smart_ptr_init(&p->_aulsolverstate, (void**)&p->aulsolverstate, ae_true, _state, make_automatic);
    ae_smart_ptr_init(&p->_sqpsolverstate, (void**)&p->sqpsolverstate, ae_true, _state, make_automatic);
    ae_smart_ptr_init(&p->_fsqpsolverstate, (void**)&p->fsqpsolverstate, ae_true, _state, make_automatic);
    ae_smart_ptr_init(&p->_orbitsolver, (void**)&p->orbitsolver, ae_true, _state, make_automatic);
    ae_vector_init(&p->idxraw2red, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->redx0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->reds, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->redbl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->redbu, 0, DT_REAL, _state, make_automatic);
    ae_smart_ptr_init(&p->_gipm2solver, (void**)&p->gipm2solver, ae_true, _state, make_automatic);
    _smoothnessmonitor_init(&p->smonitor, _state, make_automatic);
    ae_vector_init(&p->lastscaleused, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replagbc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replagbcnz, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replaglc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->replagnlc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nlcidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->nlcmul, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nlcadd, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpi, 0, DT_INT, _state, make_automatic);
}


void _minnlcstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minnlcstate       *dst = (minnlcstate*)_dst;
    const minnlcstate *src = (const minnlcstate*)_src;
    dst->solvertype = src->solvertype;
    dst->n = src->n;
    _nlpstoppingcriteria_init_copy(&dst->criteria, &src->criteria, _state, make_automatic);
    dst->aulitscnt = src->aulitscnt;
    dst->xrep = src->xrep;
    dst->stpmax = src->stpmax;
    dst->diffstep = src->diffstep;
    dst->teststep = src->teststep;
    dst->formulatype = src->formulatype;
    dst->orbitrad0 = src->orbitrad0;
    dst->orbitmaxnfev = src->orbitmaxnfev;
    dst->fsqpadditsforctol = src->fsqpadditsforctol;
    dst->fsqpctol = src->fsqpctol;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    _xlinearconstraints_init_copy(&dst->xlc, &src->xlc, _state, make_automatic);
    dst->nnlc = src->nnlc;
    ae_vector_init_copy(&dst->nl, &src->nl, _state, make_automatic);
    ae_vector_init_copy(&dst->nu, &src->nu, _state, make_automatic);
    dst->protocolversion = src->protocolversion;
    dst->issuesparserequests = src->issuesparserequests;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    dst->f = src->f;
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    ae_matrix_init_copy(&dst->j, &src->j, _state, make_automatic);
    dst->needfij = src->needfij;
    dst->needfi = src->needfi;
    dst->xupdated = src->xupdated;
    dst->requesttype = src->requesttype;
    ae_vector_init_copy(&dst->reportx, &src->reportx, _state, make_automatic);
    dst->reportf = src->reportf;
    dst->querysize = src->querysize;
    dst->queryfuncs = src->queryfuncs;
    dst->queryvars = src->queryvars;
    dst->querydim = src->querydim;
    dst->queryformulasize = src->queryformulasize;
    ae_vector_init_copy(&dst->querydata, &src->querydata, _state, make_automatic);
    ae_vector_init_copy(&dst->replyfi, &src->replyfi, _state, make_automatic);
    ae_vector_init_copy(&dst->replydj, &src->replydj, _state, make_automatic);
    _sparsematrix_init_copy(&dst->replysj, &src->replysj, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpx1, &src->tmpx1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpc1, &src->tmpc1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpf1, &src->tmpf1, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpg1, &src->tmpg1, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpj1, &src->tmpj1, _state, make_automatic);
    _sparsematrix_init_copy(&dst->tmps1, &src->tmps1, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstateaul, &src->rstateaul, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledbndl, &src->scaledbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledbndu, &src->scaledbndu, _state, make_automatic);
    ae_matrix_init_copy(&dst->scaledcleic, &src->scaledcleic, _state, make_automatic);
    ae_vector_init_copy(&dst->xc, &src->xc, _state, make_automatic);
    ae_vector_init_copy(&dst->xstart, &src->xstart, _state, make_automatic);
    ae_vector_init_copy(&dst->xbase, &src->xbase, _state, make_automatic);
    ae_vector_init_copy(&dst->fbase, &src->fbase, _state, make_automatic);
    ae_vector_init_copy(&dst->dfbase, &src->dfbase, _state, make_automatic);
    ae_vector_init_copy(&dst->fm2, &src->fm2, _state, make_automatic);
    ae_vector_init_copy(&dst->fm1, &src->fm1, _state, make_automatic);
    ae_vector_init_copy(&dst->fp1, &src->fp1, _state, make_automatic);
    ae_vector_init_copy(&dst->fp2, &src->fp2, _state, make_automatic);
    ae_vector_init_copy(&dst->batchquerydata, &src->batchquerydata, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpspaf, &src->tmpspaf, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpsptolj, &src->tmpsptolj, _state, make_automatic);
    ae_smart_ptr_init(&dst->_aulsolverstate, (void**)&dst->aulsolverstate, ae_true, _state, make_automatic);
    if( src->aulsolverstate!=NULL )
    {
        dst->aulsolverstate = (minaulstate*)ae_malloc(sizeof(minaulstate), _state); /* note: using aulsolverstate as a temporary prior to assigning its value to _aulsolverstate */
        memset(dst->aulsolverstate, 0, sizeof(minaulstate));
        _minaulstate_init_copy(dst->aulsolverstate, src->aulsolverstate, _state, ae_false);
        ae_smart_ptr_assign(&dst->_aulsolverstate, dst->aulsolverstate, ae_true, ae_true, (ae_int_t)sizeof(minaulstate), _minaulstate_init_copy, _minaulstate_destroy);
    }
    dst->userterminationneeded = src->userterminationneeded;
    ae_smart_ptr_init(&dst->_sqpsolverstate, (void**)&dst->sqpsolverstate, ae_true, _state, make_automatic);
    if( src->sqpsolverstate!=NULL )
    {
        dst->sqpsolverstate = (minsqpstate*)ae_malloc(sizeof(minsqpstate), _state); /* note: using sqpsolverstate as a temporary prior to assigning its value to _sqpsolverstate */
        memset(dst->sqpsolverstate, 0, sizeof(minsqpstate));
        _minsqpstate_init_copy(dst->sqpsolverstate, src->sqpsolverstate, _state, ae_false);
        ae_smart_ptr_assign(&dst->_sqpsolverstate, dst->sqpsolverstate, ae_true, ae_true, (ae_int_t)sizeof(minsqpstate), _minsqpstate_init_copy, _minsqpstate_destroy);
    }
    ae_smart_ptr_init(&dst->_fsqpsolverstate, (void**)&dst->fsqpsolverstate, ae_true, _state, make_automatic);
    if( src->fsqpsolverstate!=NULL )
    {
        dst->fsqpsolverstate = (minfsqpstate*)ae_malloc(sizeof(minfsqpstate), _state); /* note: using fsqpsolverstate as a temporary prior to assigning its value to _fsqpsolverstate */
        memset(dst->fsqpsolverstate, 0, sizeof(minfsqpstate));
        _minfsqpstate_init_copy(dst->fsqpsolverstate, src->fsqpsolverstate, _state, ae_false);
        ae_smart_ptr_assign(&dst->_fsqpsolverstate, dst->fsqpsolverstate, ae_true, ae_true, (ae_int_t)sizeof(minfsqpstate), _minfsqpstate_init_copy, _minfsqpstate_destroy);
    }
    ae_smart_ptr_init(&dst->_orbitsolver, (void**)&dst->orbitsolver, ae_true, _state, make_automatic);
    if( src->orbitsolver!=NULL )
    {
        dst->orbitsolver = (dfgmstate*)ae_malloc(sizeof(dfgmstate), _state); /* note: using orbitsolver as a temporary prior to assigning its value to _orbitsolver */
        memset(dst->orbitsolver, 0, sizeof(dfgmstate));
        _dfgmstate_init_copy(dst->orbitsolver, src->orbitsolver, _state, ae_false);
        ae_smart_ptr_assign(&dst->_orbitsolver, dst->orbitsolver, ae_true, ae_true, (ae_int_t)sizeof(dfgmstate), _dfgmstate_init_copy, _dfgmstate_destroy);
    }
    dst->nreduced = src->nreduced;
    ae_vector_init_copy(&dst->idxraw2red, &src->idxraw2red, _state, make_automatic);
    ae_vector_init_copy(&dst->redx0, &src->redx0, _state, make_automatic);
    ae_vector_init_copy(&dst->reds, &src->reds, _state, make_automatic);
    ae_vector_init_copy(&dst->redbl, &src->redbl, _state, make_automatic);
    ae_vector_init_copy(&dst->redbu, &src->redbu, _state, make_automatic);
    ae_smart_ptr_init(&dst->_gipm2solver, (void**)&dst->gipm2solver, ae_true, _state, make_automatic);
    if( src->gipm2solver!=NULL )
    {
        dst->gipm2solver = (nlpgipm2state*)ae_malloc(sizeof(nlpgipm2state), _state); /* note: using gipm2solver as a temporary prior to assigning its value to _gipm2solver */
        memset(dst->gipm2solver, 0, sizeof(nlpgipm2state));
        _nlpgipm2state_init_copy(dst->gipm2solver, src->gipm2solver, _state, ae_false);
        ae_smart_ptr_assign(&dst->_gipm2solver, dst->gipm2solver, ae_true, ae_true, (ae_int_t)sizeof(nlpgipm2state), _nlpgipm2state_init_copy, _nlpgipm2state_destroy);
    }
    dst->smoothnessguardlevel = src->smoothnessguardlevel;
    _smoothnessmonitor_init_copy(&dst->smonitor, &src->smonitor, _state, make_automatic);
    ae_vector_init_copy(&dst->lastscaleused, &src->lastscaleused, _state, make_automatic);
    dst->repf = src->repf;
    dst->repinneriterationscount = src->repinneriterationscount;
    dst->repouteriterationscount = src->repouteriterationscount;
    dst->repnfev = src->repnfev;
    dst->repterminationtype = src->repterminationtype;
    dst->repbcerr = src->repbcerr;
    dst->repbcidx = src->repbcidx;
    dst->replcerr = src->replcerr;
    dst->replcidx = src->replcidx;
    dst->repnlcerr = src->repnlcerr;
    dst->repnlcidx = src->repnlcidx;
    dst->repsclfeaserr = src->repsclfeaserr;
    dst->repdbgphase0its = src->repdbgphase0its;
    ae_vector_init_copy(&dst->replagbc, &src->replagbc, _state, make_automatic);
    ae_vector_init_copy(&dst->replagbcnz, &src->replagbcnz, _state, make_automatic);
    ae_vector_init_copy(&dst->replaglc, &src->replaglc, _state, make_automatic);
    ae_vector_init_copy(&dst->replagnlc, &src->replagnlc, _state, make_automatic);
    ae_vector_init_copy(&dst->nlcidx, &src->nlcidx, _state, make_automatic);
    ae_vector_init_copy(&dst->nlcmul, &src->nlcmul, _state, make_automatic);
    ae_vector_init_copy(&dst->nlcadd, &src->nlcadd, _state, make_automatic);
    dst->nlcnlec = src->nlcnlec;
    dst->nlcnlic = src->nlcnlic;
    ae_vector_init_copy(&dst->tmpi, &src->tmpi, _state, make_automatic);
}


void _minnlcstate_clear(void* _p)
{
    minnlcstate *p = (minnlcstate*)_p;
    ae_touch_ptr((void*)p);
    _nlpstoppingcriteria_clear(&p->criteria);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    _xlinearconstraints_clear(&p->xlc);
    ae_vector_clear(&p->nl);
    ae_vector_clear(&p->nu);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->fi);
    ae_matrix_clear(&p->j);
    ae_vector_clear(&p->reportx);
    ae_vector_clear(&p->querydata);
    ae_vector_clear(&p->replyfi);
    ae_vector_clear(&p->replydj);
    _sparsematrix_clear(&p->replysj);
    ae_vector_clear(&p->tmpx1);
    ae_vector_clear(&p->tmpc1);
    ae_vector_clear(&p->tmpf1);
    ae_vector_clear(&p->tmpg1);
    ae_matrix_clear(&p->tmpj1);
    _sparsematrix_clear(&p->tmps1);
    _rcommstate_clear(&p->rstate);
    _rcommstate_clear(&p->rstateaul);
    ae_vector_clear(&p->scaledbndl);
    ae_vector_clear(&p->scaledbndu);
    ae_matrix_clear(&p->scaledcleic);
    ae_vector_clear(&p->xc);
    ae_vector_clear(&p->xstart);
    ae_vector_clear(&p->xbase);
    ae_vector_clear(&p->fbase);
    ae_vector_clear(&p->dfbase);
    ae_vector_clear(&p->fm2);
    ae_vector_clear(&p->fm1);
    ae_vector_clear(&p->fp1);
    ae_vector_clear(&p->fp2);
    ae_vector_clear(&p->batchquerydata);
    ae_vector_clear(&p->tmpspaf);
    ae_vector_clear(&p->tmpsptolj);
    ae_smart_ptr_clear(&p->_aulsolverstate);
    ae_smart_ptr_clear(&p->_sqpsolverstate);
    ae_smart_ptr_clear(&p->_fsqpsolverstate);
    ae_smart_ptr_clear(&p->_orbitsolver);
    ae_vector_clear(&p->idxraw2red);
    ae_vector_clear(&p->redx0);
    ae_vector_clear(&p->reds);
    ae_vector_clear(&p->redbl);
    ae_vector_clear(&p->redbu);
    ae_smart_ptr_clear(&p->_gipm2solver);
    _smoothnessmonitor_clear(&p->smonitor);
    ae_vector_clear(&p->lastscaleused);
    ae_vector_clear(&p->replagbc);
    ae_vector_clear(&p->replagbcnz);
    ae_vector_clear(&p->replaglc);
    ae_vector_clear(&p->replagnlc);
    ae_vector_clear(&p->nlcidx);
    ae_vector_clear(&p->nlcmul);
    ae_vector_clear(&p->nlcadd);
    ae_vector_clear(&p->tmpi);
}


void _minnlcstate_destroy(void* _p)
{
    minnlcstate *p = (minnlcstate*)_p;
    ae_touch_ptr((void*)p);
    _nlpstoppingcriteria_destroy(&p->criteria);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    _xlinearconstraints_destroy(&p->xlc);
    ae_vector_destroy(&p->nl);
    ae_vector_destroy(&p->nu);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->fi);
    ae_matrix_destroy(&p->j);
    ae_vector_destroy(&p->reportx);
    ae_vector_destroy(&p->querydata);
    ae_vector_destroy(&p->replyfi);
    ae_vector_destroy(&p->replydj);
    _sparsematrix_destroy(&p->replysj);
    ae_vector_destroy(&p->tmpx1);
    ae_vector_destroy(&p->tmpc1);
    ae_vector_destroy(&p->tmpf1);
    ae_vector_destroy(&p->tmpg1);
    ae_matrix_destroy(&p->tmpj1);
    _sparsematrix_destroy(&p->tmps1);
    _rcommstate_destroy(&p->rstate);
    _rcommstate_destroy(&p->rstateaul);
    ae_vector_destroy(&p->scaledbndl);
    ae_vector_destroy(&p->scaledbndu);
    ae_matrix_destroy(&p->scaledcleic);
    ae_vector_destroy(&p->xc);
    ae_vector_destroy(&p->xstart);
    ae_vector_destroy(&p->xbase);
    ae_vector_destroy(&p->fbase);
    ae_vector_destroy(&p->dfbase);
    ae_vector_destroy(&p->fm2);
    ae_vector_destroy(&p->fm1);
    ae_vector_destroy(&p->fp1);
    ae_vector_destroy(&p->fp2);
    ae_vector_destroy(&p->batchquerydata);
    ae_vector_destroy(&p->tmpspaf);
    ae_vector_destroy(&p->tmpsptolj);
    ae_smart_ptr_destroy(&p->_aulsolverstate);
    ae_smart_ptr_destroy(&p->_sqpsolverstate);
    ae_smart_ptr_destroy(&p->_fsqpsolverstate);
    ae_smart_ptr_destroy(&p->_orbitsolver);
    ae_vector_destroy(&p->idxraw2red);
    ae_vector_destroy(&p->redx0);
    ae_vector_destroy(&p->reds);
    ae_vector_destroy(&p->redbl);
    ae_vector_destroy(&p->redbu);
    ae_smart_ptr_destroy(&p->_gipm2solver);
    _smoothnessmonitor_destroy(&p->smonitor);
    ae_vector_destroy(&p->lastscaleused);
    ae_vector_destroy(&p->replagbc);
    ae_vector_destroy(&p->replagbcnz);
    ae_vector_destroy(&p->replaglc);
    ae_vector_destroy(&p->replagnlc);
    ae_vector_destroy(&p->nlcidx);
    ae_vector_destroy(&p->nlcmul);
    ae_vector_destroy(&p->nlcadd);
    ae_vector_destroy(&p->tmpi);
}


void _minnlcreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minnlcreport *p = (minnlcreport*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->lagbc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagbcnz, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->laglc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->lagnlc, 0, DT_REAL, _state, make_automatic);
}


void _minnlcreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minnlcreport       *dst = (minnlcreport*)_dst;
    const minnlcreport *src = (const minnlcreport*)_src;
    dst->f = src->f;
    dst->iterationscount = src->iterationscount;
    dst->nfev = src->nfev;
    dst->terminationtype = src->terminationtype;
    dst->bcerr = src->bcerr;
    dst->bcidx = src->bcidx;
    dst->lcerr = src->lcerr;
    dst->lcidx = src->lcidx;
    dst->nlcerr = src->nlcerr;
    dst->nlcidx = src->nlcidx;
    dst->sclfeaserr = src->sclfeaserr;
    ae_vector_init_copy(&dst->lagbc, &src->lagbc, _state, make_automatic);
    ae_vector_init_copy(&dst->lagbcnz, &src->lagbcnz, _state, make_automatic);
    ae_vector_init_copy(&dst->laglc, &src->laglc, _state, make_automatic);
    ae_vector_init_copy(&dst->lagnlc, &src->lagnlc, _state, make_automatic);
    dst->dbgphase0its = src->dbgphase0its;
}


void _minnlcreport_clear(void* _p)
{
    minnlcreport *p = (minnlcreport*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->lagbc);
    ae_vector_clear(&p->lagbcnz);
    ae_vector_clear(&p->laglc);
    ae_vector_clear(&p->lagnlc);
}


void _minnlcreport_destroy(void* _p)
{
    minnlcreport *p = (minnlcreport*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->lagbc);
    ae_vector_destroy(&p->lagbcnz);
    ae_vector_destroy(&p->laglc);
    ae_vector_destroy(&p->lagnlc);
}


#endif
#if defined(AE_COMPILE_MONBI) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
Initialize NBI solver with the problem formulation

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void nbiscaleandinitbuf(/* Real    */ const ae_vector* x0,
     /* Real    */ const ae_vector* s,
     ae_int_t n,
     ae_int_t m,
     ae_int_t frontsize,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     const sparsematrix* sparsea,
     /* Real    */ const ae_matrix* densea,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t ksparse,
     ae_int_t kdense,
     /* Real    */ const ae_vector* nl,
     /* Real    */ const ae_vector* nu,
     ae_int_t nnlc,
     double epsx,
     ae_int_t maxits,
     ae_bool polishsolutions,
     nbistate* state,
     ae_state *_state)
{


    ae_assert(frontsize>=m, "NBIScaleAndInitBuf: FrontSize<M", _state);
    if( m==1 )
    {
        frontsize = 1;
    }
    state->n = n;
    state->m = m;
    state->epsx = epsx;
    state->maxits = maxits;
    state->xrep = ae_false;
    state->frontsize = frontsize;
    state->polishsolutions = polishsolutions;
    rsetallocv(n, 0.0, &state->tmpzero, _state);
    rsetallocv(n, 1.0, &state->tmpone, _state);
    
    /*
     * Copy and scale initial point
     */
    rcopyallocv(n, x0, &state->xstart, _state);
    rmergedivv(n, s, &state->xstart, _state);
    
    /*
     * Copy and scale box constraints
     */
    rcopyallocv(n, bndl, &state->bndl, _state);
    rcopyallocv(n, bndu, &state->bndu, _state);
    scaleshiftbcinplace(s, &state->tmpzero, &state->bndl, &state->bndu, n, _state);
    
    /*
     * Copy and scale linear constraints
     */
    state->ksparse = ksparse;
    state->kdense = kdense;
    rcopyallocv(ksparse+kdense, al, &state->cl, _state);
    rcopyallocv(ksparse+kdense, au, &state->cu, _state);
    if( ksparse>0 )
    {
        sparsecopybuf(sparsea, &state->sparsec, _state);
    }
    if( kdense>0 )
    {
        rcopyallocm(kdense, n, densea, &state->densec, _state);
    }
    scaleshiftmixedlcinplace(s, &state->tmpzero, n, &state->sparsec, ksparse, &state->densec, kdense, &state->cl, &state->cu, _state);
    
    /*
     * Copy nonlinear constraints
     */
    rcopyallocv(nnlc, nl, &state->nl, _state);
    rcopyallocv(nnlc, nu, &state->nu, _state);
    state->nnlc = nnlc;
    
    /*
     * Other fields
     */
    state->userrequestedtermination = ae_false;
    state->repterminationtype = 0;
    state->repinneriterationscount = 0;
    state->repouteriterationscount = 0;
    state->repnfev = 0;
    state->repbcerr = (double)(0);
    state->repbcidx = -1;
    state->replcerr = (double)(0);
    state->replcidx = -1;
    state->repnlcerr = (double)(0);
    state->repnlcidx = -1;
    state->repfrontsize = 0;
    
    /*
     * prepare RComm facilities
     */
    ae_vector_set_length(&state->rstate.ia, 7+1, _state);
    ae_vector_set_length(&state->rstate.ba, 0+1, _state);
    ae_vector_set_length(&state->rstate.ra, 1+1, _state);
    state->rstate.stage = -1;
    monbi_clearrequestfields(state, _state);
    rallocv(n, &state->x, _state);
    rallocv(m+nnlc, &state->fi, _state);
    rallocm(m+nnlc, n, &state->j, _state);
}


/*************************************************************************
RCOMM function

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
ae_bool nbiiteration(nbistate* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t objectiveidx;
    ae_int_t frontsize;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t frontrow;
    double v;
    double vv;
    ae_bool dotrace;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        n = state->rstate.ia.ptr.p_int[0];
        m = state->rstate.ia.ptr.p_int[1];
        objectiveidx = state->rstate.ia.ptr.p_int[2];
        frontsize = state->rstate.ia.ptr.p_int[3];
        i = state->rstate.ia.ptr.p_int[4];
        j = state->rstate.ia.ptr.p_int[5];
        k = state->rstate.ia.ptr.p_int[6];
        frontrow = state->rstate.ia.ptr.p_int[7];
        dotrace = state->rstate.ba.ptr.p_bool[0];
        v = state->rstate.ra.ptr.p_double[0];
        vv = state->rstate.ra.ptr.p_double[1];
    }
    else
    {
        n = 359;
        m = -58;
        objectiveidx = -919;
        frontsize = -909;
        i = 81;
        j = 255;
        k = 74;
        frontrow = -788;
        dotrace = ae_true;
        v = 205.0;
        vv = -838.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    if( state->rstate.stage==6 )
    {
        goto lbl_6;
    }
    if( state->rstate.stage==7 )
    {
        goto lbl_7;
    }
    if( state->rstate.stage==8 )
    {
        goto lbl_8;
    }
    if( state->rstate.stage==9 )
    {
        goto lbl_9;
    }
    if( state->rstate.stage==10 )
    {
        goto lbl_10;
    }
    if( state->rstate.stage==11 )
    {
        goto lbl_11;
    }
    if( state->rstate.stage==12 )
    {
        goto lbl_12;
    }
    if( state->rstate.stage==13 )
    {
        goto lbl_13;
    }
    if( state->rstate.stage==14 )
    {
        goto lbl_14;
    }
    if( state->rstate.stage==15 )
    {
        goto lbl_15;
    }
    
    /*
     * Routine body
     */
    n = state->n;
    m = state->m;
    frontsize = state->frontsize;
    hqrndseed(348546, 436734, &state->rs, _state);
    ae_assert(m>1||frontsize==1, "NBI: integrity check 3309 failed", _state);
    dotrace = ae_is_trace_enabled("NBI");
    
    /*
     * Trace output (if needed)
     */
    if( dotrace )
    {
        ae_trace("\n\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("//  NBI SOLVER STARTED                                                                            //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("N             = %0d (variables)\n",
            (int)(n));
        ae_trace("M             = %0d (objectives)\n",
            (int)(m));
    }
    
    /*
     * Prepare rcomm interface
     */
    monbi_clearrequestfields(state, _state);
    
    /*
     * Prepare initial state of the output
     */
    ae_assert(state->frontsize>=m, "NBI: integrity check 0503 failed", _state);
    rsetallocm(frontsize, n+m+m, _state->v_nan, &state->repparetofront, _state);
    state->repfrontsize = 0;
    state->repterminationtype = 2;
    state->userrequestedtermination = ae_false;
    
    /*
     * Convert linear and nonlinear constraints to an old format
     */
    converttwosidedlctoonesidedold(&state->sparsec, state->ksparse, &state->densec, state->kdense, n, &state->cl, &state->cu, &state->olddensec, &state->olddensect, &state->olddensek, _state);
    converttwosidednlctoonesidedold(&state->nl, &state->nu, state->nnlc, &state->nlcidx, &state->nlcmul, &state->nlcadd, &state->nlcnlec, &state->nlcnlic, _state);
    
    /*
     * Solve M initial single-objective problems that are used to find an ideal objectives vector
     */
    minnlccreate(n, &state->xstart, &state->nlcsolver, _state);
    minnlcsetcond(&state->nlcsolver, state->epsx, state->maxits, _state);
    minnlcsetbc(&state->nlcsolver, &state->bndl, &state->bndu, _state);
    minnlcsetlc(&state->nlcsolver, &state->olddensec, &state->olddensect, state->olddensek, _state);
    monbi_setnlcalgo(&state->nlcsolver, _state);
    objectiveidx = 0;
lbl_16:
    if( objectiveidx>m-1 )
    {
        goto lbl_18;
    }
    if( !dotrace )
    {
        goto lbl_19;
    }
    ae_trace("===== OPTIMIZING FOR OBJECTIVE %2d ==================================================================\n",
        (int)(objectiveidx));
    state->needfij = ae_true;
    rcopyv(n, &state->xstart, &state->x, _state);
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    state->needfij = ae_false;
    state->repnfev = state->repnfev+1;
    ae_trace("F (initial)   = ");
    tracevectorautoprec(&state->fi, 0, m, _state);
    ae_trace("\n");
lbl_19:
    
    /*
     * Optimize with respect to one objective
     */
    minnlcsetnlc(&state->nlcsolver, state->nlcnlec, state->nlcnlic, _state);
    minnlcrestartfrom(&state->nlcsolver, &state->xstart, _state);
lbl_21:
    if( !minnlciteration(&state->nlcsolver, _state) )
    {
        goto lbl_22;
    }
    if( state->userrequestedtermination )
    {
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    if( !state->nlcsolver.needfij )
    {
        goto lbl_23;
    }
    
    /*
     * Forward report to the caller
     */
    state->needfij = ae_true;
    rcopyv(n, &state->nlcsolver.x, &state->x, _state);
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->needfij = ae_false;
    
    /*
     * Convert target and constraints to the format used by the subproblem
     */
    state->nlcsolver.fi.ptr.p_double[0] = state->fi.ptr.p_double[objectiveidx];
    rcopyrr(n, &state->j, objectiveidx, &state->nlcsolver.j, 0, _state);
    for(i=0; i<=state->nlcnlec+state->nlcnlic-1; i++)
    {
        state->nlcsolver.fi.ptr.p_double[i+1] = state->fi.ptr.p_double[m+state->nlcidx.ptr.p_int[i]]*state->nlcmul.ptr.p_double[i]+state->nlcadd.ptr.p_double[i];
        rcopyrr(n, &state->j, m+state->nlcidx.ptr.p_int[i], &state->nlcsolver.j, i+1, _state);
        rmulr(n, state->nlcmul.ptr.p_double[i], &state->nlcsolver.j, i+1, _state);
    }
    goto lbl_21;
lbl_23:
    ae_assert(ae_false, "NBI: integrity check 6034 failed", _state);
    goto lbl_21;
lbl_22:
    minnlcresultsbuf(&state->nlcsolver, &state->x1, &state->nlcrep, _state);
    if( state->nlcrep.terminationtype==-8 )
    {
        state->repterminationtype = -8;
        state->repfrontsize = 0;
        result = ae_false;
        return result;
    }
    ae_assert(state->nlcrep.terminationtype>0, "NBI: integrity check 7144 failed", _state);
    state->repinneriterationscount = state->repinneriterationscount+state->nlcrep.iterationscount;
    state->repnfev = state->repnfev+state->nlcrep.nfev;
    if( !dotrace )
    {
        goto lbl_25;
    }
    state->needfij = ae_true;
    rcopyv(n, &state->x1, &state->x, _state);
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->needfij = ae_false;
    state->repnfev = state->repnfev+1;
    ae_trace("F (unpolished)= ");
    tracevectorautoprec(&state->fi, 0, m, _state);
    ae_trace(" (%0d its, feas.err=%0.2e)\n",
        (int)(state->nlcrep.iterationscount),
        (double)(rmax3(state->nlcrep.bcerr, state->nlcrep.lcerr, state->nlcrep.nlcerr, _state)));
lbl_25:
    
    /*
     * Polish the solution: having solution X1 that minimizes f_objectiveIdx,
     * now we want to minimize a sum of f[i] subject to constraint that f_i(x)<=f_i(X1).
     *
     * This stage is essential for polishing other components of the solution
     * (even with moderate condition numbers it is possible to have f[i!=objectiveIdx]
     * underoptimized due to purely numerical issues).
     */
    if( !state->polishsolutions )
    {
        goto lbl_27;
    }
    state->needfij = ae_true;
    rcopyv(n, &state->x1, &state->x, _state);
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    state->needfij = ae_false;
    state->repnfev = state->repnfev+1;
    rcopyallocv(m+state->nnlc, &state->fi, &state->fix1, _state);
    minnlcsetnlc(&state->nlcsolver, state->nlcnlec, state->nlcnlic+1, _state);
    minnlcrestartfrom(&state->nlcsolver, &state->x1, _state);
lbl_29:
    if( !minnlciteration(&state->nlcsolver, _state) )
    {
        goto lbl_30;
    }
    if( state->userrequestedtermination )
    {
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    if( !state->nlcsolver.needfij )
    {
        goto lbl_31;
    }
    
    /*
     * Forward report to the caller
     */
    state->needfij = ae_true;
    rcopyv(n, &state->nlcsolver.x, &state->x, _state);
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    state->needfij = ae_false;
    
    /*
     * Target: sum of objectives
     */
    state->nlcsolver.fi.ptr.p_double[0] = (double)(0);
    rsetr(n, 0.0, &state->nlcsolver.j, 0, _state);
    for(i=0; i<=m-1; i++)
    {
        state->nlcsolver.fi.ptr.p_double[0] = state->nlcsolver.fi.ptr.p_double[0]+state->fi.ptr.p_double[i];
        raddrr(n, 1.0, &state->j, i, &state->nlcsolver.j, 0, _state);
    }
    
    /*
     * Original nonlinear constraints
     */
    for(i=0; i<=state->nlcnlec+state->nlcnlic-1; i++)
    {
        state->nlcsolver.fi.ptr.p_double[i+1] = state->fi.ptr.p_double[m+state->nlcidx.ptr.p_int[i]]*state->nlcmul.ptr.p_double[i]+state->nlcadd.ptr.p_double[i];
        rcopyrr(n, &state->j, m+state->nlcidx.ptr.p_int[i], &state->nlcsolver.j, i+1, _state);
        rmulr(n, state->nlcmul.ptr.p_double[i], &state->nlcsolver.j, i+1, _state);
    }
    
    /*
     * Additional nonlinear constraint: f[objectiveidx] does not increase: f(x)-f(X1)<=0
     */
    state->nlcsolver.fi.ptr.p_double[1+state->nlcnlec+state->nlcnlic] = state->fi.ptr.p_double[objectiveidx]-state->fix1.ptr.p_double[objectiveidx];
    rcopyrr(n, &state->j, objectiveidx, &state->nlcsolver.j, 1+state->nlcnlec+state->nlcnlic, _state);
    
    /*
     * Done
     */
    goto lbl_29;
lbl_31:
    ae_assert(ae_false, "NBI: integrity check 7056 failed", _state);
    goto lbl_29;
lbl_30:
    minnlcresultsbuf(&state->nlcsolver, &state->x2, &state->nlcrep, _state);
    if( state->nlcrep.terminationtype==-8 )
    {
        state->repterminationtype = -8;
        state->repfrontsize = 0;
        result = ae_false;
        return result;
    }
    ae_assert(state->nlcrep.terminationtype>0, "NBI: integrity check 7144 failed", _state);
    state->repinneriterationscount = state->repinneriterationscount+state->nlcrep.iterationscount;
    state->repnfev = state->repnfev+state->nlcrep.nfev;
    if( !dotrace )
    {
        goto lbl_33;
    }
    state->needfij = ae_true;
    rcopyv(n, &state->x2, &state->x, _state);
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    state->needfij = ae_false;
    state->repnfev = state->repnfev+1;
    ae_trace("F (polished)  = ");
    tracevectorautoprec(&state->fi, 0, m, _state);
    ae_trace(" (%0d its, feas.err=%0.2e)\n",
        (int)(state->nlcrep.iterationscount),
        (double)(rmax3(state->nlcrep.bcerr, state->nlcrep.lcerr, state->nlcrep.nlcerr, _state)));
lbl_33:
    goto lbl_28;
lbl_27:
    rcopyallocv(n, &state->x1, &state->x2, _state);
lbl_28:
    
    /*
     * Save solution and objectives at the solution
     */
    state->needfij = ae_true;
    rcopyv(n, &state->x2, &state->x, _state);
    state->rstate.stage = 6;
    goto lbl_rcomm;
lbl_6:
    state->needfij = ae_false;
    state->repnfev = state->repnfev+1;
    rcopyvr(n, &state->x2, &state->repparetofront, state->repfrontsize, _state);
    for(i=0; i<=m-1; i++)
    {
        state->repparetofront.ptr.pp_double[state->repfrontsize][n+i] = state->fi.ptr.p_double[i];
    }
    for(i=0; i<=m-1; i++)
    {
        state->repparetofront.ptr.pp_double[state->repfrontsize][n+m+i] = rcase2(i==objectiveidx, (double)(1), (double)(0), _state);
    }
    state->repfrontsize = state->repfrontsize+1;
    state->repouteriterationscount = state->repouteriterationscount+1;
    if( ae_fp_greater(state->nlcrep.bcerr,state->repbcerr) )
    {
        state->repbcerr = state->nlcrep.bcerr;
        state->repbcidx = state->nlcrep.bcidx;
    }
    if( ae_fp_greater(state->nlcrep.lcerr,state->replcerr) )
    {
        state->replcerr = state->nlcrep.lcerr;
        state->replcidx = state->nlcrep.lcidx;
    }
    if( ae_fp_greater(state->nlcrep.nlcerr,state->repnlcerr) )
    {
        state->repnlcerr = state->nlcrep.nlcerr;
        state->repnlcidx = state->nlcrep.nlcidx;
    }
    
    /*
     * Report last point found and check request for termination
     */
    rcopyrv(n, &state->repparetofront, state->repfrontsize-1, &state->x, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 7;
    goto lbl_rcomm;
lbl_7:
    state->xupdated = ae_false;
    if( state->userrequestedtermination )
    {
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    objectiveidx = objectiveidx+1;
    goto lbl_16;
lbl_18:
    
    /*
     * Compute ideal vector and payoff matrix
     */
    rallocv(m, &state->fideal, _state);
    for(i=0; i<=m-1; i++)
    {
        state->fideal.ptr.p_double[i] = state->repparetofront.ptr.pp_double[i][n+i];
    }
    rallocm(m, m, &state->payoff, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            state->payoff.ptr.pp_double[i][j] = state->repparetofront.ptr.pp_double[j][n+i]-state->fideal.ptr.p_double[i];
        }
    }
    
    /*
     * Generate quasi-normal vector Delta[].
     *
     * NOTE: if |Delta| is less than 1, we normalize it. Without normalization
     *       nonlinear optimizer may fail when Delta is very small because
     *       an extremely small scale of Delta implies an extremely large scale
     *       of the artificial variable t.
     *
     *       Values of Delta larger than 1 are not normalized.
     *
     */
    rallocv(m, &state->delta, _state);
    for(i=0; i<=m-1; i++)
    {
        state->delta.ptr.p_double[i] = (double)(0);
        for(j=0; j<=m-1; j++)
        {
            state->delta.ptr.p_double[i] = state->delta.ptr.p_double[i]-state->payoff.ptr.pp_double[i][j];
        }
        if( ae_fp_greater_eq(state->delta.ptr.p_double[i],(double)(0)) )
        {
            
            /*
             * Something is wrong with payoff matrix, fix direction
             */
            state->delta.ptr.p_double[i] = (double)(0);
            for(j=0; j<=m-1; j++)
            {
                state->delta.ptr.p_double[i] = ae_minreal(state->delta.ptr.p_double[i], -state->payoff.ptr.pp_double[i][j], _state);
            }
            state->delta.ptr.p_double[i] = coalesce(state->delta.ptr.p_double[i], (double)(-1), _state);
        }
    }
    v = ae_sqrt(rdotv2(m, &state->delta, _state), _state);
    ae_assert(ae_fp_neq(v,(double)(0)), "NBI: integrity check 8546 failed", _state);
    rmulv(m, ae_maxreal((double)1/v, 1.0, _state), &state->delta, _state);
    
    /*
     * For each front row solve NBI subproblem
     */
    frontrow = m;
lbl_35:
    if( frontrow>frontsize-1 )
    {
        goto lbl_37;
    }
    if( dotrace )
    {
        ae_trace("===== OPTIMIZING FOR FRONT POINT %4d ==============================================================\n",
            (int)(frontrow));
    }
    
    /*
     * Generate convex hull coordinates vector Beta[] using the following
     * algorithm to obtain uniformly sampled points on a simplex:
     *
     * * generate M random uniform numbers
     * * take logarithms
     * * normalize sum
     *
     * Without latter two steps we will obtain heavily non-uniform distribution
     * with a sharp peak at the center.
     */
    rallocv(m, &state->beta, _state);
    v = (double)(0);
    for(i=0; i<=m-1; i++)
    {
        state->beta.ptr.p_double[i] = ae_log(hqrnduniformr(&state->rs, _state)+ae_machineepsilon, _state);
        v = v+state->beta.ptr.p_double[i];
    }
    rmulv(m, (double)1/v, &state->beta, _state);
    if( dotrace )
    {
        ae_trace("Beta (normal) = ");
        tracevectorautoprec(&state->beta, 0, m, _state);
        ae_trace("\n");
    }
    
    /*
     * Prepare initial point for a subproblem:
     * * for N first variables, select point from the front with closest values of Beta[]
     * * for last variable, set T to zero (numerical experience shows that it is good enough)
     */
    rallocv(n+1, &state->subproblemstart, _state);
    k = -1;
    v = ae_maxrealnumber;
    for(i=0; i<=frontrow-1; i++)
    {
        vv = (double)(0);
        for(j=0; j<=m-1; j++)
        {
            vv = vv+ae_sqr(state->repparetofront.ptr.pp_double[i][n+m+j]-state->beta.ptr.p_double[j], _state);
        }
        if( ae_fp_less(vv,v) )
        {
            k = i;
            v = vv;
        }
    }
    ae_assert(k>=0, "NBI: integrity check 0630 failed", _state);
    rcopyrv(n, &state->repparetofront, k, &state->subproblemstart, _state);
    state->subproblemstart.ptr.p_double[n] = 0.0;
    if( dotrace )
    {
        ae_trace("StartIdx      = %0d (front point used as the initial one)\n",
            (int)(k));
    }
    
    /*
     * Reformulate box and linear constraints
     */
    rallocv(n+1, &state->bndlx, _state);
    rallocv(n+1, &state->bndux, _state);
    rcopyv(n, &state->bndl, &state->bndlx, _state);
    rcopyv(n, &state->bndu, &state->bndux, _state);
    state->bndlx.ptr.p_double[n] = _state->v_neginf;
    state->bndux.ptr.p_double[n] = _state->v_posinf;
    if( state->olddensek>0 )
    {
        rallocm(state->olddensek, n+2, &state->olddensecx, _state);
        rcopym(state->olddensek, n, &state->olddensec, &state->olddensecx, _state);
        for(i=0; i<=state->olddensek-1; i++)
        {
            state->olddensecx.ptr.pp_double[i][n] = 0.0;
            state->olddensecx.ptr.pp_double[i][n+1] = state->olddensec.ptr.pp_double[i][n];
        }
    }
    
    /*
     * Report initial F-vector
     */
    if( !dotrace )
    {
        goto lbl_38;
    }
    state->needfij = ae_true;
    rcopyv(n, &state->subproblemstart, &state->x, _state);
    state->rstate.stage = 8;
    goto lbl_rcomm;
lbl_8:
    state->needfij = ae_false;
    state->repnfev = state->repnfev+1;
    ae_trace("F (initial)   = ");
    tracevectorautoprec(&state->fi, 0, m, _state);
    ae_trace("\n");
lbl_38:
    
    /*
     * Solve initial NBI subproblem
     */
    minnlccreate(n+1, &state->subproblemstart, &state->nlcsolver, _state);
    minnlcsetcond(&state->nlcsolver, state->epsx, state->maxits, _state);
    minnlcsetbc(&state->nlcsolver, &state->bndlx, &state->bndux, _state);
    minnlcsetlc(&state->nlcsolver, &state->olddensecx, &state->olddensect, state->olddensek, _state);
    minnlcsetnlc(&state->nlcsolver, state->nlcnlec, state->nlcnlic+m, _state);
    monbi_setnlcalgo(&state->nlcsolver, _state);
lbl_40:
    if( !minnlciteration(&state->nlcsolver, _state) )
    {
        goto lbl_41;
    }
    if( state->userrequestedtermination )
    {
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    if( !state->nlcsolver.needfij )
    {
        goto lbl_42;
    }
    
    /*
     * Forward report to the caller
     */
    state->needfij = ae_true;
    rcopyv(n, &state->nlcsolver.x, &state->x, _state);
    state->rstate.stage = 9;
    goto lbl_rcomm;
lbl_9:
    state->needfij = ae_false;
    
    /*
     * Compute target
     */
    state->nlcsolver.fi.ptr.p_double[0] = -state->nlcsolver.x.ptr.p_double[n];
    rsetr(n+1, 0.0, &state->nlcsolver.j, 0, _state);
    state->nlcsolver.j.ptr.pp_double[0][n] = (double)(-1);
    
    /*
     * Forward original nonlinear constraints
     */
    for(i=0; i<=state->nlcnlec+state->nlcnlic-1; i++)
    {
        state->nlcsolver.fi.ptr.p_double[1+i] = state->fi.ptr.p_double[m+state->nlcidx.ptr.p_int[i]]*state->nlcmul.ptr.p_double[i]+state->nlcadd.ptr.p_double[i];
        rcopyrr(n, &state->j, m+state->nlcidx.ptr.p_int[i], &state->nlcsolver.j, 1+i, _state);
        rmulr(n, state->nlcmul.ptr.p_double[i], &state->nlcsolver.j, 1+i, _state);
        state->nlcsolver.j.ptr.pp_double[1+i][n] = (double)(0);
    }
    
    /*
     * Append NBI constraints
     */
    for(i=0; i<=m-1; i++)
    {
        
        /*
         * PAYOFF*Beta + t*Delta >= F(x)-FIdeal
         *
         * F(x) - FIdeal - t*DELTA - PAYOFF*Beta <= 0
         */
        state->nlcsolver.fi.ptr.p_double[1+state->nlcnlec+state->nlcnlic+i] = state->fi.ptr.p_double[i]-state->fideal.ptr.p_double[i]-state->nlcsolver.x.ptr.p_double[n]*state->delta.ptr.p_double[i]-rdotvr(m, &state->beta, &state->payoff, i, _state);
        rcopyrr(n, &state->j, i, &state->nlcsolver.j, 1+state->nlcnlec+state->nlcnlic+i, _state);
        state->nlcsolver.j.ptr.pp_double[1+state->nlcnlec+state->nlcnlic+i][n] = -state->delta.ptr.p_double[i];
    }
    goto lbl_40;
lbl_42:
    ae_assert(ae_false, "NBI: integrity check 6034 failed", _state);
    goto lbl_40;
lbl_41:
    minnlcresultsbuf(&state->nlcsolver, &state->x1, &state->nlcrep, _state);
    if( state->nlcrep.terminationtype==-8 )
    {
        state->repterminationtype = -8;
        state->repfrontsize = 0;
        result = ae_false;
        return result;
    }
    ae_assert(state->nlcrep.terminationtype>0, "NBI: integrity check 7144 failed", _state);
    state->repinneriterationscount = state->repinneriterationscount+state->nlcrep.iterationscount;
    state->repnfev = state->repnfev+state->nlcrep.nfev;
    if( !dotrace )
    {
        goto lbl_44;
    }
    state->needfij = ae_true;
    rcopyv(n, &state->x1, &state->x, _state);
    state->rstate.stage = 10;
    goto lbl_rcomm;
lbl_10:
    state->needfij = ae_false;
    state->repnfev = state->repnfev+1;
    ae_trace("F (unpolished)= ");
    tracevectorautoprec(&state->fi, 0, m, _state);
    ae_trace(" (%0d its, feas.err=%0.2e)\n",
        (int)(state->nlcrep.iterationscount),
        (double)(rmax3(state->nlcrep.bcerr, state->nlcrep.lcerr, state->nlcrep.nlcerr, _state)));
lbl_44:
    
    /*
     * Polish the solution: having solution X1 that minimizes f_objectiveIdx,
     * now we want to minimize a sum of f[i] subject to constraint that f_i(x)<=f_i(X1).
     *
     * This stage is essential for polishing other components of the solution
     * (even with moderate condition numbers it is possible to have f[i!=objectiveIdx]
     * underoptimized due to purely numerical issues).
     */
    if( !state->polishsolutions )
    {
        goto lbl_46;
    }
    state->needfij = ae_true;
    rcopyv(n, &state->x1, &state->x, _state);
    state->rstate.stage = 11;
    goto lbl_rcomm;
lbl_11:
    state->needfij = ae_false;
    state->repnfev = state->repnfev+1;
    rcopyallocv(m+state->nnlc, &state->fi, &state->fix1, _state);
    minnlccreate(n, &state->x1, &state->nlcsolver, _state);
    minnlcsetcond(&state->nlcsolver, state->epsx, state->maxits, _state);
    minnlcsetbc(&state->nlcsolver, &state->bndlx, &state->bndux, _state);
    minnlcsetlc(&state->nlcsolver, &state->olddensec, &state->olddensect, state->olddensek, _state);
    minnlcsetnlc(&state->nlcsolver, state->nlcnlec, state->nlcnlic+m, _state);
    monbi_setnlcalgo(&state->nlcsolver, _state);
lbl_48:
    if( !minnlciteration(&state->nlcsolver, _state) )
    {
        goto lbl_49;
    }
    if( state->userrequestedtermination )
    {
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    if( !state->nlcsolver.needfij )
    {
        goto lbl_50;
    }
    
    /*
     * Forward report to the caller
     */
    state->needfij = ae_true;
    rcopyv(n, &state->nlcsolver.x, &state->x, _state);
    state->rstate.stage = 12;
    goto lbl_rcomm;
lbl_12:
    state->needfij = ae_false;
    
    /*
     * Target: sum of objectives
     */
    state->nlcsolver.fi.ptr.p_double[0] = (double)(0);
    rsetr(n, 0.0, &state->nlcsolver.j, 0, _state);
    for(i=0; i<=m-1; i++)
    {
        state->nlcsolver.fi.ptr.p_double[0] = state->nlcsolver.fi.ptr.p_double[0]+state->fi.ptr.p_double[i];
        raddrr(n, 1.0, &state->j, i, &state->nlcsolver.j, 0, _state);
    }
    
    /*
     * Original nonlinear constraints
     */
    for(i=0; i<=state->nlcnlec+state->nlcnlic-1; i++)
    {
        state->nlcsolver.fi.ptr.p_double[i+1] = state->fi.ptr.p_double[m+state->nlcidx.ptr.p_int[i]]*state->nlcmul.ptr.p_double[i]+state->nlcadd.ptr.p_double[i];
        rcopyrr(n, &state->j, m+state->nlcidx.ptr.p_int[i], &state->nlcsolver.j, i+1, _state);
        rmulr(n, state->nlcmul.ptr.p_double[i], &state->nlcsolver.j, i+1, _state);
    }
    
    /*
     * Additional nonlinear constraints: objectives do not increase: f(x)-f(X1)<=0
     */
    for(i=0; i<=m-1; i++)
    {
        state->nlcsolver.fi.ptr.p_double[1+state->nlcnlec+state->nlcnlic+i] = state->fi.ptr.p_double[i]-state->fix1.ptr.p_double[i];
        rcopyrr(n, &state->j, i, &state->nlcsolver.j, 1+state->nlcnlec+state->nlcnlic+i, _state);
    }
    
    /*
     * Done
     */
    goto lbl_48;
lbl_50:
    ae_assert(ae_false, "NBI: integrity check 6034 failed", _state);
    goto lbl_48;
lbl_49:
    minnlcresultsbuf(&state->nlcsolver, &state->x2, &state->nlcrep, _state);
    if( state->nlcrep.terminationtype==-8 )
    {
        state->repterminationtype = -8;
        state->repfrontsize = 0;
        result = ae_false;
        return result;
    }
    ae_assert(state->nlcrep.terminationtype>0, "NBI: integrity check 7144 failed", _state);
    state->repinneriterationscount = state->repinneriterationscount+state->nlcrep.iterationscount;
    state->repnfev = state->repnfev+state->nlcrep.nfev;
    if( !dotrace )
    {
        goto lbl_52;
    }
    state->needfij = ae_true;
    rcopyv(n, &state->x2, &state->x, _state);
    state->rstate.stage = 13;
    goto lbl_rcomm;
lbl_13:
    state->needfij = ae_false;
    state->repnfev = state->repnfev+1;
    ae_trace("F (polished)  = ");
    tracevectorautoprec(&state->fi, 0, m, _state);
    ae_trace(" (%0d its, feas.err=%0.2e)\n",
        (int)(state->nlcrep.iterationscount),
        (double)(rmax3(state->nlcrep.bcerr, state->nlcrep.lcerr, state->nlcrep.nlcerr, _state)));
lbl_52:
    goto lbl_47;
lbl_46:
    rcopyallocv(n, &state->x1, &state->x2, _state);
lbl_47:
    
    /*
     * Save solution and objectives at the solution, update statistics
     */
    state->needfij = ae_true;
    rcopyv(n, &state->x2, &state->x, _state);
    state->rstate.stage = 14;
    goto lbl_rcomm;
lbl_14:
    state->needfij = ae_false;
    state->repnfev = state->repnfev+1;
    ae_assert(state->repfrontsize<frontsize, "MONBI: integrity check 1753 failed", _state);
    rcopyvr(n, &state->x2, &state->repparetofront, state->repfrontsize, _state);
    for(i=0; i<=m-1; i++)
    {
        state->repparetofront.ptr.pp_double[state->repfrontsize][n+i] = state->fi.ptr.p_double[i];
    }
    for(i=0; i<=m-1; i++)
    {
        state->repparetofront.ptr.pp_double[state->repfrontsize][n+m+i] = state->beta.ptr.p_double[i];
    }
    state->repfrontsize = state->repfrontsize+1;
    state->repouteriterationscount = state->repouteriterationscount+1;
    if( ae_fp_greater(state->nlcrep.bcerr,state->repbcerr) )
    {
        state->repbcerr = state->nlcrep.bcerr;
        state->repbcidx = state->nlcrep.bcidx;
    }
    if( ae_fp_greater(state->nlcrep.lcerr,state->replcerr) )
    {
        state->replcerr = state->nlcrep.lcerr;
        state->replcidx = state->nlcrep.lcidx;
    }
    if( ae_fp_greater(state->nlcrep.nlcerr,state->repnlcerr) )
    {
        state->repnlcerr = state->nlcrep.nlcerr;
        state->repnlcidx = state->nlcrep.nlcidx;
    }
    
    /*
     * Report last point found and check request for termination
     */
    rcopyrv(n, &state->repparetofront, state->repfrontsize-1, &state->x, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 15;
    goto lbl_rcomm;
lbl_15:
    state->xupdated = ae_false;
    if( state->userrequestedtermination )
    {
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    frontrow = frontrow+1;
    goto lbl_35;
lbl_37:
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = n;
    state->rstate.ia.ptr.p_int[1] = m;
    state->rstate.ia.ptr.p_int[2] = objectiveidx;
    state->rstate.ia.ptr.p_int[3] = frontsize;
    state->rstate.ia.ptr.p_int[4] = i;
    state->rstate.ia.ptr.p_int[5] = j;
    state->rstate.ia.ptr.p_int[6] = k;
    state->rstate.ia.ptr.p_int[7] = frontrow;
    state->rstate.ba.ptr.p_bool[0] = dotrace;
    state->rstate.ra.ptr.p_double[0] = v;
    state->rstate.ra.ptr.p_double[1] = vv;
    return result;
}


/*************************************************************************
Clears request fileds (to be sure that we don't forget to clear something)
*************************************************************************/
static void monbi_clearrequestfields(nbistate* state, ae_state *_state)
{


    state->needfij = ae_false;
    state->xupdated = ae_false;
}


/*************************************************************************
Sets NLC solver
*************************************************************************/
static void monbi_setnlcalgo(minnlcstate* s, ae_state *_state)
{


    minnlcsetalgosqp(s, _state);
}


void _nbistate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    nbistate *p = (nbistate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->xstart, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->densec, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparsec, _state, make_automatic);
    ae_vector_init(&p->cl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->j, 0, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    ae_matrix_init(&p->repparetofront, 0, 0, DT_REAL, _state, make_automatic);
    _minnlcstate_init(&p->nlcsolver, _state, make_automatic);
    _minnlcreport_init(&p->nlcrep, _state, make_automatic);
    ae_vector_init(&p->tmpzero, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpone, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->olddensec, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->olddensect, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->nlcidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->nlcmul, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nlcadd, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fideal, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->payoff, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->beta, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->delta, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->subproblemstart, 0, DT_REAL, _state, make_automatic);
    _hqrndstate_init(&p->rs, _state, make_automatic);
    ae_vector_init(&p->bndlx, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndux, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->olddensecx, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fix1, 0, DT_REAL, _state, make_automatic);
}


void _nbistate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    nbistate       *dst = (nbistate*)_dst;
    const nbistate *src = (const nbistate*)_src;
    dst->n = src->n;
    dst->m = src->m;
    dst->epsx = src->epsx;
    dst->maxits = src->maxits;
    dst->xrep = src->xrep;
    ae_vector_init_copy(&dst->xstart, &src->xstart, _state, make_automatic);
    dst->frontsize = src->frontsize;
    dst->polishsolutions = src->polishsolutions;
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    dst->ksparse = src->ksparse;
    dst->kdense = src->kdense;
    ae_matrix_init_copy(&dst->densec, &src->densec, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsec, &src->sparsec, _state, make_automatic);
    ae_vector_init_copy(&dst->cl, &src->cl, _state, make_automatic);
    ae_vector_init_copy(&dst->cu, &src->cu, _state, make_automatic);
    dst->nnlc = src->nnlc;
    ae_vector_init_copy(&dst->nl, &src->nl, _state, make_automatic);
    ae_vector_init_copy(&dst->nu, &src->nu, _state, make_automatic);
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    dst->f = src->f;
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    ae_matrix_init_copy(&dst->j, &src->j, _state, make_automatic);
    dst->needfij = src->needfij;
    dst->xupdated = src->xupdated;
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    dst->userrequestedtermination = src->userrequestedtermination;
    ae_matrix_init_copy(&dst->repparetofront, &src->repparetofront, _state, make_automatic);
    dst->repfrontsize = src->repfrontsize;
    dst->repinneriterationscount = src->repinneriterationscount;
    dst->repouteriterationscount = src->repouteriterationscount;
    dst->repnfev = src->repnfev;
    dst->repterminationtype = src->repterminationtype;
    dst->repbcerr = src->repbcerr;
    dst->repbcidx = src->repbcidx;
    dst->replcerr = src->replcerr;
    dst->replcidx = src->replcidx;
    dst->repnlcerr = src->repnlcerr;
    dst->repnlcidx = src->repnlcidx;
    _minnlcstate_init_copy(&dst->nlcsolver, &src->nlcsolver, _state, make_automatic);
    _minnlcreport_init_copy(&dst->nlcrep, &src->nlcrep, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpzero, &src->tmpzero, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpone, &src->tmpone, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_matrix_init_copy(&dst->olddensec, &src->olddensec, _state, make_automatic);
    ae_vector_init_copy(&dst->olddensect, &src->olddensect, _state, make_automatic);
    dst->olddensek = src->olddensek;
    ae_vector_init_copy(&dst->nlcidx, &src->nlcidx, _state, make_automatic);
    ae_vector_init_copy(&dst->nlcmul, &src->nlcmul, _state, make_automatic);
    ae_vector_init_copy(&dst->nlcadd, &src->nlcadd, _state, make_automatic);
    dst->nlcnlec = src->nlcnlec;
    dst->nlcnlic = src->nlcnlic;
    ae_vector_init_copy(&dst->fideal, &src->fideal, _state, make_automatic);
    ae_matrix_init_copy(&dst->payoff, &src->payoff, _state, make_automatic);
    ae_vector_init_copy(&dst->beta, &src->beta, _state, make_automatic);
    ae_vector_init_copy(&dst->delta, &src->delta, _state, make_automatic);
    ae_vector_init_copy(&dst->subproblemstart, &src->subproblemstart, _state, make_automatic);
    _hqrndstate_init_copy(&dst->rs, &src->rs, _state, make_automatic);
    ae_vector_init_copy(&dst->bndlx, &src->bndlx, _state, make_automatic);
    ae_vector_init_copy(&dst->bndux, &src->bndux, _state, make_automatic);
    ae_matrix_init_copy(&dst->olddensecx, &src->olddensecx, _state, make_automatic);
    ae_vector_init_copy(&dst->x1, &src->x1, _state, make_automatic);
    ae_vector_init_copy(&dst->x2, &src->x2, _state, make_automatic);
    ae_vector_init_copy(&dst->fix1, &src->fix1, _state, make_automatic);
}


void _nbistate_clear(void* _p)
{
    nbistate *p = (nbistate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->xstart);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_matrix_clear(&p->densec);
    _sparsematrix_clear(&p->sparsec);
    ae_vector_clear(&p->cl);
    ae_vector_clear(&p->cu);
    ae_vector_clear(&p->nl);
    ae_vector_clear(&p->nu);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->fi);
    ae_matrix_clear(&p->j);
    _rcommstate_clear(&p->rstate);
    ae_matrix_clear(&p->repparetofront);
    _minnlcstate_clear(&p->nlcsolver);
    _minnlcreport_clear(&p->nlcrep);
    ae_vector_clear(&p->tmpzero);
    ae_vector_clear(&p->tmpone);
    ae_vector_clear(&p->tmp0);
    ae_matrix_clear(&p->olddensec);
    ae_vector_clear(&p->olddensect);
    ae_vector_clear(&p->nlcidx);
    ae_vector_clear(&p->nlcmul);
    ae_vector_clear(&p->nlcadd);
    ae_vector_clear(&p->fideal);
    ae_matrix_clear(&p->payoff);
    ae_vector_clear(&p->beta);
    ae_vector_clear(&p->delta);
    ae_vector_clear(&p->subproblemstart);
    _hqrndstate_clear(&p->rs);
    ae_vector_clear(&p->bndlx);
    ae_vector_clear(&p->bndux);
    ae_matrix_clear(&p->olddensecx);
    ae_vector_clear(&p->x1);
    ae_vector_clear(&p->x2);
    ae_vector_clear(&p->fix1);
}


void _nbistate_destroy(void* _p)
{
    nbistate *p = (nbistate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->xstart);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_matrix_destroy(&p->densec);
    _sparsematrix_destroy(&p->sparsec);
    ae_vector_destroy(&p->cl);
    ae_vector_destroy(&p->cu);
    ae_vector_destroy(&p->nl);
    ae_vector_destroy(&p->nu);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->fi);
    ae_matrix_destroy(&p->j);
    _rcommstate_destroy(&p->rstate);
    ae_matrix_destroy(&p->repparetofront);
    _minnlcstate_destroy(&p->nlcsolver);
    _minnlcreport_destroy(&p->nlcrep);
    ae_vector_destroy(&p->tmpzero);
    ae_vector_destroy(&p->tmpone);
    ae_vector_destroy(&p->tmp0);
    ae_matrix_destroy(&p->olddensec);
    ae_vector_destroy(&p->olddensect);
    ae_vector_destroy(&p->nlcidx);
    ae_vector_destroy(&p->nlcmul);
    ae_vector_destroy(&p->nlcadd);
    ae_vector_destroy(&p->fideal);
    ae_matrix_destroy(&p->payoff);
    ae_vector_destroy(&p->beta);
    ae_vector_destroy(&p->delta);
    ae_vector_destroy(&p->subproblemstart);
    _hqrndstate_destroy(&p->rs);
    ae_vector_destroy(&p->bndlx);
    ae_vector_destroy(&p->bndux);
    ae_matrix_destroy(&p->olddensecx);
    ae_vector_destroy(&p->x1);
    ae_vector_destroy(&p->x2);
    ae_vector_destroy(&p->fix1);
}


#endif
#if defined(AE_COMPILE_MINMO) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
                    MULTI-OBJECTIVE  OPTIMIZATION

DESCRIPTION:

The  solver  minimizes an M-dimensional vector function F(x) of N arguments
subject to any combination of:
* box constraints
* two-sided linear equality/inequality constraints AL<=A*x<=AU, where some
  of AL/AU can be infinite (i.e. missing)
* two-sided nonlinear equality/inequality constraints NL<=C(x)<=NU,  where
  some of NL/NU can be infinite (i.e. missing)

REQUIREMENTS:
* F(), C() are continuously differentiable on the feasible set and on  its
  neighborhood

USAGE:

1. User initializes algorithm state using either:
   * minmocreate()  to perform optimization with user-supplied Jacobian
   * minmocreatef() to perform optimization with numerical differentiation

2. User chooses which multi-objective solver to use. At the present moment
   only NBI (Normal Boundary Intersection) solver is implemented, which is
   activated by calling minmosetalgonbi().

3. User adds boundary and/or linear and/or nonlinear constraints by  means
   of calling one of the following functions:
   a) minmosetbc() for boundary constraints
   b) minmosetlc2()      for two-sided sparse linear constraints;
      minmosetlc2dense() for two-sided dense  linear constraints;
      minmosetlc2mixed() for two-sided mixed sparse/dense constraints
   c) minmosetnlc2()     for two-sided nonlinear constraints
   You may combine (a), (b) and (c) in one optimization problem.
   
4. User sets scale of the variables with minmosetscale() function.  It  is
   VERY important to set  scale  of  the  variables,  because  nonlinearly
   constrained problems are hard to solve when variables are badly scaled.

5. User sets  stopping  conditions  with  minmosetcond().
   
6. Finally, user calls minmooptimize()   function  which  takes  algorithm
   state  and  pointers  (delegate, etc.) to the callback functions  which
   calculate F/C

7. User calls  minmoresults()  to  get the solution

8. Optionally user may call minmorestartfrom() to solve  another   problem
   with same M,N but another starting point. minmorestartfrom() allows  to
   reuse an already initialized optimizer structure.


INPUT PARAMETERS:
    N       -   variables count, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from the size of X
    M       -   objectives count, M>0.
                M=1 is possible, although makes little sense - it is better
                to use MinNLC directly.
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to reinforce feasibility during  initial  stages  of  the
                  optimization

OUTPUT PARAMETERS:
    State   -   structure that stores algorithm state

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmocreate(ae_int_t n,
     ae_int_t m,
     /* Real    */ const ae_vector* x,
     minmostate* state,
     ae_state *_state)
{

    _minmostate_clear(state);

    ae_assert(n>=1, "MinMOCreate: N<1", _state);
    ae_assert(m>=1, "MinMOCreate: M<1", _state);
    ae_assert(x->cnt>=n, "MinMOCreate: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinMOCreate: X contains infinite or NaN values", _state);
    minmo_minmoinitinternal(n, m, x, 0.0, state, _state);
}


/*************************************************************************
This subroutine is a finite  difference variant of minmocreate().  It uses
finite differences in order to differentiate target function.

Description below contains information which is specific to this  function
only. We recommend to read comments on minmocreate() too.

INPUT PARAMETERS:
    N       -   variables count, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from the size of X
    M       -   objectives count, M>0.
                M=1 is possible, although makes little sense - it is better
                to use MinNLC directly.
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to reinforce feasibility during  initial  stages  of  the
                  optimization
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure that stores algorithm state

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is a scaling vector which can be set by minmosetscale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step means too large TRUNCATION errors,  whilst  too  small  step
   means too large NUMERICAL errors.
   1.0E-4 can be good value to start from for a unit-scaled problem.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is  less   robust   and  precise.  Imprecise  gradient  may  slow  down
   convergence, especially on highly nonlinear problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmocreatef(ae_int_t n,
     ae_int_t m,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minmostate* state,
     ae_state *_state)
{

    _minmostate_clear(state);

    ae_assert(n>=1, "MinMOCreateF: N<1", _state);
    ae_assert(m>=1, "MinMOCreateF: M<1", _state);
    ae_assert(x->cnt>=n, "MinMOCreateF: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinMOCreateF: X contains infinite or NaN values", _state);
    ae_assert(ae_isfinite(diffstep, _state), "MinMOCreateF: DiffStep is infinite or NaN!", _state);
    ae_assert(ae_fp_greater(diffstep,(double)(0)), "MinMOCreateF: DiffStep is non-positive!", _state);
    minmo_minmoinitinternal(n, m, x, diffstep, state, _state);
}


/*************************************************************************
Use the NBI (Normal Boundary Intersection)  algorithm  for  multiobjective
optimization.

NBI is a simple yet powerful multiobjective  optimization  algorithm  that
has the following attractive properties:
* it generates nearly uniformly distributed Pareto points
* it is applicable to problems with more than 2 objectives
* it naturally supports a mix of box, linear and nonlinear constraints
* it is less sensitive to the bad scaling of the targets

The only drawback of the algorithm is that for more than 2  objectives  it
can miss some small parts of the Pareto front that are  located  near  its
boundaries.

INPUT PARAMETERS:
    State       -   structure which stores algorithm state
    FrontSize   -   desired Pareto front size, FrontSize>=M,
                    where M is an objectives count
    PolishSolutions-whether additional solution improving phase is needed
                    or not:
                    * if False, the original NBI as formulated  by Das and
                      Dennis is used. It quickly produces  good solutions,
                      but these solutions can be suboptimal (usually within
                      0.1% of the optimal values).
                      The reason is that the original NBI formulation does
                      not account for  degeneracies that allow significant
                      progress for one objective with no deterioration for
                      other objectives.
                    * if True,  the  original  NBI  is  followed  by   the
                      additional solution  polishing  phase.  This  solver
                      mode is several times slower than the original  NBI,
                      but produces better solutions.

  -- ALGLIB --
     Copyright 20.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetalgonbi(minmostate* state,
     ae_int_t frontsize,
     ae_bool polishsolutions,
     ae_state *_state)
{


    ae_assert(frontsize>=state->m, "MinMOSetAlgoNBI: FrontSize<=M", _state);
    state->solvertype = icase2(polishsolutions, 1, 0, _state);
    state->frontsize = frontsize;
}


/*************************************************************************
This function sets boundary constraints for the MO optimizer.

Boundary constraints are inactive by  default  (after  initial  creation).
They are preserved after algorithm restart with MinMORestartFrom().

You may combine boundary constraints with  general  linear ones - and with
nonlinear ones! Boundary constraints are  handled  more  efficiently  than
other types.  Thus,  if  your  problem  has  mixed  constraints,  you  may
explicitly specify some of them as boundary and save some time/space.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                a very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                a very large number or +INF.

NOTE 1:  it is possible to specify  BndL[i]=BndU[i].  In  this  case  I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetbc(minmostate* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_assert(bndl->cnt>=n, "MinMOSetBC: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=n, "MinMOSetBC: Length(BndU)<N", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "MinMOSetBC: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "MinMOSetBC: BndL contains NAN or -INF", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function sets two-sided linear constraints AL <= A*x <= AU with dense
constraint matrix A.

NOTE: knowing  that  constraint  matrix  is dense may help some MO solvers
      to utilize efficient dense Level 3  BLAS  for  dense  parts  of  the
      problem. If your problem has both dense and sparse constraints,  you
      can use minmosetlc2mixed() function.

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    A       -   linear constraints, array[K,N]. Each row of  A  represents
                one  constraint. One-sided  inequality   constraints, two-
                sided inequality  constraints,  equality  constraints  are
                supported (see below)
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number of equality/inequality constraints,  K>=0;  if  not
                given, inferred from sizes of A, AL, AU.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetlc2dense(minmostate* state,
     /* Real    */ const ae_matrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t k,
     ae_state *_state)
{


    minmosetlc2mixed(state, &state->dummysparse, 0, a, k, al, au, _state);
}


/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
sparse constraining matrix A. Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    A       -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    AL, AU  -   lower and upper bounds, array[K];
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minmosetlc2(minmostate* state,
     const sparsematrix* a,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_int_t k,
     ae_state *_state)
{


    minmosetlc2mixed(state, a, k, &state->dummyr2, 0, al, au, _state);
}


/*************************************************************************
This  function  sets  two-sided linear  constraints  AL <= A*x <= AU  with
mixed constraining matrix A including sparse part (first SparseK rows) and
dense part (last DenseK rows). Recommended for large-scale problems.

This  function  overwrites  linear  (non-box)  constraints set by previous
calls (if such calls were made).

This function may be useful if constraint matrix includes large number  of
both types of rows - dense and sparse. If you have just a few sparse rows,
you  may  represent  them  in  dense  format  without losing  performance.
Similarly, if you have just a few dense rows,  you  can  store them in the
sparse format with almost same performance.

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    SparseA -   sparse matrix with size [K,N] (exactly!).
                Each row of A represents one general linear constraint.
                A can be stored in any sparse storage format.
    SparseK -   number of sparse constraints, SparseK>=0
    DenseA  -   linear constraints, array[K,N], set of dense constraints.
                Each row of A represents one general linear constraint.
    DenseK  -   number of dense constraints, DenseK>=0
    AL, AU  -   lower and upper bounds, array[SparseK+DenseK], with former
                SparseK elements corresponding to sparse constraints,  and
                latter DenseK elements corresponding to dense constraints;
                * AL[i]=AU[i] => equality constraint Ai*x
                * AL[i]<AU[i] => two-sided constraint AL[i]<=Ai*x<=AU[i]
                * AL[i]=-INF  => one-sided constraint Ai*x<=AU[i]
                * AU[i]=+INF  => one-sided constraint AL[i]<=Ai*x
                * AL[i]=-INF, AU[i]=+INF => constraint is ignored
    K       -   number  of equality/inequality constraints, K>=0.  If  K=0
                is specified, A, AL, AU are ignored.

  -- ALGLIB --
     Copyright 01.11.2019 by Bochkanov Sergey
*************************************************************************/
void minmosetlc2mixed(minmostate* state,
     const sparsematrix* sparsea,
     ae_int_t ksparse,
     /* Real    */ const ae_matrix* densea,
     ae_int_t kdense,
     /* Real    */ const ae_vector* al,
     /* Real    */ const ae_vector* au,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;


    n = state->n;
    m = kdense+ksparse;
    
    /*
     * Check input arguments
     */
    ae_assert(ksparse>=0, "MinMOSetLC2Mixed: KSparse<0", _state);
    ae_assert(ksparse==0||sparsegetncols(sparsea, _state)==n, "MinMOSetLC2: Cols(SparseA)<>N", _state);
    ae_assert(ksparse==0||sparsegetnrows(sparsea, _state)==ksparse, "MinMOSetLC2: Rows(SparseA)<>K", _state);
    ae_assert(kdense>=0, "MinMOSetLC2Mixed: KDense<0", _state);
    ae_assert(kdense==0||densea->cols>=n, "MinMOSetLC2Mixed: Cols(DenseA)<N", _state);
    ae_assert(kdense==0||densea->rows>=kdense, "MinMOSetLC2Mixed: Rows(DenseA)<K", _state);
    ae_assert(apservisfinitematrix(densea, kdense, n, _state), "MinMOSetLC2Mixed: DenseA contains infinite or NaN values!", _state);
    ae_assert(al->cnt>=kdense+ksparse, "MinMOSetLC2Mixed: Length(AL)<K", _state);
    ae_assert(au->cnt>=kdense+ksparse, "MinMOSetLC2Mixed: Length(AU)<K", _state);
    for(i=0; i<=m-1; i++)
    {
        ae_assert(ae_isfinite(al->ptr.p_double[i], _state)||ae_isneginf(al->ptr.p_double[i], _state), "MinMOSetLC2Mixed: AL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(au->ptr.p_double[i], _state)||ae_isposinf(au->ptr.p_double[i], _state), "MinMOSetLC2Mixed: AU contains NAN or -INF", _state);
    }
    
    /*
     * Quick exit if needed
     */
    if( m==0 )
    {
        state->mdense = 0;
        state->msparse = 0;
        return;
    }
    
    /*
     * Prepare
     */
    rvectorsetlengthatleast(&state->cl, m, _state);
    rvectorsetlengthatleast(&state->cu, m, _state);
    for(i=0; i<=m-1; i++)
    {
        state->cl.ptr.p_double[i] = al->ptr.p_double[i];
        state->cu.ptr.p_double[i] = au->ptr.p_double[i];
    }
    state->mdense = kdense;
    state->msparse = ksparse;
    
    /*
     * Copy dense and sparse terms
     */
    if( ksparse>0 )
    {
        sparsecopytocrsbuf(sparsea, &state->sparsec, _state);
    }
    if( kdense>0 )
    {
        rmatrixsetlengthatleast(&state->densec, kdense, n, _state);
        rmatrixcopy(kdense, n, densea, 0, 0, &state->densec, 0, 0, _state);
    }
}


/*************************************************************************
This function appends two-sided linear  constraint  AL<=A*x<=AU  to  dense
constraints list.

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    A       -   linear constraint coefficient, array[N], right side is NOT
                included.
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint Ai*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint Ai*x<=AU
                * AU=+INF  => one-sided constraint AL<=Ai*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minmoaddlc2dense(minmostate* state,
     /* Real    */ const ae_vector* a,
     double al,
     double au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_assert(a->cnt>=n, "MinMOAddLC2Dense: Length(A)<N", _state);
    ae_assert(isfinitevector(a, n, _state), "MinMOAddLC2Dense: A contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(al, _state)||ae_isneginf(al, _state), "MinMOAddLC2Dense: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(au, _state)||ae_isposinf(au, _state), "MinMOAddLC2Dense: AU is NAN or -INF", _state);
    rvectorgrowto(&state->cl, state->msparse+state->mdense+1, _state);
    rvectorgrowto(&state->cu, state->msparse+state->mdense+1, _state);
    rmatrixgrowrowsto(&state->densec, state->mdense+1, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->densec.ptr.pp_double[state->mdense][i] = a->ptr.p_double[i];
    }
    state->cl.ptr.p_double[state->msparse+state->mdense] = al;
    state->cu.ptr.p_double[state->msparse+state->mdense] = au;
    inc(&state->mdense, _state);
}


/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of sparse constraints.

Constraint is passed in the compressed  format:  as  a  list  of  non-zero
entries of the coefficient vector A. Such approach is more efficient  than
the dense storage for highly sparse constraint vectors.

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    IdxA    -   array[NNZ], indexes of non-zero elements of A:
                * can be unsorted
                * can include duplicate indexes (corresponding entries  of
                  ValA[] will be summed)
    ValA    -   array[NNZ], values of non-zero elements of A
    NNZ     -   number of non-zero coefficients in A
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minmoaddlc2(minmostate* state,
     /* Integer */ const ae_vector* idxa,
     /* Real    */ const ae_vector* vala,
     ae_int_t nnz,
     double al,
     double au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t offs;
    ae_int_t offsdst;
    ae_int_t n;
    ae_int_t didx;
    ae_int_t uidx;


    n = state->n;
    
    /*
     * Check inputs
     */
    ae_assert(nnz>=0, "MinMOAddLC2: NNZ<0", _state);
    ae_assert(idxa->cnt>=nnz, "MinMOAddLC2: Length(IdxA)<NNZ", _state);
    ae_assert(vala->cnt>=nnz, "MinMOAddLC2: Length(ValA)<NNZ", _state);
    for(i=0; i<=nnz-1; i++)
    {
        ae_assert(idxa->ptr.p_int[i]>=0&&idxa->ptr.p_int[i]<n, "MinMOAddLC2: IdxA contains indexes outside of [0,N) range", _state);
    }
    ae_assert(isfinitevector(vala, nnz, _state), "MinMOAddLC2: ValA contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(al, _state)||ae_isneginf(al, _state), "MinMOAddLC2: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(au, _state)||ae_isposinf(au, _state), "MinMOAddLC2: AU is NAN or -INF", _state);
    
    /*
     * If M=0, it means that A is uninitialized.
     * Prepare sparse matrix structure
     */
    if( state->msparse==0 )
    {
        state->sparsec.matrixtype = 1;
        state->sparsec.m = 0;
        state->sparsec.n = n;
        state->sparsec.ninitialized = 0;
        ivectorsetlengthatleast(&state->sparsec.ridx, 1, _state);
        state->sparsec.ridx.ptr.p_int[0] = 0;
    }
    ae_assert(state->sparsec.matrixtype==1&&state->sparsec.m==state->msparse, "MinMOAddLC2: integrity check failed!", _state);
    
    /*
     * Reallocate inequality bounds
     */
    rvectorgrowto(&state->cl, state->msparse+state->mdense+1, _state);
    rvectorgrowto(&state->cu, state->msparse+state->mdense+1, _state);
    for(i=state->msparse+state->mdense; i>=state->msparse+1; i--)
    {
        state->cl.ptr.p_double[i] = state->cl.ptr.p_double[i-1];
        state->cu.ptr.p_double[i] = state->cu.ptr.p_double[i-1];
    }
    state->cl.ptr.p_double[state->msparse] = al;
    state->cu.ptr.p_double[state->msparse] = au;
    
    /*
     * Reallocate sparse storage
     */
    offs = state->sparsec.ridx.ptr.p_int[state->msparse];
    ivectorgrowto(&state->sparsec.idx, offs+nnz, _state);
    rvectorgrowto(&state->sparsec.vals, offs+nnz, _state);
    ivectorgrowto(&state->sparsec.didx, state->msparse+1, _state);
    ivectorgrowto(&state->sparsec.uidx, state->msparse+1, _state);
    ivectorgrowto(&state->sparsec.ridx, state->msparse+2, _state);
    
    /*
     * If NNZ=0, perform quick and simple row append. 
     */
    if( nnz==0 )
    {
        state->sparsec.didx.ptr.p_int[state->msparse] = state->sparsec.ridx.ptr.p_int[state->msparse];
        state->sparsec.uidx.ptr.p_int[state->msparse] = state->sparsec.ridx.ptr.p_int[state->msparse];
        state->sparsec.ridx.ptr.p_int[state->msparse+1] = state->sparsec.ridx.ptr.p_int[state->msparse];
        inc(&state->sparsec.m, _state);
        inc(&state->msparse, _state);
        return;
    }
    
    /*
     * Now we are sure that SparseC contains properly initialized sparse
     * matrix (or some appropriate dummy for M=0) and we have NNZ>0
     * (no need to care about degenerate cases).
     *
     * Append rows to SparseC:
     * * append data
     * * sort in place
     * * merge duplicate indexes
     * * compute DIdx and UIdx
     *
     */
    for(i=0; i<=nnz-1; i++)
    {
        state->sparsec.idx.ptr.p_int[offs+i] = idxa->ptr.p_int[i];
        state->sparsec.vals.ptr.p_double[offs+i] = vala->ptr.p_double[i];
    }
    tagsortmiddleir(&state->sparsec.idx, &state->sparsec.vals, offs, nnz, _state);
    offsdst = offs;
    for(i=1; i<=nnz-1; i++)
    {
        if( state->sparsec.idx.ptr.p_int[offsdst]!=state->sparsec.idx.ptr.p_int[offs+i] )
        {
            offsdst = offsdst+1;
            state->sparsec.idx.ptr.p_int[offsdst] = state->sparsec.idx.ptr.p_int[offs+i];
            state->sparsec.vals.ptr.p_double[offsdst] = state->sparsec.vals.ptr.p_double[offs+i];
        }
        else
        {
            state->sparsec.vals.ptr.p_double[offsdst] = state->sparsec.vals.ptr.p_double[offsdst]+state->sparsec.vals.ptr.p_double[offs+i];
        }
    }
    nnz = offsdst-offs+1;
    uidx = -1;
    didx = -1;
    for(j=offs; j<=offsdst; j++)
    {
        k = state->sparsec.idx.ptr.p_int[j];
        if( k==state->msparse )
        {
            didx = j;
        }
        else
        {
            if( k>state->msparse&&uidx==-1 )
            {
                uidx = j;
                break;
            }
        }
    }
    if( uidx==-1 )
    {
        uidx = offsdst+1;
    }
    if( didx==-1 )
    {
        didx = uidx;
    }
    state->sparsec.didx.ptr.p_int[state->msparse] = didx;
    state->sparsec.uidx.ptr.p_int[state->msparse] = uidx;
    state->sparsec.ridx.ptr.p_int[state->msparse+1] = offsdst+1;
    state->sparsec.ninitialized = state->sparsec.ridx.ptr.p_int[state->msparse+1];
    inc(&state->sparsec.m, _state);
    inc(&state->msparse, _state);
}


/*************************************************************************
This function appends two-sided linear constraint  AL <= A*x <= AU  to the
list of currently present sparse constraints.

Constraint vector A is  passed  as  a  dense  array  which  is  internally
sparsified by this function.

INPUT PARAMETERS:
    State   -   structure previously allocated with minmocreate() call.
    DA      -   array[N], constraint vector
    AL, AU  -   lower and upper bounds;
                * AL=AU    => equality constraint A*x
                * AL<AU    => two-sided constraint AL<=A*x<=AU
                * AL=-INF  => one-sided constraint A*x<=AU
                * AU=+INF  => one-sided constraint AL<=A*x
                * AL=-INF, AU=+INF => constraint is ignored

  -- ALGLIB --
     Copyright 19.07.2018 by Bochkanov Sergey
*************************************************************************/
void minmoaddlc2sparsefromdense(minmostate* state,
     /* Real    */ const ae_vector* da,
     double al,
     double au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t nzi;
    ae_int_t offs;
    ae_int_t n;
    ae_int_t nnz;
    ae_int_t didx;
    ae_int_t uidx;


    n = state->n;
    
    /*
     * Check inputs
     */
    ae_assert(da->cnt>=n, "MinMOAddLC2SparseFromDense: Length(DA)<N", _state);
    ae_assert(isfinitevector(da, n, _state), "MinMOAddLC2SparseFromDense: DA contains infinities/NANs", _state);
    ae_assert(ae_isfinite(al, _state)||ae_isneginf(al, _state), "MinMOAddLC2SparseFromDense: AL is NAN or +INF", _state);
    ae_assert(ae_isfinite(au, _state)||ae_isposinf(au, _state), "MinMOAddLC2SparseFromDense: AU is NAN or -INF", _state);
    
    /*
     * If M=0, it means that A is uninitialized.
     * Prepare sparse matrix structure
     */
    if( state->msparse==0 )
    {
        state->sparsec.matrixtype = 1;
        state->sparsec.m = 0;
        state->sparsec.n = n;
        state->sparsec.ninitialized = 0;
        ivectorsetlengthatleast(&state->sparsec.ridx, 1, _state);
        state->sparsec.ridx.ptr.p_int[0] = 0;
    }
    ae_assert(state->sparsec.matrixtype==1&&state->sparsec.m==state->msparse, "MinMOAddLC2SparseFromDense: integrity check failed!", _state);
    
    /*
     * Reallocate inequality bounds
     */
    rvectorgrowto(&state->cl, state->msparse+state->mdense+1, _state);
    rvectorgrowto(&state->cu, state->msparse+state->mdense+1, _state);
    for(i=state->msparse+state->mdense; i>=state->msparse+1; i--)
    {
        state->cl.ptr.p_double[i] = state->cl.ptr.p_double[i-1];
        state->cu.ptr.p_double[i] = state->cu.ptr.p_double[i-1];
    }
    state->cl.ptr.p_double[state->msparse] = al;
    state->cu.ptr.p_double[state->msparse] = au;
    
    /*
     * Determine nonzeros count.
     * Reallocate sparse storage.
     */
    nnz = 0;
    for(i=0; i<=n-1; i++)
    {
        if( !(da->ptr.p_double[i]==0.0) )
        {
            nnz = nnz+1;
        }
    }
    offs = state->sparsec.ridx.ptr.p_int[state->msparse];
    ivectorgrowto(&state->sparsec.idx, offs+nnz, _state);
    rvectorgrowto(&state->sparsec.vals, offs+nnz, _state);
    ivectorgrowto(&state->sparsec.didx, state->msparse+1, _state);
    ivectorgrowto(&state->sparsec.uidx, state->msparse+1, _state);
    ivectorgrowto(&state->sparsec.ridx, state->msparse+2, _state);
    
    /*
     * If NNZ=0, perform quick and simple row append. 
     */
    if( nnz==0 )
    {
        state->sparsec.didx.ptr.p_int[state->msparse] = state->sparsec.ridx.ptr.p_int[state->msparse];
        state->sparsec.uidx.ptr.p_int[state->msparse] = state->sparsec.ridx.ptr.p_int[state->msparse];
        state->sparsec.ridx.ptr.p_int[state->msparse+1] = state->sparsec.ridx.ptr.p_int[state->msparse];
        inc(&state->sparsec.m, _state);
        inc(&state->msparse, _state);
        return;
    }
    
    /*
     * Now we are sure that SparseC contains properly initialized sparse
     * matrix (or some appropriate dummy for M=0) and we have NNZ>0
     * (no need to care about degenerate cases).
     *
     * Append rows to SparseC:
     * * append data
     * * compute DIdx and UIdx
     *
     */
    nzi = 0;
    for(i=0; i<=n-1; i++)
    {
        if( !(da->ptr.p_double[i]==0.0) )
        {
            state->sparsec.idx.ptr.p_int[offs+nzi] = i;
            state->sparsec.vals.ptr.p_double[offs+nzi] = da->ptr.p_double[i];
            nzi = nzi+1;
        }
    }
    uidx = -1;
    didx = -1;
    for(j=offs; j<=offs+nnz-1; j++)
    {
        k = state->sparsec.idx.ptr.p_int[j];
        if( k==state->msparse )
        {
            didx = j;
        }
        else
        {
            if( k>state->msparse&&uidx==-1 )
            {
                uidx = j;
                break;
            }
        }
    }
    if( uidx==-1 )
    {
        uidx = offs+nnz;
    }
    if( didx==-1 )
    {
        didx = uidx;
    }
    state->sparsec.didx.ptr.p_int[state->msparse] = didx;
    state->sparsec.uidx.ptr.p_int[state->msparse] = uidx;
    state->sparsec.ridx.ptr.p_int[state->msparse+1] = offs+nnz;
    state->sparsec.ninitialized = state->sparsec.ridx.ptr.p_int[state->msparse+1];
    inc(&state->sparsec.m, _state);
    inc(&state->msparse, _state);
}


/*************************************************************************
This function sets two-sided nonlinear constraints for MinMO optimizer.

In fact, this function sets  only  constraints  COUNT  and  their  BOUNDS.
Constraints  themselves  (constraint  functions)   are   passed   to   the
MinMOOptimize() method as callbacks.

MinMOOptimize() method accepts a user-defined vector function F[]  and its
Jacobian J[], where:
* first M components of F[]  and  first  M rows  of  J[]   correspond   to
  multiple objectives
* subsequent NNLC components of F[] (and rows of J[]) correspond  to  two-
  sided nonlinear constraints NL<=C(x)<=NU, where
  * NL[i]=NU[i] => I-th row is an equality constraint Ci(x)=NL
  * NL[i]<NU[i] => I-th tow is a  two-sided constraint NL[i]<=Ci(x)<=NU[i]
  * NL[i]=-INF  => I-th row is an one-sided constraint Ci(x)<=NU[i]
  * NU[i]=+INF  => I-th row is an one-sided constraint NL[i]<=Ci(x)
  * NL[i]=-INF, NU[i]=+INF => constraint is ignored

NOTE: you may combine nonlinear constraints with linear/boundary ones.  If
      your problem has mixed constraints, you  may explicitly specify some
      of them as linear or box ones.
      It helps optimizer to handle them more efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinMOCreate call.
    NL      -   array[NNLC], lower bounds, can contain -INF
    NU      -   array[NNLC], lower bounds, can contain +INF
    NNLC    -   constraints count, NNLC>=0

NOTE 1: nonlinear constraints are satisfied only  approximately!   It   is
        possible that the algorithm will evaluate the function  outside of
        the feasible area!
        
NOTE 2: algorithm scales variables  according  to the scale  specified by
        MinMOSetScale()  function,  so  it can handle problems with badly
        scaled variables (as long as we KNOW their scales).
           
        However,  there  is  no  way  to  automatically  scale   nonlinear
        constraints. Inappropriate scaling  of nonlinear  constraints  may
        ruin convergence. Solving problem with  constraint  "1000*G0(x)=0"
        is NOT the same as solving it with constraint "0.001*G0(x)=0".
           
        It means that YOU are  the  one who is responsible for the correct
        scaling of the nonlinear constraints Gi(x) and Hi(x). We recommend
        you to scale nonlinear constraints in such a way that the Jacobian
        rows have approximately unit magnitude  (for  problems  with  unit
        scale) or have magnitude approximately equal to 1/S[i] (where S is
        a scale set by MinMOSetScale() function).

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetnlc2(minmostate* state,
     /* Real    */ const ae_vector* nl,
     /* Real    */ const ae_vector* nu,
     ae_int_t nnlc,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(nnlc>=0, "MinMOSetNLC2: NNLC<0", _state);
    ae_assert(nl->cnt>=nnlc, "MinMOSetNLC2: Length(NL)<NNLC", _state);
    ae_assert(nu->cnt>=nnlc, "MinMOSetNLC2: Length(NU)<NNLC", _state);
    state->nnlc = nnlc;
    ae_vector_set_length(&state->fi, state->m+nnlc, _state);
    ae_matrix_set_length(&state->j, state->m+nnlc, state->n, _state);
    rallocv(nnlc, &state->nl, _state);
    rallocv(nnlc, &state->nu, _state);
    for(i=0; i<=nnlc-1; i++)
    {
        ae_assert(ae_isfinite(nl->ptr.p_double[i], _state)||ae_isneginf(nl->ptr.p_double[i], _state), "MinMOSetNLC2: NL[i] is +INF or NAN", _state);
        ae_assert(ae_isfinite(nu->ptr.p_double[i], _state)||ae_isposinf(nu->ptr.p_double[i], _state), "MinMOSetNLC2: NU[i] is -INF or NAN", _state);
        state->nl.ptr.p_double[i] = nl->ptr.p_double[i];
        state->nu.ptr.p_double[i] = nu->ptr.p_double[i];
    }
}


/*************************************************************************
This function sets stopping conditions for inner iterations of the optimizer.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsX    -   >=0
                The subroutine finishes its work if  on  k+1-th  iteration
                the condition |v|<=EpsX is fulfilled, where:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - step vector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinMOSetScale()
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsX=0 and MaxIts=0 (simultaneously) will lead to an automatic
selection of the stopping condition.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetcond(minmostate* state,
     double epsx,
     ae_int_t maxits,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsx, _state), "MinMOSetCond: EpsX is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "MinMOSetCond: negative EpsX", _state);
    ae_assert(maxits>=0, "MinMOSetCond: negative MaxIts!", _state);
    if( ae_fp_eq(epsx,(double)(0))&&maxits==0 )
    {
        epsx = 1.0E-6;
    }
    state->epsx = epsx;
    state->maxits = maxits;
}


/*************************************************************************
This function sets scaling coefficients for the MO optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Scaling is also used by finite difference variant of the optimizer  - step
along I-th axis is equal to DiffStep*S[I].

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetscale(minmostate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(s->cnt>=state->n, "MinMOSetScale: Length(S)<N", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "MinMOSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "MinMOSetScale: S contains zero elements", _state);
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function turns on/off reporting of the Pareto front points.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback  function  (if  it
was provided to MinMOOptimize) every time we find a Pareto front point.

NOTE: according to the communication protocol used by ALGLIB,  the  solver
      passes two parameters to the rep() callback - a current point and  a
      target value at the current point.
      However, because  we solve a  multi-objective  problem,  the  target
      parameter is not used and set to zero.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmosetxrep(minmostate* state, ae_bool needxrep, ae_state *_state)
{


    state->xrep = needxrep;
}


/*************************************************************************

NOTES:

1. This function has two different implementations: one which  uses  exact
   (analytical) user-supplied Jacobian, and one which uses  only  function
   vector and numerically  differentiates  function  in  order  to  obtain
   gradient.

   Depending  on  the  specific  function  used to create optimizer object
   you should choose appropriate variant of MinMOOptimize() -  one   which
   needs function vector AND Jacobian or one which needs ONLY function.

   Be careful to choose variant of MinMOOptimize()  which  corresponds  to
   your optimization scheme! Table below lists different  combinations  of
   callback (function/gradient) passed to MinMOOptimize()   and   specific
   function used to create optimizer.


                     |         USER PASSED TO MinMOOptimize()
   CREATED WITH      |  function only   |  function and gradient
   ------------------------------------------------------------
   MinMOCreateF()    |     works               FAILS
   MinMOCreate()     |     FAILS               works

   Here "FAILS" denotes inappropriate combinations  of  optimizer creation
   function  and  MinMOOptimize()  version.   Attemps   to    use     such
   combination will lead to exception. Either  you  did  not pass gradient
   when it WAS needed or you passed gradient when it was NOT needed.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
ae_bool minmoiteration(minmostate* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t nnlc;
    ae_int_t i;
    ae_int_t k;
    double vleft;
    double vright;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        n = state->rstate.ia.ptr.p_int[0];
        m = state->rstate.ia.ptr.p_int[1];
        nnlc = state->rstate.ia.ptr.p_int[2];
        i = state->rstate.ia.ptr.p_int[3];
        k = state->rstate.ia.ptr.p_int[4];
        vleft = state->rstate.ra.ptr.p_double[0];
        vright = state->rstate.ra.ptr.p_double[1];
    }
    else
    {
        n = 359;
        m = -58;
        nnlc = -919;
        i = -909;
        k = 81;
        vleft = 255.0;
        vright = 74.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    if( state->rstate.stage==6 )
    {
        goto lbl_6;
    }
    if( state->rstate.stage==7 )
    {
        goto lbl_7;
    }
    if( state->rstate.stage==8 )
    {
        goto lbl_8;
    }
    
    /*
     * Routine body
     */
    n = state->n;
    m = state->m;
    nnlc = state->nnlc;
    
    /*
     * Initialize
     */
    state->repterminationtype = 0;
    state->repinneriterationscount = 0;
    state->repouteriterationscount = 0;
    state->repnfev = 0;
    state->repbcerr = (double)(0);
    state->repbcidx = -1;
    state->replcerr = (double)(0);
    state->replcidx = -1;
    state->repnlcerr = (double)(0);
    state->repnlcidx = -1;
    state->repfrontsize = 0;
    minmo_clearrequestfields(state, _state);
    
    /*
     * Check correctness of constraint bounds
     */
    for(i=0; i<=n-1; i++)
    {
        if( (ae_isfinite(state->bndl.ptr.p_double[i], _state)&&ae_isfinite(state->bndu.ptr.p_double[i], _state))&&ae_fp_greater(state->bndl.ptr.p_double[i]-state->bndu.ptr.p_double[i],state->repbcerr) )
        {
            state->repterminationtype = -3;
            state->repbcerr = state->bndl.ptr.p_double[i]-state->bndu.ptr.p_double[i];
            state->repbcidx = i;
        }
    }
    for(i=0; i<=state->mdense+state->msparse-1; i++)
    {
        if( (ae_isfinite(state->cl.ptr.p_double[i], _state)&&ae_isfinite(state->cu.ptr.p_double[i], _state))&&ae_fp_greater(state->cl.ptr.p_double[i]-state->cu.ptr.p_double[i],state->replcerr) )
        {
            state->repterminationtype = -3;
            state->replcerr = state->cl.ptr.p_double[i]-state->cu.ptr.p_double[i];
            state->replcidx = i;
        }
    }
    for(i=0; i<=state->nnlc-1; i++)
    {
        if( (ae_isfinite(state->nl.ptr.p_double[i], _state)&&ae_isfinite(state->nu.ptr.p_double[i], _state))&&ae_fp_greater(state->nl.ptr.p_double[i]-state->nu.ptr.p_double[i],state->repnlcerr) )
        {
            state->repterminationtype = -3;
            state->repnlcerr = state->nl.ptr.p_double[i]-state->nu.ptr.p_double[i];
            state->repnlcidx = i;
        }
    }
    if( state->repterminationtype<0 )
    {
        result = ae_false;
        return result;
    }
    
    /*
     * NBI solver
     */
    if( !(state->solvertype==0||state->solvertype==1) )
    {
        goto lbl_9;
    }
    
    /*
     * Prepare NBI solver
     */
    nbiscaleandinitbuf(&state->xstart, &state->s, n, m, state->frontsize, &state->bndl, &state->bndu, &state->sparsec, &state->densec, &state->cl, &state->cu, state->msparse, state->mdense, &state->nl, &state->nu, state->nnlc, state->epsx, state->maxits, state->solvertype==1, &state->nbi, _state);
    
    /*
     * Perform iterations
     */
lbl_11:
    if( !nbiiteration(&state->nbi, _state) )
    {
        goto lbl_12;
    }
    
    /*
     * Forward request to caller
     */
    if( !state->nbi.needfij )
    {
        goto lbl_13;
    }
    
    /*
     * Evaluate target function/Jacobian
     */
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_15;
    }
    
    /*
     * Analytic Jacobian is provided.
     *
     * Unscale point and forward request. We use scaled NBI.BndL/NBI.U to make sure
     * that points exactly at scaled bounds are mapped to points exactly at raw bounds.
     */
    minmo_unscale(state, &state->nbi.x, &state->nbi.bndl, &state->nbi.bndu, &state->x, _state);
    state->needfij = ae_true;
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    state->needfij = ae_false;
    for(i=0; i<=m+nnlc-1; i++)
    {
        state->nbi.fi.ptr.p_double[i] = state->fi.ptr.p_double[i];
        rcopyrr(n, &state->j, i, &state->nbi.j, i, _state);
        rmergemulvr(n, &state->s, &state->nbi.j, i, _state);
    }
    goto lbl_16;
lbl_15:
    
    /*
     * Numerical differentiation
     */
    rallocv(n, &state->xbase, _state);
    rallocv(m+nnlc, &state->fm1, _state);
    rallocv(m+nnlc, &state->fp1, _state);
    rallocv(m+nnlc, &state->fm2, _state);
    rallocv(m+nnlc, &state->fp2, _state);
    minmo_unscale(state, &state->nbi.x, &state->nbi.bndl, &state->nbi.bndu, &state->xbase, _state);
    k = 0;
lbl_17:
    if( k>n-1 )
    {
        goto lbl_19;
    }
    vleft = state->xbase.ptr.p_double[k]-state->s.ptr.p_double[k]*state->diffstep;
    vright = state->xbase.ptr.p_double[k]+state->s.ptr.p_double[k]*state->diffstep;
    if( !((state->hasbndl.ptr.p_bool[k]&&ae_fp_less(vleft,state->bndl.ptr.p_double[k]))||(state->hasbndu.ptr.p_bool[k]&&ae_fp_greater(vright,state->bndu.ptr.p_double[k]))) )
    {
        goto lbl_20;
    }
    
    /*
     * Box constraint is violated by the 4-point centered formula, use 2-point uncentered one
     */
    if( state->hasbndl.ptr.p_bool[k]&&ae_fp_less(vleft,state->bndl.ptr.p_double[k]) )
    {
        vleft = state->bndl.ptr.p_double[k];
    }
    if( state->hasbndu.ptr.p_bool[k]&&ae_fp_greater(vright,state->bndu.ptr.p_double[k]) )
    {
        vright = state->bndu.ptr.p_double[k];
    }
    ae_assert(ae_fp_less_eq(vleft,vright), "MinMO: integrity check 3445 failed", _state);
    if( ae_fp_eq(vleft,vright) )
    {
        
        /*
         * Fixed variable
         */
        rsetc(m+nnlc, 0.0, &state->j, k, _state);
        goto lbl_18;
    }
    
    /*
     * Compute target at VLeft and VRight
     */
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = vleft;
    state->needfi = ae_true;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->needfi = ae_false;
    rcopyv(m+nnlc, &state->fi, &state->fm1, _state);
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = vright;
    state->needfi = ae_true;
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->needfi = ae_false;
    rcopyv(m+nnlc, &state->fi, &state->fp1, _state);
    
    /*
     * Compute derivative
     */
    raddv(m+nnlc, -1.0, &state->fm1, &state->fp1, _state);
    rmulv(m+nnlc, (double)1/(vright-vleft), &state->fp1, _state);
    rcopyvc(m+nnlc, &state->fp1, &state->j, k, _state);
    goto lbl_21;
lbl_20:
    
    /*
     * 4-point centered formula does not violate box constraints.
     * Compute target values at grid points.
     */
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = state->x.ptr.p_double[k]-state->s.ptr.p_double[k]*state->diffstep;
    state->needfi = ae_true;
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    state->needfi = ae_false;
    rcopyv(m+nnlc, &state->fi, &state->fm2, _state);
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = state->x.ptr.p_double[k]-0.5*state->s.ptr.p_double[k]*state->diffstep;
    state->needfi = ae_true;
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    state->needfi = ae_false;
    rcopyv(m+nnlc, &state->fi, &state->fm1, _state);
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = state->x.ptr.p_double[k]+0.5*state->s.ptr.p_double[k]*state->diffstep;
    state->needfi = ae_true;
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    state->needfi = ae_false;
    rcopyv(m+nnlc, &state->fi, &state->fp1, _state);
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = state->x.ptr.p_double[k]+state->s.ptr.p_double[k]*state->diffstep;
    state->needfi = ae_true;
    state->rstate.stage = 6;
    goto lbl_rcomm;
lbl_6:
    state->needfi = ae_false;
    rcopyv(m+nnlc, &state->fi, &state->fp2, _state);
    
    /*
     * Compute derivative
     */
    raddv(m+nnlc, -1.0, &state->fm1, &state->fp1, _state);
    raddv(m+nnlc, -1.0, &state->fm2, &state->fp2, _state);
    rmulv(m+nnlc, 8.0, &state->fp1, _state);
    raddv(m+nnlc, -1.0, &state->fp2, &state->fp1, _state);
    rmulv(m+nnlc, (double)1/((double)6*state->diffstep*state->s.ptr.p_double[k]), &state->fp1, _state);
    rcopyvc(m+nnlc, &state->fp1, &state->j, k, _state);
lbl_21:
lbl_18:
    k = k+1;
    goto lbl_17;
lbl_19:
    rcopyv(n, &state->xbase, &state->x, _state);
    state->needfi = ae_true;
    state->rstate.stage = 7;
    goto lbl_rcomm;
lbl_7:
    state->needfi = ae_false;
    rcopyv(m+nnlc, &state->fi, &state->nbi.fi, _state);
    rcopym(m+nnlc, n, &state->j, &state->nbi.j, _state);
lbl_16:
    inc(&state->repnfev, _state);
    goto lbl_11;
lbl_13:
    if( !state->nbi.xupdated )
    {
        goto lbl_22;
    }
    
    /*
     * Report current point
     */
    if( !state->xrep )
    {
        goto lbl_24;
    }
    minmo_unscale(state, &state->nbi.x, &state->nbi.bndl, &state->nbi.bndu, &state->x, _state);
    state->f = 0.0;
    state->xupdated = ae_true;
    state->rstate.stage = 8;
    goto lbl_rcomm;
lbl_8:
    state->xupdated = ae_false;
lbl_24:
    goto lbl_11;
lbl_22:
    ae_assert(ae_false, "MINMO: unexpected callback request", _state);
    goto lbl_11;
lbl_12:
    
    /*
     * Done
     */
    state->repfrontsize = state->nbi.repfrontsize;
    rcopyallocm(state->repfrontsize, n+m, &state->nbi.repparetofront, &state->repparetofront, _state);
    state->repinneriterationscount = state->nbi.repinneriterationscount;
    state->repouteriterationscount = state->nbi.repouteriterationscount;
    state->repterminationtype = state->nbi.repterminationtype;
    state->repbcerr = state->nbi.repbcerr;
    state->repbcidx = state->nbi.repbcidx;
    state->replcerr = state->nbi.replcerr;
    state->replcidx = state->nbi.replcidx;
    state->repnlcerr = state->nbi.repnlcerr;
    state->repnlcidx = state->nbi.repnlcidx;
    result = ae_false;
    return result;
lbl_9:
    
    /*
     * Unexpected solver
     */
    ae_assert(ae_false, "MINBO: unexpected solver type", _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = n;
    state->rstate.ia.ptr.p_int[1] = m;
    state->rstate.ia.ptr.p_int[2] = nnlc;
    state->rstate.ia.ptr.p_int[3] = i;
    state->rstate.ia.ptr.p_int[4] = k;
    state->rstate.ra.ptr.p_double[0] = vleft;
    state->rstate.ra.ptr.p_double[1] = vright;
    return result;
}


/*************************************************************************
MinMO results:  the  solution  found,  completion  codes  and   additional
information.

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    ParetoFront-array[FrontSize,N+M], approximate Pareto front.
                Its columns have the following structure:
                * first N columns are variable values
                * next  M columns are objectives at these points
                Its rows have the following structure:
                * first M rows contain solutions to single-objective tasks
                  with I-th row storing result for  I-th  objective  being
                  minimized ignoring other ones.
                  Thus, ParetoFront[I,N+I] for  0<=I<M  stores  so  called
                  'ideal objective vector'.
                * subsequent FrontSize-M rows  store  variables/objectives
                  at  various  randomly  and  nearly   uniformly   sampled
                  locations of the Pareto front.
                  
    FrontSize-  front size, >=0.
                * no larger than the number passed to setalgo()
                * for  a  single-objective  task,  FrontSize=1  is  ALWAYS
                  returned, no matter what was specified during setalgo()
                  call.
                * if  the   solver   was   prematurely   terminated   with
                  minnorequesttermination(), an  incomplete  Pareto  front
                  will be returned (it may even have less than M rows)
                * if a  failure (negative completion code) was   signaled,
                  FrontSize=0 will be returned
                  
    Rep     -   optimization report, contains information about completion
                code, constraint violation at the solution and so on.
                
                You   should   check   rep.terminationtype  in  order   to
                distinguish successful termination from unsuccessful one:
                
                === FAILURE CODES ===
                * -8    internal  integrity control  detected  infinite or
                        NAN   values    in   function/gradient.   Abnormal
                        termination signalled.
                * -3    constraint bounds are  infeasible,  i.e.  we  have
                        box/linear/nonlinear constraint  with  two  bounds
                        present, and a lower one being  greater  than  the
                        upper one.
                        Note: less obvious infeasibilities of  constraints
                              do NOT  trigger  emergency  completion;  you
                              have to examine rep.bcerr/rep.lcerr/rep.nlcerr
                              to detect possibly inconsistent constraints.
                              
                === SUCCESS CODES ===
                *  2   scaled step is no more than EpsX.
                *  5   MaxIts steps were taken.
                *  8   user   requested    algorithm    termination    via
                       minmorequesttermination(), last accepted point   is
                       returned.
                
                More information about fields of this  structure  can  be
                found in the comments on minmoreport datatype.
   
  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmoresults(const minmostate* state,
     /* Real    */ ae_matrix* paretofront,
     ae_int_t* frontsize,
     minmoreport* rep,
     ae_state *_state)
{
    ae_int_t i;

    ae_matrix_clear(paretofront);
    *frontsize = 0;
    _minmoreport_clear(rep);

    rep->inneriterationscount = state->repinneriterationscount;
    rep->outeriterationscount = state->repouteriterationscount;
    rep->nfev = state->repnfev;
    rep->terminationtype = state->repterminationtype;
    rep->bcerr = state->repbcerr;
    rep->bcidx = state->repbcidx;
    rep->lcerr = state->replcerr;
    rep->lcidx = state->replcidx;
    rep->nlcerr = state->repnlcerr;
    rep->nlcidx = state->repnlcidx;
    if( rep->terminationtype>0 )
    {
        *frontsize = state->repfrontsize;
        ae_matrix_set_length(paretofront, *frontsize, state->n+state->m, _state);
        rcopym(*frontsize, state->n+state->m, &state->repparetofront, paretofront, _state);
        for(i=0; i<=*frontsize-1; i++)
        {
            rmergemulvr(state->n, &state->s, paretofront, i, _state);
        }
    }
    else
    {
        *frontsize = 0;
        ae_matrix_set_length(paretofront, 0, 0, _state);
    }
}


/*************************************************************************
This subroutine  submits  request  for  the  termination  of  the  running
optimizer.

It should be called from the user-supplied callback when user decides that
it is time to "smoothly" terminate optimization process, or from some other
thread. As a result, optimizer stops  at  the  state  which  was  "current
accepted" when termination request was submitted and returns error code  8
(successful termination).

Usually it results in an incomplete Pareto front being returned.

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.
      
NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmorequesttermination(minmostate* state, ae_state *_state)
{


    state->nbi.userrequestedtermination = ae_true;
}


/*************************************************************************
This subroutine restarts algorithm from the new point.
All optimization parameters (including constraints) are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have  same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinMOCreate call.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 01.03.2023 by Bochkanov Sergey
*************************************************************************/
void minmorestartfrom(minmostate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    
    /*
     * First, check for errors in the inputs
     */
    ae_assert(x->cnt>=n, "MinMORestartFrom: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinMORestartFrom: X contains infinite or NaN values!", _state);
    
    /*
     * Set XC
     */
    ae_v_move(&state->xstart.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
    
    /*
     * prepare RComm facilities
     */
    ae_vector_set_length(&state->rstate.ia, 4+1, _state);
    ae_vector_set_length(&state->rstate.ra, 1+1, _state);
    state->rstate.stage = -1;
    minmo_clearrequestfields(state, _state);
}


/*************************************************************************
Set V1 reverse communication protocol
*************************************************************************/
void minmosetprotocolv1(minmostate* state, ae_state *_state)
{


    state->protocolversion = 1;
    ae_vector_set_length(&state->rstate.ia, 4+1, _state);
    ae_vector_set_length(&state->rstate.ra, 1+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Clears request fileds (to be sure that we don't forget to clear something)
*************************************************************************/
static void minmo_clearrequestfields(minmostate* state, ae_state *_state)
{


    state->needfi = ae_false;
    state->needfij = ae_false;
    state->xupdated = ae_false;
}


/*************************************************************************
Internal initialization subroutine.
Sets default NLC solver with default criteria.
*************************************************************************/
static void minmo_minmoinitinternal(ae_int_t n,
     ae_int_t m,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minmostate* state,
     ae_state *_state)
{


    
    /*
     * Initialize
     */
    state->protocolversion = 1;
    state->n = n;
    state->m = m;
    state->diffstep = diffstep;
    rsetallocv(n, _state->v_neginf, &state->bndl, _state);
    rsetallocv(n, _state->v_posinf, &state->bndu, _state);
    bsetallocv(n, ae_false, &state->hasbndl, _state);
    bsetallocv(n, ae_false, &state->hasbndu, _state);
    rsetallocv(n, 1.0, &state->s, _state);
    rcopyallocv(n, x, &state->xstart, _state);
    minmosetlc2dense(state, &state->dummyr2, &state->dummyr1, &state->dummyr1, 0, _state);
    minmosetnlc2(state, &state->dummyr1, &state->dummyr1, 0, _state);
    minmosetcond(state, 0.0, 0, _state);
    minmosetxrep(state, ae_false, _state);
    minmosetalgonbi(state, 10, ae_true, _state);
    minmorestartfrom(state, x, _state);
    
    /*
     * Prepare RComm.X
     */
    rallocv(n, &state->x, _state);
}


/*************************************************************************
Unscales X (converts from scaled variables to original ones), paying special
attention to box constraints (output is always feasible; active constraints
are mapped to active ones).
*************************************************************************/
static void minmo_unscale(const minmostate* state,
     /* Real    */ const ae_vector* xs,
     /* Real    */ const ae_vector* scaledbndl,
     /* Real    */ const ae_vector* scaledbndu,
     /* Real    */ ae_vector* xu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    for(i=0; i<=n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i]&&xs->ptr.p_double[i]<=scaledbndl->ptr.p_double[i] )
        {
            xu->ptr.p_double[i] = state->bndl.ptr.p_double[i];
            continue;
        }
        if( state->hasbndu.ptr.p_bool[i]&&xs->ptr.p_double[i]>=scaledbndu->ptr.p_double[i] )
        {
            xu->ptr.p_double[i] = state->bndu.ptr.p_double[i];
            continue;
        }
        xu->ptr.p_double[i] = xs->ptr.p_double[i]*state->s.ptr.p_double[i];
        if( state->hasbndl.ptr.p_bool[i]&&xu->ptr.p_double[i]<state->bndl.ptr.p_double[i] )
        {
            xu->ptr.p_double[i] = state->bndl.ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i]&&xu->ptr.p_double[i]>state->bndu.ptr.p_double[i] )
        {
            xu->ptr.p_double[i] = state->bndu.ptr.p_double[i];
        }
    }
}


void _minmostate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minmostate *p = (minmostate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xstart, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_matrix_init(&p->densec, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->sparsec, _state, make_automatic);
    ae_vector_init(&p->cl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->nu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->j, 0, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    _nbistate_init(&p->nbi, _state, make_automatic);
    ae_matrix_init(&p->repparetofront, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dummyr1, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->dummyr2, 0, 0, DT_REAL, _state, make_automatic);
    _sparsematrix_init(&p->dummysparse, _state, make_automatic);
    ae_vector_init(&p->xbase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fm2, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fm1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fp1, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fp2, 0, DT_REAL, _state, make_automatic);
}


void _minmostate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minmostate       *dst = (minmostate*)_dst;
    const minmostate *src = (const minmostate*)_src;
    dst->n = src->n;
    dst->m = src->m;
    dst->diffstep = src->diffstep;
    dst->solvertype = src->solvertype;
    dst->epsx = src->epsx;
    dst->maxits = src->maxits;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    dst->xrep = src->xrep;
    ae_vector_init_copy(&dst->xstart, &src->xstart, _state, make_automatic);
    dst->frontsize = src->frontsize;
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    dst->msparse = src->msparse;
    dst->mdense = src->mdense;
    ae_matrix_init_copy(&dst->densec, &src->densec, _state, make_automatic);
    _sparsematrix_init_copy(&dst->sparsec, &src->sparsec, _state, make_automatic);
    ae_vector_init_copy(&dst->cl, &src->cl, _state, make_automatic);
    ae_vector_init_copy(&dst->cu, &src->cu, _state, make_automatic);
    dst->nnlc = src->nnlc;
    ae_vector_init_copy(&dst->nl, &src->nl, _state, make_automatic);
    ae_vector_init_copy(&dst->nu, &src->nu, _state, make_automatic);
    dst->protocolversion = src->protocolversion;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    dst->f = src->f;
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    ae_matrix_init_copy(&dst->j, &src->j, _state, make_automatic);
    dst->needfij = src->needfij;
    dst->needfi = src->needfi;
    dst->xupdated = src->xupdated;
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    _nbistate_init_copy(&dst->nbi, &src->nbi, _state, make_automatic);
    dst->repfrontsize = src->repfrontsize;
    ae_matrix_init_copy(&dst->repparetofront, &src->repparetofront, _state, make_automatic);
    dst->repinneriterationscount = src->repinneriterationscount;
    dst->repouteriterationscount = src->repouteriterationscount;
    dst->repnfev = src->repnfev;
    dst->repterminationtype = src->repterminationtype;
    dst->repbcerr = src->repbcerr;
    dst->repbcidx = src->repbcidx;
    dst->replcerr = src->replcerr;
    dst->replcidx = src->replcidx;
    dst->repnlcerr = src->repnlcerr;
    dst->repnlcidx = src->repnlcidx;
    ae_vector_init_copy(&dst->dummyr1, &src->dummyr1, _state, make_automatic);
    ae_matrix_init_copy(&dst->dummyr2, &src->dummyr2, _state, make_automatic);
    _sparsematrix_init_copy(&dst->dummysparse, &src->dummysparse, _state, make_automatic);
    ae_vector_init_copy(&dst->xbase, &src->xbase, _state, make_automatic);
    ae_vector_init_copy(&dst->fm2, &src->fm2, _state, make_automatic);
    ae_vector_init_copy(&dst->fm1, &src->fm1, _state, make_automatic);
    ae_vector_init_copy(&dst->fp1, &src->fp1, _state, make_automatic);
    ae_vector_init_copy(&dst->fp2, &src->fp2, _state, make_automatic);
}


void _minmostate_clear(void* _p)
{
    minmostate *p = (minmostate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->xstart);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_matrix_clear(&p->densec);
    _sparsematrix_clear(&p->sparsec);
    ae_vector_clear(&p->cl);
    ae_vector_clear(&p->cu);
    ae_vector_clear(&p->nl);
    ae_vector_clear(&p->nu);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->fi);
    ae_matrix_clear(&p->j);
    _rcommstate_clear(&p->rstate);
    _nbistate_clear(&p->nbi);
    ae_matrix_clear(&p->repparetofront);
    ae_vector_clear(&p->dummyr1);
    ae_matrix_clear(&p->dummyr2);
    _sparsematrix_clear(&p->dummysparse);
    ae_vector_clear(&p->xbase);
    ae_vector_clear(&p->fm2);
    ae_vector_clear(&p->fm1);
    ae_vector_clear(&p->fp1);
    ae_vector_clear(&p->fp2);
}


void _minmostate_destroy(void* _p)
{
    minmostate *p = (minmostate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->xstart);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_matrix_destroy(&p->densec);
    _sparsematrix_destroy(&p->sparsec);
    ae_vector_destroy(&p->cl);
    ae_vector_destroy(&p->cu);
    ae_vector_destroy(&p->nl);
    ae_vector_destroy(&p->nu);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->fi);
    ae_matrix_destroy(&p->j);
    _rcommstate_destroy(&p->rstate);
    _nbistate_destroy(&p->nbi);
    ae_matrix_destroy(&p->repparetofront);
    ae_vector_destroy(&p->dummyr1);
    ae_matrix_destroy(&p->dummyr2);
    _sparsematrix_destroy(&p->dummysparse);
    ae_vector_destroy(&p->xbase);
    ae_vector_destroy(&p->fm2);
    ae_vector_destroy(&p->fm1);
    ae_vector_destroy(&p->fp1);
    ae_vector_destroy(&p->fp2);
}


void _minmoreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minmoreport *p = (minmoreport*)_p;
    ae_touch_ptr((void*)p);
}


void _minmoreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minmoreport       *dst = (minmoreport*)_dst;
    const minmoreport *src = (const minmoreport*)_src;
    dst->inneriterationscount = src->inneriterationscount;
    dst->outeriterationscount = src->outeriterationscount;
    dst->nfev = src->nfev;
    dst->terminationtype = src->terminationtype;
    dst->bcerr = src->bcerr;
    dst->bcidx = src->bcidx;
    dst->lcerr = src->lcerr;
    dst->lcidx = src->lcidx;
    dst->nlcerr = src->nlcerr;
    dst->nlcidx = src->nlcidx;
}


void _minmoreport_clear(void* _p)
{
    minmoreport *p = (minmoreport*)_p;
    ae_touch_ptr((void*)p);
}


void _minmoreport_destroy(void* _p)
{
    minmoreport *p = (minmoreport*)_p;
    ae_touch_ptr((void*)p);
}


#endif
#if defined(AE_COMPILE_MINNS) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
                  NONSMOOTH NONCONVEX OPTIMIZATION
            SUBJECT TO BOX/LINEAR/NONLINEAR-NONSMOOTH CONSTRAINTS

DESCRIPTION:

The  subroutine  minimizes  function   F(x)  of N arguments subject to any
combination of:
* bound constraints
* linear inequality constraints
* linear equality constraints
* nonlinear equality constraints Gi(x)=0
* nonlinear inequality constraints Hi(x)<=0

IMPORTANT: see MinNSSetAlgoAGS for important  information  on  performance
           restrictions of AGS solver.

REQUIREMENTS:
* starting point X0 must be feasible or not too far away from the feasible
  set
* F(), G(), H() are continuous, locally Lipschitz  and  continuously  (but
  not necessarily twice) differentiable in an open dense  subset  of  R^N.
  Functions F(), G() and H() may be nonsmooth and non-convex.
  Informally speaking, it means  that  functions  are  composed  of  large
  differentiable "patches" with nonsmoothness having  place  only  at  the
  boundaries between these "patches".
  Most real-life nonsmooth  functions  satisfy  these  requirements.  Say,
  anything which involves finite number of abs(), min() and max() is  very
  likely to pass the test.
  Say, it is possible to optimize anything of the following:
  * f=abs(x0)+2*abs(x1)
  * f=max(x0,x1)
  * f=sin(max(x0,x1)+abs(x2))
* for nonlinearly constrained problems: F()  must  be  bounded from  below
  without nonlinear constraints (this requirement is due to the fact that,
  contrary to box and linear constraints, nonlinear ones  require  special
  handling).
* user must provide function value and gradient for F(), H(), G()  at  all
  points where function/gradient can be calculated. If optimizer  requires
  value exactly at the boundary between "patches" (say, at x=0 for f=abs(x)),
  where gradient is not defined, user may resolve tie arbitrarily (in  our
  case - return +1 or -1 at its discretion).
* NS solver supports numerical differentiation, i.e. it may  differentiate
  your function for you,  but  it  results  in  2N  increase  of  function
  evaluations. Not recommended unless you solve really small problems. See
  minnscreatef() for more information on this functionality.

USAGE:

1. User initializes algorithm state with MinNSCreate() call  and   chooses
   what NLC solver to use. There is some solver which is used by  default,
   with default settings, but you should NOT rely on  default  choice.  It
   may change in future releases of ALGLIB without notice, and no one  can
   guarantee that new solver will be  able  to  solve  your  problem  with
   default settings.

   From the other side, if you choose solver explicitly, you can be pretty
   sure that it will work with new ALGLIB releases.

   In the current release following solvers can be used:
   * AGS solver (activated with MinNSSetAlgoAGS() function)

2. User adds boundary and/or linear and/or nonlinear constraints by  means
   of calling one of the following functions:
   a) MinNSSetBC() for boundary constraints
   b) MinNSSetLC() for linear constraints
   c) MinNSSetNLC() for nonlinear constraints
   You may combine (a), (b) and (c) in one optimization problem.

3. User sets scale of the variables with MinNSSetScale() function. It   is
   VERY important to set  scale  of  the  variables,  because  nonlinearly
   constrained problems are hard to solve when variables are badly scaled.

4. User sets stopping conditions with MinNSSetCond().

5. Finally, user calls MinNSOptimize()  function  which  takes   algorithm
   state and pointer (delegate, etc) to callback function which calculates
   F/G/H.

7. User calls MinNSResults() to get solution

8. Optionally user may call MinNSRestartFrom() to solve   another  problem
   with same N but another starting point. MinNSRestartFrom()  allows   to
   reuse already initialized structure.


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

NOTE: minnscreatef() function may be used if  you  do  not  have  analytic
      gradient.   This   function  creates  solver  which  uses  numerical
      differentiation with user-specified step.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnscreate(ae_int_t n,
     /* Real    */ const ae_vector* x,
     minnsstate* state,
     ae_state *_state)
{

    _minnsstate_clear(state);

    ae_assert(n>=1, "MinNSCreate: N<1", _state);
    ae_assert(x->cnt>=n, "MinNSCreate: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinNSCreate: X contains infinite or NaN values", _state);
    minns_minnsinitinternal(n, x, 0.0, state, _state);
}


/*************************************************************************
Version of minnscreatef() which uses numerical differentiation. I.e.,  you
do not have to calculate derivatives yourself. However, this version needs
2N times more function evaluations.

2-point differentiation formula is  used,  because  more  precise  4-point
formula is unstable when used on non-smooth functions.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.
    DiffStep-   differentiation  step,  DiffStep>0.   Algorithm   performs
                numerical differentiation  with  step  for  I-th  variable
                being equal to DiffStep*S[I] (here S[] is a  scale vector,
                set by minnssetscale() function).
                Do not use  too  small  steps,  because  it  may  lead  to
                catastrophic cancellation during intermediate calculations.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnscreatef(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minnsstate* state,
     ae_state *_state)
{

    _minnsstate_clear(state);

    ae_assert(n>=1, "MinNSCreateF: N<1", _state);
    ae_assert(x->cnt>=n, "MinNSCreateF: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinNSCreateF: X contains infinite or NaN values", _state);
    ae_assert(ae_isfinite(diffstep, _state), "MinNSCreateF: DiffStep is infinite or NaN!", _state);
    ae_assert(ae_fp_greater(diffstep,(double)(0)), "MinNSCreateF: DiffStep is non-positive!", _state);
    minns_minnsinitinternal(n, x, diffstep, state, _state);
}


/*************************************************************************
This function sets boundary constraints.

Boundary constraints are inactive by default (after initial creation).
They are preserved after algorithm restart with minnsrestartfrom().

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF.

NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

NOTE 2: AGS solver has following useful properties:
* bound constraints are always satisfied exactly
* function is evaluated only INSIDE area specified by  bound  constraints,
  even  when  numerical  differentiation is used (algorithm adjusts  nodes
  according to boundary constraints)

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnssetbc(minnsstate* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->n;
    ae_assert(bndl->cnt>=n, "MinNSSetBC: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=n, "MinNSSetBC: Length(BndU)<N", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "MinNSSetBC: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "MinNSSetBC: BndL contains NAN or -INF", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function sets linear constraints.

Linear constraints are inactive by default (after initial creation).
They are preserved after algorithm restart with minnsrestartfrom().

INPUT PARAMETERS:
    State   -   structure previously allocated with minnscreate() call.
    C       -   linear constraints, array[K,N+1].
                Each row of C represents one constraint, either equality
                or inequality (see below):
                * first N elements correspond to coefficients,
                * last element corresponds to the right part.
                All elements of C (including right part) must be finite.
    CT      -   type of constraints, array[K]:
                * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
                * if CT[i]=0, then I-th constraint is C[i,*]*x  = C[i,n+1]
                * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
    K       -   number of equality/inequality constraints, K>=0:
                * if given, only leading K elements of C/CT are used
                * if not given, automatically determined from sizes of C/CT

NOTE: linear (non-bound) constraints are satisfied only approximately:

* there always exists some minor violation (about current sampling  radius
  in magnitude during optimization, about EpsX in the solution) due to use
  of penalty method to handle constraints.
* numerical differentiation, if used, may  lead  to  function  evaluations
  outside  of the feasible  area,   because   algorithm  does  NOT  change
  numerical differentiation formula according to linear constraints.

If you want constraints to be  satisfied  exactly, try to reformulate your
problem  in  such  manner  that  all constraints will become boundary ones
(this kind of constraints is always satisfied exactly, both in  the  final
solution and in all intermediate points).

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnssetlc(minnsstate* state,
     /* Real    */ const ae_matrix* c,
     /* Integer */ const ae_vector* ct,
     ae_int_t k,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;


    n = state->n;
    
    /*
     * First, check for errors in the inputs
     */
    ae_assert(k>=0, "MinNSSetLC: K<0", _state);
    ae_assert(c->cols>=n+1||k==0, "MinNSSetLC: Cols(C)<N+1", _state);
    ae_assert(c->rows>=k, "MinNSSetLC: Rows(C)<K", _state);
    ae_assert(ct->cnt>=k, "MinNSSetLC: Length(CT)<K", _state);
    ae_assert(apservisfinitematrix(c, k, n+1, _state), "MinNSSetLC: C contains infinite or NaN values!", _state);
    
    /*
     * Handle zero K
     */
    if( k==0 )
    {
        state->nec = 0;
        state->nic = 0;
        return;
    }
    
    /*
     * Equality constraints are stored first, in the upper
     * NEC rows of State.CLEIC matrix. Inequality constraints
     * are stored in the next NIC rows.
     *
     * NOTE: we convert inequality constraints to the form
     * A*x<=b before copying them.
     */
    rmatrixsetlengthatleast(&state->cleic, k, n+1, _state);
    state->nec = 0;
    state->nic = 0;
    for(i=0; i<=k-1; i++)
    {
        if( ct->ptr.p_int[i]==0 )
        {
            ae_v_move(&state->cleic.ptr.pp_double[state->nec][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
            state->nec = state->nec+1;
        }
    }
    for(i=0; i<=k-1; i++)
    {
        if( ct->ptr.p_int[i]!=0 )
        {
            if( ct->ptr.p_int[i]>0 )
            {
                ae_v_moveneg(&state->cleic.ptr.pp_double[state->nec+state->nic][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
            }
            else
            {
                ae_v_move(&state->cleic.ptr.pp_double[state->nec+state->nic][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
            }
            state->nic = state->nic+1;
        }
    }
}


/*************************************************************************
This function sets nonlinear constraints.

In fact, this function sets NUMBER of nonlinear  constraints.  Constraints
itself (constraint functions) are passed to minnsoptimize() method.   This
method requires user-defined vector function F[]  and  its  Jacobian  J[],
where:
* first component of F[] and first row  of  Jacobian  J[]  correspond   to
  function being minimized
* next NLEC components of F[] (and rows  of  J)  correspond  to  nonlinear
  equality constraints G_i(x)=0
* next NLIC components of F[] (and rows  of  J)  correspond  to  nonlinear
  inequality constraints H_i(x)<=0

NOTE: you may combine nonlinear constraints with linear/boundary ones.  If
      your problem has mixed constraints, you  may explicitly specify some
      of them as linear ones. It may help optimizer to  handle  them  more
      efficiently.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnscreate() call.
    NLEC    -   number of Non-Linear Equality Constraints (NLEC), >=0
    NLIC    -   number of Non-Linear Inquality Constraints (NLIC), >=0

NOTE 1: nonlinear constraints are satisfied only  approximately!   It   is
        possible   that  algorithm  will  evaluate  function  outside   of
        the feasible area!

NOTE 2: algorithm scales variables  according  to   scale   specified   by
        minnssetscale()  function,  so  it can handle problems with  badly
        scaled variables (as long as we KNOW their scales).

        However,  there  is  no  way  to  automatically  scale   nonlinear
        constraints Gi(x) and Hi(x). Inappropriate scaling  of  Gi/Hi  may
        ruin convergence. Solving problem with  constraint  "1000*G0(x)=0"
        is NOT same as solving it with constraint "0.001*G0(x)=0".

        It  means  that  YOU  are  the  one who is responsible for correct
        scaling of nonlinear constraints Gi(x) and Hi(x). We recommend you
        to scale nonlinear constraints in such way that I-th component  of
        dG/dX (or dH/dx) has approximately unit  magnitude  (for  problems
        with unit scale)  or  has  magnitude approximately equal to 1/S[i]
        (where S is a scale set by minnssetscale() function).

NOTE 3: nonlinear constraints are always hard to handle,  no  matter  what
        algorithm you try to use. Even basic box/linear constraints modify
        function  curvature   by  adding   valleys  and  ridges.  However,
        nonlinear constraints add valleys which are very  hard  to  follow
        due to their "curved" nature.

        It means that optimization with single nonlinear constraint may be
        significantly slower than optimization with multiple linear  ones.
        It is normal situation, and we recommend you to  carefully  choose
        Rho parameter of minnssetalgoags(), because too  large  value  may
        slow down convergence.


  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnssetnlc(minnsstate* state,
     ae_int_t nlec,
     ae_int_t nlic,
     ae_state *_state)
{


    ae_assert(nlec>=0, "MinNSSetNLC: NLEC<0", _state);
    ae_assert(nlic>=0, "MinNSSetNLC: NLIC<0", _state);
    state->ng = nlec;
    state->nh = nlic;
    ae_vector_set_length(&state->fi, 1+state->ng+state->nh, _state);
    ae_matrix_set_length(&state->j, 1+state->ng+state->nh, state->n, _state);
}


/*************************************************************************
This function sets stopping conditions for iterations of optimizer.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsX    -   >=0
                The AGS solver finishes its work if  on  k+1-th  iteration
                sampling radius decreases below EpsX.
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsX=0  and  MaxIts=0  (simultaneously)  will  lead  to  automatic
stopping criterion selection. We do not recommend you to rely  on  default
choice in production code.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnssetcond(minnsstate* state,
     double epsx,
     ae_int_t maxits,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsx, _state), "MinNSSetCond: EpsX is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "MinNSSetCond: negative EpsX", _state);
    ae_assert(maxits>=0, "MinNSSetCond: negative MaxIts!", _state);
    if( ae_fp_eq(epsx,(double)(0))&&maxits==0 )
    {
        epsx = 1.0E-6;
    }
    state->epsx = epsx;
    state->maxits = maxits;
}


/*************************************************************************
This function sets scaling coefficients for NLC optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Scaling is also used by finite difference variant of the optimizer  - step
along I-th axis is equal to DiffStep*S[I].

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnssetscale(minnsstate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(s->cnt>=state->n, "MinNSSetScale: Length(S)<N", _state);
    for(i=0; i<=state->n-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "MinNSSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "MinNSSetScale: S contains zero elements", _state);
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function tells MinNS unit to use  AGS  (adaptive  gradient  sampling)
algorithm for nonsmooth constrained  optimization.  This  algorithm  is  a
slight modification of one described in  "An  Adaptive  Gradient  Sampling
Algorithm for Nonsmooth Optimization" by Frank E. Curtisy and Xiaocun Quez.

This optimizer has following benefits and drawbacks:
+ robustness; it can be used with nonsmooth and nonconvex functions.
+ relatively easy tuning; most of the metaparameters are easy to select.
- it has convergence of steepest descent, slower than CG/LBFGS.
- each iteration involves evaluation of ~2N gradient values  and  solution
  of 2Nx2N quadratic programming problem, which  limits  applicability  of
  algorithm by small-scale problems (up to 50-100).

IMPORTANT: this  algorithm  has  convergence  guarantees,   i.e.  it  will
           steadily move towards some stationary point of the function.

           However, "stationary point" does not  always  mean  "solution".
           Nonsmooth problems often have "flat spots",  i.e.  areas  where
           function do not change at all. Such "flat spots" are stationary
           points by definition, and algorithm may be caught here.

           Nonsmooth CONVEX tasks are not prone to  this  problem. Say, if
           your function has form f()=MAX(f0,f1,...), and f_i are  convex,
           then f() is convex too and you have guaranteed  convergence  to
           solution.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    Radius  -   initial sampling radius, >=0.

                Internally multiplied  by  vector of  per-variable  scales
                specified by minnssetscale()).

                You should select relatively large sampling radius, roughly
                proportional to scaled length of the first  steps  of  the
                algorithm. Something close to 0.1 in magnitude  should  be
                good for most problems.

                AGS solver can automatically decrease radius, so too large
                radius is  not a problem (assuming that you  won't  choose
                so large radius that algorithm  will  sample  function  in
                too far away points, where gradient value is irrelevant).

                Too small radius won't cause algorithm to fail, but it may
                slow down algorithm (it may  have  to  perform  too  short
                steps).
    Penalty -   penalty coefficient for nonlinear constraints:
                * for problem with nonlinear constraints  should  be  some
                  problem-specific  positive   value,  large  enough  that
                  penalty term changes shape of the function.
                  Starting  from  some  problem-specific   value   penalty
                  coefficient becomes  large  enough  to  exactly  enforce
                  nonlinear constraints;  larger  values  do  not  improve
                  precision.
                  Increasing it too much may slow down convergence, so you
                  should choose it carefully.
                * can be zero for problems WITHOUT  nonlinear  constraints
                  (i.e. for unconstrained ones or ones with  just  box  or
                  linear constraints)
                * if you specify zero value for problem with at least  one
                  nonlinear  constraint,  algorithm  will  terminate  with
                  error code -1.

ALGORITHM OUTLINE

The very basic outline of unconstrained AGS algorithm is given below:

0. If sampling radius is below EpsX  or  we  performed  more  then  MaxIts
   iterations - STOP.
1. sample O(N) gradient values at random locations  around  current point;
   informally speaking, this sample is an implicit piecewise  linear model
   of the function, although algorithm formulation does  not  mention that
   explicitly
2. solve quadratic programming problem in order to find descent direction
3. if QP solver tells us that we  are  near  solution,  decrease  sampling
   radius and move to (0)
4. perform backtracking line search
5. after moving to new point, goto (0)

Constraint handling details:
* box constraints are handled exactly by algorithm
* linear/nonlinear constraints are handled by adding L1  penalty.  Because
  our solver can handle nonsmoothness, we can  use  L1  penalty  function,
  which is an exact one  (i.e.  exact  solution  is  returned  under  such
  penalty).
* penalty coefficient for  linear  constraints  is  chosen  automatically;
  however, penalty coefficient for nonlinear constraints must be specified
  by user.

===== TRACING AGS SOLVER =================================================

AGS solver supports advanced tracing capabilities. You can trace algorithm
output by specifying following trace symbols (case-insensitive)  by  means
of trace_file() call:
* 'AGS'         - for basic trace of algorithm  steps and decisions.  Only
                  short scalars (function values and deltas) are  printed.
                  N-dimensional quantities like search directions are  NOT
                  printed.
* 'AGS.DETAILED'- for output of points being visited and search directions
                  This  symbol  also  implicitly  defines  'AGS'. You  can
                  control output format by additionally specifying:
                  * nothing     to output in  6-digit exponential format
                  * 'PREC.E15'  to output in 15-digit exponential format
                  * 'PREC.F6'   to output in  6-digit fixed-point format
* 'AGS.DETAILED.SAMPLE'-
                  for output of points being visited ,  search  directions
                  and gradient sample. May take a LOT of  space ,  do  not
                  use it on problems with more that several tens of vars.
                  This  symbol   also    implicitly   defines   'AGS'  and
                  'AGS.DETAILED'.

By default trace is disabled and adds  no  overhead  to  the  optimization
process. However, specifying any of the symbols adds some  formatting  and
output-related overhead.

You may specify multiple symbols by separating them with commas:
>
> alglib::trace_file("AGS,PREC.F6", "path/to/trace.log")
> 


  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnssetalgoags(minnsstate* state,
     double radius,
     double penalty,
     ae_state *_state)
{


    ae_assert(ae_isfinite(radius, _state), "MinNSSetAlgoAGS: Radius is not finite", _state);
    ae_assert(ae_fp_greater(radius,(double)(0)), "MinNSSetAlgoAGS: Radius<=0", _state);
    ae_assert(ae_isfinite(penalty, _state), "MinNSSetAlgoAGS: Penalty is not finite", _state);
    ae_assert(ae_fp_greater_eq(penalty,0.0), "MinNSSetAlgoAGS: Penalty<0", _state);
    state->agsrhononlinear = penalty;
    state->agsradius = radius;
    state->solvertype = 0;
}


/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to minnsoptimize().

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minnssetxrep(minnsstate* state, ae_bool needxrep, ae_state *_state)
{


    state->xrep = needxrep;
}


/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.

NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnsrequesttermination(minnsstate* state, ae_state *_state)
{


    state->userterminationneeded = ae_true;
}


/*************************************************************************

NOTES:

1. This function has two different implementations: one which  uses  exact
   (analytical) user-supplied Jacobian, and one which uses  only  function
   vector and numerically  differentiates  function  in  order  to  obtain
   gradient.

   Depending  on  the  specific  function  used to create optimizer object
   you should choose appropriate variant of  minnsoptimize() -  one  which
   accepts function AND Jacobian or one which accepts ONLY function.

   Be careful to choose variant of minnsoptimize()  which  corresponds  to
   your optimization scheme! Table below lists different  combinations  of
   callback (function/gradient) passed to minnsoptimize()    and  specific
   function used to create optimizer.


                     |         USER PASSED TO minnsoptimize()
   CREATED WITH      |  function only   |  function and gradient
   ------------------------------------------------------------
   minnscreatef()    |     works               FAILS
   minnscreate()     |     FAILS               works

   Here "FAILS" denotes inappropriate combinations  of  optimizer creation
   function  and  minnsoptimize()  version.   Attemps   to    use     such
   combination will lead to exception. Either  you  did  not pass gradient
   when it WAS needed or you passed gradient when it was NOT needed.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
ae_bool minnsiteration(minnsstate* state, ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t ng;
    ae_int_t nh;
    double v;
    double xp;
    double xm;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        i = state->rstate.ia.ptr.p_int[0];
        j = state->rstate.ia.ptr.p_int[1];
        k = state->rstate.ia.ptr.p_int[2];
        n = state->rstate.ia.ptr.p_int[3];
        nec = state->rstate.ia.ptr.p_int[4];
        nic = state->rstate.ia.ptr.p_int[5];
        ng = state->rstate.ia.ptr.p_int[6];
        nh = state->rstate.ia.ptr.p_int[7];
        v = state->rstate.ra.ptr.p_double[0];
        xp = state->rstate.ra.ptr.p_double[1];
        xm = state->rstate.ra.ptr.p_double[2];
    }
    else
    {
        i = 359;
        j = -58;
        k = -919;
        n = -909;
        nec = 81;
        nic = 255;
        ng = 74;
        nh = -788;
        v = 809.0;
        xp = 205.0;
        xm = -838.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    
    /*
     * Routine body
     */
    
    /*
     * Init
     */
    state->replcerr = 0.0;
    state->repnlcerr = 0.0;
    state->repterminationtype = 0;
    state->repinneriterationscount = 0;
    state->repouteriterationscount = 0;
    state->repnfev = 0;
    state->repvaridx = 0;
    state->repfuncidx = 0;
    state->userterminationneeded = ae_false;
    state->dbgncholesky = 0;
    n = state->n;
    nec = state->nec;
    nic = state->nic;
    ng = state->ng;
    nh = state->nh;
    minns_clearrequestfields(state, _state);
    
    /*
     * AGS solver
     */
    if( state->solvertype!=0 )
    {
        goto lbl_4;
    }
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        rvectorsetlengthatleast(&state->xbase, n, _state);
        rvectorsetlengthatleast(&state->fbase, 1+ng+nh, _state);
        rvectorsetlengthatleast(&state->fm, 1+ng+nh, _state);
        rvectorsetlengthatleast(&state->fp, 1+ng+nh, _state);
    }
    rvectorsetlengthatleast(&state->xscaled, n, _state);
    rvectorsetlengthatleast(&state->rawg, n, _state);
    rvectorsetlengthatleast(&state->meritg, n, _state);
    ae_vector_set_length(&state->rstateags.ia, 13+1, _state);
    ae_vector_set_length(&state->rstateags.ba, 5+1, _state);
    ae_vector_set_length(&state->rstateags.ra, 10+1, _state);
    state->rstateags.stage = -1;
lbl_6:
    if( !minns_agsiteration(state, _state) )
    {
        goto lbl_7;
    }
    rcopyv(n, &state->x, &state->xscaled, _state);
    minns_unscalepointbc(state, &state->x, _state);
    
    /*
     * Numerical differentiation (if needed) - intercept NeedFiJ
     * request and replace it by sequence of NeedFi requests
     */
    if( !(ae_fp_neq(state->diffstep,(double)(0))&&state->needfij) )
    {
        goto lbl_8;
    }
    state->needfij = ae_false;
    state->needfi = ae_true;
    ae_v_move(&state->xbase.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    ae_v_move(&state->fbase.ptr.p_double[0], 1, &state->fi.ptr.p_double[0], 1, ae_v_len(0,ng+nh));
    state->repnfev = state->repnfev+1;
    k = 0;
lbl_10:
    if( k>n-1 )
    {
        goto lbl_12;
    }
    v = state->xbase.ptr.p_double[k];
    xm = v-state->diffstep*state->s.ptr.p_double[k];
    xp = v+state->diffstep*state->s.ptr.p_double[k];
    if( state->hasbndl.ptr.p_bool[k]&&ae_fp_less(xm,state->bndl.ptr.p_double[k]) )
    {
        xm = state->bndl.ptr.p_double[k];
    }
    if( state->hasbndu.ptr.p_bool[k]&&ae_fp_greater(xp,state->bndu.ptr.p_double[k]) )
    {
        xp = state->bndu.ptr.p_double[k];
    }
    ae_assert(ae_fp_less_eq(xm,xp), "MinNS: integrity check failed (3y634)", _state);
    if( ae_fp_eq(xm,xp) )
    {
        goto lbl_13;
    }
    
    /*
     * Compute F(XM) and F(XP)
     */
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = xm;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    rcopyv(1+ng+nh, &state->fi, &state->fm, _state);
    rcopyv(n, &state->xbase, &state->x, _state);
    state->x.ptr.p_double[k] = xp;
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    rcopyv(1+ng+nh, &state->fi, &state->fp, _state);
    
    /*
     * Compute subgradient at XBase
     */
    rcopymulvc(1+ng+nh, (double)1/(xp-xm), &state->fp, &state->j, k, _state);
    raddvc(1+ng+nh, -(double)1/(xp-xm), &state->fm, &state->j, k, _state);
    state->repnfev = state->repnfev+2;
    goto lbl_14;
lbl_13:
    rsetc(1+ng+nh, 0.0, &state->j, k, _state);
lbl_14:
    k = k+1;
    goto lbl_10;
lbl_12:
    
    /*
     * Restore previous values of fields and continue
     */
    rcopyv(n, &state->xscaled, &state->x, _state);
    rcopyv(1+ng+nh, &state->fbase, &state->fi, _state);
    state->needfi = ae_false;
    state->needfij = ae_true;
    goto lbl_9;
lbl_8:
    
    /*
     * Forward request to caller
     */
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    inc(&state->repnfev, _state);
    rcopyv(n, &state->xscaled, &state->x, _state);
lbl_9:
    
    /*
     * Postprocess Jacobian: scale and produce 'raw' and 'merit' functions
     */
    for(i=0; i<=ng+nh; i++)
    {
        rmergemulvr(n, &state->s, &state->j, i, _state);
    }
    state->rawf = state->fi.ptr.p_double[0];
    state->meritf = state->fi.ptr.p_double[0];
    rcopyrv(n, &state->j, 0, &state->rawg, _state);
    rcopyrv(n, &state->j, 0, &state->meritg, _state);
    for(i=0; i<=nec+nic-1; i++)
    {
        v = rdotvr(n, &state->x, &state->scaledcleic, i, _state)-state->scaledcleic.ptr.pp_double[i][n];
        if( i>=nec&&ae_fp_less(v,(double)(0)) )
        {
            continue;
        }
        state->meritf = state->meritf+state->rholinear*ae_fabs(v, _state);
        raddrv(n, state->rholinear*(double)ae_sign(v, _state), &state->scaledcleic, i, &state->meritg, _state);
    }
    for(i=1; i<=ng+nh; i++)
    {
        v = state->fi.ptr.p_double[i];
        if( i<=ng&&ae_fp_eq(v,(double)(0)) )
        {
            continue;
        }
        if( i>ng&&ae_fp_less_eq(v,(double)(0)) )
        {
            continue;
        }
        state->meritf = state->meritf+state->agsrhononlinear*ae_fabs(v, _state);
        raddrv(n, state->agsrhononlinear*(double)ae_sign(v, _state), &state->j, i, &state->meritg, _state);
    }
    
    /*
     * Done
     */
    goto lbl_6;
    goto lbl_6;
lbl_7:
    result = ae_false;
    return result;
lbl_4:
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = i;
    state->rstate.ia.ptr.p_int[1] = j;
    state->rstate.ia.ptr.p_int[2] = k;
    state->rstate.ia.ptr.p_int[3] = n;
    state->rstate.ia.ptr.p_int[4] = nec;
    state->rstate.ia.ptr.p_int[5] = nic;
    state->rstate.ia.ptr.p_int[6] = ng;
    state->rstate.ia.ptr.p_int[7] = nh;
    state->rstate.ra.ptr.p_double[0] = v;
    state->rstate.ra.ptr.p_double[1] = xp;
    state->rstate.ra.ptr.p_double[2] = xm;
    return result;
}


/*************************************************************************
MinNS results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report. You should check Rep.TerminationType
                in  order  to  distinguish  successful  termination  from
                unsuccessful one:
                * -8   internal integrity control  detected  infinite  or
                       NAN   values   in   function/gradient.    Abnormal
                       termination signalled.
                * -3   box constraints are inconsistent
                * -1   inconsistent parameters were passed:
                       * penalty parameter for minnssetalgoags() is zero,
                         but we have nonlinear constraints set by minnssetnlc()
                *  2   sampling radius decreased below epsx
                *  7    stopping conditions are too stringent,
                        further improvement is impossible,
                        X contains best point found so far.
                *  8    User requested termination via minnsrequesttermination()

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnsresults(const minnsstate* state,
     /* Real    */ ae_vector* x,
     minnsreport* rep,
     ae_state *_state)
{

    ae_vector_clear(x);
    _minnsreport_clear(rep);

    minnsresultsbuf(state, x, rep, _state);
}


/*************************************************************************

Buffered implementation of minnsresults() which uses pre-allocated  buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnsresultsbuf(const minnsstate* state,
     /* Real    */ ae_vector* x,
     minnsreport* rep,
     ae_state *_state)
{
    ae_int_t i;


    if( x->cnt<state->n )
    {
        ae_vector_set_length(x, state->n, _state);
    }
    rep->iterationscount = state->repinneriterationscount;
    rep->nfev = state->repnfev;
    rep->varidx = state->repvaridx;
    rep->funcidx = state->repfuncidx;
    rep->terminationtype = state->repterminationtype;
    rep->cerr = ae_maxreal(state->replcerr, state->repnlcerr, _state);
    rep->lcerr = state->replcerr;
    rep->nlcerr = state->repnlcerr;
    if( state->repterminationtype>0 )
    {
        ae_v_move(&x->ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,state->n-1));
    }
    else
    {
        for(i=0; i<=state->n-1; i++)
        {
            x->ptr.p_double[i] = _state->v_nan;
        }
    }
}


/*************************************************************************
This subroutine restarts algorithm from new point.
All optimization parameters (including constraints) are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have  same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure previously allocated with minnscreate() call.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 18.05.2015 by Bochkanov Sergey
*************************************************************************/
void minnsrestartfrom(minnsstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;


    n = state->n;
    
    /*
     * First, check for errors in the inputs
     */
    ae_assert(x->cnt>=n, "MinNSRestartFrom: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinNSRestartFrom: X contains infinite or NaN values!", _state);
    
    /*
     * Set XC
     */
    ae_v_move(&state->xstart.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
    
    /*
     * prepare RComm facilities
     */
    ae_vector_set_length(&state->rstate.ia, 7+1, _state);
    ae_vector_set_length(&state->rstate.ra, 2+1, _state);
    state->rstate.stage = -1;
    minns_clearrequestfields(state, _state);
}


/*************************************************************************
Set V1 reverse communication protocol
*************************************************************************/
void minnssetprotocolv1(minnsstate* state, ae_state *_state)
{


    state->protocolversion = 1;
    ae_vector_set_length(&state->rstate.ia, 7+1, _state);
    ae_vector_set_length(&state->rstate.ra, 2+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Clears request fileds (to be sure that we don't forget to clear something)
*************************************************************************/
static void minns_clearrequestfields(minnsstate* state, ae_state *_state)
{


    state->needfi = ae_false;
    state->needfij = ae_false;
    state->xupdated = ae_false;
}


/*************************************************************************
Internal initialization subroutine.
Sets default NLC solver with default criteria.
*************************************************************************/
static void minns_minnsinitinternal(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minnsstate* state,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_matrix c;
    ae_vector ct;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);

    state->protocolversion = 1;
    state->agsinitstp = 0.2;
    state->agsstattold = ae_sqrt(ae_machineepsilon, _state);
    state->agsshortstpabs = 1.0E-10;
    state->agsshortstprel = 0.75;
    state->agsshortf = (double)10*ae_machineepsilon;
    state->agsrhononlinear = 0.0;
    state->agsraddecay = 0.2;
    state->agsalphadecay = 0.5;
    state->agsdecrease = 0.1;
    state->agsmaxraddecays = 50;
    state->agsmaxbacktrack = 20;
    state->agsmaxbacktracknonfull = 8;
    state->agspenaltylevel = 50.0;
    state->agspenaltyincrease = 100.0;
    state->agsminupdate = ae_maxint(5, n/2, _state);
    state->agssamplesize = ae_maxint(2*n+1, state->agsminupdate+1, _state);
    state->agsshortlimit = 4+state->agssamplesize/state->agsminupdate;
    
    /*
     * Initialize other params
     */
    state->n = n;
    state->diffstep = diffstep;
    ae_vector_set_length(&state->bndl, n, _state);
    ae_vector_set_length(&state->hasbndl, n, _state);
    ae_vector_set_length(&state->bndu, n, _state);
    ae_vector_set_length(&state->hasbndu, n, _state);
    ae_vector_set_length(&state->s, n, _state);
    ae_vector_set_length(&state->xstart, n, _state);
    ae_vector_set_length(&state->xc, n, _state);
    ae_vector_set_length(&state->xn, n, _state);
    ae_vector_set_length(&state->d, n, _state);
    ae_vector_set_length(&state->x, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->bndl.ptr.p_double[i] = _state->v_neginf;
        state->hasbndl.ptr.p_bool[i] = ae_false;
        state->bndu.ptr.p_double[i] = _state->v_posinf;
        state->hasbndu.ptr.p_bool[i] = ae_false;
        state->s.ptr.p_double[i] = 1.0;
        state->xstart.ptr.p_double[i] = x->ptr.p_double[i];
        state->xc.ptr.p_double[i] = x->ptr.p_double[i];
    }
    minnssetlc(state, &c, &ct, 0, _state);
    minnssetnlc(state, 0, 0, _state);
    minnssetcond(state, 0.0, 0, _state);
    minnssetxrep(state, ae_false, _state);
    minnssetalgoags(state, 0.1, 1000.0, _state);
    minnsrestartfrom(state, x, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
This function performs actual processing for AUL algorith. It expects that
caller redirects its reverse communication  requests  NeedFiJ/XUpdated  to
external user who will provide analytic derivative (or handle reports about
progress).

In case external user does not have analytic derivative, it is responsibility
of caller to intercept NeedFiJ request and  replace  it  with  appropriate
numerical differentiation scheme.

  -- ALGLIB --
     Copyright 06.06.2015 by Bochkanov Sergey
*************************************************************************/
static ae_bool minns_agsiteration(minnsstate* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t ng;
    ae_int_t nh;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double radius0;
    double radius;
    ae_int_t radiusdecays;
    double alpha;
    double recommendedstep;
    double dhd;
    double dnrminf;
    double v;
    double vv;
    ae_int_t maxsamplesize;
    ae_int_t cursamplesize;
    double v0;
    double v1;
    ae_bool b;
    ae_bool alphadecreased;
    ae_int_t shortstepscnt;
    ae_int_t backtrackits;
    ae_int_t maxbacktrackits;
    ae_bool fullsample;
    double currentf0;
    ae_bool dotrace;
    ae_bool dodetailedtrace;
    ae_bool dotracesample;
    ae_bool result;


    
    /*
     * 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( state->rstateags.stage>=0 )
    {
        n = state->rstateags.ia.ptr.p_int[0];
        nec = state->rstateags.ia.ptr.p_int[1];
        nic = state->rstateags.ia.ptr.p_int[2];
        ng = state->rstateags.ia.ptr.p_int[3];
        nh = state->rstateags.ia.ptr.p_int[4];
        i = state->rstateags.ia.ptr.p_int[5];
        j = state->rstateags.ia.ptr.p_int[6];
        k = state->rstateags.ia.ptr.p_int[7];
        radiusdecays = state->rstateags.ia.ptr.p_int[8];
        maxsamplesize = state->rstateags.ia.ptr.p_int[9];
        cursamplesize = state->rstateags.ia.ptr.p_int[10];
        shortstepscnt = state->rstateags.ia.ptr.p_int[11];
        backtrackits = state->rstateags.ia.ptr.p_int[12];
        maxbacktrackits = state->rstateags.ia.ptr.p_int[13];
        b = state->rstateags.ba.ptr.p_bool[0];
        alphadecreased = state->rstateags.ba.ptr.p_bool[1];
        fullsample = state->rstateags.ba.ptr.p_bool[2];
        dotrace = state->rstateags.ba.ptr.p_bool[3];
        dodetailedtrace = state->rstateags.ba.ptr.p_bool[4];
        dotracesample = state->rstateags.ba.ptr.p_bool[5];
        radius0 = state->rstateags.ra.ptr.p_double[0];
        radius = state->rstateags.ra.ptr.p_double[1];
        alpha = state->rstateags.ra.ptr.p_double[2];
        recommendedstep = state->rstateags.ra.ptr.p_double[3];
        dhd = state->rstateags.ra.ptr.p_double[4];
        dnrminf = state->rstateags.ra.ptr.p_double[5];
        v = state->rstateags.ra.ptr.p_double[6];
        vv = state->rstateags.ra.ptr.p_double[7];
        v0 = state->rstateags.ra.ptr.p_double[8];
        v1 = state->rstateags.ra.ptr.p_double[9];
        currentf0 = state->rstateags.ra.ptr.p_double[10];
    }
    else
    {
        n = 939;
        nec = -526;
        nic = 763;
        ng = -541;
        nh = -698;
        i = -900;
        j = -318;
        k = -940;
        radiusdecays = 1016;
        maxsamplesize = -229;
        cursamplesize = -536;
        shortstepscnt = 487;
        backtrackits = -115;
        maxbacktrackits = 886;
        b = ae_false;
        alphadecreased = ae_false;
        fullsample = ae_true;
        dotrace = ae_true;
        dodetailedtrace = ae_true;
        dotracesample = ae_true;
        radius0 = 922.0;
        radius = -154.0;
        alpha = 306.0;
        recommendedstep = -1011.0;
        dhd = 951.0;
        dnrminf = -463.0;
        v = 88.0;
        vv = -861.0;
        v0 = -678.0;
        v1 = -731.0;
        currentf0 = -675.0;
    }
    if( state->rstateags.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstateags.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstateags.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstateags.stage==3 )
    {
        goto lbl_3;
    }
    
    /*
     * Routine body
     */
    ae_assert(state->solvertype==0, "MinNS: internal error", _state);
    n = state->n;
    nec = state->nec;
    nic = state->nic;
    ng = state->ng;
    nh = state->nh;
    dotrace = ae_is_trace_enabled("AGS");
    dodetailedtrace = dotrace&&ae_is_trace_enabled("AGS.DETAILED");
    dotracesample = dodetailedtrace&&ae_is_trace_enabled("AGS.DETAILED.SAMPLE");
    
    /*
     * Trace output (if needed)
     */
    if( dotrace )
    {
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
        ae_trace("//  AGS SOLVER STARTED                                                                            //\n");
        ae_trace("////////////////////////////////////////////////////////////////////////////////////////////////////\n");
    }
    
    /*
     * Check consistency of parameters
     */
    if( ng+nh>0&&ae_fp_eq(state->agsrhononlinear,(double)(0)) )
    {
        if( dotrace )
        {
            ae_trace("> inconsistent parameters detected, stopping\n\n");
        }
        state->repterminationtype = -1;
        result = ae_false;
        return result;
    }
    
    /*
     * Allocate arrays.
     */
    rvectorsetlengthatleast(&state->colmax, n, _state);
    rvectorsetlengthatleast(&state->diagh, n, _state);
    rvectorsetlengthatleast(&state->signmin, n, _state);
    rvectorsetlengthatleast(&state->signmax, n, _state);
    maxsamplesize = state->agssamplesize;
    rmatrixsetlengthatleast(&state->samplex, maxsamplesize+1, n, _state);
    rmatrixsetlengthatleast(&state->samplegm, maxsamplesize+1, n, _state);
    rmatrixsetlengthatleast(&state->samplegmbc, maxsamplesize+1, n, _state);
    rvectorsetlengthatleast(&state->samplef, maxsamplesize+1, _state);
    
    /*
     * Prepare optimizer
     */
    rvectorsetlengthatleast(&state->tmp0, maxsamplesize, _state);
    rvectorsetlengthatleast(&state->tmp1, maxsamplesize, _state);
    ivectorsetlengthatleast(&state->tmp3, 1, _state);
    rmatrixsetlengthatleast(&state->tmp2, 1, maxsamplesize+1, _state);
    for(i=0; i<=maxsamplesize-1; i++)
    {
        state->tmp0.ptr.p_double[i] = 0.0;
        state->tmp1.ptr.p_double[i] = _state->v_posinf;
    }
    
    /*
     * Prepare RNG, seed it with fixed values so
     * that each run on same problem yeilds same results
     */
    hqrndseed(7235, 98532, &state->agsrs, _state);
    
    /*
     * Prepare initial point subject to current bound constraints and
     * perform scaling of bound constraints, linear constraints, point itself
     */
    rvectorsetlengthatleast(&state->scaledbndl, n, _state);
    rvectorsetlengthatleast(&state->scaledbndu, n, _state);
    for(i=0; i<=n-1; i++)
    {
        
        /*
         * Check and scale constraints
         */
        if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_less(state->bndu.ptr.p_double[i],state->bndl.ptr.p_double[i]) )
        {
            if( dotrace )
            {
                ae_trace("> inconsistent box constraints detected, stopping\n\n");
            }
            state->repterminationtype = -3;
            result = ae_false;
            return result;
        }
        if( state->hasbndl.ptr.p_bool[i] )
        {
            state->scaledbndl.ptr.p_double[i] = state->bndl.ptr.p_double[i]/state->s.ptr.p_double[i];
        }
        else
        {
            state->scaledbndl.ptr.p_double[i] = _state->v_neginf;
        }
        if( state->hasbndu.ptr.p_bool[i] )
        {
            state->scaledbndu.ptr.p_double[i] = state->bndu.ptr.p_double[i]/state->s.ptr.p_double[i];
        }
        else
        {
            state->scaledbndu.ptr.p_double[i] = _state->v_posinf;
        }
        if( state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i] )
        {
            ae_assert(ae_fp_less_eq(state->scaledbndl.ptr.p_double[i],state->scaledbndu.ptr.p_double[i]), "MinNS: integrity check failed (dfdf)", _state);
        }
        if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->bndl.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            ae_assert(ae_fp_eq(state->scaledbndl.ptr.p_double[i],state->scaledbndu.ptr.p_double[i]), "MinNS: integrity check failed (dsgh)", _state);
        }
        
        /*
         * Scale and constrain point
         */
        state->xc.ptr.p_double[i] = state->xstart.ptr.p_double[i];
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less_eq(state->xc.ptr.p_double[i],state->bndl.ptr.p_double[i]) )
        {
            state->xc.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i];
            continue;
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater_eq(state->xc.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            state->xc.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i];
            continue;
        }
        state->xc.ptr.p_double[i] = state->xc.ptr.p_double[i]/state->s.ptr.p_double[i];
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less_eq(state->xc.ptr.p_double[i],state->scaledbndl.ptr.p_double[i]) )
        {
            state->xc.ptr.p_double[i] = state->scaledbndl.ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater_eq(state->xc.ptr.p_double[i],state->scaledbndu.ptr.p_double[i]) )
        {
            state->xc.ptr.p_double[i] = state->scaledbndu.ptr.p_double[i];
        }
    }
    rmatrixsetlengthatleast(&state->scaledcleic, nec+nic, n+1, _state);
    for(i=0; i<=nec+nic-1; i++)
    {
        
        /*
         * Scale and normalize linear constraints
         */
        vv = 0.0;
        for(j=0; j<=n-1; j++)
        {
            v = state->cleic.ptr.pp_double[i][j]*state->s.ptr.p_double[j];
            state->scaledcleic.ptr.pp_double[i][j] = v;
            vv = vv+v*v;
        }
        vv = ae_sqrt(vv, _state);
        state->scaledcleic.ptr.pp_double[i][n] = state->cleic.ptr.pp_double[i][n];
        if( ae_fp_greater(vv,(double)(0)) )
        {
            for(j=0; j<=n; j++)
            {
                state->scaledcleic.ptr.pp_double[i][j] = state->scaledcleic.ptr.pp_double[i][j]/vv;
            }
        }
    }
    
    /*
     * Main cycle
     *
     * We maintain several variables during iteration:
     * * RecommendedStep-   current estimate of recommended step length;
     *                      must be Radius0 on first entry
     * * Radius         -   current sampling radius
     * * CurSampleSize  -   current sample size (may change in future versions)
     * * FullSample     -   whether we have full sample, or only partial one
     * * RadiusDecays   -   total number of decreases performed for sampling radius
     */
    radius = state->agsradius;
    radius0 = radius;
    recommendedstep = ae_minreal(radius0, state->agsinitstp, _state);
    cursamplesize = 1;
    radiusdecays = 0;
    shortstepscnt = 0;
    fullsample = ae_false;
    state->rholinear = 0.0;
lbl_4:
    if( ae_false )
    {
        goto lbl_5;
    }
    if( dotrace )
    {
        ae_trace("\n=== ITERATION %5d STARTED ========================================================================\n",
            (int)(state->repinneriterationscount));
    }
    
    /*
     * First phase of iteration - central point:
     *
     * 1. evaluate function at central point - first entry in sample.
     *    Its status is ignored, it is always recalculated.
     * 2. report point and check gradient/function value for NAN/INF
     * 3. check penalty coefficients for linear terms; increase them
     *    if directional derivative of function being optimized (not
     *    merit function!) is larger than derivative of penalty.
     * 4. update report on constraint violation
     */
    cursamplesize = ae_maxint(cursamplesize, 1, _state);
    ae_v_move(&state->samplex.ptr.pp_double[0][0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minns_clearrequestfields(state, _state);
    state->needfij = ae_true;
    state->rstateags.stage = 0;
    goto lbl_rcomm;
lbl_0:
    state->needfij = ae_false;
    currentf0 = state->rawf;
    state->replcerr = 0.0;
    for(i=0; i<=nec+nic-1; i++)
    {
        v = -state->scaledcleic.ptr.pp_double[i][n];
        for(j=0; j<=n-1; j++)
        {
            v = v+state->scaledcleic.ptr.pp_double[i][j]*state->xc.ptr.p_double[j];
        }
        if( i>=nec&&ae_fp_less_eq(v,(double)(0)) )
        {
            continue;
        }
        state->replcerr = ae_maxreal(state->replcerr, ae_fabs(v, _state), _state);
    }
    state->repnlcerr = 0.0;
    for(i=1; i<=ng+nh; i++)
    {
        v = state->fi.ptr.p_double[i];
        if( i>ng&&ae_fp_less_eq(v,(double)(0)) )
        {
            continue;
        }
        state->repnlcerr = ae_maxreal(state->repnlcerr, ae_fabs(v, _state), _state);
    }
    state->samplef.ptr.p_double[0] = state->meritf;
    rcopyvr(n, &state->meritg, &state->samplegm, 0, _state);
    if( !state->xrep )
    {
        goto lbl_6;
    }
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->f = currentf0;
    minns_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstateags.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->xupdated = ae_false;
lbl_6:
    if( state->userterminationneeded )
    {
        
        /*
         * User requested termination
         */
        if( dotrace )
        {
            ae_trace("> termination requested by user\n\n");
        }
        state->repterminationtype = 8;
        goto lbl_5;
    }
    v = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr(state->samplegm.ptr.pp_double[0][i], _state);
    }
    if( !ae_isfinite(v, _state)||!ae_isfinite(state->samplef.ptr.p_double[0], _state) )
    {
        
        /*
         * Abnormal termination - infinities in function/gradient
         */
        if( dotrace )
        {
            ae_trace("> NAN/INF detected in function/gradient, termination\n\n");
        }
        state->repterminationtype = -8;
        goto lbl_5;
    }
    ae_assert(ae_fp_greater(state->agspenaltylevel,1.0), "MinNS: integrity error", _state);
    ae_assert(ae_fp_greater(state->agspenaltyincrease,state->agspenaltylevel), "MinNS: integrity error", _state);
    if( ae_fp_greater(ae_sqrt(rdotv2(n, &state->rawg, _state), _state)*state->agspenaltylevel,state->rholinear) )
    {
        state->rholinear = ae_sqrt(rdotv2(n, &state->rawg, _state), _state)*state->agspenaltyincrease;
        if( dotrace )
        {
            ae_trace("> penalty parameter needs increase, iteration restarted\n\n");
        }
        cursamplesize = 0;
        goto lbl_4;
    }
    
    /*
     * Trace if needed
     */
    if( dotrace )
    {
        if( dodetailedtrace )
        {
            ae_trace("> printing raw data (prior to applying variable and function scales)\n");
            ae_trace("X (raw)       = ");
            tracevectorunscaledunshiftedautoprec(&state->xc, n, &state->s, ae_true, &state->s, ae_false, _state);
            ae_trace("\n");
            ae_trace("> printing scaled data (after applying variable and function scales)\n");
            ae_trace("X (scaled)    = ");
            tracevectorautoprec(&state->xc, 0, n, _state);
            ae_trace("\n");
        }
        ae_trace("sampleRad     = %0.3e\n",
            (double)(radius));
        ae_trace("lin.violation = %0.3e    (scaled violation of linear constraints)\n",
            (double)(state->replcerr));
        ae_trace("nlc.violation = %0.3e    (scaled violation of nonlinear constraints)\n",
            (double)(state->repnlcerr));
        ae_trace("targetF       = %0.3e    (target function)\n",
            (double)(currentf0));
        ae_trace("meritF        = %0.3e    (merit  function)\n",
            (double)(state->samplef.ptr.p_double[0]));
        ae_trace("Rho linear    = %0.3e\n",
            (double)(state->rholinear));
        ae_trace("Rho nonlinear = %0.3e\n",
            (double)(state->agsrhononlinear));
        ae_trace("----------------------------------------------------------------------------------------------------\n");
    }
    
    /*
     * Check stopping conditions.
     */
    if( radiusdecays>=state->agsmaxraddecays )
    {
        
        /*
         * Too many attempts to decrease radius
         */
        if( dotrace )
        {
            ae_trace("> stopping condition met: too many attempts to decrease radius\n\n");
        }
        state->repterminationtype = 7;
        goto lbl_5;
    }
    if( state->repinneriterationscount>=state->maxits&&state->maxits>0 )
    {
        
        /*
         * Too many iterations
         */
        if( dotrace )
        {
            ae_trace("> stopping condition met: %0d iterations performed\n\n",
                (int)(state->repinneriterationscount));
        }
        state->repterminationtype = 5;
        goto lbl_5;
    }
    if( ae_fp_less_eq(radius,state->epsx*state->agsraddecay) )
    {
        
        /*
         * Radius is smaller than required step tolerance multiplied by radius decay.
         *
         * Additional decay is required in order to make sure that optimization session
         * with radius equal to EpsX was successfully done.
         */
        if( dotrace )
        {
            ae_trace("> stopping condition met: sampling radius is smaller than %0.3e\n\n",
                (double)(state->epsx));
        }
        state->repterminationtype = 2;
        goto lbl_5;
    }
    
    /*
     * Update sample:
     *
     * 1. invalidate entries which are too far away from XC
     *    and move all valid entries to beginning of the sample.
     * 2. add new entries until we have AGSSampleSize
     *    items in our sample. We remove oldest entries from
     *    sample until we have enough place to add at least
     *    AGSMinUpdate items.
     * 3. prepare "modified" gradient sample with respect to
     *    boundary constraints.
     */
    ae_assert(cursamplesize>=1, "MinNS: integrity check failed (2367)", _state);
    k = 1;
    for(i=1; i<=cursamplesize-1; i++)
    {
        
        /*
         * If entry is outside of Radius-ball around XC, discard it.
         */
        v = 0.0;
        for(j=0; j<=n-1; j++)
        {
            v = ae_maxreal(v, ae_fabs(state->samplex.ptr.pp_double[i][j]-state->xc.ptr.p_double[j], _state), _state);
        }
        if( ae_fp_greater(v,radius) )
        {
            continue;
        }
        
        /*
         * Move to the beginning
         */
        rcopyrr(n, &state->samplex, i, &state->samplex, k, _state);
        rcopyrr(n, &state->samplegm, i, &state->samplegm, k, _state);
        state->samplef.ptr.p_double[k] = state->samplef.ptr.p_double[i];
        k = k+1;
    }
    cursamplesize = k;
    if( state->agssamplesize-cursamplesize<state->agsminupdate )
    {
        
        /*
         * Remove oldest entries
         */
        k = state->agsminupdate-(state->agssamplesize-cursamplesize);
        ae_assert(k<=cursamplesize-1, "MinNS: integrity check failed (2662)", _state);
        for(i=1; i<=cursamplesize-k-1; i++)
        {
            rcopyrr(n, &state->samplex, i+k, &state->samplex, i, _state);
            rcopyrr(n, &state->samplegm, i+k, &state->samplegm, i, _state);
            state->samplef.ptr.p_double[i] = state->samplef.ptr.p_double[i+k];
        }
        cursamplesize = cursamplesize-k;
    }
    k = 0;
    i = cursamplesize;
lbl_8:
    if( i>ae_minint(cursamplesize+state->agsminupdate, state->agssamplesize, _state)-1 )
    {
        goto lbl_10;
    }
    for(j=0; j<=n-1; j++)
    {
        
        /*
         * Undistorted position
         */
        state->samplex.ptr.pp_double[i][j] = state->xc.ptr.p_double[j];
        
        /*
         * Do not apply distortion if the variable is fixed
         */
        if( (state->hasbndl.ptr.p_bool[j]&&state->hasbndu.ptr.p_bool[j])&&ae_fp_eq(state->scaledbndl.ptr.p_double[j],state->scaledbndu.ptr.p_double[j]) )
        {
            continue;
        }
        
        /*
         * Apply distortion
         */
        if( ae_fp_greater_eq(hqrnduniformr(&state->agsrs, _state),0.5) )
        {
            
            /*
             * Sample at the left side with 50% probability
             */
            v0 = state->samplex.ptr.pp_double[i][j]-radius;
            v1 = state->samplex.ptr.pp_double[i][j];
            if( state->hasbndl.ptr.p_bool[j] )
            {
                v0 = ae_maxreal(state->scaledbndl.ptr.p_double[j], v0, _state);
            }
        }
        else
        {
            
            /*
             * Sample at the right side with 50% probability
             */
            v0 = state->samplex.ptr.pp_double[i][j];
            v1 = state->samplex.ptr.pp_double[i][j]+radius;
            if( state->hasbndu.ptr.p_bool[j] )
            {
                v1 = ae_minreal(state->scaledbndu.ptr.p_double[j], v1, _state);
            }
        }
        ae_assert(ae_fp_greater_eq(v1,v0), "MinNS: integrity check failed (9743)", _state);
        state->samplex.ptr.pp_double[i][j] = boundval(v0+(v1-v0)*hqrnduniformr(&state->agsrs, _state), v0, v1, _state);
    }
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->samplex.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
    minns_clearrequestfields(state, _state);
    state->needfij = ae_true;
    state->rstateags.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->needfij = ae_false;
    state->samplef.ptr.p_double[i] = state->meritf;
    rcopyvr(n, &state->meritg, &state->samplegm, i, _state);
    k = k+1;
    i = i+1;
    goto lbl_8;
lbl_10:
    cursamplesize = cursamplesize+k;
    fullsample = cursamplesize==state->agssamplesize;
    for(j=0; j<=cursamplesize-1; j++)
    {
        
        /*
         * For J-th element in gradient sample, process all of its components
         * and modify them according to status of box constraints
         */
        for(i=0; i<=n-1; i++)
        {
            ae_assert(!state->hasbndl.ptr.p_bool[i]||ae_fp_greater_eq(state->xc.ptr.p_double[i],state->scaledbndl.ptr.p_double[i]), "MinNS: integrity error", _state);
            ae_assert(!state->hasbndu.ptr.p_bool[i]||ae_fp_less_eq(state->xc.ptr.p_double[i],state->scaledbndu.ptr.p_double[i]), "MinNS: integrity error", _state);
            state->samplegmbc.ptr.pp_double[j][i] = state->samplegm.ptr.pp_double[j][i];
            if( (state->hasbndl.ptr.p_bool[i]&&state->hasbndu.ptr.p_bool[i])&&ae_fp_eq(state->scaledbndl.ptr.p_double[i],state->scaledbndu.ptr.p_double[i]) )
            {
                
                /*
                 * I-th box constraint is of equality type (lower bound matches upper one).
                 * Simplest case, always active.
                 */
                state->samplegmbc.ptr.pp_double[j][i] = 0.0;
                continue;
            }
            if( state->hasbndl.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->scaledbndl.ptr.p_double[i]) )
            {
                
                /*
                 * We are at lower bound: activate/deactivate constraint depending on gradient at XC
                 */
                if( ae_fp_greater_eq(state->samplegm.ptr.pp_double[0][i],0.0) )
                {
                    state->samplegmbc.ptr.pp_double[j][i] = 0.0;
                }
                continue;
            }
            if( state->hasbndu.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->scaledbndu.ptr.p_double[i]) )
            {
                
                /*
                 * We are at upper bound: activate/deactivate constraint depending on gradient at XC
                 */
                if( ae_fp_less_eq(state->samplegm.ptr.pp_double[0][i],0.0) )
                {
                    state->samplegmbc.ptr.pp_double[j][i] = 0.0;
                }
                continue;
            }
        }
    }
    if( dotracesample )
    {
        ae_trace("> gradient sample\n");
        for(i=0; i<=cursamplesize-1; i++)
        {
            ae_trace("SampleGrad[]  = ");
            tracerowautoprec(&state->samplegmbc, i, 0, n, _state);
            ae_trace("\n");
        }
    }
    
    /*
     * Calculate diagonal Hessian.
     *
     * This Hessian serves two purposes:
     * * first, it improves performance of gradient descent step
     * * second, it improves condition number of QP subproblem
     *   solved to determine step
     *
     * The idea is that for each variable we check whether sample
     * includes entries with alternating sign of gradient:
     * * if gradients with different signs are present, Hessian
     *   component is set to M/R, where M is a maximum magnitude
     *   of corresponding gradient component, R is a sampling radius.
     *   Note that sign=0 and sign=1 are treated as different ones
     * * if all gradients have same sign, Hessian component is
     *   set to M/R0, where R0 is initial sampling radius.
     */
    for(j=0; j<=n-1; j++)
    {
        state->colmax.ptr.p_double[j] = 0.0;
        state->signmin.ptr.p_double[j] = (double)(1);
        state->signmax.ptr.p_double[j] = (double)(-1);
    }
    for(i=0; i<=cursamplesize-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = state->samplegmbc.ptr.pp_double[i][j];
            state->colmax.ptr.p_double[j] = ae_maxreal(state->colmax.ptr.p_double[j], ae_fabs(v, _state), _state);
            state->signmin.ptr.p_double[j] = ae_minreal(state->signmin.ptr.p_double[j], (double)(ae_sign(v, _state)), _state);
            state->signmax.ptr.p_double[j] = ae_maxreal(state->signmax.ptr.p_double[j], (double)(ae_sign(v, _state)), _state);
        }
    }
    for(j=0; j<=n-1; j++)
    {
        if( ae_fp_neq(state->signmin.ptr.p_double[j],state->signmax.ptr.p_double[j]) )
        {
            
            /*
             * Alternating signs of gradient - step is proportional to current sampling radius
             */
            ae_assert(ae_fp_neq(state->colmax.ptr.p_double[j],(double)(0)), "MinNS: integrity check failed (2975)", _state);
            ae_assert(ae_fp_neq(radius,(double)(0)), "MinNS: integrity check failed (8473)", _state);
            state->diagh.ptr.p_double[j] = state->colmax.ptr.p_double[j]/radius;
            continue;
        }
        if( ae_fp_neq(state->colmax.ptr.p_double[j],(double)(0)) )
        {
            
            /*
             * Non-alternating sign of gradient, but non-zero.
             * Step is proportional to recommended step
             */
            ae_assert(ae_fp_neq(recommendedstep,(double)(0)), "MinNS: integrity check failed (3274)", _state);
            state->diagh.ptr.p_double[j] = state->colmax.ptr.p_double[j]/recommendedstep;
            continue;
        }
        state->diagh.ptr.p_double[j] = (double)(1);
    }
    if( dodetailedtrace )
    {
        ae_trace("> diagonal quasi-Hessian\n");
        ae_trace("H             = ");
        tracevectorautoprec(&state->diagh, 0, n, _state);
        ae_trace("\n");
    }
    
    /*
     * PROJECTION PHASE
     *
     * We project zero vector on convex hull of gradient sample.
     * If projection is small enough, we decrease radius and restart.
     * Otherwise, this phase returns search direction in State.D.
     *
     * NOTE: because we use iterative solver, it may have trouble
     *       dealing with ill-conditioned problems. So we also employ
     *       second, backup test for stationarity - when too many
     *       subsequent backtracking searches resulted in short steps.
     */
    minns_solveqp(&state->samplegmbc, &state->diagh, cursamplesize, n, &state->tmp0, &state->dbgncholesky, &state->nsqp, _state);
    for(j=0; j<=n-1; j++)
    {
        state->d.ptr.p_double[j] = 0.0;
    }
    for(i=0; i<=cursamplesize-1; i++)
    {
        v = state->tmp0.ptr.p_double[i];
        ae_v_addd(&state->d.ptr.p_double[0], 1, &state->samplegmbc.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
    }
    v = 0.0;
    for(j=0; j<=n-1; j++)
    {
        v = ae_maxreal(v, ae_fabs(state->d.ptr.p_double[j]/coalesce(state->colmax.ptr.p_double[j], 1.0, _state), _state), _state);
    }
    if( dotrace )
    {
        ae_trace("> stationarity test:\n|proj(0)|     = %0.3e (projection of zero vector on convex hull of gradient sample)\n",
            (double)(v));
    }
    if( ae_fp_less_eq(v,state->agsstattold) )
    {
        
        /*
         * Stationarity test succeeded.
         * Decrease radius and restart.
         *
         * NOTE: we also clear ShortStepsCnt on restart
         */
        if( dotrace )
        {
            ae_trace("> stationarity test satisfied, decreasing radius\n");
        }
        radius = radius*state->agsraddecay;
        shortstepscnt = 0;
        inc(&radiusdecays, _state);
        inc(&state->repinneriterationscount, _state);
        goto lbl_4;
    }
    for(i=0; i<=n-1; i++)
    {
        state->d.ptr.p_double[i] = -state->d.ptr.p_double[i]/state->diagh.ptr.p_double[i];
    }
    
    /*
     * Perform backtracking line search.
     * Update initial step length depending on search results.
     * Here we assume that D is non-zero.
     *
     * NOTE: if AGSShortLimit subsequent line searches resulted
     *       in steps shorter than AGSStatTolStp, we decrease radius.
     */
    dhd = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        dhd = dhd+state->d.ptr.p_double[i]*state->diagh.ptr.p_double[i]*state->d.ptr.p_double[i];
    }
    dnrminf = rmaxabsv(n, &state->d, _state);
    if( dotrace )
    {
        ae_trace("> search direction is ready:\n|D|           = %0.3e (inf-norm)\n(D,grad)      = %0.3e\n",
            (double)(dnrminf),
            (double)(rdotvr(n, &state->d, &state->samplegmbc, 0, _state)));
        if( dodetailedtrace )
        {
            ae_trace("D             = ");
            tracevectorautoprec(&state->d, 0, n, _state);
            ae_trace("\n");
        }
    }
    ae_assert(ae_fp_greater(dnrminf,(double)(0)), "MinNS: integrity error (2752)", _state);
    alpha = recommendedstep/dnrminf;
    alphadecreased = ae_false;
    backtrackits = 0;
    if( fullsample )
    {
        maxbacktrackits = state->agsmaxbacktrack;
    }
    else
    {
        maxbacktrackits = state->agsmaxbacktracknonfull;
    }
lbl_11:
    if( ae_false )
    {
        goto lbl_12;
    }
    
    /*
     * Prepare XN and evaluate merit function at XN
     */
    ae_v_move(&state->xn.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_addd(&state->xn.ptr.p_double[0], 1, &state->d.ptr.p_double[0], 1, ae_v_len(0,n-1), alpha);
    enforceboundaryconstraints(&state->xn, &state->scaledbndl, &state->hasbndl, &state->scaledbndu, &state->hasbndu, n, 0, _state);
    ae_v_move(&state->samplex.ptr.pp_double[maxsamplesize][0], 1, &state->xn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minns_clearrequestfields(state, _state);
    state->needfij = ae_true;
    state->rstateags.stage = 3;
    goto lbl_rcomm;
lbl_3:
    state->needfij = ae_false;
    state->samplef.ptr.p_double[maxsamplesize] = state->meritf;
    rcopyvr(n, &state->meritg, &state->samplegm, maxsamplesize, _state);
    
    /*
     * Check sufficient decrease condition
     */
    ae_assert(ae_fp_greater(dnrminf,(double)(0)), "MinNS: integrity error (9642)", _state);
    if( ae_fp_less_eq(state->samplef.ptr.p_double[maxsamplesize],state->samplef.ptr.p_double[0]-alpha*state->agsdecrease*dhd) )
    {
        goto lbl_12;
    }
    
    /*
     * Decrease Alpha
     */
    alpha = alpha*state->agsalphadecay;
    alphadecreased = ae_true;
    
    /*
     * Update and check iterations counter.
     */
    inc(&backtrackits, _state);
    if( backtrackits>=maxbacktrackits )
    {
        
        /*
         * Too many backtracking searches performed without success.
         * Terminate iterations.
         */
        alpha = 0.0;
        alphadecreased = ae_true;
        ae_v_move(&state->xn.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
        goto lbl_12;
    }
    goto lbl_11;
lbl_12:
    if( dotrace )
    {
        ae_trace("> backtracking line search finished:\nstp           = %0.3e\n",
            (double)(alpha));
    }
    if( (ae_fp_less_eq(alpha*dnrminf,state->agsshortstpabs)||ae_fp_less_eq(alpha*dnrminf,state->agsshortstprel*radius))||ae_fp_less_eq(ae_fabs(state->samplef.ptr.p_double[0]-state->samplef.ptr.p_double[maxsamplesize], _state),state->agsshortf) )
    {
        inc(&shortstepscnt, _state);
    }
    else
    {
        shortstepscnt = 0;
    }
    if( shortstepscnt>=state->agsshortlimit )
    {
        
        /*
         * Too many subsequent short steps.
         *
         * It may be possible that optimizer is unable to find out
         * that we have to decrease radius because of ill-conditioned
         * gradients.
         *
         * Decrease radius and restart.
         */
        if( dotrace )
        {
            ae_trace("> too many subsequent short steps, decreasing radius\n");
        }
        radius = radius*state->agsraddecay;
        shortstepscnt = 0;
        inc(&radiusdecays, _state);
        inc(&state->repinneriterationscount, _state);
        goto lbl_4;
    }
    if( !alphadecreased )
    {
        recommendedstep = recommendedstep*2.0;
    }
    if( alphadecreased&&fullsample )
    {
        recommendedstep = recommendedstep*0.5;
    }
    
    /*
     * Next iteration
     */
    ae_v_move(&state->xc.ptr.p_double[0], 1, &state->xn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    inc(&state->repinneriterationscount, _state);
    goto lbl_4;
lbl_5:
    
    /*
     * Convert back from scaled to unscaled representation
     */
    minns_unscalepointbc(state, &state->xc, _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstateags.ia.ptr.p_int[0] = n;
    state->rstateags.ia.ptr.p_int[1] = nec;
    state->rstateags.ia.ptr.p_int[2] = nic;
    state->rstateags.ia.ptr.p_int[3] = ng;
    state->rstateags.ia.ptr.p_int[4] = nh;
    state->rstateags.ia.ptr.p_int[5] = i;
    state->rstateags.ia.ptr.p_int[6] = j;
    state->rstateags.ia.ptr.p_int[7] = k;
    state->rstateags.ia.ptr.p_int[8] = radiusdecays;
    state->rstateags.ia.ptr.p_int[9] = maxsamplesize;
    state->rstateags.ia.ptr.p_int[10] = cursamplesize;
    state->rstateags.ia.ptr.p_int[11] = shortstepscnt;
    state->rstateags.ia.ptr.p_int[12] = backtrackits;
    state->rstateags.ia.ptr.p_int[13] = maxbacktrackits;
    state->rstateags.ba.ptr.p_bool[0] = b;
    state->rstateags.ba.ptr.p_bool[1] = alphadecreased;
    state->rstateags.ba.ptr.p_bool[2] = fullsample;
    state->rstateags.ba.ptr.p_bool[3] = dotrace;
    state->rstateags.ba.ptr.p_bool[4] = dodetailedtrace;
    state->rstateags.ba.ptr.p_bool[5] = dotracesample;
    state->rstateags.ra.ptr.p_double[0] = radius0;
    state->rstateags.ra.ptr.p_double[1] = radius;
    state->rstateags.ra.ptr.p_double[2] = alpha;
    state->rstateags.ra.ptr.p_double[3] = recommendedstep;
    state->rstateags.ra.ptr.p_double[4] = dhd;
    state->rstateags.ra.ptr.p_double[5] = dnrminf;
    state->rstateags.ra.ptr.p_double[6] = v;
    state->rstateags.ra.ptr.p_double[7] = vv;
    state->rstateags.ra.ptr.p_double[8] = v0;
    state->rstateags.ra.ptr.p_double[9] = v1;
    state->rstateags.ra.ptr.p_double[10] = currentf0;
    return result;
}


/*************************************************************************
This function performs transformation of  X  from  scaled  coordinates  to
unscaled ones, paying special attention to box constraints:
* points which were exactly at the boundary before scaling will be  mapped
  to corresponding boundary after scaling
* in any case, unscaled box constraints will be satisfied

  -- ALGLIB --
     Copyright 02.06.2015 by Bochkanov Sergey
*************************************************************************/
static void minns_unscalepointbc(const minnsstate* state,
     /* Real    */ ae_vector* x,
     ae_state *_state)
{
    ae_int_t i;


    for(i=0; i<=state->n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less_eq(x->ptr.p_double[i],state->scaledbndl.ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = state->bndl.ptr.p_double[i];
            continue;
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater_eq(x->ptr.p_double[i],state->scaledbndu.ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = state->bndu.ptr.p_double[i];
            continue;
        }
        x->ptr.p_double[i] = x->ptr.p_double[i]*state->s.ptr.p_double[i];
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less_eq(x->ptr.p_double[i],state->bndl.ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = state->bndl.ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater_eq(x->ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            x->ptr.p_double[i] = state->bndu.ptr.p_double[i];
        }
    }
}


/*************************************************************************
This function solves QP problem of the form

        [                        ]
    min [ 0.5*c'*(G*inv(H)*G')*c ] s.t. c[i]>=0, SUM(c[i])=1.0
        [                        ]

where G is stored in SampleG[] array, diagonal H is stored in DiagH[].

DbgNCholesky is incremented every time we perform Cholesky decomposition.

  -- ALGLIB --
     Copyright 02.06.2015 by Bochkanov Sergey
*************************************************************************/
static void minns_solveqp(/* Real    */ const ae_matrix* sampleg,
     /* Real    */ const ae_vector* diagh,
     ae_int_t nsample,
     ae_int_t nvars,
     /* Real    */ ae_vector* coeffs,
     ae_int_t* dbgncholesky,
     minnsqp* state,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double vv;
    ae_int_t n;
    ae_int_t idx0;
    ae_int_t idx1;
    ae_int_t ncandbnd;
    ae_int_t innerits;
    ae_int_t outerits;
    double dnrm;
    double stp;
    double stpmax;
    ae_int_t actidx;
    double dtol;
    ae_bool kickneeded;
    double kicklength;
    double lambdav;
    double maxdiag;
    ae_bool wasactivation;
    ae_bool werechanges;
    ae_int_t termcnt;


    n = nsample;
    
    /*
     * Allocate arrays, prepare data
     */
    rvectorsetlengthatleast(coeffs, n, _state);
    rvectorsetlengthatleast(&state->xc, n, _state);
    rvectorsetlengthatleast(&state->xn, n, _state);
    rvectorsetlengthatleast(&state->x0, n, _state);
    rvectorsetlengthatleast(&state->gc, n, _state);
    rvectorsetlengthatleast(&state->d, n, _state);
    rmatrixsetlengthatleast(&state->uh, n, n, _state);
    rmatrixsetlengthatleast(&state->ch, n, n, _state);
    rmatrixsetlengthatleast(&state->rk, nsample, nvars, _state);
    rvectorsetlengthatleast(&state->invutc, n, _state);
    rvectorsetlengthatleast(&state->tmp0, n, _state);
    bvectorsetlengthatleast(&state->tmpb, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->xc.ptr.p_double[i] = 1.0/(double)n;
        coeffs->ptr.p_double[i] = 1.0/(double)n;
    }
    for(i=0; i<=nsample-1; i++)
    {
        for(j=0; j<=nvars-1; j++)
        {
            state->rk.ptr.pp_double[i][j] = sampleg->ptr.pp_double[i][j]/ae_sqrt(diagh->ptr.p_double[j], _state);
        }
    }
    rmatrixsyrk(nsample, nvars, 1.0, &state->rk, 0, 0, 0, 0.0, &state->uh, 0, 0, ae_true, _state);
    maxdiag = 0.0;
    for(i=0; i<=nsample-1; i++)
    {
        maxdiag = ae_maxreal(maxdiag, state->uh.ptr.pp_double[i][i], _state);
    }
    maxdiag = coalesce(maxdiag, 1.0, _state);
    
    /*
     * Main cycle:
     */
    innerits = 0;
    outerits = 0;
    dtol = 1.0E5*ae_machineepsilon;
    kicklength = ae_machineepsilon;
    lambdav = 1.0E5*ae_machineepsilon;
    termcnt = 0;
    for(;;)
    {
        
        /*
         * Save current point to X0
         */
        ae_v_move(&state->x0.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
        
        /*
         * Calculate gradient at initial point, solve NNLS problem
         * to determine descent direction D subject to constraints.
         *
         * In order to do so we solve following constrained
         * minimization problem:
         *         (                         )^2
         *     min ( SUM(lambda[i]*A[i]) + G )
         *         (                         )
         * Here:
         * * G is a gradient (column vector)
         * * A[i] is a column vector of I-th constraint
         * * lambda[i] is a Lagrange multiplier corresponding to I-th constraint
         *
         * NOTE: all A[i] except for last one have only one element being set,
         *       so we rely on sparse capabilities of NNLS solver. However,
         *       in order to use these capabilities we have to reorder variables
         *       in such way that sparse ones come first.
         *
         * After finding lambda[] coefficients, we can find constrained descent
         * direction by subtracting lambda[i]*A[i] from D=-G. We make use of the
         * fact that first NCandBnd columns are just columns of identity matrix,
         * so we can perform exact projection by explicitly setting elements of D
         * to zeros.
         */
        minns_qpcalculategradfunc(sampleg, diagh, nsample, nvars, &state->xc, &state->gc, &state->fc, &state->tmp0, _state);
        ivectorsetlengthatleast(&state->tmpidx, n, _state);
        rvectorsetlengthatleast(&state->tmpd, n, _state);
        rmatrixsetlengthatleast(&state->tmpc2, n, 1, _state);
        idx0 = 0;
        ncandbnd = 0;
        for(i=0; i<=n-1; i++)
        {
            if( ae_fp_eq(state->xc.ptr.p_double[i],0.0) )
            {
                ncandbnd = ncandbnd+1;
            }
        }
        idx1 = ncandbnd;
        for(i=0; i<=n-1; i++)
        {
            if( ae_fp_eq(state->xc.ptr.p_double[i],0.0) )
            {
                
                /*
                 * Candidate for activation of boundary constraint,
                 * comes first.
                 *
                 * NOTE: multiplication by -1 is due to the fact that
                 *       it is lower bound, and has specific direction
                 *       of constraint gradient.
                 */
                state->tmpidx.ptr.p_int[idx0] = i;
                state->tmpd.ptr.p_double[idx0] = (-state->gc.ptr.p_double[i])*(double)(-1);
                state->tmpc2.ptr.pp_double[idx0][0] = 1.0*(double)(-1);
                idx0 = idx0+1;
            }
            else
            {
                
                /*
                 * We are far away from boundary.
                 */
                state->tmpidx.ptr.p_int[idx1] = i;
                state->tmpd.ptr.p_double[idx1] = -state->gc.ptr.p_double[i];
                state->tmpc2.ptr.pp_double[idx1][0] = 1.0;
                idx1 = idx1+1;
            }
        }
        ae_assert(idx0==ncandbnd, "MinNSQP: integrity check failed (2346)", _state);
        ae_assert(idx1==n, "MinNSQP: integrity check failed (4535)", _state);
        snnlsinit(n, 1, n, &state->nnls, _state);
        snnlssetproblem(&state->nnls, &state->tmpc2, &state->tmpd, ncandbnd, 1, n, _state);
        snnlsdropnnc(&state->nnls, ncandbnd, _state);
        snnlssolve(&state->nnls, &state->tmplambdas, _state);
        for(i=0; i<=n-1; i++)
        {
            state->d.ptr.p_double[i] = -state->gc.ptr.p_double[i]-state->tmplambdas.ptr.p_double[ncandbnd];
        }
        for(i=0; i<=ncandbnd-1; i++)
        {
            if( ae_fp_greater(state->tmplambdas.ptr.p_double[i],(double)(0)) )
            {
                state->d.ptr.p_double[state->tmpidx.ptr.p_int[i]] = 0.0;
            }
        }
        
        /*
         * Additional stage to "polish" D (improve situation
         * with sum-to-one constraint and boundary constraints)
         * and to perform additional integrity check.
         *
         * After this stage we are pretty sure that:
         * * if x[i]=0.0, then d[i]>=0.0
         * * if d[i]<0.0, then x[i]>0.0
         */
        for(i=0; i<=n-1; i++)
        {
            if( ae_fp_eq(state->xc.ptr.p_double[i],0.0)&&ae_fp_less(state->d.ptr.p_double[i],0.0) )
            {
                state->d.ptr.p_double[i] = 0.0;
            }
        }
        
        /*
         * Decide whether we need "kick" stage: special stage
         * that moves us away from boundary constraints which are
         * not strictly active (i.e. such constraints that x[i]=0.0 and d[i]>0).
         *
         * If we need kick stage, we make a kick - and restart iteration.
         * If not, after this block we can rely on the fact that
         * for all x[i]=0.0 we have d[i]=0.0
         */
        kickneeded = ae_false;
        for(i=0; i<=n-1; i++)
        {
            if( ae_fp_eq(state->xc.ptr.p_double[i],0.0)&&ae_fp_greater(state->d.ptr.p_double[i],0.0) )
            {
                kickneeded = ae_true;
            }
        }
        if( kickneeded )
        {
            
            /*
             * Perform kick.
             * Restart.
             * Do not increase outer iterations counter.
             */
            v = 0.0;
            for(i=0; i<=n-1; i++)
            {
                if( ae_fp_eq(state->xc.ptr.p_double[i],0.0)&&ae_fp_greater(state->d.ptr.p_double[i],0.0) )
                {
                    state->xc.ptr.p_double[i] = state->xc.ptr.p_double[i]+kicklength;
                }
                v = v+state->xc.ptr.p_double[i];
            }
            ae_assert(ae_fp_greater(v,0.0), "MinNSQP: integrity check failed (2572)", _state);
            for(i=0; i<=n-1; i++)
            {
                state->xc.ptr.p_double[i] = state->xc.ptr.p_double[i]/v;
            }
            inc(&innerits, _state);
            continue;
        }
        
        /*
         * Calculate Cholesky decomposition of constrained Hessian
         * for Newton phase.
         */
        for(;;)
        {
            for(i=0; i<=n-1; i++)
            {
                
                /*
                 * Diagonal element
                 */
                if( ae_fp_greater(state->xc.ptr.p_double[i],0.0) )
                {
                    state->ch.ptr.pp_double[i][i] = state->uh.ptr.pp_double[i][i]+lambdav*maxdiag;
                }
                else
                {
                    state->ch.ptr.pp_double[i][i] = 1.0;
                }
                
                /*
                 * Offdiagonal elements
                 */
                for(j=i+1; j<=n-1; j++)
                {
                    if( ae_fp_greater(state->xc.ptr.p_double[i],0.0)&&ae_fp_greater(state->xc.ptr.p_double[j],0.0) )
                    {
                        state->ch.ptr.pp_double[i][j] = state->uh.ptr.pp_double[i][j];
                    }
                    else
                    {
                        state->ch.ptr.pp_double[i][j] = 0.0;
                    }
                }
            }
            inc(dbgncholesky, _state);
            if( !spdmatrixcholeskyrec(&state->ch, 0, n, ae_true, &state->tmp0, _state) )
            {
                
                /*
                 * Cholesky decomposition failed.
                 * Increase LambdaV and repeat iteration.
                 * Do not increase outer iterations counter.
                 */
                lambdav = lambdav*(double)10;
                continue;
            }
            break;
        }
        
        /*
         * Newton phase
         */
        for(;;)
        {
            
            /*
             * Calculate constrained (equality and sum-to-one) descent direction D.
             *
             * Here we use Sherman-Morrison update to calculate direction subject to
             * sum-to-one constraint.
             */
            minns_qpcalculategradfunc(sampleg, diagh, nsample, nvars, &state->xc, &state->gc, &state->fc, &state->tmp0, _state);
            for(i=0; i<=n-1; i++)
            {
                if( ae_fp_greater(state->xc.ptr.p_double[i],0.0) )
                {
                    state->invutc.ptr.p_double[i] = 1.0;
                    state->d.ptr.p_double[i] = -state->gc.ptr.p_double[i];
                }
                else
                {
                    state->invutc.ptr.p_double[i] = 0.0;
                    state->d.ptr.p_double[i] = 0.0;
                }
            }
            minns_qpsolveut(&state->ch, n, &state->invutc, _state);
            minns_qpsolveut(&state->ch, n, &state->d, _state);
            v = 0.0;
            vv = 0.0;
            for(i=0; i<=n-1; i++)
            {
                vv = vv+ae_sqr(state->invutc.ptr.p_double[i], _state);
                v = v+state->invutc.ptr.p_double[i]*state->d.ptr.p_double[i];
            }
            for(i=0; i<=n-1; i++)
            {
                state->d.ptr.p_double[i] = state->d.ptr.p_double[i]-v/vv*state->invutc.ptr.p_double[i];
            }
            minns_qpsolveu(&state->ch, n, &state->d, _state);
            v = 0.0;
            k = 0;
            for(i=0; i<=n-1; i++)
            {
                v = v+state->d.ptr.p_double[i];
                if( ae_fp_neq(state->d.ptr.p_double[i],0.0) )
                {
                    k = k+1;
                }
            }
            if( k>0&&ae_fp_greater(v,0.0) )
            {
                vv = v/(double)k;
                for(i=0; i<=n-1; i++)
                {
                    if( ae_fp_neq(state->d.ptr.p_double[i],0.0) )
                    {
                        state->d.ptr.p_double[i] = state->d.ptr.p_double[i]-vv;
                    }
                }
            }
            
            /*
             * Calculate length of D, maximum step and component which is
             * activated by this step.
             *
             * Break if D is exactly zero. We do not break here if DNrm is
             * small - this check is performed later. It is important to
             * perform last step with nearly-zero D, it allows us to have
             * extra-precision in solution which is often needed for convergence
             * of AGS algorithm.
             */
            dnrm = 0.0;
            for(i=0; i<=n-1; i++)
            {
                dnrm = dnrm+ae_sqr(state->d.ptr.p_double[i], _state);
            }
            dnrm = ae_sqrt(dnrm, _state);
            actidx = -1;
            stpmax = 1.0E50;
            for(i=0; i<=n-1; i++)
            {
                if( ae_fp_less(state->d.ptr.p_double[i],0.0) )
                {
                    v = stpmax;
                    stpmax = safeminposrv(state->xc.ptr.p_double[i], -state->d.ptr.p_double[i], stpmax, _state);
                    if( ae_fp_less(stpmax,v) )
                    {
                        actidx = i;
                    }
                }
            }
            if( ae_fp_eq(dnrm,0.0) )
            {
                break;
            }
            
            /*
             * Calculate trial function value at unconstrained full step.
             * If trial value is greater or equal to FC, terminate iterations.
             */
            for(i=0; i<=n-1; i++)
            {
                state->xn.ptr.p_double[i] = state->xc.ptr.p_double[i]+1.0*state->d.ptr.p_double[i];
            }
            minns_qpcalculatefunc(sampleg, diagh, nsample, nvars, &state->xn, &state->fn, &state->tmp0, _state);
            if( ae_fp_greater_eq(state->fn,state->fc) )
            {
                break;
            }
            
            /*
             * Perform step
             * Update Hessian
             * Update XC
             *
             * Break if:
             * a) no constraint was activated
             * b) norm of D is small enough
             */
            stp = ae_minreal(1.0, stpmax, _state);
            for(i=0; i<=n-1; i++)
            {
                state->xn.ptr.p_double[i] = ae_maxreal(state->xc.ptr.p_double[i]+stp*state->d.ptr.p_double[i], 0.0, _state);
            }
            if( ae_fp_eq(stp,stpmax)&&actidx>=0 )
            {
                state->xn.ptr.p_double[actidx] = 0.0;
            }
            wasactivation = ae_false;
            for(i=0; i<=n-1; i++)
            {
                state->tmpb.ptr.p_bool[i] = ae_fp_eq(state->xn.ptr.p_double[i],0.0)&&ae_fp_neq(state->xc.ptr.p_double[i],0.0);
                wasactivation = wasactivation||state->tmpb.ptr.p_bool[i];
            }
            ae_v_move(&state->xc.ptr.p_double[0], 1, &state->xn.ptr.p_double[0], 1, ae_v_len(0,n-1));
            if( !wasactivation )
            {
                break;
            }
            if( ae_fp_less_eq(dnrm,dtol) )
            {
                break;
            }
            spdmatrixcholeskyupdatefixbuf(&state->ch, n, ae_true, &state->tmpb, &state->tmp0, _state);
        }
        
        /*
         * Compare status of boundary constraints - if nothing changed during
         * last outer iteration, TermCnt is increased. Otherwise it is reset
         * to zero.
         *
         * When TermCnt is large enough, we terminate algorithm.
         */
        werechanges = ae_false;
        for(i=0; i<=n-1; i++)
        {
            werechanges = werechanges||ae_sign(state->x0.ptr.p_double[i], _state)!=ae_sign(state->xc.ptr.p_double[i], _state);
        }
        if( !werechanges )
        {
            inc(&termcnt, _state);
        }
        else
        {
            termcnt = 0;
        }
        if( termcnt>=2 )
        {
            break;
        }
        
        /*
         * Increase number of outer iterations.
         * Break if we performed too many.
         */
        inc(&outerits, _state);
        if( outerits==10 )
        {
            break;
        }
    }
    
    /*
     * Store result
     */
    for(i=0; i<=n-1; i++)
    {
        coeffs->ptr.p_double[i] = state->xc.ptr.p_double[i];
    }
}


/*************************************************************************
Function/gradient calculation for QP solver.

  -- ALGLIB --
     Copyright 02.06.2015 by Bochkanov Sergey
*************************************************************************/
static void minns_qpcalculategradfunc(/* Real    */ const ae_matrix* sampleg,
     /* Real    */ const ae_vector* diagh,
     ae_int_t nsample,
     ae_int_t nvars,
     /* Real    */ const ae_vector* coeffs,
     /* Real    */ ae_vector* g,
     double* f,
     /* Real    */ ae_vector* tmp,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;

    *f = 0.0;

    rvectorsetlengthatleast(g, nsample, _state);
    rvectorsetlengthatleast(tmp, nvars, _state);
    
    /*
     * Calculate GS*p
     */
    for(j=0; j<=nvars-1; j++)
    {
        tmp->ptr.p_double[j] = 0.0;
    }
    for(i=0; i<=nsample-1; i++)
    {
        v = coeffs->ptr.p_double[i];
        ae_v_addd(&tmp->ptr.p_double[0], 1, &sampleg->ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1), v);
    }
    
    /*
     * Calculate F
     */
    *f = 0.0;
    for(i=0; i<=nvars-1; i++)
    {
        *f = *f+0.5*ae_sqr(tmp->ptr.p_double[i], _state)/diagh->ptr.p_double[i];
    }
    
    /*
     * Multiply by inverse Hessian
     */
    for(i=0; i<=nvars-1; i++)
    {
        tmp->ptr.p_double[i] = tmp->ptr.p_double[i]/diagh->ptr.p_double[i];
    }
    
    /*
     * Function gradient
     */
    for(i=0; i<=nsample-1; i++)
    {
        v = ae_v_dotproduct(&sampleg->ptr.pp_double[i][0], 1, &tmp->ptr.p_double[0], 1, ae_v_len(0,nvars-1));
        g->ptr.p_double[i] = v;
    }
}


/*************************************************************************
Function calculation for QP solver.

  -- ALGLIB --
     Copyright 02.06.2015 by Bochkanov Sergey
*************************************************************************/
static void minns_qpcalculatefunc(/* Real    */ const ae_matrix* sampleg,
     /* Real    */ const ae_vector* diagh,
     ae_int_t nsample,
     ae_int_t nvars,
     /* Real    */ const ae_vector* coeffs,
     double* f,
     /* Real    */ ae_vector* tmp,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;

    *f = 0.0;

    rvectorsetlengthatleast(tmp, nvars, _state);
    
    /*
     * Calculate GS*p
     */
    for(j=0; j<=nvars-1; j++)
    {
        tmp->ptr.p_double[j] = 0.0;
    }
    for(i=0; i<=nsample-1; i++)
    {
        v = coeffs->ptr.p_double[i];
        ae_v_addd(&tmp->ptr.p_double[0], 1, &sampleg->ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1), v);
    }
    
    /*
     * Calculate F
     */
    *f = 0.0;
    for(i=0; i<=nvars-1; i++)
    {
        *f = *f+0.5*ae_sqr(tmp->ptr.p_double[i], _state)/diagh->ptr.p_double[i];
    }
}


/*************************************************************************
Triangular solver for QP solver.

  -- ALGLIB --
     Copyright 02.06.2015 by Bochkanov Sergey
*************************************************************************/
static void minns_qpsolveu(/* Real    */ const ae_matrix* a,
     ae_int_t n,
     /* Real    */ ae_vector* x,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;


    
    /*
     * A^(-1)*X
     */
    for(i=n-1; i>=0; i--)
    {
        v = x->ptr.p_double[i];
        for(j=i+1; j<=n-1; j++)
        {
            v = v-a->ptr.pp_double[i][j]*x->ptr.p_double[j];
        }
        x->ptr.p_double[i] = v/a->ptr.pp_double[i][i];
    }
}


/*************************************************************************
Triangular solver for QP solver.

  -- ALGLIB --
     Copyright 02.06.2015 by Bochkanov Sergey
*************************************************************************/
static void minns_qpsolveut(/* Real    */ const ae_matrix* a,
     ae_int_t n,
     /* Real    */ ae_vector* x,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;


    
    /*
     * A^(-T)*X
     */
    for(i=0; i<=n-1; i++)
    {
        x->ptr.p_double[i] = x->ptr.p_double[i]/a->ptr.pp_double[i][i];
        v = x->ptr.p_double[i];
        for(j=i+1; j<=n-1; j++)
        {
            x->ptr.p_double[j] = x->ptr.p_double[j]-a->ptr.pp_double[i][j]*v;
        }
    }
}


void _minnsqp_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minnsqp *p = (minnsqp*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->xc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->uh, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->ch, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->rk, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->invutc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpidx, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->tmpd, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmplambdas, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmpc2, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpb, 0, DT_BOOL, _state, make_automatic);
    _snnlssolver_init(&p->nnls, _state, make_automatic);
}


void _minnsqp_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minnsqp       *dst = (minnsqp*)_dst;
    const minnsqp *src = (const minnsqp*)_src;
    dst->fc = src->fc;
    dst->fn = src->fn;
    ae_vector_init_copy(&dst->xc, &src->xc, _state, make_automatic);
    ae_vector_init_copy(&dst->xn, &src->xn, _state, make_automatic);
    ae_vector_init_copy(&dst->x0, &src->x0, _state, make_automatic);
    ae_vector_init_copy(&dst->gc, &src->gc, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    ae_matrix_init_copy(&dst->uh, &src->uh, _state, make_automatic);
    ae_matrix_init_copy(&dst->ch, &src->ch, _state, make_automatic);
    ae_matrix_init_copy(&dst->rk, &src->rk, _state, make_automatic);
    ae_vector_init_copy(&dst->invutc, &src->invutc, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpidx, &src->tmpidx, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpd, &src->tmpd, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpc, &src->tmpc, _state, make_automatic);
    ae_vector_init_copy(&dst->tmplambdas, &src->tmplambdas, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmpc2, &src->tmpc2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmpb, &src->tmpb, _state, make_automatic);
    _snnlssolver_init_copy(&dst->nnls, &src->nnls, _state, make_automatic);
}


void _minnsqp_clear(void* _p)
{
    minnsqp *p = (minnsqp*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->xc);
    ae_vector_clear(&p->xn);
    ae_vector_clear(&p->x0);
    ae_vector_clear(&p->gc);
    ae_vector_clear(&p->d);
    ae_matrix_clear(&p->uh);
    ae_matrix_clear(&p->ch);
    ae_matrix_clear(&p->rk);
    ae_vector_clear(&p->invutc);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmpidx);
    ae_vector_clear(&p->tmpd);
    ae_vector_clear(&p->tmpc);
    ae_vector_clear(&p->tmplambdas);
    ae_matrix_clear(&p->tmpc2);
    ae_vector_clear(&p->tmpb);
    _snnlssolver_clear(&p->nnls);
}


void _minnsqp_destroy(void* _p)
{
    minnsqp *p = (minnsqp*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->xc);
    ae_vector_destroy(&p->xn);
    ae_vector_destroy(&p->x0);
    ae_vector_destroy(&p->gc);
    ae_vector_destroy(&p->d);
    ae_matrix_destroy(&p->uh);
    ae_matrix_destroy(&p->ch);
    ae_matrix_destroy(&p->rk);
    ae_vector_destroy(&p->invutc);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmpidx);
    ae_vector_destroy(&p->tmpd);
    ae_vector_destroy(&p->tmpc);
    ae_vector_destroy(&p->tmplambdas);
    ae_matrix_destroy(&p->tmpc2);
    ae_vector_destroy(&p->tmpb);
    _snnlssolver_destroy(&p->nnls);
}


void _minnsstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minnsstate *p = (minnsstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_matrix_init(&p->cleic, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fi, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->j, 0, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    _rcommstate_init(&p->rstateags, _state, make_automatic);
    _hqrndstate_init(&p->agsrs, _state, make_automatic);
    ae_vector_init(&p->xstart, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->rawg, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->meritg, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->colmax, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagh, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->signmin, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->signmax, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scaledbndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->scaledbndu, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->scaledcleic, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->samplex, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->samplegm, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->samplegmbc, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->samplef, 0, DT_REAL, _state, make_automatic);
    _minnsqp_init(&p->nsqp, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->tmp2, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp3, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->xbase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fbase, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->fm, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xscaled, 0, DT_REAL, _state, make_automatic);
}


void _minnsstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minnsstate       *dst = (minnsstate*)_dst;
    const minnsstate *src = (const minnsstate*)_src;
    dst->solvertype = src->solvertype;
    dst->n = src->n;
    dst->epsx = src->epsx;
    dst->maxits = src->maxits;
    dst->xrep = src->xrep;
    dst->diffstep = src->diffstep;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    dst->nec = src->nec;
    dst->nic = src->nic;
    ae_matrix_init_copy(&dst->cleic, &src->cleic, _state, make_automatic);
    dst->ng = src->ng;
    dst->nh = src->nh;
    dst->protocolversion = src->protocolversion;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    dst->f = src->f;
    ae_vector_init_copy(&dst->fi, &src->fi, _state, make_automatic);
    ae_matrix_init_copy(&dst->j, &src->j, _state, make_automatic);
    dst->needfij = src->needfij;
    dst->needfi = src->needfi;
    dst->xupdated = src->xupdated;
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    _rcommstate_init_copy(&dst->rstateags, &src->rstateags, _state, make_automatic);
    _hqrndstate_init_copy(&dst->agsrs, &src->agsrs, _state, make_automatic);
    dst->agsradius = src->agsradius;
    dst->agssamplesize = src->agssamplesize;
    dst->agsraddecay = src->agsraddecay;
    dst->agsalphadecay = src->agsalphadecay;
    dst->agsdecrease = src->agsdecrease;
    dst->agsinitstp = src->agsinitstp;
    dst->agsstattold = src->agsstattold;
    dst->agsshortstpabs = src->agsshortstpabs;
    dst->agsshortstprel = src->agsshortstprel;
    dst->agsshortf = src->agsshortf;
    dst->agsshortlimit = src->agsshortlimit;
    dst->agsrhononlinear = src->agsrhononlinear;
    dst->agsminupdate = src->agsminupdate;
    dst->agsmaxraddecays = src->agsmaxraddecays;
    dst->agsmaxbacktrack = src->agsmaxbacktrack;
    dst->agsmaxbacktracknonfull = src->agsmaxbacktracknonfull;
    dst->agspenaltylevel = src->agspenaltylevel;
    dst->agspenaltyincrease = src->agspenaltyincrease;
    ae_vector_init_copy(&dst->xstart, &src->xstart, _state, make_automatic);
    ae_vector_init_copy(&dst->xc, &src->xc, _state, make_automatic);
    ae_vector_init_copy(&dst->xn, &src->xn, _state, make_automatic);
    ae_vector_init_copy(&dst->rawg, &src->rawg, _state, make_automatic);
    ae_vector_init_copy(&dst->meritg, &src->meritg, _state, make_automatic);
    dst->rawf = src->rawf;
    dst->meritf = src->meritf;
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    ae_vector_init_copy(&dst->colmax, &src->colmax, _state, make_automatic);
    ae_vector_init_copy(&dst->diagh, &src->diagh, _state, make_automatic);
    ae_vector_init_copy(&dst->signmin, &src->signmin, _state, make_automatic);
    ae_vector_init_copy(&dst->signmax, &src->signmax, _state, make_automatic);
    dst->userterminationneeded = src->userterminationneeded;
    ae_vector_init_copy(&dst->scaledbndl, &src->scaledbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->scaledbndu, &src->scaledbndu, _state, make_automatic);
    ae_matrix_init_copy(&dst->scaledcleic, &src->scaledcleic, _state, make_automatic);
    dst->rholinear = src->rholinear;
    ae_matrix_init_copy(&dst->samplex, &src->samplex, _state, make_automatic);
    ae_matrix_init_copy(&dst->samplegm, &src->samplegm, _state, make_automatic);
    ae_matrix_init_copy(&dst->samplegmbc, &src->samplegmbc, _state, make_automatic);
    ae_vector_init_copy(&dst->samplef, &src->samplef, _state, make_automatic);
    _minnsqp_init_copy(&dst->nsqp, &src->nsqp, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
    ae_matrix_init_copy(&dst->tmp2, &src->tmp2, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp3, &src->tmp3, _state, make_automatic);
    ae_vector_init_copy(&dst->xbase, &src->xbase, _state, make_automatic);
    ae_vector_init_copy(&dst->fbase, &src->fbase, _state, make_automatic);
    ae_vector_init_copy(&dst->fp, &src->fp, _state, make_automatic);
    ae_vector_init_copy(&dst->fm, &src->fm, _state, make_automatic);
    ae_vector_init_copy(&dst->xscaled, &src->xscaled, _state, make_automatic);
    dst->repinneriterationscount = src->repinneriterationscount;
    dst->repouteriterationscount = src->repouteriterationscount;
    dst->repnfev = src->repnfev;
    dst->repvaridx = src->repvaridx;
    dst->repfuncidx = src->repfuncidx;
    dst->repterminationtype = src->repterminationtype;
    dst->replcerr = src->replcerr;
    dst->repnlcerr = src->repnlcerr;
    dst->dbgncholesky = src->dbgncholesky;
}


void _minnsstate_clear(void* _p)
{
    minnsstate *p = (minnsstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_matrix_clear(&p->cleic);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->fi);
    ae_matrix_clear(&p->j);
    _rcommstate_clear(&p->rstate);
    _rcommstate_clear(&p->rstateags);
    _hqrndstate_clear(&p->agsrs);
    ae_vector_clear(&p->xstart);
    ae_vector_clear(&p->xc);
    ae_vector_clear(&p->xn);
    ae_vector_clear(&p->rawg);
    ae_vector_clear(&p->meritg);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->colmax);
    ae_vector_clear(&p->diagh);
    ae_vector_clear(&p->signmin);
    ae_vector_clear(&p->signmax);
    ae_vector_clear(&p->scaledbndl);
    ae_vector_clear(&p->scaledbndu);
    ae_matrix_clear(&p->scaledcleic);
    ae_matrix_clear(&p->samplex);
    ae_matrix_clear(&p->samplegm);
    ae_matrix_clear(&p->samplegmbc);
    ae_vector_clear(&p->samplef);
    _minnsqp_clear(&p->nsqp);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->tmp1);
    ae_matrix_clear(&p->tmp2);
    ae_vector_clear(&p->tmp3);
    ae_vector_clear(&p->xbase);
    ae_vector_clear(&p->fbase);
    ae_vector_clear(&p->fp);
    ae_vector_clear(&p->fm);
    ae_vector_clear(&p->xscaled);
}


void _minnsstate_destroy(void* _p)
{
    minnsstate *p = (minnsstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_matrix_destroy(&p->cleic);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->fi);
    ae_matrix_destroy(&p->j);
    _rcommstate_destroy(&p->rstate);
    _rcommstate_destroy(&p->rstateags);
    _hqrndstate_destroy(&p->agsrs);
    ae_vector_destroy(&p->xstart);
    ae_vector_destroy(&p->xc);
    ae_vector_destroy(&p->xn);
    ae_vector_destroy(&p->rawg);
    ae_vector_destroy(&p->meritg);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->colmax);
    ae_vector_destroy(&p->diagh);
    ae_vector_destroy(&p->signmin);
    ae_vector_destroy(&p->signmax);
    ae_vector_destroy(&p->scaledbndl);
    ae_vector_destroy(&p->scaledbndu);
    ae_matrix_destroy(&p->scaledcleic);
    ae_matrix_destroy(&p->samplex);
    ae_matrix_destroy(&p->samplegm);
    ae_matrix_destroy(&p->samplegmbc);
    ae_vector_destroy(&p->samplef);
    _minnsqp_destroy(&p->nsqp);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->tmp1);
    ae_matrix_destroy(&p->tmp2);
    ae_vector_destroy(&p->tmp3);
    ae_vector_destroy(&p->xbase);
    ae_vector_destroy(&p->fbase);
    ae_vector_destroy(&p->fp);
    ae_vector_destroy(&p->fm);
    ae_vector_destroy(&p->xscaled);
}


void _minnsreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minnsreport *p = (minnsreport*)_p;
    ae_touch_ptr((void*)p);
}


void _minnsreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minnsreport       *dst = (minnsreport*)_dst;
    const minnsreport *src = (const minnsreport*)_src;
    dst->iterationscount = src->iterationscount;
    dst->nfev = src->nfev;
    dst->cerr = src->cerr;
    dst->lcerr = src->lcerr;
    dst->nlcerr = src->nlcerr;
    dst->terminationtype = src->terminationtype;
    dst->varidx = src->varidx;
    dst->funcidx = src->funcidx;
}


void _minnsreport_clear(void* _p)
{
    minnsreport *p = (minnsreport*)_p;
    ae_touch_ptr((void*)p);
}


void _minnsreport_destroy(void* _p)
{
    minnsreport *p = (minnsreport*)_p;
    ae_touch_ptr((void*)p);
}


#endif
#if defined(AE_COMPILE_MINCOMP) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
Obsolete function, use MinLBFGSSetPrecDefault() instead.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetdefaultpreconditioner(minlbfgsstate* state,
     ae_state *_state)
{


    minlbfgssetprecdefault(state, _state);
}


/*************************************************************************
Obsolete function, use MinLBFGSSetCholeskyPreconditioner() instead.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minlbfgssetcholeskypreconditioner(minlbfgsstate* state,
     /* Real    */ const ae_matrix* p,
     ae_bool isupper,
     ae_state *_state)
{


    minlbfgssetpreccholesky(state, p, isupper, _state);
}


/*************************************************************************
This is obsolete function which was used by previous version of the  BLEIC
optimizer. It does nothing in the current version of BLEIC.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetbarrierwidth(minbleicstate* state,
     double mu,
     ae_state *_state)
{


}


/*************************************************************************
This is obsolete function which was used by previous version of the  BLEIC
optimizer. It does nothing in the current version of BLEIC.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbleicsetbarrierdecay(minbleicstate* state,
     double mudecay,
     ae_state *_state)
{


}


/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 25.03.2010 by Bochkanov Sergey
*************************************************************************/
void minasacreate(ae_int_t n,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     minasastate* state,
     ae_state *_state)
{
    ae_int_t i;

    _minasastate_clear(state);

    ae_assert(n>=1, "MinASA: N too small!", _state);
    ae_assert(x->cnt>=n, "MinCGCreate: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, n, _state), "MinCGCreate: X contains infinite or NaN values!", _state);
    ae_assert(bndl->cnt>=n, "MinCGCreate: Length(BndL)<N!", _state);
    ae_assert(isfinitevector(bndl, n, _state), "MinCGCreate: BndL contains infinite or NaN values!", _state);
    ae_assert(bndu->cnt>=n, "MinCGCreate: Length(BndU)<N!", _state);
    ae_assert(isfinitevector(bndu, n, _state), "MinCGCreate: BndU contains infinite or NaN values!", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_fp_less_eq(bndl->ptr.p_double[i],bndu->ptr.p_double[i]), "MinASA: inconsistent bounds!", _state);
        ae_assert(ae_fp_less_eq(bndl->ptr.p_double[i],x->ptr.p_double[i]), "MinASA: infeasible X!", _state);
        ae_assert(ae_fp_less_eq(x->ptr.p_double[i],bndu->ptr.p_double[i]), "MinASA: infeasible X!", _state);
    }
    
    /*
     * Initialize
     */
    state->n = n;
    minasasetcond(state, (double)(0), (double)(0), (double)(0), 0, _state);
    minasasetxrep(state, ae_false, _state);
    minasasetstpmax(state, (double)(0), _state);
    minasasetalgorithm(state, -1, _state);
    ae_vector_set_length(&state->bndl, n, _state);
    ae_vector_set_length(&state->bndu, n, _state);
    ae_vector_set_length(&state->ak, n, _state);
    ae_vector_set_length(&state->xk, n, _state);
    ae_vector_set_length(&state->dk, n, _state);
    ae_vector_set_length(&state->an, n, _state);
    ae_vector_set_length(&state->xn, n, _state);
    ae_vector_set_length(&state->dn, n, _state);
    ae_vector_set_length(&state->x, n, _state);
    ae_vector_set_length(&state->d, n, _state);
    ae_vector_set_length(&state->g, n, _state);
    ae_vector_set_length(&state->gc, n, _state);
    ae_vector_set_length(&state->work, n, _state);
    ae_vector_set_length(&state->yk, n, _state);
    minasarestartfrom(state, x, bndl, bndu, _state);
}


/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minasasetcond(minasastate* state,
     double epsg,
     double epsf,
     double epsx,
     ae_int_t maxits,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsg, _state), "MinASASetCond: EpsG is not finite number!", _state);
    ae_assert(ae_fp_greater_eq(epsg,(double)(0)), "MinASASetCond: negative EpsG!", _state);
    ae_assert(ae_isfinite(epsf, _state), "MinASASetCond: EpsF is not finite number!", _state);
    ae_assert(ae_fp_greater_eq(epsf,(double)(0)), "MinASASetCond: negative EpsF!", _state);
    ae_assert(ae_isfinite(epsx, _state), "MinASASetCond: EpsX is not finite number!", _state);
    ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "MinASASetCond: negative EpsX!", _state);
    ae_assert(maxits>=0, "MinASASetCond: negative MaxIts!", _state);
    if( ((ae_fp_eq(epsg,(double)(0))&&ae_fp_eq(epsf,(double)(0)))&&ae_fp_eq(epsx,(double)(0)))&&maxits==0 )
    {
        epsx = 1.0E-6;
    }
    state->epsg = epsg;
    state->epsf = epsf;
    state->epsx = epsx;
    state->maxits = maxits;
}


/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minasasetxrep(minasastate* state, ae_bool needxrep, ae_state *_state)
{


    state->xrep = needxrep;
}


/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minasasetalgorithm(minasastate* state,
     ae_int_t algotype,
     ae_state *_state)
{


    ae_assert(algotype>=-1&&algotype<=1, "MinASASetAlgorithm: incorrect AlgoType!", _state);
    if( algotype==-1 )
    {
        algotype = 1;
    }
    state->cgtype = algotype;
}


/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minasasetstpmax(minasastate* state, double stpmax, ae_state *_state)
{


    ae_assert(ae_isfinite(stpmax, _state), "MinASASetStpMax: StpMax is not finite!", _state);
    ae_assert(ae_fp_greater_eq(stpmax,(double)(0)), "MinASASetStpMax: StpMax<0!", _state);
    state->stpmax = stpmax;
}


/*************************************************************************

  -- ALGLIB --
     Copyright 20.03.2009 by Bochkanov Sergey
*************************************************************************/
ae_bool minasaiteration(minasastate* state, ae_state *_state)
{
    ae_int_t n;
    ae_int_t i;
    double betak;
    double v;
    double vv;
    ae_int_t mcinfo;
    ae_bool b;
    ae_bool stepfound;
    ae_int_t diffcnt;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        n = state->rstate.ia.ptr.p_int[0];
        i = state->rstate.ia.ptr.p_int[1];
        mcinfo = state->rstate.ia.ptr.p_int[2];
        diffcnt = state->rstate.ia.ptr.p_int[3];
        b = state->rstate.ba.ptr.p_bool[0];
        stepfound = state->rstate.ba.ptr.p_bool[1];
        betak = state->rstate.ra.ptr.p_double[0];
        v = state->rstate.ra.ptr.p_double[1];
        vv = state->rstate.ra.ptr.p_double[2];
    }
    else
    {
        n = 359;
        i = -58;
        mcinfo = -919;
        diffcnt = -909;
        b = ae_true;
        stepfound = ae_true;
        betak = 74.0;
        v = -788.0;
        vv = 809.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    if( state->rstate.stage==6 )
    {
        goto lbl_6;
    }
    if( state->rstate.stage==7 )
    {
        goto lbl_7;
    }
    if( state->rstate.stage==8 )
    {
        goto lbl_8;
    }
    if( state->rstate.stage==9 )
    {
        goto lbl_9;
    }
    if( state->rstate.stage==10 )
    {
        goto lbl_10;
    }
    if( state->rstate.stage==11 )
    {
        goto lbl_11;
    }
    if( state->rstate.stage==12 )
    {
        goto lbl_12;
    }
    if( state->rstate.stage==13 )
    {
        goto lbl_13;
    }
    if( state->rstate.stage==14 )
    {
        goto lbl_14;
    }
    
    /*
     * Routine body
     */
    
    /*
     * Prepare
     */
    n = state->n;
    state->repterminationtype = 0;
    state->repiterationscount = 0;
    state->repnfev = 0;
    state->debugrestartscount = 0;
    state->cgtype = 1;
    ae_v_move(&state->xk.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_eq(state->xk.ptr.p_double[i],state->bndl.ptr.p_double[i])||ae_fp_eq(state->xk.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            state->ak.ptr.p_double[i] = (double)(0);
        }
        else
        {
            state->ak.ptr.p_double[i] = (double)(1);
        }
    }
    state->mu = 0.1;
    state->curalgo = 0;
    
    /*
     * Calculate F/G, initialize algorithm
     */
    mincomp_clearrequestfields(state, _state);
    state->needfg = ae_true;
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    state->needfg = ae_false;
    if( !state->xrep )
    {
        goto lbl_15;
    }
    
    /*
     * progress report
     */
    mincomp_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->xupdated = ae_false;
lbl_15:
    if( ae_fp_less_eq(mincomp_asaboundedantigradnorm(state, _state),state->epsg) )
    {
        state->repterminationtype = 4;
        result = ae_false;
        return result;
    }
    state->repnfev = state->repnfev+1;
    
    /*
     * Main cycle
     *
     * At the beginning of new iteration:
     * * CurAlgo stores current algorithm selector
     * * State.XK, State.F and State.G store current X/F/G
     * * State.AK stores current set of active constraints
     */
lbl_17:
    if( ae_false )
    {
        goto lbl_18;
    }
    
    /*
     * GPA algorithm
     */
    if( state->curalgo!=0 )
    {
        goto lbl_19;
    }
    state->k = 0;
    state->acount = 0;
lbl_21:
    if( ae_false )
    {
        goto lbl_22;
    }
    
    /*
     * Determine Dk = proj(xk - gk)-xk
     */
    for(i=0; i<=n-1; i++)
    {
        state->d.ptr.p_double[i] = boundval(state->xk.ptr.p_double[i]-state->g.ptr.p_double[i], state->bndl.ptr.p_double[i], state->bndu.ptr.p_double[i], _state)-state->xk.ptr.p_double[i];
    }
    
    /*
     * Armijo line search.
     * * exact search with alpha=1 is tried first,
     *   'exact' means that we evaluate f() EXACTLY at
     *   bound(x-g,bndl,bndu), without intermediate floating
     *   point operations.
     * * alpha<1 are tried if explicit search wasn't successful
     * Result is placed into XN.
     *
     * Two types of search are needed because we can't
     * just use second type with alpha=1 because in finite
     * precision arithmetics (x1-x0)+x0 may differ from x1.
     * So while x1 is correctly bounded (it lie EXACTLY on
     * boundary, if it is active), (x1-x0)+x0 may be
     * not bounded.
     */
    v = ae_v_dotproduct(&state->d.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->dginit = v;
    state->finit = state->f;
    if( !(ae_fp_less_eq(mincomp_asad1norm(state, _state),state->stpmax)||ae_fp_eq(state->stpmax,(double)(0))) )
    {
        goto lbl_23;
    }
    
    /*
     * Try alpha=1 step first
     */
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = boundval(state->xk.ptr.p_double[i]-state->g.ptr.p_double[i], state->bndl.ptr.p_double[i], state->bndu.ptr.p_double[i], _state);
    }
    mincomp_clearrequestfields(state, _state);
    state->needfg = ae_true;
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->needfg = ae_false;
    state->repnfev = state->repnfev+1;
    stepfound = ae_fp_less_eq(state->f,state->finit+mincomp_gpaftol*state->dginit);
    goto lbl_24;
lbl_23:
    stepfound = ae_false;
lbl_24:
    if( !stepfound )
    {
        goto lbl_25;
    }
    
    /*
     * we are at the boundary(ies)
     */
    ae_v_move(&state->xn.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->stp = (double)(1);
    goto lbl_26;
lbl_25:
    
    /*
     * alpha=1 is too large, try smaller values
     */
    state->stp = (double)(1);
    linminnormalized(&state->d, &state->stp, n, _state);
    state->dginit = state->dginit/state->stp;
    state->stp = mincomp_gpadecay*state->stp;
    if( ae_fp_greater(state->stpmax,(double)(0)) )
    {
        state->stp = ae_minreal(state->stp, state->stpmax, _state);
    }
lbl_27:
    if( ae_false )
    {
        goto lbl_28;
    }
    v = state->stp;
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xk.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_addd(&state->x.ptr.p_double[0], 1, &state->d.ptr.p_double[0], 1, ae_v_len(0,n-1), v);
    mincomp_clearrequestfields(state, _state);
    state->needfg = ae_true;
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    state->needfg = ae_false;
    state->repnfev = state->repnfev+1;
    if( ae_fp_less_eq(state->stp,mincomp_stpmin) )
    {
        goto lbl_28;
    }
    if( ae_fp_less_eq(state->f,state->finit+state->stp*mincomp_gpaftol*state->dginit) )
    {
        goto lbl_28;
    }
    state->stp = state->stp*mincomp_gpadecay;
    goto lbl_27;
lbl_28:
    ae_v_move(&state->xn.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
lbl_26:
    state->repiterationscount = state->repiterationscount+1;
    if( !state->xrep )
    {
        goto lbl_29;
    }
    
    /*
     * progress report
     */
    mincomp_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    state->xupdated = ae_false;
lbl_29:
    
    /*
     * Calculate new set of active constraints.
     * Reset counter if active set was changed.
     * Prepare for the new iteration
     */
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_eq(state->xn.ptr.p_double[i],state->bndl.ptr.p_double[i])||ae_fp_eq(state->xn.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            state->an.ptr.p_double[i] = (double)(0);
        }
        else
        {
            state->an.ptr.p_double[i] = (double)(1);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_neq(state->ak.ptr.p_double[i],state->an.ptr.p_double[i]) )
        {
            state->acount = -1;
            break;
        }
    }
    state->acount = state->acount+1;
    ae_v_move(&state->xk.ptr.p_double[0], 1, &state->xn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->ak.ptr.p_double[0], 1, &state->an.ptr.p_double[0], 1, ae_v_len(0,n-1));
    
    /*
     * Stopping conditions
     */
    if( !(state->repiterationscount>=state->maxits&&state->maxits>0) )
    {
        goto lbl_31;
    }
    
    /*
     * Too many iterations
     */
    state->repterminationtype = 5;
    if( !state->xrep )
    {
        goto lbl_33;
    }
    mincomp_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    state->xupdated = ae_false;
lbl_33:
    result = ae_false;
    return result;
lbl_31:
    if( ae_fp_greater(mincomp_asaboundedantigradnorm(state, _state),state->epsg) )
    {
        goto lbl_35;
    }
    
    /*
     * Gradient is small enough
     */
    state->repterminationtype = 4;
    if( !state->xrep )
    {
        goto lbl_37;
    }
    mincomp_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 6;
    goto lbl_rcomm;
lbl_6:
    state->xupdated = ae_false;
lbl_37:
    result = ae_false;
    return result;
lbl_35:
    v = ae_v_dotproduct(&state->d.ptr.p_double[0], 1, &state->d.ptr.p_double[0], 1, ae_v_len(0,n-1));
    if( ae_fp_greater(ae_sqrt(v, _state)*state->stp,state->epsx) )
    {
        goto lbl_39;
    }
    
    /*
     * Step size is too small, no further improvement is
     * possible
     */
    state->repterminationtype = 2;
    if( !state->xrep )
    {
        goto lbl_41;
    }
    mincomp_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 7;
    goto lbl_rcomm;
lbl_7:
    state->xupdated = ae_false;
lbl_41:
    result = ae_false;
    return result;
lbl_39:
    if( ae_fp_greater(state->finit-state->f,state->epsf*ae_maxreal(ae_fabs(state->finit, _state), ae_maxreal(ae_fabs(state->f, _state), 1.0, _state), _state)) )
    {
        goto lbl_43;
    }
    
    /*
     * F(k+1)-F(k) is small enough
     */
    state->repterminationtype = 1;
    if( !state->xrep )
    {
        goto lbl_45;
    }
    mincomp_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 8;
    goto lbl_rcomm;
lbl_8:
    state->xupdated = ae_false;
lbl_45:
    result = ae_false;
    return result;
lbl_43:
    
    /*
     * Decide - should we switch algorithm or not
     */
    if( mincomp_asauisempty(state, _state) )
    {
        if( ae_fp_greater_eq(mincomp_asaginorm(state, _state),state->mu*mincomp_asad1norm(state, _state)) )
        {
            state->curalgo = 1;
            goto lbl_22;
        }
        else
        {
            state->mu = state->mu*mincomp_asarho;
        }
    }
    else
    {
        if( state->acount==mincomp_n1 )
        {
            if( ae_fp_greater_eq(mincomp_asaginorm(state, _state),state->mu*mincomp_asad1norm(state, _state)) )
            {
                state->curalgo = 1;
                goto lbl_22;
            }
        }
    }
    
    /*
     * Next iteration
     */
    state->k = state->k+1;
    goto lbl_21;
lbl_22:
lbl_19:
    
    /*
     * CG algorithm
     */
    if( state->curalgo!=1 )
    {
        goto lbl_47;
    }
    
    /*
     * first, check that there are non-active constraints.
     * move to GPA algorithm, if all constraints are active
     */
    b = ae_true;
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_neq(state->ak.ptr.p_double[i],(double)(0)) )
        {
            b = ae_false;
            break;
        }
    }
    if( b )
    {
        state->curalgo = 0;
        goto lbl_17;
    }
    
    /*
     * CG iterations
     */
    state->fold = state->f;
    ae_v_move(&state->xk.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
    for(i=0; i<=n-1; i++)
    {
        state->dk.ptr.p_double[i] = -state->g.ptr.p_double[i]*state->ak.ptr.p_double[i];
        state->gc.ptr.p_double[i] = state->g.ptr.p_double[i]*state->ak.ptr.p_double[i];
    }
lbl_49:
    if( ae_false )
    {
        goto lbl_50;
    }
    
    /*
     * Store G[k] for later calculation of Y[k]
     */
    for(i=0; i<=n-1; i++)
    {
        state->yk.ptr.p_double[i] = -state->gc.ptr.p_double[i];
    }
    
    /*
     * Make a CG step in direction given by DK[]:
     * * calculate step. Step projection into feasible set
     *   is used. It has several benefits: a) step may be
     *   found with usual line search, b) multiple constraints
     *   may be activated with one step, c) activated constraints
     *   are detected in a natural way - just compare x[i] with
     *   bounds
     * * update active set, set B to True, if there
     *   were changes in the set.
     */
    ae_v_move(&state->d.ptr.p_double[0], 1, &state->dk.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->xn.ptr.p_double[0], 1, &state->xk.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->mcstage = 0;
    state->stp = (double)(1);
    linminnormalized(&state->d, &state->stp, n, _state);
    if( ae_fp_neq(state->laststep,(double)(0)) )
    {
        state->stp = state->laststep;
    }
    mcsrch(n, &state->xn, &state->f, &state->gc, &state->d, &state->stp, state->stpmax, mincomp_gtol, &mcinfo, &state->nfev, &state->work, &state->lstate, &state->mcstage, _state);
lbl_51:
    if( state->mcstage==0 )
    {
        goto lbl_52;
    }
    
    /*
     * preprocess data: bound State.XN so it belongs to the
     * feasible set and store it in the State.X
     */
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = boundval(state->xn.ptr.p_double[i], state->bndl.ptr.p_double[i], state->bndu.ptr.p_double[i], _state);
    }
    
    /*
     * RComm
     */
    mincomp_clearrequestfields(state, _state);
    state->needfg = ae_true;
    state->rstate.stage = 9;
    goto lbl_rcomm;
lbl_9:
    state->needfg = ae_false;
    
    /*
     * postprocess data: zero components of G corresponding to
     * the active constraints
     */
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_eq(state->x.ptr.p_double[i],state->bndl.ptr.p_double[i])||ae_fp_eq(state->x.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            state->gc.ptr.p_double[i] = (double)(0);
        }
        else
        {
            state->gc.ptr.p_double[i] = state->g.ptr.p_double[i];
        }
    }
    mcsrch(n, &state->xn, &state->f, &state->gc, &state->d, &state->stp, state->stpmax, mincomp_gtol, &mcinfo, &state->nfev, &state->work, &state->lstate, &state->mcstage, _state);
    goto lbl_51;
lbl_52:
    diffcnt = 0;
    for(i=0; i<=n-1; i++)
    {
        
        /*
         * XN contains unprojected result, project it,
         * save copy to X (will be used for progress reporting)
         */
        state->xn.ptr.p_double[i] = boundval(state->xn.ptr.p_double[i], state->bndl.ptr.p_double[i], state->bndu.ptr.p_double[i], _state);
        
        /*
         * update active set
         */
        if( ae_fp_eq(state->xn.ptr.p_double[i],state->bndl.ptr.p_double[i])||ae_fp_eq(state->xn.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            state->an.ptr.p_double[i] = (double)(0);
        }
        else
        {
            state->an.ptr.p_double[i] = (double)(1);
        }
        if( ae_fp_neq(state->an.ptr.p_double[i],state->ak.ptr.p_double[i]) )
        {
            diffcnt = diffcnt+1;
        }
        state->ak.ptr.p_double[i] = state->an.ptr.p_double[i];
    }
    ae_v_move(&state->xk.ptr.p_double[0], 1, &state->xn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->repnfev = state->repnfev+state->nfev;
    state->repiterationscount = state->repiterationscount+1;
    if( !state->xrep )
    {
        goto lbl_53;
    }
    
    /*
     * progress report
     */
    mincomp_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 10;
    goto lbl_rcomm;
lbl_10:
    state->xupdated = ae_false;
lbl_53:
    
    /*
     * Update info about step length
     */
    v = ae_v_dotproduct(&state->d.ptr.p_double[0], 1, &state->d.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->laststep = ae_sqrt(v, _state)*state->stp;
    
    /*
     * Check stopping conditions.
     */
    if( ae_fp_greater(mincomp_asaboundedantigradnorm(state, _state),state->epsg) )
    {
        goto lbl_55;
    }
    
    /*
     * Gradient is small enough
     */
    state->repterminationtype = 4;
    if( !state->xrep )
    {
        goto lbl_57;
    }
    mincomp_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 11;
    goto lbl_rcomm;
lbl_11:
    state->xupdated = ae_false;
lbl_57:
    result = ae_false;
    return result;
lbl_55:
    if( !(state->repiterationscount>=state->maxits&&state->maxits>0) )
    {
        goto lbl_59;
    }
    
    /*
     * Too many iterations
     */
    state->repterminationtype = 5;
    if( !state->xrep )
    {
        goto lbl_61;
    }
    mincomp_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 12;
    goto lbl_rcomm;
lbl_12:
    state->xupdated = ae_false;
lbl_61:
    result = ae_false;
    return result;
lbl_59:
    if( !(ae_fp_greater_eq(mincomp_asaginorm(state, _state),state->mu*mincomp_asad1norm(state, _state))&&diffcnt==0) )
    {
        goto lbl_63;
    }
    
    /*
     * These conditions (EpsF/EpsX) are explicitly or implicitly
     * related to the current step size and influenced
     * by changes in the active constraints.
     *
     * For these reasons they are checked only when we don't
     * want to 'unstick' at the end of the iteration and there
     * were no changes in the active set.
     *
     * NOTE: consition |G|>=Mu*|D1| must be exactly opposite
     * to the condition used to switch back to GPA. At least
     * one inequality must be strict, otherwise infinite cycle
     * may occur when |G|=Mu*|D1| (we DON'T test stopping
     * conditions and we DON'T switch to GPA, so we cycle
     * indefinitely).
     */
    if( ae_fp_greater(state->fold-state->f,state->epsf*ae_maxreal(ae_fabs(state->fold, _state), ae_maxreal(ae_fabs(state->f, _state), 1.0, _state), _state)) )
    {
        goto lbl_65;
    }
    
    /*
     * F(k+1)-F(k) is small enough
     */
    state->repterminationtype = 1;
    if( !state->xrep )
    {
        goto lbl_67;
    }
    mincomp_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 13;
    goto lbl_rcomm;
lbl_13:
    state->xupdated = ae_false;
lbl_67:
    result = ae_false;
    return result;
lbl_65:
    if( ae_fp_greater(state->laststep,state->epsx) )
    {
        goto lbl_69;
    }
    
    /*
     * X(k+1)-X(k) is small enough
     */
    state->repterminationtype = 2;
    if( !state->xrep )
    {
        goto lbl_71;
    }
    mincomp_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 14;
    goto lbl_rcomm;
lbl_14:
    state->xupdated = ae_false;
lbl_71:
    result = ae_false;
    return result;
lbl_69:
lbl_63:
    
    /*
     * Check conditions for switching
     */
    if( ae_fp_less(mincomp_asaginorm(state, _state),state->mu*mincomp_asad1norm(state, _state)) )
    {
        state->curalgo = 0;
        goto lbl_50;
    }
    if( diffcnt>0 )
    {
        if( mincomp_asauisempty(state, _state)||diffcnt>=mincomp_n2 )
        {
            state->curalgo = 1;
        }
        else
        {
            state->curalgo = 0;
        }
        goto lbl_50;
    }
    
    /*
     * Calculate D(k+1)
     *
     * Line search may result in:
     * * maximum feasible step being taken (already processed)
     * * point satisfying Wolfe conditions
     * * some kind of error (CG is restarted by assigning 0.0 to Beta)
     */
    if( mcinfo==1 )
    {
        
        /*
         * Standard Wolfe conditions are satisfied:
         * * calculate Y[K] and BetaK
         */
        ae_v_add(&state->yk.ptr.p_double[0], 1, &state->gc.ptr.p_double[0], 1, ae_v_len(0,n-1));
        vv = ae_v_dotproduct(&state->yk.ptr.p_double[0], 1, &state->dk.ptr.p_double[0], 1, ae_v_len(0,n-1));
        v = ae_v_dotproduct(&state->gc.ptr.p_double[0], 1, &state->gc.ptr.p_double[0], 1, ae_v_len(0,n-1));
        state->betady = v/vv;
        v = ae_v_dotproduct(&state->gc.ptr.p_double[0], 1, &state->yk.ptr.p_double[0], 1, ae_v_len(0,n-1));
        state->betahs = v/vv;
        if( state->cgtype==0 )
        {
            betak = state->betady;
        }
        if( state->cgtype==1 )
        {
            betak = ae_maxreal((double)(0), ae_minreal(state->betady, state->betahs, _state), _state);
        }
    }
    else
    {
        
        /*
         * Something is wrong (may be function is too wild or too flat).
         *
         * We'll set BetaK=0, which will restart CG algorithm.
         * We can stop later (during normal checks) if stopping conditions are met.
         */
        betak = (double)(0);
        state->debugrestartscount = state->debugrestartscount+1;
    }
    ae_v_moveneg(&state->dn.ptr.p_double[0], 1, &state->gc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_addd(&state->dn.ptr.p_double[0], 1, &state->dk.ptr.p_double[0], 1, ae_v_len(0,n-1), betak);
    ae_v_move(&state->dk.ptr.p_double[0], 1, &state->dn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    
    /*
     * update other information
     */
    state->fold = state->f;
    state->k = state->k+1;
    goto lbl_49;
lbl_50:
lbl_47:
    goto lbl_17;
lbl_18:
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = n;
    state->rstate.ia.ptr.p_int[1] = i;
    state->rstate.ia.ptr.p_int[2] = mcinfo;
    state->rstate.ia.ptr.p_int[3] = diffcnt;
    state->rstate.ba.ptr.p_bool[0] = b;
    state->rstate.ba.ptr.p_bool[1] = stepfound;
    state->rstate.ra.ptr.p_double[0] = betak;
    state->rstate.ra.ptr.p_double[1] = v;
    state->rstate.ra.ptr.p_double[2] = vv;
    return result;
}


/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 20.03.2009 by Bochkanov Sergey
*************************************************************************/
void minasaresults(const minasastate* state,
     /* Real    */ ae_vector* x,
     minasareport* rep,
     ae_state *_state)
{

    ae_vector_clear(x);
    _minasareport_clear(rep);

    minasaresultsbuf(state, x, rep, _state);
}


/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 20.03.2009 by Bochkanov Sergey
*************************************************************************/
void minasaresultsbuf(const minasastate* state,
     /* Real    */ ae_vector* x,
     minasareport* rep,
     ae_state *_state)
{
    ae_int_t i;


    if( x->cnt<state->n )
    {
        ae_vector_set_length(x, state->n, _state);
    }
    ae_v_move(&x->ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,state->n-1));
    rep->iterationscount = state->repiterationscount;
    rep->nfev = state->repnfev;
    rep->terminationtype = state->repterminationtype;
    rep->activeconstraints = 0;
    for(i=0; i<=state->n-1; i++)
    {
        if( ae_fp_eq(state->ak.ptr.p_double[i],(double)(0)) )
        {
            rep->activeconstraints = rep->activeconstraints+1;
        }
    }
}


/*************************************************************************
Obsolete optimization algorithm.
Was replaced by MinBLEIC subpackage.

  -- ALGLIB --
     Copyright 30.07.2010 by Bochkanov Sergey
*************************************************************************/
void minasarestartfrom(minasastate* state,
     /* Real    */ const ae_vector* x,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{


    ae_assert(x->cnt>=state->n, "MinASARestartFrom: Length(X)<N!", _state);
    ae_assert(isfinitevector(x, state->n, _state), "MinASARestartFrom: X contains infinite or NaN values!", _state);
    ae_assert(bndl->cnt>=state->n, "MinASARestartFrom: Length(BndL)<N!", _state);
    ae_assert(isfinitevector(bndl, state->n, _state), "MinASARestartFrom: BndL contains infinite or NaN values!", _state);
    ae_assert(bndu->cnt>=state->n, "MinASARestartFrom: Length(BndU)<N!", _state);
    ae_assert(isfinitevector(bndu, state->n, _state), "MinASARestartFrom: BndU contains infinite or NaN values!", _state);
    ae_v_move(&state->x.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,state->n-1));
    ae_v_move(&state->bndl.ptr.p_double[0], 1, &bndl->ptr.p_double[0], 1, ae_v_len(0,state->n-1));
    ae_v_move(&state->bndu.ptr.p_double[0], 1, &bndu->ptr.p_double[0], 1, ae_v_len(0,state->n-1));
    state->laststep = (double)(0);
    ae_vector_set_length(&state->rstate.ia, 3+1, _state);
    ae_vector_set_length(&state->rstate.ba, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 2+1, _state);
    state->rstate.stage = -1;
    mincomp_clearrequestfields(state, _state);
}


/*************************************************************************
Set V1 reverse communication protocol
*************************************************************************/
void minasasetprotocolv1(minasastate* state, ae_state *_state)
{


    ae_vector_set_length(&state->rstate.ia, 3+1, _state);
    ae_vector_set_length(&state->rstate.ba, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 2+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Returns norm of bounded anti-gradient.

Bounded antigradient is a vector obtained from  anti-gradient  by  zeroing
components which point outwards:
    result = norm(v)
    v[i]=0     if ((-g[i]<0)and(x[i]=bndl[i])) or
                  ((-g[i]>0)and(x[i]=bndu[i]))
    v[i]=-g[i] otherwise

This function may be used to check a stopping criterion.

  -- ALGLIB --
     Copyright 20.03.2009 by Bochkanov Sergey
*************************************************************************/
static double mincomp_asaboundedantigradnorm(const minasastate* state,
     ae_state *_state)
{
    ae_int_t i;
    double v;
    double result;


    result = (double)(0);
    for(i=0; i<=state->n-1; i++)
    {
        v = -state->g.ptr.p_double[i];
        if( ae_fp_eq(state->x.ptr.p_double[i],state->bndl.ptr.p_double[i])&&ae_fp_less(-state->g.ptr.p_double[i],(double)(0)) )
        {
            v = (double)(0);
        }
        if( ae_fp_eq(state->x.ptr.p_double[i],state->bndu.ptr.p_double[i])&&ae_fp_greater(-state->g.ptr.p_double[i],(double)(0)) )
        {
            v = (double)(0);
        }
        result = result+ae_sqr(v, _state);
    }
    result = ae_sqrt(result, _state);
    return result;
}


/*************************************************************************
Returns norm of GI(x).

GI(x) is  a  gradient  vector  whose  components  associated  with  active
constraints are zeroed. It  differs  from  bounded  anti-gradient  because
components  of   GI(x)   are   zeroed  independently  of  sign(g[i]),  and
anti-gradient's components are zeroed with respect to both constraint  and
sign.

  -- ALGLIB --
     Copyright 20.03.2009 by Bochkanov Sergey
*************************************************************************/
static double mincomp_asaginorm(const minasastate* state,
     ae_state *_state)
{
    ae_int_t i;
    double result;


    result = (double)(0);
    for(i=0; i<=state->n-1; i++)
    {
        if( ae_fp_neq(state->x.ptr.p_double[i],state->bndl.ptr.p_double[i])&&ae_fp_neq(state->x.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            result = result+ae_sqr(state->g.ptr.p_double[i], _state);
        }
    }
    result = ae_sqrt(result, _state);
    return result;
}


/*************************************************************************
Returns norm(D1(State.X))

For a meaning of D1 see 'NEW ACTIVE SET ALGORITHM FOR BOX CONSTRAINED
OPTIMIZATION' by WILLIAM W. HAGER AND HONGCHAO ZHANG.

  -- ALGLIB --
     Copyright 20.03.2009 by Bochkanov Sergey
*************************************************************************/
static double mincomp_asad1norm(const minasastate* state,
     ae_state *_state)
{
    ae_int_t i;
    double result;


    result = (double)(0);
    for(i=0; i<=state->n-1; i++)
    {
        result = result+ae_sqr(boundval(state->x.ptr.p_double[i]-state->g.ptr.p_double[i], state->bndl.ptr.p_double[i], state->bndu.ptr.p_double[i], _state)-state->x.ptr.p_double[i], _state);
    }
    result = ae_sqrt(result, _state);
    return result;
}


/*************************************************************************
Returns True, if U set is empty.

* State.X is used as point,
* State.G - as gradient,
* D is calculated within function (because State.D may have different
  meaning depending on current optimization algorithm)

For a meaning of U see 'NEW ACTIVE SET ALGORITHM FOR BOX CONSTRAINED
OPTIMIZATION' by WILLIAM W. HAGER AND HONGCHAO ZHANG.

  -- ALGLIB --
     Copyright 20.03.2009 by Bochkanov Sergey
*************************************************************************/
static ae_bool mincomp_asauisempty(const minasastate* state,
     ae_state *_state)
{
    ae_int_t i;
    double d;
    double d2;
    double d32;
    ae_bool result;


    d = mincomp_asad1norm(state, _state);
    d2 = ae_sqrt(d, _state);
    d32 = d*d2;
    result = ae_true;
    for(i=0; i<=state->n-1; i++)
    {
        if( ae_fp_greater_eq(ae_fabs(state->g.ptr.p_double[i], _state),d2)&&ae_fp_greater_eq(ae_minreal(state->x.ptr.p_double[i]-state->bndl.ptr.p_double[i], state->bndu.ptr.p_double[i]-state->x.ptr.p_double[i], _state),d32) )
        {
            result = ae_false;
            return result;
        }
    }
    return result;
}


/*************************************************************************
Clears request fileds (to be sure that we don't forgot to clear something)
*************************************************************************/
static void mincomp_clearrequestfields(minasastate* state,
     ae_state *_state)
{


    state->needfg = ae_false;
    state->xupdated = ae_false;
}


void _minasastate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minasastate *p = (minasastate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ak, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xk, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dk, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->an, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->dn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->work, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->yk, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->gc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    _linminstate_init(&p->lstate, _state, make_automatic);
}


void _minasastate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minasastate       *dst = (minasastate*)_dst;
    const minasastate *src = (const minasastate*)_src;
    dst->n = src->n;
    dst->epsg = src->epsg;
    dst->epsf = src->epsf;
    dst->epsx = src->epsx;
    dst->maxits = src->maxits;
    dst->xrep = src->xrep;
    dst->stpmax = src->stpmax;
    dst->cgtype = src->cgtype;
    dst->k = src->k;
    dst->nfev = src->nfev;
    dst->mcstage = src->mcstage;
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    dst->curalgo = src->curalgo;
    dst->acount = src->acount;
    dst->mu = src->mu;
    dst->finit = src->finit;
    dst->dginit = src->dginit;
    ae_vector_init_copy(&dst->ak, &src->ak, _state, make_automatic);
    ae_vector_init_copy(&dst->xk, &src->xk, _state, make_automatic);
    ae_vector_init_copy(&dst->dk, &src->dk, _state, make_automatic);
    ae_vector_init_copy(&dst->an, &src->an, _state, make_automatic);
    ae_vector_init_copy(&dst->xn, &src->xn, _state, make_automatic);
    ae_vector_init_copy(&dst->dn, &src->dn, _state, make_automatic);
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    dst->fold = src->fold;
    dst->stp = src->stp;
    ae_vector_init_copy(&dst->work, &src->work, _state, make_automatic);
    ae_vector_init_copy(&dst->yk, &src->yk, _state, make_automatic);
    ae_vector_init_copy(&dst->gc, &src->gc, _state, make_automatic);
    dst->laststep = src->laststep;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    dst->f = src->f;
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    dst->needfg = src->needfg;
    dst->xupdated = src->xupdated;
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    dst->repiterationscount = src->repiterationscount;
    dst->repnfev = src->repnfev;
    dst->repterminationtype = src->repterminationtype;
    dst->debugrestartscount = src->debugrestartscount;
    _linminstate_init_copy(&dst->lstate, &src->lstate, _state, make_automatic);
    dst->betahs = src->betahs;
    dst->betady = src->betady;
}


void _minasastate_clear(void* _p)
{
    minasastate *p = (minasastate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->ak);
    ae_vector_clear(&p->xk);
    ae_vector_clear(&p->dk);
    ae_vector_clear(&p->an);
    ae_vector_clear(&p->xn);
    ae_vector_clear(&p->dn);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->work);
    ae_vector_clear(&p->yk);
    ae_vector_clear(&p->gc);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->g);
    _rcommstate_clear(&p->rstate);
    _linminstate_clear(&p->lstate);
}


void _minasastate_destroy(void* _p)
{
    minasastate *p = (minasastate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->ak);
    ae_vector_destroy(&p->xk);
    ae_vector_destroy(&p->dk);
    ae_vector_destroy(&p->an);
    ae_vector_destroy(&p->xn);
    ae_vector_destroy(&p->dn);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->work);
    ae_vector_destroy(&p->yk);
    ae_vector_destroy(&p->gc);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->g);
    _rcommstate_destroy(&p->rstate);
    _linminstate_destroy(&p->lstate);
}


void _minasareport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minasareport *p = (minasareport*)_p;
    ae_touch_ptr((void*)p);
}


void _minasareport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minasareport       *dst = (minasareport*)_dst;
    const minasareport *src = (const minasareport*)_src;
    dst->iterationscount = src->iterationscount;
    dst->nfev = src->nfev;
    dst->terminationtype = src->terminationtype;
    dst->activeconstraints = src->activeconstraints;
}


void _minasareport_clear(void* _p)
{
    minasareport *p = (minasareport*)_p;
    ae_touch_ptr((void*)p);
}


void _minasareport_destroy(void* _p)
{
    minasareport *p = (minasareport*)_p;
    ae_touch_ptr((void*)p);
}


#endif
#if defined(AE_COMPILE_MINBC) || !defined(AE_PARTIAL_BUILD)


/*************************************************************************
                     BOX CONSTRAINED OPTIMIZATION
          WITH FAST ACTIVATION OF MULTIPLE BOX CONSTRAINTS

DESCRIPTION:
The  subroutine  minimizes  function   F(x) of N arguments subject  to box
constraints (with some of box constraints actually being equality ones).

This optimizer uses algorithm similar to that of MinBLEIC (optimizer  with
general linear constraints), but presence of box-only  constraints  allows
us to use faster constraint activation strategies. On large-scale problems,
with multiple constraints active at the solution, this  optimizer  can  be
several times faster than BLEIC.

REQUIREMENTS:
* user must provide function value and gradient
* starting point X0 must be feasible or
  not too far away from the feasible set
* grad(f) must be Lipschitz continuous on a level set:
  L = { x : f(x)<=f(x0) }
* function must be defined everywhere on the feasible set F

USAGE:

Constrained optimization if far more complex than the unconstrained one.
Here we give very brief outline of the BC optimizer. We strongly recommend
you to read examples in the ALGLIB Reference Manual and to read ALGLIB User Guide
on optimization, which is available at http://www.alglib.net/optimization/

1. User initializes algorithm state with MinBCCreate() call

2. USer adds box constraints by calling MinBCSetBC() function.

3. User sets stopping conditions with MinBCSetCond().

4. User calls MinBCOptimize() function which takes algorithm  state and
   pointer (delegate, etc.) to callback function which calculates F/G.

5. User calls MinBCResults() to get solution

6. Optionally user may call MinBCRestartFrom() to solve another problem
   with same N but another starting point.
   MinBCRestartFrom() allows to reuse already initialized structure.


INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size ofX
    X       -   starting point, array[N]:
                * it is better to set X to a feasible point
                * but X can be infeasible, in which case algorithm will try
                  to find feasible point first, using X as initial
                  approximation.

OUTPUT PARAMETERS:
    State   -   structure stores algorithm state

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbccreate(ae_int_t n,
     /* Real    */ const ae_vector* x,
     minbcstate* state,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix c;
    ae_vector ct;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    _minbcstate_clear(state);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);

    ae_assert(n>=1, "MinBCCreate: N<1", _state);
    ae_assert(x->cnt>=n, "MinBCCreate: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinBCCreate: X contains infinite or NaN values!", _state);
    minbc_minbcinitinternal(n, x, 0.0, state, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
The subroutine is finite difference variant of MinBCCreate().  It  uses
finite differences in order to differentiate target function.

Description below contains information which is specific to  this function
only. We recommend to read comments on MinBCCreate() in  order  to  get
more information about creation of BC optimizer.

INPUT PARAMETERS:
    N       -   problem dimension, N>0:
                * if given, only leading N elements of X are used
                * if not given, automatically determined from size of X
    X       -   starting point, array[0..N-1].
    DiffStep-   differentiation step, >0

OUTPUT PARAMETERS:
    State   -   structure which stores algorithm state

NOTES:
1. algorithm uses 4-point central formula for differentiation.
2. differentiation step along I-th axis is equal to DiffStep*S[I] where
   S[] is scaling vector which can be set by MinBCSetScale() call.
3. we recommend you to use moderate values of  differentiation  step.  Too
   large step will result in too large truncation  errors, while too small
   step will result in too large numerical  errors.  1.0E-6  can  be  good
   value to start with.
4. Numerical  differentiation  is   very   inefficient  -   one   gradient
   calculation needs 4*N function evaluations. This function will work for
   any N - either small (1...10), moderate (10...100) or  large  (100...).
   However, performance penalty will be too severe for any N's except  for
   small ones.
   We should also say that code which relies on numerical  differentiation
   is  less  robust and precise. CG needs exact gradient values. Imprecise
   gradient may slow  down  convergence, especially  on  highly  nonlinear
   problems.
   Thus  we  recommend to use this function for fast prototyping on small-
   dimensional problems only, and to implement analytical gradient as soon
   as possible.

  -- ALGLIB --
     Copyright 16.05.2011 by Bochkanov Sergey
*************************************************************************/
void minbccreatef(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minbcstate* state,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix c;
    ae_vector ct;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    _minbcstate_clear(state);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);

    ae_assert(n>=1, "MinBCCreateF: N<1", _state);
    ae_assert(x->cnt>=n, "MinBCCreateF: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinBCCreateF: X contains infinite or NaN values!", _state);
    ae_assert(ae_isfinite(diffstep, _state), "MinBCCreateF: DiffStep is infinite or NaN!", _state);
    ae_assert(ae_fp_greater(diffstep,(double)(0)), "MinBCCreateF: DiffStep is non-positive!", _state);
    minbc_minbcinitinternal(n, x, diffstep, state, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
This function sets boundary constraints for BC optimizer.

Boundary constraints are inactive by default (after initial creation).
They are preserved after algorithm restart with MinBCRestartFrom().

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    BndL    -   lower bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very small number or -INF.
    BndU    -   upper bounds, array[N].
                If some (all) variables are unbounded, you may specify
                very large number or +INF.

NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
variable will be "frozen" at X[i]=BndL[i]=BndU[i].

NOTE 2: this solver has following useful properties:
* bound constraints are always satisfied exactly
* function is evaluated only INSIDE area specified by  bound  constraints,
  even  when  numerical  differentiation is used (algorithm adjusts  nodes
  according to boundary constraints)

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetbc(minbcstate* state,
     /* Real    */ const ae_vector* bndl,
     /* Real    */ const ae_vector* bndu,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t n;


    n = state->nmain;
    ae_assert(bndl->cnt>=n, "MinBCSetBC: Length(BndL)<N", _state);
    ae_assert(bndu->cnt>=n, "MinBCSetBC: Length(BndU)<N", _state);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "MinBCSetBC: BndL contains NAN or +INF", _state);
        ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "MinBCSetBC: BndL contains NAN or -INF", _state);
        state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
        state->hasbndl.ptr.p_bool[i] = ae_isfinite(bndl->ptr.p_double[i], _state);
        state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
        state->hasbndu.ptr.p_bool[i] = ae_isfinite(bndu->ptr.p_double[i], _state);
    }
}


/*************************************************************************
This function sets stopping conditions for the optimizer.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    EpsG    -   >=0
                The  subroutine  finishes  its  work   if   the  condition
                |v|<EpsG is satisfied, where:
                * |.| means Euclidian norm
                * v - scaled gradient vector, v[i]=g[i]*s[i]
                * g - gradient
                * s - scaling coefficients set by MinBCSetScale()
    EpsF    -   >=0
                The  subroutine  finishes  its work if on k+1-th iteration
                the  condition  |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1}
                is satisfied.
    EpsX    -   >=0
                The subroutine finishes its work if  on  k+1-th  iteration
                the condition |v|<=EpsX is fulfilled, where:
                * |.| means Euclidian norm
                * v - scaled step vector, v[i]=dx[i]/s[i]
                * dx - step vector, dx=X(k+1)-X(k)
                * s - scaling coefficients set by MinBCSetScale()
    MaxIts  -   maximum number of iterations. If MaxIts=0, the  number  of
                iterations is unlimited.

Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead
to automatic stopping criterion selection.

NOTE: when SetCond() called with non-zero MaxIts, BC solver may perform
      slightly more than MaxIts iterations. I.e., MaxIts  sets  non-strict
      limit on iterations count.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetcond(minbcstate* state,
     double epsg,
     double epsf,
     double epsx,
     ae_int_t maxits,
     ae_state *_state)
{


    ae_assert(ae_isfinite(epsg, _state), "MinBCSetCond: EpsG is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsg,(double)(0)), "MinBCSetCond: negative EpsG", _state);
    ae_assert(ae_isfinite(epsf, _state), "MinBCSetCond: EpsF is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsf,(double)(0)), "MinBCSetCond: negative EpsF", _state);
    ae_assert(ae_isfinite(epsx, _state), "MinBCSetCond: EpsX is not finite number", _state);
    ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "MinBCSetCond: negative EpsX", _state);
    ae_assert(maxits>=0, "MinBCSetCond: negative MaxIts!", _state);
    if( ((ae_fp_eq(epsg,(double)(0))&&ae_fp_eq(epsf,(double)(0)))&&ae_fp_eq(epsx,(double)(0)))&&maxits==0 )
    {
        epsx = 1.0E-6;
    }
    state->epsg = epsg;
    state->epsf = epsf;
    state->epsx = epsx;
    state->maxits = maxits;
}


/*************************************************************************
This function sets scaling coefficients for BC optimizer.

ALGLIB optimizers use scaling matrices to test stopping  conditions  (step
size and gradient are scaled before comparison with tolerances).  Scale of
the I-th variable is a translation invariant measure of:
a) "how large" the variable is
b) how large the step should be to make significant changes in the function

Scaling is also used by finite difference variant of the optimizer  - step
along I-th axis is equal to DiffStep*S[I].

In  most  optimizers  (and  in  the  BC  too)  scaling is NOT a form of
preconditioning. It just  affects  stopping  conditions.  You  should  set
preconditioner  by  separate  call  to  one  of  the  MinBCSetPrec...()
functions.

There is a special  preconditioning  mode, however,  which  uses   scaling
coefficients to form diagonal preconditioning matrix. You  can  turn  this
mode on, if you want.   But  you should understand that scaling is not the
same thing as preconditioning - these are two different, although  related
forms of tuning solver.

INPUT PARAMETERS:
    State   -   structure stores algorithm state
    S       -   array[N], non-zero scaling coefficients
                S[i] may be negative, sign doesn't matter.

  -- ALGLIB --
     Copyright 14.01.2011 by Bochkanov Sergey
*************************************************************************/
void minbcsetscale(minbcstate* state,
     /* Real    */ const ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(s->cnt>=state->nmain, "MinBCSetScale: Length(S)<N", _state);
    for(i=0; i<=state->nmain-1; i++)
    {
        ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "MinBCSetScale: S contains infinite or NAN elements", _state);
        ae_assert(ae_fp_neq(s->ptr.p_double[i],(double)(0)), "MinBCSetScale: S contains zero elements", _state);
        state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
Modification of the preconditioner: preconditioning is turned off.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetprecdefault(minbcstate* state, ae_state *_state)
{


    state->prectype = 0;
}


/*************************************************************************
Modification  of  the  preconditioner:  diagonal of approximate Hessian is
used.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    D       -   diagonal of the approximate Hessian, array[0..N-1],
                (if larger, only leading N elements are used).

NOTE 1: D[i] should be positive. Exception will be thrown otherwise.

NOTE 2: you should pass diagonal of approximate Hessian - NOT ITS INVERSE.

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetprecdiag(minbcstate* state,
     /* Real    */ const ae_vector* d,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(d->cnt>=state->nmain, "MinBCSetPrecDiag: D is too short", _state);
    for(i=0; i<=state->nmain-1; i++)
    {
        ae_assert(ae_isfinite(d->ptr.p_double[i], _state), "MinBCSetPrecDiag: D contains infinite or NAN elements", _state);
        ae_assert(ae_fp_greater(d->ptr.p_double[i],(double)(0)), "MinBCSetPrecDiag: D contains non-positive elements", _state);
    }
    rvectorsetlengthatleast(&state->diagh, state->nmain, _state);
    state->prectype = 2;
    for(i=0; i<=state->nmain-1; i++)
    {
        state->diagh.ptr.p_double[i] = d->ptr.p_double[i];
    }
}


/*************************************************************************
Modification of the preconditioner: scale-based diagonal preconditioning.

This preconditioning mode can be useful when you  don't  have  approximate
diagonal of Hessian, but you know that your  variables  are  badly  scaled
(for  example,  one  variable is in [1,10], and another in [1000,100000]),
and most part of the ill-conditioning comes from different scales of vars.

In this case simple  scale-based  preconditioner,  with H[i] = 1/(s[i]^2),
can greatly improve convergence.

IMPRTANT: you should set scale of your variables  with  MinBCSetScale()
call  (before  or after MinBCSetPrecScale() call). Without knowledge of
the scale of your variables scale-based preconditioner will be  just  unit
matrix.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state

  -- ALGLIB --
     Copyright 13.10.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetprecscale(minbcstate* state, ae_state *_state)
{


    state->prectype = 3;
}


/*************************************************************************
This function turns on/off reporting.

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    NeedXRep-   whether iteration reports are needed or not

If NeedXRep is True, algorithm will call rep() callback function if  it is
provided to MinBCOptimize().

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetxrep(minbcstate* state, ae_bool needxrep, ae_state *_state)
{


    state->xrep = needxrep;
}


/*************************************************************************
This function sets maximum step length

INPUT PARAMETERS:
    State   -   structure which stores algorithm state
    StpMax  -   maximum step length, >=0. Set StpMax to 0.0,  if you don't
                want to limit step length.

Use this subroutine when you optimize target function which contains exp()
or  other  fast  growing  functions,  and optimization algorithm makes too
large  steps  which  lead   to overflow. This function allows us to reject
steps  that  are  too  large  (and  therefore  expose  us  to the possible
overflow) without actually calculating function value at the x+stp*d.

  -- ALGLIB --
     Copyright 02.04.2010 by Bochkanov Sergey
*************************************************************************/
void minbcsetstpmax(minbcstate* state, double stpmax, ae_state *_state)
{


    ae_assert(ae_isfinite(stpmax, _state), "MinBCSetStpMax: StpMax is not finite!", _state);
    ae_assert(ae_fp_greater_eq(stpmax,(double)(0)), "MinBCSetStpMax: StpMax<0!", _state);
    state->stpmax = stpmax;
}


/*************************************************************************
NOTES:

1. This function has two different implementations: one which  uses  exact
   (analytical) user-supplied gradient,  and one which uses function value
   only  and  numerically  differentiates  function  in  order  to  obtain
   gradient.

   Depending  on  the  specific  function  used to create optimizer object
   (either  MinBCCreate() for analytical gradient or  MinBCCreateF()
   for numerical differentiation) you should choose appropriate variant of
   MinBCOptimize() - one  which  accepts  function  AND gradient or one
   which accepts function ONLY.

   Be careful to choose variant of MinBCOptimize() which corresponds to
   your optimization scheme! Table below lists different  combinations  of
   callback (function/gradient) passed to MinBCOptimize()  and specific
   function used to create optimizer.


                     |         USER PASSED TO MinBCOptimize()
   CREATED WITH      |  function only   |  function and gradient
   ------------------------------------------------------------
   MinBCCreateF()    |     works               FAILS
   MinBCCreate()     |     FAILS               works

   Here "FAIL" denotes inappropriate combinations  of  optimizer  creation
   function  and  MinBCOptimize()  version.   Attemps   to   use   such
   combination (for  example,  to  create optimizer with MinBCCreateF()
   and  to  pass  gradient  information  to  MinCGOptimize()) will lead to
   exception being thrown. Either  you  did  not pass gradient when it WAS
   needed or you passed gradient when it was NOT needed.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
ae_bool minbciteration(minbcstate* state, ae_state *_state)
{
    ae_int_t freezeidx;
    double freezeval;
    double scaleddnorm;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    double v;
    double vv;
    double v0;
    ae_bool b;
    ae_int_t mcinfo;
    ae_int_t itidx;
    double ginit;
    double gdecay;
    ae_bool activationstatus;
    double activationstep;
    ae_bool result;


    
    /*
     * 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( state->rstate.stage>=0 )
    {
        freezeidx = state->rstate.ia.ptr.p_int[0];
        n = state->rstate.ia.ptr.p_int[1];
        m = state->rstate.ia.ptr.p_int[2];
        i = state->rstate.ia.ptr.p_int[3];
        j = state->rstate.ia.ptr.p_int[4];
        mcinfo = state->rstate.ia.ptr.p_int[5];
        itidx = state->rstate.ia.ptr.p_int[6];
        b = state->rstate.ba.ptr.p_bool[0];
        activationstatus = state->rstate.ba.ptr.p_bool[1];
        freezeval = state->rstate.ra.ptr.p_double[0];
        scaleddnorm = state->rstate.ra.ptr.p_double[1];
        v = state->rstate.ra.ptr.p_double[2];
        vv = state->rstate.ra.ptr.p_double[3];
        v0 = state->rstate.ra.ptr.p_double[4];
        ginit = state->rstate.ra.ptr.p_double[5];
        gdecay = state->rstate.ra.ptr.p_double[6];
        activationstep = state->rstate.ra.ptr.p_double[7];
    }
    else
    {
        freezeidx = 359;
        n = -58;
        m = -919;
        i = -909;
        j = 81;
        mcinfo = 255;
        itidx = 74;
        b = ae_false;
        activationstatus = ae_true;
        freezeval = 205.0;
        scaleddnorm = -838.0;
        v = 939.0;
        vv = -526.0;
        v0 = 763.0;
        ginit = -541.0;
        gdecay = -698.0;
        activationstep = -900.0;
    }
    if( state->rstate.stage==0 )
    {
        goto lbl_0;
    }
    if( state->rstate.stage==1 )
    {
        goto lbl_1;
    }
    if( state->rstate.stage==2 )
    {
        goto lbl_2;
    }
    if( state->rstate.stage==3 )
    {
        goto lbl_3;
    }
    if( state->rstate.stage==4 )
    {
        goto lbl_4;
    }
    if( state->rstate.stage==5 )
    {
        goto lbl_5;
    }
    if( state->rstate.stage==6 )
    {
        goto lbl_6;
    }
    if( state->rstate.stage==7 )
    {
        goto lbl_7;
    }
    if( state->rstate.stage==8 )
    {
        goto lbl_8;
    }
    if( state->rstate.stage==9 )
    {
        goto lbl_9;
    }
    if( state->rstate.stage==10 )
    {
        goto lbl_10;
    }
    if( state->rstate.stage==11 )
    {
        goto lbl_11;
    }
    if( state->rstate.stage==12 )
    {
        goto lbl_12;
    }
    if( state->rstate.stage==13 )
    {
        goto lbl_13;
    }
    if( state->rstate.stage==14 )
    {
        goto lbl_14;
    }
    if( state->rstate.stage==15 )
    {
        goto lbl_15;
    }
    if( state->rstate.stage==16 )
    {
        goto lbl_16;
    }
    if( state->rstate.stage==17 )
    {
        goto lbl_17;
    }
    if( state->rstate.stage==18 )
    {
        goto lbl_18;
    }
    if( state->rstate.stage==19 )
    {
        goto lbl_19;
    }
    if( state->rstate.stage==20 )
    {
        goto lbl_20;
    }
    if( state->rstate.stage==21 )
    {
        goto lbl_21;
    }
    if( state->rstate.stage==22 )
    {
        goto lbl_22;
    }
    if( state->rstate.stage==23 )
    {
        goto lbl_23;
    }
    if( state->rstate.stage==24 )
    {
        goto lbl_24;
    }
    if( state->rstate.stage==25 )
    {
        goto lbl_25;
    }
    if( state->rstate.stage==26 )
    {
        goto lbl_26;
    }
    if( state->rstate.stage==27 )
    {
        goto lbl_27;
    }
    if( state->rstate.stage==28 )
    {
        goto lbl_28;
    }
    if( state->rstate.stage==29 )
    {
        goto lbl_29;
    }
    
    /*
     * Routine body
     */
    
    /*
     * Algorithm parameters:
     * * M          number of L-BFGS corrections.
     *              This coefficient remains fixed during iterations.
     * * GDecay     desired decrease of constrained gradient during L-BFGS iterations.
     *              This coefficient is decreased after each L-BFGS round until
     *              it reaches minimum decay.
     */
    m = ae_minint(5, state->nmain, _state);
    gdecay = minbc_initialdecay;
    
    /*
     * Init
     */
    n = state->nmain;
    for(i=0; i<=n-1; i++)
    {
        state->xc.ptr.p_double[i] = state->xstart.ptr.p_double[i];
    }
    if( !enforceboundaryconstraints(&state->xc, &state->bndl, &state->hasbndl, &state->bndu, &state->hasbndu, n, 0, _state) )
    {
        
        /*
         * Inconsistent constraints
         */
        state->repterminationtype = -3;
        result = ae_false;
        return result;
    }
    state->userterminationneeded = ae_false;
    state->repterminationtype = 0;
    state->repiterationscount = 0;
    state->repnfev = 0;
    state->repvaridx = -1;
    rmatrixsetlengthatleast(&state->bufyk, m+1, n, _state);
    rmatrixsetlengthatleast(&state->bufsk, m+1, n, _state);
    rvectorsetlengthatleast(&state->bufrho, m, _state);
    rvectorsetlengthatleast(&state->buftheta, m, _state);
    rvectorsetlengthatleast(&state->tmp0, n, _state);
    smoothnessmonitorinit(&state->smonitor, &state->s, n, 1, state->smoothnessguardlevel>0, _state);
    for(i=0; i<=n-1; i++)
    {
        state->lastscaleused.ptr.p_double[i] = state->s.ptr.p_double[i];
        state->invs.ptr.p_double[i] = (double)1/state->s.ptr.p_double[i];
    }
    
    /*
     * Fill TmpPrec with current preconditioner
     */
    rvectorsetlengthatleast(&state->tmpprec, n, _state);
    for(i=0; i<=n-1; i++)
    {
        if( state->prectype==2 )
        {
            state->tmpprec.ptr.p_double[i] = (double)1/state->diagh.ptr.p_double[i];
            continue;
        }
        if( state->prectype==3 )
        {
            state->tmpprec.ptr.p_double[i] = ae_sqr(state->s.ptr.p_double[i], _state);
            continue;
        }
        state->tmpprec.ptr.p_double[i] = (double)(1);
    }
    
    /*
     *  Check correctness of user-supplied gradient
     */
    minbc_clearrequestfields(state, _state);
    if( !(ae_fp_eq(state->diffstep,(double)(0))&&ae_fp_greater(state->teststep,(double)(0))) )
    {
        goto lbl_30;
    }
lbl_32:
    if( !smoothnessmonitorcheckgradientatx0(&state->smonitor, &state->xc, &state->s, &state->bndl, &state->bndu, ae_true, state->teststep, _state) )
    {
        goto lbl_33;
    }
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = state->smonitor.x.ptr.p_double[i];
    }
    state->needfg = ae_true;
    state->rstate.stage = 0;
    goto lbl_rcomm;
lbl_0:
    state->needfg = ae_false;
    state->smonitor.fi.ptr.p_double[0] = state->f;
    for(i=0; i<=n-1; i++)
    {
        state->smonitor.j.ptr.pp_double[0][i] = state->g.ptr.p_double[i];
    }
    goto lbl_32;
lbl_33:
lbl_30:
    
    /*
     * Main cycle of BC-PG algorithm
     */
    state->repterminationtype = 0;
    state->lastscaledgoodstep = (double)(0);
    state->nonmonotoniccnt = ae_round(1.5*(double)n, _state)+5;
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minbc_clearrequestfields(state, _state);
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_34;
    }
    state->needfg = ae_true;
    state->rstate.stage = 1;
    goto lbl_rcomm;
lbl_1:
    state->needfg = ae_false;
    goto lbl_35;
lbl_34:
    state->needf = ae_true;
    state->rstate.stage = 2;
    goto lbl_rcomm;
lbl_2:
    state->needf = ae_false;
lbl_35:
    state->fc = state->f;
    trimprepare(state->f, &state->trimthreshold, _state);
    state->repnfev = state->repnfev+1;
    if( !state->xrep )
    {
        goto lbl_36;
    }
    
    /*
     * Report current point
     */
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->f = state->fc;
    state->xupdated = ae_true;
    state->rstate.stage = 3;
    goto lbl_rcomm;
lbl_3:
    state->xupdated = ae_false;
lbl_36:
    if( state->userterminationneeded )
    {
        
        /*
         * User requested termination
         */
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
lbl_38:
    if( ae_false )
    {
        goto lbl_39;
    }
    
    /*
     * Steepest descent phase
     *
     * (a) calculate unconstrained gradient
     * (b) check F/G for NAN/INF, abnormally terminate algorithm if needed
     * (c) perform one steepest descent step, activating only those constraints
     *     which prevent us from moving outside of box-constrained area
     */
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minbc_clearrequestfields(state, _state);
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_40;
    }
    
    /*
     * Analytic gradient
     */
    state->needfg = ae_true;
    state->rstate.stage = 4;
    goto lbl_rcomm;
lbl_4:
    state->needfg = ae_false;
    goto lbl_41;
lbl_40:
    
    /*
     * Numerical differentiation
     */
    state->needf = ae_true;
    state->rstate.stage = 5;
    goto lbl_rcomm;
lbl_5:
    state->fbase = state->f;
    i = 0;
lbl_42:
    if( i>n-1 )
    {
        goto lbl_44;
    }
    v = state->x.ptr.p_double[i];
    b = ae_false;
    if( state->hasbndl.ptr.p_bool[i] )
    {
        b = b||ae_fp_less(v-state->diffstep*state->s.ptr.p_double[i],state->bndl.ptr.p_double[i]);
    }
    if( state->hasbndu.ptr.p_bool[i] )
    {
        b = b||ae_fp_greater(v+state->diffstep*state->s.ptr.p_double[i],state->bndu.ptr.p_double[i]);
    }
    if( b )
    {
        goto lbl_45;
    }
    state->x.ptr.p_double[i] = v-state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 6;
    goto lbl_rcomm;
lbl_6:
    state->fm2 = state->f;
    state->x.ptr.p_double[i] = v-0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 7;
    goto lbl_rcomm;
lbl_7:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = v+0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 8;
    goto lbl_rcomm;
lbl_8:
    state->fp1 = state->f;
    state->x.ptr.p_double[i] = v+state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 9;
    goto lbl_rcomm;
lbl_9:
    state->fp2 = state->f;
    state->g.ptr.p_double[i] = ((double)8*(state->fp1-state->fm1)-(state->fp2-state->fm2))/((double)6*state->diffstep*state->s.ptr.p_double[i]);
    goto lbl_46;
lbl_45:
    state->xm1 = v-state->diffstep*state->s.ptr.p_double[i];
    state->xp1 = v+state->diffstep*state->s.ptr.p_double[i];
    if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less(state->xm1,state->bndl.ptr.p_double[i]) )
    {
        state->xm1 = state->bndl.ptr.p_double[i];
    }
    if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater(state->xp1,state->bndu.ptr.p_double[i]) )
    {
        state->xp1 = state->bndu.ptr.p_double[i];
    }
    state->x.ptr.p_double[i] = state->xm1;
    state->rstate.stage = 10;
    goto lbl_rcomm;
lbl_10:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = state->xp1;
    state->rstate.stage = 11;
    goto lbl_rcomm;
lbl_11:
    state->fp1 = state->f;
    if( ae_fp_neq(state->xm1,state->xp1) )
    {
        state->g.ptr.p_double[i] = (state->fp1-state->fm1)/(state->xp1-state->xm1);
    }
    else
    {
        state->g.ptr.p_double[i] = (double)(0);
    }
lbl_46:
    state->x.ptr.p_double[i] = v;
    i = i+1;
    goto lbl_42;
lbl_44:
    state->f = state->fbase;
    state->needf = ae_false;
lbl_41:
    state->fc = state->f;
    ae_v_move(&state->ugc.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->cgc.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    projectgradientintobc(&state->xc, &state->cgc, &state->bndl, &state->hasbndl, &state->bndu, &state->hasbndu, n, 0, _state);
    ginit = 0.0;
    for(i=0; i<=n-1; i++)
    {
        ginit = ginit+ae_sqr(state->cgc.ptr.p_double[i]*state->s.ptr.p_double[i], _state);
    }
    ginit = ae_sqrt(ginit, _state);
    if( !ae_isfinite(ginit, _state)||!ae_isfinite(state->fc, _state) )
    {
        
        /*
         * Abnormal termination - infinities in function/gradient
         */
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    if( state->userterminationneeded )
    {
        
        /*
         * User requested termination
         */
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    if( ae_fp_less_eq(ginit,state->epsg) )
    {
        
        /*
         * Gradient is small enough.
         * Optimization is terminated
         */
        state->repterminationtype = 4;
        result = ae_false;
        return result;
    }
    for(i=0; i<=n-1; i++)
    {
        state->d.ptr.p_double[i] = -state->tmpprec.ptr.p_double[i]*state->cgc.ptr.p_double[i];
    }
    scaleddnorm = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        scaleddnorm = scaleddnorm+ae_sqr(state->d.ptr.p_double[i]/state->s.ptr.p_double[i], _state);
    }
    scaleddnorm = ae_sqrt(scaleddnorm, _state);
    ae_assert(ae_fp_greater(scaleddnorm,(double)(0)), "MinBC: integrity check failed", _state);
    if( ae_fp_greater(state->lastscaledgoodstep,(double)(0)) )
    {
        state->stp = state->lastscaledgoodstep/scaleddnorm;
    }
    else
    {
        state->stp = 1.0/scaleddnorm;
    }
    calculatestepbound(&state->xc, &state->d, 1.0, &state->bndl, &state->hasbndl, &state->bndu, &state->hasbndu, n, 0, &freezeidx, &freezeval, &state->curstpmax, _state);
    activationstep = state->curstpmax;
    if( freezeidx<0||ae_fp_greater(state->curstpmax,1.0E50) )
    {
        state->curstpmax = 1.0E50;
    }
    if( ae_fp_greater(state->stpmax,(double)(0)) )
    {
        state->curstpmax = ae_minreal(state->curstpmax, state->stpmax/scaleddnorm, _state);
    }
    ae_v_move(&state->xn.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->cgn.ptr.p_double[0], 1, &state->cgc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->ugn.ptr.p_double[0], 1, &state->ugc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->fn = state->fc;
    state->mcstage = 0;
    smoothnessmonitorstartlinesearch1u(&state->smonitor, &state->s, &state->invs, &state->xn, state->fn, &state->ugn, state->repiterationscount, -1, _state);
    mcsrch(n, &state->xn, &state->fn, &state->cgn, &state->d, &state->stp, state->curstpmax, minbc_gtol, &mcinfo, &state->nfev, &state->work, &state->lstate, &state->mcstage, _state);
lbl_47:
    if( state->mcstage==0 )
    {
        goto lbl_48;
    }
    
    /*
     * Copy XN to X, perform on-the-fly correction w.r.t box
     * constraints (projection onto feasible set).
     */
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = state->xn.ptr.p_double[i];
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less(state->xn.ptr.p_double[i],state->bndl.ptr.p_double[i]) )
        {
            state->x.ptr.p_double[i] = state->bndl.ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater(state->xn.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            state->x.ptr.p_double[i] = state->bndu.ptr.p_double[i];
        }
    }
    
    /*
     * Gradient, either user-provided or numerical differentiation
     */
    minbc_clearrequestfields(state, _state);
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_49;
    }
    
    /*
     * Analytic gradient
     */
    state->needfg = ae_true;
    state->rstate.stage = 12;
    goto lbl_rcomm;
lbl_12:
    state->needfg = ae_false;
    state->repnfev = state->repnfev+1;
    goto lbl_50;
lbl_49:
    
    /*
     * Numerical differentiation
     */
    state->needf = ae_true;
    state->rstate.stage = 13;
    goto lbl_rcomm;
lbl_13:
    state->fbase = state->f;
    i = 0;
lbl_51:
    if( i>n-1 )
    {
        goto lbl_53;
    }
    v = state->x.ptr.p_double[i];
    b = ae_false;
    if( state->hasbndl.ptr.p_bool[i] )
    {
        b = b||ae_fp_less(v-state->diffstep*state->s.ptr.p_double[i],state->bndl.ptr.p_double[i]);
    }
    if( state->hasbndu.ptr.p_bool[i] )
    {
        b = b||ae_fp_greater(v+state->diffstep*state->s.ptr.p_double[i],state->bndu.ptr.p_double[i]);
    }
    if( b )
    {
        goto lbl_54;
    }
    state->x.ptr.p_double[i] = v-state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 14;
    goto lbl_rcomm;
lbl_14:
    state->fm2 = state->f;
    state->x.ptr.p_double[i] = v-0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 15;
    goto lbl_rcomm;
lbl_15:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = v+0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 16;
    goto lbl_rcomm;
lbl_16:
    state->fp1 = state->f;
    state->x.ptr.p_double[i] = v+state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 17;
    goto lbl_rcomm;
lbl_17:
    state->fp2 = state->f;
    state->g.ptr.p_double[i] = ((double)8*(state->fp1-state->fm1)-(state->fp2-state->fm2))/((double)6*state->diffstep*state->s.ptr.p_double[i]);
    state->repnfev = state->repnfev+4;
    goto lbl_55;
lbl_54:
    state->xm1 = v-state->diffstep*state->s.ptr.p_double[i];
    state->xp1 = v+state->diffstep*state->s.ptr.p_double[i];
    if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less(state->xm1,state->bndl.ptr.p_double[i]) )
    {
        state->xm1 = state->bndl.ptr.p_double[i];
    }
    if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater(state->xp1,state->bndu.ptr.p_double[i]) )
    {
        state->xp1 = state->bndu.ptr.p_double[i];
    }
    state->x.ptr.p_double[i] = state->xm1;
    state->rstate.stage = 18;
    goto lbl_rcomm;
lbl_18:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = state->xp1;
    state->rstate.stage = 19;
    goto lbl_rcomm;
lbl_19:
    state->fp1 = state->f;
    if( ae_fp_neq(state->xm1,state->xp1) )
    {
        state->g.ptr.p_double[i] = (state->fp1-state->fm1)/(state->xp1-state->xm1);
    }
    else
    {
        state->g.ptr.p_double[i] = (double)(0);
    }
    state->repnfev = state->repnfev+2;
lbl_55:
    state->x.ptr.p_double[i] = v;
    i = i+1;
    goto lbl_51;
lbl_53:
    state->f = state->fbase;
    state->needf = ae_false;
lbl_50:
    
    /*
     * Back to MCSRCH
     */
    smoothnessmonitorenqueuepoint1u(&state->smonitor, &state->s, &state->invs, &state->d, state->stp, &state->x, state->f, &state->g, _state);
    trimfunction(&state->f, &state->g, n, state->trimthreshold, _state);
    state->fn = state->f;
    ae_v_move(&state->cgn.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->ugn.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,n-1));
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_eq(state->d.ptr.p_double[i],(double)(0)) )
        {
            state->cgn.ptr.p_double[i] = (double)(0);
        }
    }
    mcsrch(n, &state->xn, &state->fn, &state->cgn, &state->d, &state->stp, state->curstpmax, minbc_gtol, &mcinfo, &state->nfev, &state->work, &state->lstate, &state->mcstage, _state);
    goto lbl_47;
lbl_48:
    smoothnessmonitorfinalizelinesearch(&state->smonitor, _state);
    v = state->fn;
    for(i=0; i<=n-1; i++)
    {
        v = 0.1*v+state->ugn.ptr.p_double[i];
    }
    if( !ae_isfinite(v, _state) )
    {
        
        /*
         * Abnormal termination - infinities in function/gradient
         */
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    if( mcinfo!=1&&mcinfo!=5 )
    {
        
        /*
         * We can not find step which decreases function value. We have
         * two possibilities:
         * (a) numerical properties of the function do not allow us to
         *     find good step.
         * (b) we are close to activation of some constraint, and it is
         *     so close that step which activates it leads to change in
         *     target function which is smaller than numerical noise.
         *
         * Optimization algorithm must be able to handle case (b), because
         * inability to handle it will cause failure when algorithm
         * started very close to boundary of the feasible area.
         *
         * In order to correctly handle such cases we allow limited amount
         * of small steps which increase function value.
         */
        if( (freezeidx>=0&&ae_fp_less_eq(scaleddnorm*state->curstpmax,minbc_maxnonmonotoniclen))&&state->nonmonotoniccnt>0 )
        {
            
            /*
             * We enforce non-monotonic step:
             * * Stp    := CurStpMax
             * * MCINFO := 5
             * * XN     := XC+CurStpMax*D
             * * non-monotonic counter is decreased
             *
             * NOTE: UGN/CGN are not updated because step is so short that we assume that
             *       GN is approximately equal to GC.
             */
            state->stp = state->curstpmax;
            mcinfo = 5;
            v = state->curstpmax;
            ae_v_move(&state->xn.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
            ae_v_addd(&state->xn.ptr.p_double[0], 1, &state->d.ptr.p_double[0], 1, ae_v_len(0,n-1), v);
            state->nonmonotoniccnt = state->nonmonotoniccnt-1;
        }
        else
        {
            
            /*
             * Numerical properties of the function does not allow
             * us to solve problem. Algorithm is terminated
             */
            state->repterminationtype = 7;
            result = ae_false;
            return result;
        }
    }
    if( state->userterminationneeded )
    {
        
        /*
         * User requested termination
         */
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    ae_assert(mcinfo!=5||ae_fp_eq(state->stp,state->curstpmax), "MinBC: integrity check failed", _state);
    postprocessboundedstep(&state->xn, &state->xc, &state->bndl, &state->hasbndl, &state->bndu, &state->hasbndu, n, 0, freezeidx, freezeval, state->stp, activationstep, _state);
    state->fp = state->fc;
    state->fc = state->fn;
    ae_v_move(&state->xp.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->xc.ptr.p_double[0], 1, &state->xn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->cgc.ptr.p_double[0], 1, &state->cgn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->ugc.ptr.p_double[0], 1, &state->ugn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    if( !state->xrep )
    {
        goto lbl_56;
    }
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minbc_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 20;
    goto lbl_rcomm;
lbl_20:
    state->xupdated = ae_false;
lbl_56:
    state->repiterationscount = state->repiterationscount+1;
    if( mcinfo==1 )
    {
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_sqr((state->xc.ptr.p_double[i]-state->xp.ptr.p_double[i])/state->s.ptr.p_double[i], _state);
        }
        v = ae_sqrt(v, _state);
        if( ae_fp_less_eq(v,state->epsx) )
        {
            
            /*
             * Step is small enough
             */
            state->repterminationtype = 2;
            result = ae_false;
            return result;
        }
        if( ae_fp_less_eq(ae_fabs(state->fp-state->fc, _state),state->epsf*ae_maxreal(ae_fabs(state->fc, _state), ae_maxreal(ae_fabs(state->fp, _state), 1.0, _state), _state)) )
        {
            
            /*
             * Function change is small enough
             */
            state->repterminationtype = 1;
            result = ae_false;
            return result;
        }
    }
    if( state->maxits>0&&state->repiterationscount>=state->maxits )
    {
        
        /*
         * Iteration counter exceeded limit
         */
        state->repterminationtype = 5;
        result = ae_false;
        return result;
    }
    
    /*
     * LBFGS stage:
     * * during LBFGS iterations we activate new constraints, but never
     *   deactivate already active ones.
     * * we perform at most N iterations of LBFGS before re-evaluating
     *   active set and restarting LBFGS.
     *
     * About termination:
     * * LBFGS iterations can be terminated because of two reasons:
     *   * "termination" - non-zero termination code in RepTerminationType,
     *     which means that optimization is done
     *   * "restart" - zero RepTerminationType, which means that we
     *     have to re-evaluate active set and resume LBFGS stage.
     * * one more option is "refresh" - to continue LBFGS iterations,
     *   but with all BFGS updates (Sk/Yk pairs) being dropped;
     *   it happens after changes in active set
     */
    ginit = 0.0;
    for(i=0; i<=n-1; i++)
    {
        state->cgc.ptr.p_double[i] = state->ugc.ptr.p_double[i];
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->bndl.ptr.p_double[i]) )
        {
            state->cgc.ptr.p_double[i] = (double)(0);
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            state->cgc.ptr.p_double[i] = (double)(0);
        }
        ginit = ginit+ae_sqr(state->cgc.ptr.p_double[i]*state->s.ptr.p_double[i], _state);
    }
    ginit = ae_sqrt(ginit, _state);
    state->bufsize = 0;
    itidx = 0;
lbl_58:
    if( itidx>n-1 )
    {
        goto lbl_60;
    }
    
    /*
     * At the beginning of each iteration:
     * * XC stores current point
     * * FC stores current function value
     * * UGC stores current unconstrained gradient
     * * CGC stores current constrained gradient
     * * D stores constrained step direction (calculated at this block)
     *
     * 1. Calculate search direction D according to L-BFGS algorithm
     *    using constrained preconditioner to perform inner multiplication.
     * 2. Evaluate scaled length of direction D; restart LBFGS if D is zero
     *    (it may be possible that we found minimum, but it is also possible
     *    that some constraints need deactivation)
     * 3. If D is non-zero, try to use previous scaled step length as initial estimate for new step.
     * 4. Calculate bound on step length.
     */
    ae_v_move(&state->work.ptr.p_double[0], 1, &state->cgc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    for(i=state->bufsize-1; i>=0; i--)
    {
        v = ae_v_dotproduct(&state->bufsk.ptr.pp_double[i][0], 1, &state->work.ptr.p_double[0], 1, ae_v_len(0,n-1));
        state->buftheta.ptr.p_double[i] = v;
        vv = v*state->bufrho.ptr.p_double[i];
        ae_v_subd(&state->work.ptr.p_double[0], 1, &state->bufyk.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), vv);
    }
    for(i=0; i<=n-1; i++)
    {
        state->work.ptr.p_double[i] = state->tmpprec.ptr.p_double[i]*state->work.ptr.p_double[i];
    }
    for(i=0; i<=state->bufsize-1; i++)
    {
        v = ae_v_dotproduct(&state->bufyk.ptr.pp_double[i][0], 1, &state->work.ptr.p_double[0], 1, ae_v_len(0,n-1));
        vv = state->bufrho.ptr.p_double[i]*(-v+state->buftheta.ptr.p_double[i]);
        ae_v_addd(&state->work.ptr.p_double[0], 1, &state->bufsk.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), vv);
    }
    ae_v_moveneg(&state->d.ptr.p_double[0], 1, &state->work.ptr.p_double[0], 1, ae_v_len(0,n-1));
    b = ae_false;
    for(i=0; i<=n-1; i++)
    {
        b = b||((state->hasbndl.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->bndl.ptr.p_double[i]))&&ae_fp_neq(state->d.ptr.p_double[i],(double)(0)));
        b = b||((state->hasbndu.ptr.p_bool[i]&&ae_fp_eq(state->xc.ptr.p_double[i],state->bndu.ptr.p_double[i]))&&ae_fp_neq(state->d.ptr.p_double[i],(double)(0)));
    }
    ae_assert(!b, "MinBC: integrity check failed (q)", _state);
    scaleddnorm = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        scaleddnorm = scaleddnorm+ae_sqr(state->d.ptr.p_double[i]/state->s.ptr.p_double[i], _state);
    }
    scaleddnorm = ae_sqrt(scaleddnorm, _state);
    if( ae_fp_eq(scaleddnorm,(double)(0)) )
    {
        
        /*
         * Search direction is zero.
         * Skip back to steepest descent phase.
         */
        goto lbl_60;
    }
    if( ae_fp_greater(state->lastscaledgoodstep,(double)(0)) )
    {
        state->stp = state->lastscaledgoodstep/scaleddnorm;
    }
    else
    {
        state->stp = 1.0/scaleddnorm;
    }
    state->curstpmax = 1.0E50;
    if( ae_fp_greater(state->stpmax,(double)(0)) )
    {
        state->curstpmax = ae_minreal(state->curstpmax, state->stpmax/scaleddnorm, _state);
    }
    
    /*
     * Minimize G(t) = F(CONSTRAIN(XC + t*D)), with t being scalar, XC and D being vectors.
     */
    ae_v_move(&state->xn.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->cgn.ptr.p_double[0], 1, &state->cgc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->ugn.ptr.p_double[0], 1, &state->ugc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    state->fn = state->fc;
    state->mcstage = 0;
    smoothnessmonitorstartlinesearch1u(&state->smonitor, &state->s, &state->invs, &state->xn, state->fn, &state->ugn, state->repiterationscount, -1, _state);
    mcsrch(n, &state->xn, &state->fn, &state->cgn, &state->d, &state->stp, state->curstpmax, minbc_gtol, &mcinfo, &state->nfev, &state->work, &state->lstate, &state->mcstage, _state);
lbl_61:
    if( state->mcstage==0 )
    {
        goto lbl_62;
    }
    
    /*
     * Copy XN to X, perform on-the-fly correction w.r.t box
     * constraints (projection onto feasible set).
     */
    for(i=0; i<=n-1; i++)
    {
        state->x.ptr.p_double[i] = state->xn.ptr.p_double[i];
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less_eq(state->xn.ptr.p_double[i],state->bndl.ptr.p_double[i]) )
        {
            state->x.ptr.p_double[i] = state->bndl.ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater_eq(state->xn.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            state->x.ptr.p_double[i] = state->bndu.ptr.p_double[i];
        }
    }
    
    /*
     * Gradient, either user-provided or numerical differentiation
     */
    minbc_clearrequestfields(state, _state);
    if( ae_fp_neq(state->diffstep,(double)(0)) )
    {
        goto lbl_63;
    }
    
    /*
     * Analytic gradient
     */
    state->needfg = ae_true;
    state->rstate.stage = 21;
    goto lbl_rcomm;
lbl_21:
    state->needfg = ae_false;
    state->repnfev = state->repnfev+1;
    goto lbl_64;
lbl_63:
    
    /*
     * Numerical differentiation
     */
    state->needf = ae_true;
    state->rstate.stage = 22;
    goto lbl_rcomm;
lbl_22:
    state->fbase = state->f;
    i = 0;
lbl_65:
    if( i>n-1 )
    {
        goto lbl_67;
    }
    v = state->x.ptr.p_double[i];
    b = ae_false;
    if( state->hasbndl.ptr.p_bool[i] )
    {
        b = b||ae_fp_less(v-state->diffstep*state->s.ptr.p_double[i],state->bndl.ptr.p_double[i]);
    }
    if( state->hasbndu.ptr.p_bool[i] )
    {
        b = b||ae_fp_greater(v+state->diffstep*state->s.ptr.p_double[i],state->bndu.ptr.p_double[i]);
    }
    if( b )
    {
        goto lbl_68;
    }
    state->x.ptr.p_double[i] = v-state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 23;
    goto lbl_rcomm;
lbl_23:
    state->fm2 = state->f;
    state->x.ptr.p_double[i] = v-0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 24;
    goto lbl_rcomm;
lbl_24:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = v+0.5*state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 25;
    goto lbl_rcomm;
lbl_25:
    state->fp1 = state->f;
    state->x.ptr.p_double[i] = v+state->diffstep*state->s.ptr.p_double[i];
    state->rstate.stage = 26;
    goto lbl_rcomm;
lbl_26:
    state->fp2 = state->f;
    state->g.ptr.p_double[i] = ((double)8*(state->fp1-state->fm1)-(state->fp2-state->fm2))/((double)6*state->diffstep*state->s.ptr.p_double[i]);
    state->repnfev = state->repnfev+4;
    goto lbl_69;
lbl_68:
    state->xm1 = v-state->diffstep*state->s.ptr.p_double[i];
    state->xp1 = v+state->diffstep*state->s.ptr.p_double[i];
    if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less(state->xm1,state->bndl.ptr.p_double[i]) )
    {
        state->xm1 = state->bndl.ptr.p_double[i];
    }
    if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater(state->xp1,state->bndu.ptr.p_double[i]) )
    {
        state->xp1 = state->bndu.ptr.p_double[i];
    }
    state->x.ptr.p_double[i] = state->xm1;
    state->rstate.stage = 27;
    goto lbl_rcomm;
lbl_27:
    state->fm1 = state->f;
    state->x.ptr.p_double[i] = state->xp1;
    state->rstate.stage = 28;
    goto lbl_rcomm;
lbl_28:
    state->fp1 = state->f;
    if( ae_fp_neq(state->xm1,state->xp1) )
    {
        state->g.ptr.p_double[i] = (state->fp1-state->fm1)/(state->xp1-state->xm1);
    }
    else
    {
        state->g.ptr.p_double[i] = (double)(0);
    }
    state->repnfev = state->repnfev+2;
lbl_69:
    state->x.ptr.p_double[i] = v;
    i = i+1;
    goto lbl_65;
lbl_67:
    state->f = state->fbase;
    state->needf = ae_false;
lbl_64:
    
    /*
     * Back to MCSRCH
     */
    smoothnessmonitorenqueuepoint1u(&state->smonitor, &state->s, &state->invs, &state->d, state->stp, &state->x, state->f, &state->g, _state);
    trimfunction(&state->f, &state->g, n, state->trimthreshold, _state);
    state->fn = state->f;
    for(i=0; i<=n-1; i++)
    {
        state->ugn.ptr.p_double[i] = state->g.ptr.p_double[i];
        state->cgn.ptr.p_double[i] = state->g.ptr.p_double[i];
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less_eq(state->xn.ptr.p_double[i],state->bndl.ptr.p_double[i]) )
        {
            state->cgn.ptr.p_double[i] = (double)(0);
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater_eq(state->xn.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            state->cgn.ptr.p_double[i] = (double)(0);
        }
    }
    mcsrch(n, &state->xn, &state->fn, &state->cgn, &state->d, &state->stp, state->curstpmax, minbc_gtol, &mcinfo, &state->nfev, &state->work, &state->lstate, &state->mcstage, _state);
    goto lbl_61;
lbl_62:
    smoothnessmonitorfinalizelinesearch(&state->smonitor, _state);
    for(i=0; i<=n-1; i++)
    {
        if( state->hasbndl.ptr.p_bool[i]&&ae_fp_less_eq(state->xn.ptr.p_double[i],state->bndl.ptr.p_double[i]) )
        {
            state->xn.ptr.p_double[i] = state->bndl.ptr.p_double[i];
        }
        if( state->hasbndu.ptr.p_bool[i]&&ae_fp_greater_eq(state->xn.ptr.p_double[i],state->bndu.ptr.p_double[i]) )
        {
            state->xn.ptr.p_double[i] = state->bndu.ptr.p_double[i];
        }
    }
    ae_v_moveneg(&state->bufsk.ptr.pp_double[state->bufsize][0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_moveneg(&state->bufyk.ptr.pp_double[state->bufsize][0], 1, &state->cgc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_add(&state->bufsk.ptr.pp_double[state->bufsize][0], 1, &state->xn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_add(&state->bufyk.ptr.pp_double[state->bufsize][0], 1, &state->cgn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    
    /*
     * Handle special situations:
     * * check for presence of NAN/INF in function/gradient
     * * handle failure of line search
     */
    v = state->fn;
    for(i=0; i<=n-1; i++)
    {
        v = 0.1*v+state->ugn.ptr.p_double[i];
    }
    if( !ae_isfinite(v, _state) )
    {
        
        /*
         * Abnormal termination - infinities in function/gradient
         */
        state->repterminationtype = -8;
        result = ae_false;
        return result;
    }
    if( state->userterminationneeded )
    {
        
        /*
         * User requested termination
         */
        state->repterminationtype = 8;
        result = ae_false;
        return result;
    }
    if( mcinfo!=1 )
    {
        
        /*
         * Terminate LBFGS phase
         */
        goto lbl_60;
    }
    
    /*
     * Current point is updated:
     * * move XC/FC/GC to XP/FP/GP
     * * move XN/FN/GN to XC/FC/GC
     * * report current point and update iterations counter
     * * push new pair SK/YK to LBFGS buffer
     * * update length of the good step
     */
    activationstatus = ae_false;
    for(i=0; i<=n-1; i++)
    {
        if( (state->hasbndl.ptr.p_bool[i]&&ae_fp_eq(state->xn.ptr.p_double[i],state->bndl.ptr.p_double[i]))&&ae_fp_neq(state->xn.ptr.p_double[i],state->xc.ptr.p_double[i]) )
        {
            activationstatus = ae_true;
        }
        if( (state->hasbndu.ptr.p_bool[i]&&ae_fp_eq(state->xn.ptr.p_double[i],state->bndu.ptr.p_double[i]))&&ae_fp_neq(state->xn.ptr.p_double[i],state->xc.ptr.p_double[i]) )
        {
            activationstatus = ae_true;
        }
    }
    state->fp = state->fc;
    state->fc = state->fn;
    ae_v_move(&state->xp.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->xc.ptr.p_double[0], 1, &state->xn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->cgc.ptr.p_double[0], 1, &state->cgn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    ae_v_move(&state->ugc.ptr.p_double[0], 1, &state->ugn.ptr.p_double[0], 1, ae_v_len(0,n-1));
    if( !state->xrep )
    {
        goto lbl_70;
    }
    ae_v_move(&state->x.ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
    minbc_clearrequestfields(state, _state);
    state->xupdated = ae_true;
    state->rstate.stage = 29;
    goto lbl_rcomm;
lbl_29:
    state->xupdated = ae_false;
lbl_70:
    state->repiterationscount = state->repiterationscount+1;
    if( state->bufsize==m )
    {
        
        /*
         * Buffer is full, shift contents by one row
         */
        for(i=0; i<=state->bufsize-1; i++)
        {
            ae_v_move(&state->bufsk.ptr.pp_double[i][0], 1, &state->bufsk.ptr.pp_double[i+1][0], 1, ae_v_len(0,n-1));
            ae_v_move(&state->bufyk.ptr.pp_double[i][0], 1, &state->bufyk.ptr.pp_double[i+1][0], 1, ae_v_len(0,n-1));
        }
        for(i=0; i<=state->bufsize-2; i++)
        {
            state->bufrho.ptr.p_double[i] = state->bufrho.ptr.p_double[i+1];
            state->buftheta.ptr.p_double[i] = state->buftheta.ptr.p_double[i+1];
        }
    }
    else
    {
        
        /*
         * Buffer is not full, increase buffer size by 1
         */
        state->bufsize = state->bufsize+1;
    }
    v = ae_v_dotproduct(&state->bufyk.ptr.pp_double[state->bufsize-1][0], 1, &state->bufsk.ptr.pp_double[state->bufsize-1][0], 1, ae_v_len(0,n-1));
    vv = ae_v_dotproduct(&state->bufyk.ptr.pp_double[state->bufsize-1][0], 1, &state->bufyk.ptr.pp_double[state->bufsize-1][0], 1, ae_v_len(0,n-1));
    if( ae_fp_eq(v,(double)(0))||ae_fp_eq(vv,(double)(0)) )
    {
        
        /*
         * Strange internal error in LBFGS - either YK=0
         * (which should not have been) or (SK,YK)=0 (again,
         * unexpected). It should not take place because
         * MCINFO=1, which signals "good" step. But just
         * to be sure we have special branch of code which
         * restarts LBFGS
         */
        goto lbl_60;
    }
    state->bufrho.ptr.p_double[state->bufsize-1] = (double)1/v;
    ae_assert(state->bufsize<=m, "MinBC: internal error", _state);
    v = (double)(0);
    vv = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr((state->xc.ptr.p_double[i]-state->xp.ptr.p_double[i])/state->s.ptr.p_double[i], _state);
        vv = vv+ae_sqr(state->xc.ptr.p_double[i]-state->xp.ptr.p_double[i], _state);
    }
    minbc_updateestimateofgoodstep(&state->lastscaledgoodstep, ae_sqrt(v, _state), _state);
    
    /*
     * Check MaxIts-based stopping condition.
     */
    if( state->maxits>0&&state->repiterationscount>=state->maxits )
    {
        state->repterminationtype = 5;
        result = ae_false;
        return result;
    }
    
    /*
     * Smooth reset (LBFGS memory model is refreshed) or hard restart:
     * * LBFGS model is refreshed, if line search was performed with activation of constraints
     * * algorithm is restarted if scaled gradient decreased below GDecay
     */
    if( activationstatus )
    {
        state->bufsize = 0;
        goto lbl_59;
    }
    v = 0.0;
    for(i=0; i<=n-1; i++)
    {
        v = v+ae_sqr(state->cgc.ptr.p_double[i]*state->s.ptr.p_double[i], _state);
    }
    if( ae_fp_less(ae_sqrt(v, _state),gdecay*ginit) )
    {
        goto lbl_60;
    }
lbl_59:
    itidx = itidx+1;
    goto lbl_58;
lbl_60:
    
    /*
     * Decrease decay coefficient. Subsequent L-BFGS stages will
     * have more stringent stopping criteria.
     */
    gdecay = ae_maxreal(gdecay*minbc_decaycorrection, minbc_mindecay, _state);
    goto lbl_38;
lbl_39:
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    state->rstate.ia.ptr.p_int[0] = freezeidx;
    state->rstate.ia.ptr.p_int[1] = n;
    state->rstate.ia.ptr.p_int[2] = m;
    state->rstate.ia.ptr.p_int[3] = i;
    state->rstate.ia.ptr.p_int[4] = j;
    state->rstate.ia.ptr.p_int[5] = mcinfo;
    state->rstate.ia.ptr.p_int[6] = itidx;
    state->rstate.ba.ptr.p_bool[0] = b;
    state->rstate.ba.ptr.p_bool[1] = activationstatus;
    state->rstate.ra.ptr.p_double[0] = freezeval;
    state->rstate.ra.ptr.p_double[1] = scaleddnorm;
    state->rstate.ra.ptr.p_double[2] = v;
    state->rstate.ra.ptr.p_double[3] = vv;
    state->rstate.ra.ptr.p_double[4] = v0;
    state->rstate.ra.ptr.p_double[5] = ginit;
    state->rstate.ra.ptr.p_double[6] = gdecay;
    state->rstate.ra.ptr.p_double[7] = activationstep;
    return result;
}


/*************************************************************************
This  function  activates/deactivates verification  of  the  user-supplied
analytic gradient.

Upon  activation  of  this  option  OptGuard  integrity  checker  performs
numerical differentiation of your target function  at  the  initial  point
(note: future versions may also perform check  at  the  final  point)  and
compares numerical gradient with analytic one provided by you.

If difference is too large, an error flag is set and optimization  session
continues. After optimization session is over, you can retrieve the report
which  stores  both  gradients  and  specific  components  highlighted  as
suspicious by the OptGuard.

The primary OptGuard report can be retrieved with minbcoptguardresults().

IMPORTANT: gradient check is a high-overhead option which  will  cost  you
           about 3*N additional function evaluations. In many cases it may
           cost as much as the rest of the optimization session.
           
           YOU SHOULD NOT USE IT IN THE PRODUCTION CODE UNLESS YOU WANT TO
           CHECK DERIVATIVES PROVIDED BY SOME THIRD PARTY.

NOTE: unlike previous incarnation of the gradient checking code,  OptGuard
      does NOT interrupt optimization even if it discovers bad gradient.
      
INPUT PARAMETERS:
    State       -   structure used to store algorithm state
    TestStep    -   verification step used for numerical differentiation:
                    * TestStep=0 turns verification off
                    * TestStep>0 activates verification
                    You should carefully choose TestStep. Value  which  is
                    too large (so large that  function  behavior  is  non-
                    cubic at this scale) will lead  to  false  alarms. Too
                    short step will result in rounding  errors  dominating
                    numerical derivative.
                    
                    You may use different step for different parameters by
                    means of setting scale with minbcsetscale().

=== EXPLANATION ==========================================================                    

In order to verify gradient algorithm performs following steps:
  * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i],
    where X[i] is i-th component of the initial point and S[i] is a  scale
    of i-th parameter
  * F(X) is evaluated at these trial points
  * we perform one more evaluation in the middle point of the interval
  * we  build  cubic  model using function values and derivatives at trial
    points and we compare its prediction with actual value in  the  middle
    point

  -- ALGLIB --
     Copyright 15.06.2014 by Bochkanov Sergey
*************************************************************************/
void minbcoptguardgradient(minbcstate* state,
     double teststep,
     ae_state *_state)
{


    ae_assert(ae_isfinite(teststep, _state), "MinBCOptGuardGradient: TestStep contains NaN or INF", _state);
    ae_assert(ae_fp_greater_eq(teststep,(double)(0)), "MinBCOptGuardGradient: invalid argument TestStep(TestStep<0)", _state);
    state->teststep = teststep;
}


/*************************************************************************
This  function  activates/deactivates nonsmoothness monitoring  option  of
the  OptGuard  integrity  checker. Smoothness  monitor  silently  observes
solution process and tries to detect ill-posed problems, i.e. ones with:
a) discontinuous target function (non-C0)
b) nonsmooth     target function (non-C1)

Smoothness monitoring does NOT interrupt optimization  even if it suspects
that your problem is nonsmooth. It just sets corresponding  flags  in  the
OptGuard report which can be retrieved after optimization is over.

Smoothness monitoring is a moderate overhead option which often adds  less
than 1% to the optimizer running time. Thus, you can use it even for large
scale problems.

NOTE: OptGuard does  NOT  guarantee  that  it  will  always  detect  C0/C1
      continuity violations.
      
      First, minor errors are hard to  catch - say, a 0.0001 difference in
      the model values at two sides of the gap may be due to discontinuity
      of the model - or simply because the model has changed.
      
      Second, C1-violations  are  especially  difficult  to  detect  in  a
      noninvasive way. The optimizer usually  performs  very  short  steps
      near the nonsmoothness, and differentiation  usually   introduces  a
      lot of numerical noise.  It  is  hard  to  tell  whether  some  tiny
      discontinuity in the slope is due to real nonsmoothness or just  due
      to numerical noise alone.
      
      Our top priority was to avoid false positives, so in some rare cases
      minor errors may went unnoticed (however, in most cases they can  be
      spotted with restart from different initial point).

INPUT PARAMETERS:
    state   -   algorithm state
    level   -   monitoring level:
                * 0 - monitoring is disabled
                * 1 - noninvasive low-overhead monitoring; function values
                      and/or gradients are recorded, but OptGuard does not
                      try to perform additional evaluations  in  order  to
                      get more information about suspicious locations.
  
=== EXPLANATION ==========================================================

One major source of headache during optimization  is  the  possibility  of
the coding errors in the target function/constraints (or their gradients).
Such  errors   most   often   manifest   themselves  as  discontinuity  or
nonsmoothness of the target/constraints.

Another frequent situation is when you try to optimize something involving
lots of min() and max() operations, i.e. nonsmooth target. Although not  a
coding error, it is nonsmoothness anyway - and smooth  optimizers  usually
stop right after encountering nonsmoothness, well before reaching solution.

OptGuard integrity checker helps you to catch such situations: it monitors
function values/gradients being passed  to  the  optimizer  and  tries  to
errors. Upon discovering suspicious pair of points it  raises  appropriate
flag (and allows you to continue optimization). When optimization is done,
you can study OptGuard result.

  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbcoptguardsmoothness(minbcstate* state,
     ae_int_t level,
     ae_state *_state)
{


    ae_assert(level==0||level==1, "MinBCOptGuardSmoothness: unexpected value of level parameter", _state);
    state->smoothnessguardlevel = level;
}


/*************************************************************************
Results of OptGuard integrity check, should be called  after  optimization
session is over.

=== PRIMARY REPORT =======================================================

OptGuard performs several checks which are intended to catch common errors
in the implementation of nonlinear function/gradient:
* incorrect analytic gradient
* discontinuous (non-C0) target functions (constraints)
* nonsmooth     (non-C1) target functions (constraints)

Each of these checks is activated with appropriate function:
* minbcoptguardgradient() for gradient verification
* minbcoptguardsmoothness() for C0/C1 checks

Following flags are set when these errors are suspected:
* rep.badgradsuspected, and additionally:
  * rep.badgradvidx for specific variable (gradient element) suspected
  * rep.badgradxbase, a point where gradient is tested
  * rep.badgraduser, user-provided gradient  (stored  as  2D  matrix  with
    single row in order to make  report  structure  compatible  with  more
    complex optimizers like MinNLC or MinLM)
  * rep.badgradnum,   reference    gradient    obtained    via   numerical
    differentiation (stored as  2D matrix with single row in order to make
    report structure compatible with more complex optimizers  like  MinNLC
    or MinLM)
* rep.nonc0suspected
* rep.nonc1suspected

=== ADDITIONAL REPORTS/LOGS ==============================================
    
Several different tests are performed to catch C0/C1 errors, you can  find
out specific test signaled error by looking to:
* rep.nonc0test0positive, for non-C0 test #0
* rep.nonc1test0positive, for non-C1 test #0
* rep.nonc1test1positive, for non-C1 test #1

Additional information (including line search logs)  can  be  obtained  by
means of:
* minbcoptguardnonc1test0results()
* minbcoptguardnonc1test1results()
which return detailed error reports, specific points where discontinuities
were found, and so on.

==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    rep     -   generic OptGuard report;  more  detailed  reports  can  be
                retrieved with other functions.

NOTE: false negatives (nonsmooth problems are not identified as  nonsmooth
      ones) are possible although unlikely.
      
      The reason  is  that  you  need  to  make several evaluations around
      nonsmoothness  in  order  to  accumulate  enough  information  about
      function curvature. Say, if you start right from the nonsmooth point,
      optimizer simply won't get enough data to understand what  is  going
      wrong before it terminates due to abrupt changes in the  derivative.
      It is also  possible  that  "unlucky"  step  will  move  us  to  the
      termination too quickly.
      
      Our current approach is to have less than 0.1%  false  negatives  in
      our test examples  (measured  with  multiple  restarts  from  random
      points), and to have exactly 0% false positives.
   
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbcoptguardresults(minbcstate* state,
     optguardreport* rep,
     ae_state *_state)
{

    _optguardreport_clear(rep);

    smoothnessmonitorexportreport(&state->smonitor, rep, _state);
}


/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #0

Nonsmoothness (non-C1) test #0 studies  function  values  (not  gradient!)
obtained during line searches and monitors  behavior  of  the  directional
derivative estimate.

This test is less powerful than test #1, but it does  not  depend  on  the
gradient values and thus it is more robust against artifacts introduced by
numerical differentiation.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], f[] - arrays of length CNT which store step lengths and  function
  values at these points; f[i] is evaluated in x0+stp[i]*d.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
  
==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #0 "strong" report
    lngrep  -   C1 test #0 "long" report
    
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbcoptguardnonc1test0results(const minbcstate* state,
     optguardnonc1test0report* strrep,
     optguardnonc1test0report* lngrep,
     ae_state *_state)
{

    _optguardnonc1test0report_clear(strrep);
    _optguardnonc1test0report_clear(lngrep);

    smoothnessmonitorexportc1test0report(&state->smonitor.nonc1test0strrep, &state->lastscaleused, strrep, _state);
    smoothnessmonitorexportc1test0report(&state->smonitor.nonc1test0lngrep, &state->lastscaleused, lngrep, _state);
}


/*************************************************************************
Detailed results of the OptGuard integrity check for nonsmoothness test #1

Nonsmoothness (non-C1)  test  #1  studies  individual  components  of  the
gradient computed during line search.

When precise analytic gradient is provided this test is more powerful than
test #0  which  works  with  function  values  and  ignores  user-provided
gradient.  However,  test  #0  becomes  more   powerful   when   numerical
differentiation is employed (in such cases test #1 detects  higher  levels
of numerical noise and becomes too conservative).

This test also tells specific components of the gradient which violate  C1
continuity, which makes it more informative than #0, which just tells that
continuity is violated.

Two reports are returned:
* a "strongest" one, corresponding  to  line   search  which  had  highest
  value of the nonsmoothness indicator
* a "longest" one, corresponding to line search which  had  more  function
  evaluations, and thus is more detailed

In both cases following fields are returned:

* positive - is TRUE  when test flagged suspicious point;  FALSE  if  test
  did not notice anything (in the latter cases fields below are empty).
* vidx - is an index of the variable in [0,N) with nonsmooth derivative
* x0[], d[] - arrays of length N which store initial point  and  direction
  for line search (d[] can be normalized, but does not have to)
* stp[], g[] - arrays of length CNT which store step lengths and  gradient
  values at these points; g[i] is evaluated in  x0+stp[i]*d  and  contains
  vidx-th component of the gradient.
* stpidxa, stpidxb - we  suspect  that  function  violates  C1  continuity
  between steps #stpidxa and #stpidxb (usually we have  stpidxb=stpidxa+3,
  with  most  likely  position  of  the  violation  between  stpidxa+1 and
  stpidxa+2.
  
==========================================================================
= SHORTLY SPEAKING: build a 2D plot of (stp,f) and look at it -  you  will
=                   see where C1 continuity is violated.
==========================================================================
     
INPUT PARAMETERS:
    state   -   algorithm state

OUTPUT PARAMETERS:
    strrep  -   C1 test #1 "strong" report
    lngrep  -   C1 test #1 "long" report
    
  -- ALGLIB --
     Copyright 21.11.2018 by Bochkanov Sergey
*************************************************************************/
void minbcoptguardnonc1test1results(minbcstate* state,
     optguardnonc1test1report* strrep,
     optguardnonc1test1report* lngrep,
     ae_state *_state)
{

    _optguardnonc1test1report_clear(strrep);
    _optguardnonc1test1report_clear(lngrep);

    smoothnessmonitorexportc1test1report(&state->smonitor.nonc1test1strrep, &state->lastscaleused, strrep, _state);
    smoothnessmonitorexportc1test1report(&state->smonitor.nonc1test1lngrep, &state->lastscaleused, lngrep, _state);
}


/*************************************************************************
BC results

INPUT PARAMETERS:
    State   -   algorithm state

OUTPUT PARAMETERS:
    X       -   array[0..N-1], solution
    Rep     -   optimization report. You should check Rep.TerminationType
                in  order  to  distinguish  successful  termination  from
                unsuccessful one:
                * -8    internal integrity control  detected  infinite or
                        NAN   values   in   function/gradient.   Abnormal
                        termination signalled.
                * -3   inconsistent constraints.
                *  1   relative function improvement is no more than EpsF.
                *  2   scaled step is no more than EpsX.
                *  4   scaled gradient norm is no more than EpsG.
                *  5   MaxIts steps was taken
                *  8   terminated by user who called minbcrequesttermination().
                       X contains point which was "current accepted"  when
                       termination request was submitted.
                More information about fields of this  structure  can  be
                found in the comments on MinBCReport datatype.
   
  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbcresults(const minbcstate* state,
     /* Real    */ ae_vector* x,
     minbcreport* rep,
     ae_state *_state)
{

    ae_vector_clear(x);
    _minbcreport_clear(rep);

    minbcresultsbuf(state, x, rep, _state);
}


/*************************************************************************
BC results

Buffered implementation of MinBCResults() which uses pre-allocated buffer
to store X[]. If buffer size is  too  small,  it  resizes  buffer.  It  is
intended to be used in the inner cycles of performance critical algorithms
where array reallocation penalty is too large to be ignored.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbcresultsbuf(const minbcstate* state,
     /* Real    */ ae_vector* x,
     minbcreport* rep,
     ae_state *_state)
{
    ae_int_t i;


    if( x->cnt<state->nmain )
    {
        ae_vector_set_length(x, state->nmain, _state);
    }
    rep->iterationscount = state->repiterationscount;
    rep->nfev = state->repnfev;
    rep->varidx = state->repvaridx;
    rep->terminationtype = state->repterminationtype;
    if( state->repterminationtype>0 )
    {
        ae_v_move(&x->ptr.p_double[0], 1, &state->xc.ptr.p_double[0], 1, ae_v_len(0,state->nmain-1));
    }
    else
    {
        for(i=0; i<=state->nmain-1; i++)
        {
            x->ptr.p_double[i] = _state->v_nan;
        }
    }
}


/*************************************************************************
This subroutine restarts algorithm from new point.
All optimization parameters (including constraints) are left unchanged.

This  function  allows  to  solve multiple  optimization  problems  (which
must have  same number of dimensions) without object reallocation penalty.

INPUT PARAMETERS:
    State   -   structure previously allocated with MinBCCreate call.
    X       -   new starting point.

  -- ALGLIB --
     Copyright 28.11.2010 by Bochkanov Sergey
*************************************************************************/
void minbcrestartfrom(minbcstate* state,
     /* Real    */ const ae_vector* x,
     ae_state *_state)
{
    ae_int_t n;


    n = state->nmain;
    
    /*
     * First, check for errors in the inputs
     */
    ae_assert(x->cnt>=n, "MinBCRestartFrom: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "MinBCRestartFrom: X contains infinite or NaN values!", _state);
    
    /*
     * Set XC
     */
    ae_v_move(&state->xstart.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
    
    /*
     * prepare RComm facilities
     */
    ae_vector_set_length(&state->rstate.ia, 6+1, _state);
    ae_vector_set_length(&state->rstate.ba, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 7+1, _state);
    state->rstate.stage = -1;
    minbc_clearrequestfields(state, _state);
}


/*************************************************************************
This subroutine submits request for termination of running  optimizer.  It
should be called from user-supplied callback when user decides that it  is
time to "smoothly" terminate optimization process.  As  result,  optimizer
stops at point which was "current accepted" when termination  request  was
submitted and returns error code 8 (successful termination).

INPUT PARAMETERS:
    State   -   optimizer structure

NOTE: after  request  for  termination  optimizer  may   perform   several
      additional calls to user-supplied callbacks. It does  NOT  guarantee
      to stop immediately - it just guarantees that these additional calls
      will be discarded later.

NOTE: calling this function on optimizer which is NOT running will have no
      effect.
      
NOTE: multiple calls to this function are possible. First call is counted,
      subsequent calls are silently ignored.

  -- ALGLIB --
     Copyright 08.10.2014 by Bochkanov Sergey
*************************************************************************/
void minbcrequesttermination(minbcstate* state, ae_state *_state)
{


    state->userterminationneeded = ae_true;
}


/*************************************************************************
Set V1 reverse communication protocol
*************************************************************************/
void minbcsetprotocolv1(minbcstate* state, ae_state *_state)
{


    state->protocolversion = 1;
    ae_vector_set_length(&state->rstate.ia, 6+1, _state);
    ae_vector_set_length(&state->rstate.ba, 1+1, _state);
    ae_vector_set_length(&state->rstate.ra, 7+1, _state);
    state->rstate.stage = -1;
}


/*************************************************************************
Clears request fileds (to be sure that we don't forget to clear something)
*************************************************************************/
static void minbc_clearrequestfields(minbcstate* state, ae_state *_state)
{


    state->needf = ae_false;
    state->needfg = ae_false;
    state->xupdated = ae_false;
}


/*************************************************************************
Internal initialization subroutine.
*************************************************************************/
static void minbc_minbcinitinternal(ae_int_t n,
     /* Real    */ const ae_vector* x,
     double diffstep,
     minbcstate* state,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_matrix c;
    ae_vector ct;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);

    
    /*
     * Initialize
     */
    state->protocolversion = 1;
    state->teststep = (double)(0);
    state->smoothnessguardlevel = 0;
    smoothnessmonitorinit(&state->smonitor, &state->s, 0, 0, ae_false, _state);
    state->nmain = n;
    state->diffstep = diffstep;
    rvectorsetlengthatleast(&state->bndl, n, _state);
    bvectorsetlengthatleast(&state->hasbndl, n, _state);
    rvectorsetlengthatleast(&state->bndu, n, _state);
    bvectorsetlengthatleast(&state->hasbndu, n, _state);
    rvectorsetlengthatleast(&state->xstart, n, _state);
    rvectorsetlengthatleast(&state->xc, n, _state);
    rvectorsetlengthatleast(&state->cgc, n, _state);
    rvectorsetlengthatleast(&state->ugc, n, _state);
    rvectorsetlengthatleast(&state->xn, n, _state);
    rvectorsetlengthatleast(&state->cgn, n, _state);
    rvectorsetlengthatleast(&state->ugn, n, _state);
    rvectorsetlengthatleast(&state->xp, n, _state);
    rvectorsetlengthatleast(&state->d, n, _state);
    rvectorsetlengthatleast(&state->s, n, _state);
    rvectorsetlengthatleast(&state->invs, n, _state);
    rvectorsetlengthatleast(&state->lastscaleused, n, _state);
    rvectorsetlengthatleast(&state->x, n, _state);
    rvectorsetlengthatleast(&state->g, n, _state);
    rvectorsetlengthatleast(&state->work, n, _state);
    for(i=0; i<=n-1; i++)
    {
        state->bndl.ptr.p_double[i] = _state->v_neginf;
        state->hasbndl.ptr.p_bool[i] = ae_false;
        state->bndu.ptr.p_double[i] = _state->v_posinf;
        state->hasbndu.ptr.p_bool[i] = ae_false;
        state->s.ptr.p_double[i] = 1.0;
        state->invs.ptr.p_double[i] = 1.0;
        state->lastscaleused.ptr.p_double[i] = 1.0;
    }
    minbcsetcond(state, 0.0, 0.0, 0.0, 0, _state);
    minbcsetxrep(state, ae_false, _state);
    minbcsetstpmax(state, 0.0, _state);
    minbcsetprecdefault(state, _state);
    minbcrestartfrom(state, x, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
This subroutine updates estimate of the good step length given:
1) previous estimate
2) new length of the good step

It makes sure that estimate does not change too rapidly - ratio of new and
old estimates will be at least 0.01, at most 100.0

In case previous estimate of good step is zero (no estimate), new estimate
is used unconditionally.

  -- ALGLIB --
     Copyright 16.01.2013 by Bochkanov Sergey
*************************************************************************/
static void minbc_updateestimateofgoodstep(double* estimate,
     double newstep,
     ae_state *_state)
{


    if( ae_fp_eq(*estimate,(double)(0)) )
    {
        *estimate = newstep;
        return;
    }
    if( ae_fp_less(newstep,*estimate*0.01) )
    {
        *estimate = *estimate*0.01;
        return;
    }
    if( ae_fp_greater(newstep,*estimate*(double)100) )
    {
        *estimate = *estimate*(double)100;
        return;
    }
    *estimate = newstep;
}


void _minbcstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minbcstate *p = (minbcstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->diagh, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
    _rcommstate_init(&p->rstate, _state, make_automatic);
    ae_vector_init(&p->xc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ugc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cgc, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->ugn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->cgn, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xp, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->d, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->hasbndl, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->hasbndu, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->xstart, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmpprec, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->work, 0, DT_REAL, _state, make_automatic);
    _linminstate_init(&p->lstate, _state, make_automatic);
    ae_matrix_init(&p->bufyk, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->bufsk, 0, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->bufrho, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->buftheta, 0, DT_REAL, _state, make_automatic);
    _smoothnessmonitor_init(&p->smonitor, _state, make_automatic);
    ae_vector_init(&p->lastscaleused, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->invs, 0, DT_REAL, _state, make_automatic);
}


void _minbcstate_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minbcstate       *dst = (minbcstate*)_dst;
    const minbcstate *src = (const minbcstate*)_src;
    dst->nmain = src->nmain;
    dst->epsg = src->epsg;
    dst->epsf = src->epsf;
    dst->epsx = src->epsx;
    dst->maxits = src->maxits;
    dst->xrep = src->xrep;
    dst->stpmax = src->stpmax;
    dst->diffstep = src->diffstep;
    ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
    dst->prectype = src->prectype;
    ae_vector_init_copy(&dst->diagh, &src->diagh, _state, make_automatic);
    dst->protocolversion = src->protocolversion;
    ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
    dst->f = src->f;
    ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
    dst->needf = src->needf;
    dst->needfg = src->needfg;
    dst->xupdated = src->xupdated;
    dst->userterminationneeded = src->userterminationneeded;
    _rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
    ae_vector_init_copy(&dst->xc, &src->xc, _state, make_automatic);
    ae_vector_init_copy(&dst->ugc, &src->ugc, _state, make_automatic);
    ae_vector_init_copy(&dst->cgc, &src->cgc, _state, make_automatic);
    ae_vector_init_copy(&dst->xn, &src->xn, _state, make_automatic);
    ae_vector_init_copy(&dst->ugn, &src->ugn, _state, make_automatic);
    ae_vector_init_copy(&dst->cgn, &src->cgn, _state, make_automatic);
    ae_vector_init_copy(&dst->xp, &src->xp, _state, make_automatic);
    dst->fc = src->fc;
    dst->fn = src->fn;
    dst->fp = src->fp;
    ae_vector_init_copy(&dst->d, &src->d, _state, make_automatic);
    dst->lastscaledgoodstep = src->lastscaledgoodstep;
    ae_vector_init_copy(&dst->hasbndl, &src->hasbndl, _state, make_automatic);
    ae_vector_init_copy(&dst->hasbndu, &src->hasbndu, _state, make_automatic);
    ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
    ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
    dst->repiterationscount = src->repiterationscount;
    dst->repnfev = src->repnfev;
    dst->repvaridx = src->repvaridx;
    dst->repterminationtype = src->repterminationtype;
    ae_vector_init_copy(&dst->xstart, &src->xstart, _state, make_automatic);
    dst->fbase = src->fbase;
    dst->fm2 = src->fm2;
    dst->fm1 = src->fm1;
    dst->fp1 = src->fp1;
    dst->fp2 = src->fp2;
    dst->xm1 = src->xm1;
    dst->xp1 = src->xp1;
    dst->gm1 = src->gm1;
    dst->gp1 = src->gp1;
    ae_vector_init_copy(&dst->tmpprec, &src->tmpprec, _state, make_automatic);
    ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
    dst->nfev = src->nfev;
    dst->mcstage = src->mcstage;
    dst->stp = src->stp;
    dst->curstpmax = src->curstpmax;
    ae_vector_init_copy(&dst->work, &src->work, _state, make_automatic);
    _linminstate_init_copy(&dst->lstate, &src->lstate, _state, make_automatic);
    dst->trimthreshold = src->trimthreshold;
    dst->nonmonotoniccnt = src->nonmonotoniccnt;
    ae_matrix_init_copy(&dst->bufyk, &src->bufyk, _state, make_automatic);
    ae_matrix_init_copy(&dst->bufsk, &src->bufsk, _state, make_automatic);
    ae_vector_init_copy(&dst->bufrho, &src->bufrho, _state, make_automatic);
    ae_vector_init_copy(&dst->buftheta, &src->buftheta, _state, make_automatic);
    dst->bufsize = src->bufsize;
    dst->teststep = src->teststep;
    dst->smoothnessguardlevel = src->smoothnessguardlevel;
    _smoothnessmonitor_init_copy(&dst->smonitor, &src->smonitor, _state, make_automatic);
    ae_vector_init_copy(&dst->lastscaleused, &src->lastscaleused, _state, make_automatic);
    ae_vector_init_copy(&dst->invs, &src->invs, _state, make_automatic);
}


void _minbcstate_clear(void* _p)
{
    minbcstate *p = (minbcstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->s);
    ae_vector_clear(&p->diagh);
    ae_vector_clear(&p->x);
    ae_vector_clear(&p->g);
    _rcommstate_clear(&p->rstate);
    ae_vector_clear(&p->xc);
    ae_vector_clear(&p->ugc);
    ae_vector_clear(&p->cgc);
    ae_vector_clear(&p->xn);
    ae_vector_clear(&p->ugn);
    ae_vector_clear(&p->cgn);
    ae_vector_clear(&p->xp);
    ae_vector_clear(&p->d);
    ae_vector_clear(&p->hasbndl);
    ae_vector_clear(&p->hasbndu);
    ae_vector_clear(&p->bndl);
    ae_vector_clear(&p->bndu);
    ae_vector_clear(&p->xstart);
    ae_vector_clear(&p->tmpprec);
    ae_vector_clear(&p->tmp0);
    ae_vector_clear(&p->work);
    _linminstate_clear(&p->lstate);
    ae_matrix_clear(&p->bufyk);
    ae_matrix_clear(&p->bufsk);
    ae_vector_clear(&p->bufrho);
    ae_vector_clear(&p->buftheta);
    _smoothnessmonitor_clear(&p->smonitor);
    ae_vector_clear(&p->lastscaleused);
    ae_vector_clear(&p->invs);
}


void _minbcstate_destroy(void* _p)
{
    minbcstate *p = (minbcstate*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->s);
    ae_vector_destroy(&p->diagh);
    ae_vector_destroy(&p->x);
    ae_vector_destroy(&p->g);
    _rcommstate_destroy(&p->rstate);
    ae_vector_destroy(&p->xc);
    ae_vector_destroy(&p->ugc);
    ae_vector_destroy(&p->cgc);
    ae_vector_destroy(&p->xn);
    ae_vector_destroy(&p->ugn);
    ae_vector_destroy(&p->cgn);
    ae_vector_destroy(&p->xp);
    ae_vector_destroy(&p->d);
    ae_vector_destroy(&p->hasbndl);
    ae_vector_destroy(&p->hasbndu);
    ae_vector_destroy(&p->bndl);
    ae_vector_destroy(&p->bndu);
    ae_vector_destroy(&p->xstart);
    ae_vector_destroy(&p->tmpprec);
    ae_vector_destroy(&p->tmp0);
    ae_vector_destroy(&p->work);
    _linminstate_destroy(&p->lstate);
    ae_matrix_destroy(&p->bufyk);
    ae_matrix_destroy(&p->bufsk);
    ae_vector_destroy(&p->bufrho);
    ae_vector_destroy(&p->buftheta);
    _smoothnessmonitor_destroy(&p->smonitor);
    ae_vector_destroy(&p->lastscaleused);
    ae_vector_destroy(&p->invs);
}


void _minbcreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    minbcreport *p = (minbcreport*)_p;
    ae_touch_ptr((void*)p);
}


void _minbcreport_init_copy(void* _dst, const void* _src, ae_state *_state, ae_bool make_automatic)
{
    minbcreport       *dst = (minbcreport*)_dst;
    const minbcreport *src = (const minbcreport*)_src;
    dst->iterationscount = src->iterationscount;
    dst->nfev = src->nfev;
    dst->varidx = src->varidx;
    dst->terminationtype = src->terminationtype;
}


void _minbcreport_clear(void* _p)
{
    minbcreport *p = (minbcreport*)_p;
    ae_touch_ptr((void*)p);
}


void _minbcreport_destroy(void* _p)
{
    minbcreport *p = (minbcreport*)_p;
    ae_touch_ptr((void*)p);
}


#endif

}

