// File: /workspace/github_cpp_makefile_star2_O2/myudelson[P]hmm-scalable/HMMProblemSt.cpp
// Function: HMMProblemSt::doLinearStep(FitBitSt*, bool)
void HMMProblemSt::doLinearStep(FitBitSt *fb, bool direction) {
    NPAR i;
    NCAT l,k, nK = this->task->nK, skill1_n = this->skill1_n;
   // NDAT c = 0;
    
    // MOVED TO Cycle Bit
    // fb->scaleGradients(direction, this->active_set_block); // it knows whether to scale gradient or direction
    
    fb->PARAMcopy = init1D<NUMBER>(fb->nPARAM);
    fb->GRADcopy = init1D<NUMBER>(fb->nPARAM); // copy of gradient or direction, we will need just the grad's copy saved
    NUMBER * save_GRAD = init1D<NUMBER>((NDAT)fb->nPARAM); // save GRAD since while stepping it would be recomputed
    
    // all of the variables were single value, now we ad `_s` suffix and make them arrays
    NUMBER* e_s = initToValue1D<NUMBER>(nK,this->task->ArmijoSeed); // step seed
    bool* compliesArmijo_s = initToValue1D<bool>(nK, false);
    bool* compliesWolfe2_s = initToValue1D<bool>(nK, false); // second wolfe condition is turned on, if satisfied - honored, if not, just the 1st is used
//    NCAT n_compliesArmijo_or_Wolfe2 = 0;
//    NCAT n_e_lt_ArmijoMinStep = 0;
    NCAT n_compliesArmijo_or_compliesWolfe2_or_n_e_lt_ArmijoMinStep = 0;

    NUMBER* e_Armijo_s = initToValue1D<NUMBER>(nK,-1); // e (step size) at which Armijo (Wolfe 1) criterion is satisfied, 'cos both criterions are sometimes not met.
    NUMBER* f_xkplus1_Armijo_s = initToValue1D<NUMBER>(nK,0); // f_xkplus1_Armijo at which Armijo (Wolfe 1) criterion is satisfied, 'cos both criterions are sometimes not met.
    NUMBER* f_xk_s = initToValue1D<NUMBER>(nK,0);
    for(k=0; k<nK; k++) f_xk_s[k] = this->loglik_k[k];
    NUMBER* f_xkplus1_s = initToValue1D<NUMBER>(nK,0);
    NUMBER* p_k_by_neg_p_k_s = initToValue1D<NUMBER>(nK,0);
    NUMBER* p_k_by_neg_p_kp1_s = initToValue1D<NUMBER>(nK,0);

    cpy1D(fb->PARAM,fb->PARAMcopy,fb->nPARAM);
    if(!direction)
        cpy1D(fb->GRAD,fb->GRADcopy,fb->nPARAM);
    else {
        cpy1D(fb->DIR,fb->GRADcopy,fb->nPARAM); // in case we are using DIR, GRADcopy is the DIR
        // negate1D(fb->GRADcopy, fb->nPARAM); // negate, because we treat is as GRADient, direction has inverse direction
        // ^^ was already negated in the Conjugate part
    }
    cpy1D(fb->GRAD, save_GRAD, fb->nPARAM); // copy grads
    // compute p_k * -p_k for all k
    
    NCAT nK_active = 0, nK_active_local = 0 /*set to 1 later to be changed*/;
    
    for(k=0; k<nK; k++) {
        nK_active += this->active_set_block[k]>0;
        if(this->active_set_block[k]>0) {
            for(i=0; i<skill1_n; i++) {
                l = skill1_n*k + i;
                p_k_by_neg_p_k_s[k] -= fb->GRAD[l] * fb->GRADcopy[l]; // in case we are using DIR, GRADcopy is the DIR
            }
        }
    } // vvvv REDO
//    for(l=0; l<fb->nPARAM; l++) {
//        k = fb->param_blocks[l];
//        nK_active += this->active_set_block[k]>0;
//        if(this->active_set_block[k]>0) {
//            p_k_by_neg_p_k_s[k] -= fb->GRAD[l] * fb->GRADcopy[l]; // in case we are using DIR, GRADcopy is the DIR
//        }
//    }
    nK_active_local = nK_active;
    
    // copy active set for original values would be used locally
    NPAR * active_set_block_local = init1D<NPAR>((NDAT)nK);
    cpy1D(this->active_set_block, active_set_block_local, nK);
    
    int iter = 0; // limit iter steps to 20, via ArmijoMinStep (now 10)
    //while( n_compliesArmijo_or_Wolfe2<nK_active/*nK*/ && n_e_lt_ArmijoMinStep<nK_active/*nK*/ ) {
    while( nK_active_local > 0) { // n_compliesArmijo_or_compliesWolfe2_or_n_e_lt_ArmijoMinStep < nK_active/*nK*/) {
        nK_active_local = 0;
        // n_compliesArmijo_or_Wolfe2 = 0;
        // n_e_lt_ArmijoMinStep = 0;
        //n_compliesArmijo_or_compliesWolfe2_or_n_e_lt_ArmijoMinStep = 0;
        // step
//        c = 0;
//        for(k=0; k<nK; k++) {
//            if(this->active_set_block[k]>0) {
//                for(i=0; i<skill1_n; i++) {
//                    l = skill1_n*k + i;
//                    fb->PARAM[l] = fb->PARAMcopy[l] - e_s[k] * fb->GRADcopy[l]; // in case we are using DIR, GRADcopy is the DIR
////                    if(fb->PARAM[l]<0 || fb->PARAM[l]>1) {
////                        c++;
////                    }
//                }
//            }
//        } // vvvv REDO
        for(l=0; l<fb->nPARAM; l++) {
            k = fb->param_blocks[l];
            if(this->active_set_block[k]>0) {
                fb->PARAM[l] = fb->PARAMcopy[l] - e_s[k] * fb->GRADcopy[l]; // in case we are using DIR, GRADcopy is the DIR
            }
        }
        
//        if(c>0) {
//            fprintf(stderr, "ERROR! %d values are not within [0, 1] range!\n",c);
//        }

        // project parameters to simplex if needs be --- it's necessary here, since we might make frequent step back-tracks
        fb->projectToSimplex( this->non01constraints, this->active_set_block );
        
        // recompute gradients
        computeGradients();
        fb->scaleGradients(false, this->active_set_block);
        
        NCAT nK_deactiv = 0;
        NCAT nK_reactiv = 0;
        for(k=0; k<nK; k++) {
            if(this->active_set_block[k]>0) {
                // compute f(x_{k+1})
                f_xkplus1_s[k] = this->loglik_k[k];
                // compute Armijo compliance
                compliesArmijo_s[k] = (f_xkplus1_s[k] <= (f_xk_s[k] + (this->task->ArmijoC1 * e_s[k] * p_k_by_neg_p_k_s[k])));
                // compute Wolfe 2
                for(i=0; i<skill1_n; i++) {
                    l = skill1_n*k + i;
                    p_k_by_neg_p_kp1_s[k] -= fb->GRAD[l] * fb->GRADcopy[l]; // in case we are using DIR, GRADcopy is the DIR
                }
                compliesWolfe2_s[k] = (p_k_by_neg_p_kp1_s[k] >= this->task->ArmijoC2 * p_k_by_neg_p_k_s[k]);
                
                // decide
                if( compliesArmijo_s[k] && e_Armijo_s[k]==-1 ){
                    e_Armijo_s[k] = e_s[k]; // save the first time Armijo is statisfied, in case we'll roll back to it when Wolfe 2 is finally not satisfied
                    f_xkplus1_Armijo_s[k] = f_xkplus1_s[k];
                }
                // n_compliesArmijo_or_Wolfe2 += (compliesArmijo_s[k] || compliesWolfe2_s[k]);
                // n_e_lt_ArmijoMinStep += (e_s[k] <= this->task->ArmijoMinStep);
                if(compliesArmijo_s[k] || (compliesArmijo_s[k] && compliesWolfe2_s[k]) || (e_s[k] <= this->task->ArmijoMinStep)) {
                    this->active_set_block[k] = 0;
                    //nK_active--;
                    nK_deactiv++;
                    n_compliesArmijo_or_compliesWolfe2_or_n_e_lt_ArmijoMinStep++;// += (compliesArmijo_s[k] || compliesWolfe2_s[k] || (e_s[k] <= this->task->ArmijoMinStep));
                }
                else
                    e_s[k] /= this->task->ArmijoReduceFactor;
            }// if active
        }
        // rehash active set using reachability/connectivity
        // decide on active set (propagate dependencies)
        // this->task->connectivities[0] skill-skill
        for(k=0; k<nK; k++) {
            if(this->active_set_block[k]>0) {
                for(NCAT l=0; l<=nK; l++) {
                    if(l!=k && this->task->connectivities[0][k][l]==1 && this->active_set_block[l]==0) {
                        this->active_set_block[l] = 1;
                        //nK_active++;
                        nK_reactiv++;
                    }
                } // for all potential skills
            }
        }
        // coun active
        for(k=0; k<nK; k++) nK_active_local += this->active_set_block[k]>0;

        iter++;
    } // armijo loop
    
    // reinstate original active set
    cpy1D(active_set_block_local, this->active_set_block, nK);
    // reinstate original gradients
    cpy1D(save_GRAD, fb->GRAD, nK);

//    c = 0;
    for(k=0; k<nK; k++) {
        if(this->active_set_block[k]>0) {
            if(!compliesArmijo_s[k]) { // we couldn't step away from current, copy the inital point back
                e_s[k] = 0;
                f_xkplus1_s[k] = f_xk_s[k];
                cpy1D(&fb->PARAMcopy[k*skill1_n],&fb->PARAM[k*skill1_n],skill1_n);
            } else {
                if(compliesArmijo_s[k] && !compliesWolfe2_s[k]) { // we couldn't step away from current, copy the inital point back
                    e_s[k] = e_Armijo_s[k]; // return the first Armijo-compliant e
                    f_xkplus1_s[k] = f_xkplus1_Armijo_s[k]; // return the first Armijo-compliant f_xkplus1
                    // create new versions of FBS_PAR using e_Armijo as a step
//                    if(!direction) { // if step on GRAD not on DIR -- this is useless, why do it here
//                        cpy1D(&fb->GRADcopy[k*skill1_n],&fb->GRAD[k*skill1_n],skill1_n);
//                    }
                    // step
                    for(i=0; i<skill1_n; i++) {
                        l = skill1_n*k + i;
                        fb->PARAM[l] = fb->PARAMcopy[l] - e_s[k] * fb->GRADcopy[l]; // in case we are using DIR, GRADcopy is the DIR
//                        if(fb->PARAM[l]<0 || fb->PARAM[l]>1) {
//                            c++;
//                        }
                    }
                    // ^^^^^ end of create new versions of FBS_PAR using e_Armijo as a step
                }
            }
        }// if in active set
    }
    // project to simplex
//    if(c>0) {
//        fprintf(stderr, "ERROR! %d values are not within [0, 1] range!\n",c);
//    }
    fb->projectToSimplex( this->non01constraints, this->active_set_block );

//    nK_active = 0;                                                // DEBUG check for reinstatement
//    for(k=0; k<nK; k++) nK_active += this->active_set_block[k]>0; // DEBUG check for reinstatement
    
    // free memory (1)
    free(e_s);
    free(compliesArmijo_s);
    free(compliesWolfe2_s);
    free(e_Armijo_s);
    free(f_xkplus1_Armijo_s);
    free(f_xk_s);
    free(f_xkplus1_s);
    free(p_k_by_neg_p_k_s);
    free(p_k_by_neg_p_kp1_s);
    // free memory (2)
    free(active_set_block_local);
    free(fb->PARAMcopy);
    free(fb->GRADcopy);
    free(save_GRAD);
    fb->PARAMcopy = NULL;
    fb->GRADcopy = NULL;
    return;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x68, %rsp
movl %edx, 0x18(%rsp)
movq %rsi, %rbx
movq %rdi, %r15
movq 0x68(%rdi), %rax
movl 0x908(%rax), %r13d
movslq %r13d, %rbp
movslq 0x38(%rdi), %r12
movl 0xc(%rsi), %edi
callq 0x6a57
movq %rax, 0x28(%rbx)
movl 0xc(%rbx), %edi
callq 0x6a57
movq %rax, 0x40(%rbx)
movl 0xc(%rbx), %edi
callq 0x6a57
movq %rax, 0x40(%rsp)
movq 0x68(%r15), %rax
movsd 0x970(%rax), %xmm0
movl %ebp, %edi
callq 0xec30
movq %rax, 0x20(%rsp)
xorl %r14d, %r14d
movl %ebp, %edi
xorl %esi, %esi
callq 0xef86
movq %rax, 0x48(%rsp)
movl %ebp, %edi
xorl %esi, %esi
callq 0xef86
movq %rax, 0x58(%rsp)
movsd 0x2d68(%rip), %xmm0     # 0x10948
movl %ebp, %edi
callq 0xec30
movq %rax, 0x28(%rsp)
xorpd %xmm0, %xmm0
movl %ebp, %edi
callq 0xec30
movq %rax, 0x50(%rsp)
xorpd %xmm0, %xmm0
movl %ebp, %edi
callq 0xec30
movq %rax, 0x30(%rsp)
movq 0x80(%r15), %rax
movq %rbp, 0x60(%rsp)
testl %ebp, %ebp
movl $0x0, %edx
cmovgl %r13d, %edx
movq 0x30(%rsp), %rcx
cmpq %r14, %rdx
je 0xdc3e
movsd (%rax,%r14,8), %xmm0
movsd %xmm0, (%rcx,%r14,8)
incq %r14
jmp 0xdc28
movq %rdx, 0x8(%rsp)
xorpd %xmm0, %xmm0
movl %r13d, %edi
callq 0xec30
movq %rax, 0x10(%rsp)
xorpd %xmm0, %xmm0
movl %r13d, %edi
callq 0xec30
movq %rax, 0x38(%rsp)
xorpd %xmm0, %xmm0
movl %r13d, %edi
callq 0xec30
movq %rax, %rbp
movq 0x10(%rbx), %rsi
movq 0x28(%rbx), %rdi
movslq 0xc(%rbx), %rdx
shlq $0x3, %rdx
callq 0x3180
movq 0x40(%rbx), %rdi
movslq 0xc(%rbx), %rdx
shlq $0x3, %rdx
leaq 0x48(%rbx), %rax
leaq 0x30(%rbx), %rcx
cmpb $0x0, 0x18(%rsp)
cmovneq %rax, %rcx
movq (%rcx), %rsi
callq 0x3180
movq 0x30(%rbx), %rsi
movslq 0xc(%rbx), %rdx
shlq $0x3, %rdx
movq 0x40(%rsp), %rdi
callq 0x3180
movq 0x58(%r15), %rax
xorl %ecx, %ecx
xorl %r14d, %r14d
cmpq 0x8(%rsp), %rcx
je 0xdd36
movb (%rax,%rcx), %sil
xorl %edx, %edx
testb %sil, %sil
setg %sil
jle 0xdd2b
movq %rcx, %rdi
imulq %r12, %rdi
xorl %r8d, %r8d
movsbl %r8b, %r9d
cmpl %r9d, %r12d
jle 0xdd2b
movsbq %r8b, %r9
addq %rdi, %r9
movq 0x30(%rbx), %r10
movq 0x40(%rbx), %r11
movsd (%r10,%r9,8), %xmm0
mulsd (%r11,%r9,8), %xmm0
movq 0x38(%rsp), %r9
movsd (%r9,%rcx,8), %xmm1
subsd %xmm0, %xmm1
movsd %xmm1, (%r9,%rcx,8)
incb %r8b
jmp 0xdced
movb %sil, %dl
addl %edx, %r14d
incq %rcx
jmp 0xdccd
movl %r13d, %edi
callq 0x92ec
movq 0x58(%r15), %rsi
movq %rax, 0x18(%rsp)
movq %rax, %rdi
movq 0x60(%rsp), %rdx
callq 0x3180
incl %r13d
movq 0x20(%rsp), %r11
testl %r14d, %r14d
je 0xdfa5
movl 0xc(%rbx), %eax
movq 0x58(%r15), %rcx
testl %eax, %eax
movl $0x0, %edx
cmovlel %edx, %eax
xorl %edx, %edx
movq 0x48(%rsp), %r14
cmpq %rdx, %rax
je 0xddbe
movq 0xb8(%rbx), %rsi
movslq (%rsi,%rdx,4), %rsi
cmpb $0x0, (%rcx,%rsi)
jle 0xddb9
movq 0x10(%rbx), %rdi
movq 0x28(%rbx), %r8
movsd (%r8,%rdx,8), %xmm0
movsd (%r11,%rsi,8), %xmm1
movq 0x40(%rbx), %rsi
mulsd (%rsi,%rdx,8), %xmm1
subsd %xmm1, %xmm0
movsd %xmm0, (%rdi,%rdx,8)
incq %rdx
jmp 0xdd7d
movzbl 0x50(%r15), %esi
movq 0x58(%r15), %rdx
movq %rbx, %rdi
callq 0x74d0
movq (%r15), %rax
movq %r15, %rdi
callq *0x80(%rax)
movq 0x58(%r15), %rdx
movq %rbx, %rdi
xorl %esi, %esi
callq 0x7404
movsd 0x2b57(%rip), %xmm2     # 0x10948
movq 0x20(%rsp), %r11
xorl %eax, %eax
movq 0x8(%rsp), %rsi
cmpq %rsi, %rax
je 0xdf39
movq 0x58(%r15), %rcx
cmpb $0x0, (%rcx,%rax)
jle 0xdf31
movq 0x80(%r15), %rdx
movsd (%rdx,%rax,8), %xmm0
movq 0x10(%rsp), %rdx
movsd %xmm0, (%rdx,%rax,8)
movq 0x68(%r15), %rdx
movsd 0x958(%rdx), %xmm1
mulsd (%r11,%rax,8), %xmm1
movq 0x38(%rsp), %rsi
mulsd (%rsi,%rax,8), %xmm1
movq 0x30(%rsp), %rsi
addsd (%rsi,%rax,8), %xmm1
ucomisd %xmm0, %xmm1
setae (%r14,%rax)
movq %rax, %rsi
imulq %r12, %rsi
xorl %edi, %edi
movsbl %dil, %r8d
cmpl %r8d, %r12d
jle 0xde9b
movsbq %dil, %r8
addq %rsi, %r8
movq 0x30(%rbx), %r9
movq 0x40(%rbx), %r10
movsd (%r9,%r8,8), %xmm0
mulsd (%r10,%r8,8), %xmm0
movsd (%rbp,%rax,8), %xmm1
subsd %xmm0, %xmm1
movsd %xmm1, (%rbp,%rax,8)
incb %dil
jmp 0xde62
movsd (%rbp,%rax,8), %xmm0
movsd 0x960(%rdx), %xmm1
movq 0x38(%rsp), %rsi
mulsd (%rsi,%rax,8), %xmm1
ucomisd %xmm1, %xmm0
movq 0x58(%rsp), %rsi
setae (%rsi,%rax)
cmpb $0x1, (%r14,%rax)
jne 0xdf04
movq 0x28(%rsp), %rdx
movsd (%rdx,%rax,8), %xmm0
ucomisd %xmm2, %xmm0
movq 0x8(%rsp), %rsi
jne 0xdf2d
jp 0xdf2d
movsd (%r11,%rax,8), %xmm0
movq 0x28(%rsp), %rdx
movsd %xmm0, (%rdx,%rax,8)
movq 0x10(%rsp), %rdx
movsd (%rdx,%rax,8), %xmm0
movq 0x50(%rsp), %rdx
movsd %xmm0, (%rdx,%rax,8)
jmp 0xdf2d
movsd (%r11,%rax,8), %xmm0
movsd 0x978(%rdx), %xmm1
ucomisd %xmm0, %xmm1
movq 0x8(%rsp), %rsi
jae 0xdf2d
divsd 0x968(%rdx), %xmm0
movsd %xmm0, (%r11,%rax,8)
jmp 0xdf31
movb $0x0, (%rcx,%rax)
incq %rax
jmp 0xddfd
xorl %eax, %eax
movq 0x58(%r15), %rcx
cmpq %rsi, %rax
je 0xdf86
cmpb $0x0, (%rcx,%rax)
jle 0xdf81
xorl %ecx, %ecx
cmpq %rcx, %r13
je 0xdf81
cmpq %rcx, %rax
je 0xdf7c
movq 0x68(%r15), %rdx
movq 0x8f0(%rdx), %rdx
movq (%rdx), %rdx
movq (%rdx,%rax,8), %rdx
cmpb $0x1, (%rdx,%rcx)
jne 0xdf7c
movq 0x58(%r15), %rdx
cmpb $0x0, (%rdx,%rcx)
jne 0xdf7c
movb $0x1, (%rdx,%rcx)
incq %rcx
jmp 0xdf4c
incq %rax
jmp 0xdf3b
xorl %eax, %eax
xorl %r14d, %r14d
cmpq %rax, %rsi
je 0xdd5c
xorl %edx, %edx
cmpb $0x0, (%rcx,%rax)
setg %dl
addl %edx, %r14d
incq %rax
jmp 0xdf8b
movq 0x58(%r15), %rdi
movq 0x18(%rsp), %rsi
movq 0x60(%rsp), %r14
movq %r14, %rdx
callq 0x3180
movq 0x30(%rbx), %rdi
shlq $0x3, %r14
movq 0x40(%rsp), %rsi
movq %r14, %rdx
callq 0x3180
movq 0x20(%rsp), %r8
leaq (,%r12,8), %r13
xorl %r14d, %r14d
movq 0x8(%rsp), %rsi
cmpq %rsi, %r14
je 0xe0d1
movq 0x58(%r15), %rax
cmpb $0x0, (%rax,%r14)
jle 0xe0c9
movq 0x48(%rsp), %rax
cmpb $0x0, (%rax,%r14)
je 0xe086
movq 0x58(%rsp), %rax
cmpb $0x0, (%rax,%r14)
jne 0xe0c9
movq 0x28(%rsp), %rax
movsd (%rax,%r14,8), %xmm0
movsd %xmm0, (%r8,%r14,8)
movq 0x50(%rsp), %rax
movsd (%rax,%r14,8), %xmm0
movq 0x10(%rsp), %rax
movsd %xmm0, (%rax,%r14,8)
movq %r14, %rax
imulq %r12, %rax
xorl %ecx, %ecx
movsbl %cl, %edx
cmpl %edx, %r12d
jle 0xe0c9
movsbq %cl, %rdx
addq %rax, %rdx
movq 0x10(%rbx), %rsi
movsd (%r8,%r14,8), %xmm0
movq 0x40(%rbx), %rdi
mulsd (%rdi,%rdx,8), %xmm0
movq 0x28(%rbx), %rdi
movsd (%rdi,%rdx,8), %xmm1
subsd %xmm0, %xmm1
movsd %xmm1, (%rsi,%rdx,8)
movq 0x8(%rsp), %rsi
incb %cl
jmp 0xe049
andq $0x0, (%r8,%r14,8)
movq 0x30(%rsp), %rax
movsd (%rax,%r14,8), %xmm0
movq 0x10(%rsp), %rax
movsd %xmm0, (%rax,%r14,8)
movq %r14, %rdi
imulq %r12, %rdi
shlq $0x3, %rdi
movq 0x28(%rbx), %rsi
addq %rdi, %rsi
addq 0x10(%rbx), %rdi
movq %r13, %rdx
callq 0x3180
movq 0x8(%rsp), %rsi
movq 0x20(%rsp), %r8
incq %r14
jmp 0xdfe5
movzbl 0x50(%r15), %esi
movq 0x58(%r15), %rdx
movq %rbx, %rdi
movq %r8, %r14
callq 0x74d0
movq %r14, %rdi
callq 0x3230
movq 0x48(%rsp), %rdi
callq 0x3230
movq 0x58(%rsp), %rdi
callq 0x3230
movq 0x28(%rsp), %rdi
callq 0x3230
movq 0x50(%rsp), %rdi
callq 0x3230
movq 0x30(%rsp), %rdi
callq 0x3230
movq 0x10(%rsp), %rdi
callq 0x3230
movq 0x38(%rsp), %rdi
callq 0x3230
movq %rbp, %rdi
callq 0x3230
movq 0x18(%rsp), %rdi
callq 0x3230
movq 0x28(%rbx), %rdi
callq 0x3230
movq 0x40(%rbx), %rdi
callq 0x3230
movq 0x40(%rsp), %rdi
callq 0x3230
andq $0x0, 0x28(%rbx)
andq $0x0, 0x40(%rbx)
addq $0x68, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq