// File: /workspace/github_cpp_makefile_star2_O1/myudelson[P]hmm-scalable/HMMProblemSt.cpp
// Function: HMMProblemSt::CycleBit(FitBitSt*)
void HMMProblemSt::CycleBit(FitBitSt *fb) {
    NCAT k, nK = this->task->nK;
    NCAT n_conv = 0;
    NDAT n_iter = 0;
    
    // inital copy parameter values to the t-1 slice
    NDAT* sizes_k = initToValue1D<NDAT>(nK, skill1_n);   // for full skill paremter vectors
    NDAT* offsets_k = init1D<NDAT>(nK);  // for full skill parameter vectors
    NCAT* blocks_k = init1D<NDAT>(nK);  // index per block
    NUMBER objval = 0.0;
    for(k=0; k<nK; k++) {
        offsets_k[k] = k*this->skill1_n;
        blocks_k[k] = k;
    }
    
    if(this->task->solver==METHOD_BW) {
        objval = computeAlphaAndPOParam(NULL); // just alpha and p(O|param)
        computeBeta();
        computeXiGamma();
    } else {
        objval = computeGradients();
        if( this->task->solver==METHOD_GD || this->task->solver==METHOD_CGD || this->task->solver==METHOD_GBB )
            fb->scaleGradients(false, this->active_set_block);
    }

    // set p(O|param) for pre-step
    fb->fit_result.pO0 = objval; // overall start before this cycle
    fb->fit_result.pO  = objval; // overall finish after this cycle
    if( fb->fit_result.pO0_prefit == -1.0 ) {
        fb->fit_result.pO0_prefit = objval; // overall before fitting started
    }
    for(k=0; k<nK; k++) {
        if(n_iter==0) {
            fb->fit_results[k].pO0 = this->loglik_k[k];
            fb->fit_results[k].pO  = this->loglik_k[k];
            if( fb->fit_results[k].pO0_prefit == -1.0 ) {
                fb->fit_results[k].pO0_prefit = this->loglik_k[k];
                fb->fit_results[k].ndat = this->ndat_k[k];
            }
        }
    }
    while( n_conv!=nK && n_iter<=this->task->maxiter ) {
        // save t-1, t-2 (tm1, tm2) values before making the step or updating the gradient
        cpy1D(fb->PARAMm1, fb->PARAMm2, fb->nPARAM);
        cpy1D(fb->PARAM, fb->PARAMm1, fb->nPARAM);
        if( this->task->solver==METHOD_CGD || this->task->solver==METHOD_GBB)
            cpy1D(fb->GRAD, fb->GRADm1, fb->nPARAM);
        // for congugate gradient descent – work with direction
        if (this->task->solver==METHOD_CGD) {
            if( n_iter==0 ) {
                cpy1D(fb->GRAD, fb->DIR, fb->nPARAM); // copy to DIR
                negate1D(fb->DIR, fb->nPARAM);
            }
            else
                cpy1D(fb->DIR, fb->DIRm1, fb->nPARAM);// fb->copy(FBS_DIR,  FBS_DIRm1);
        }

        
        // Make the step
        if( this->task->solver==METHOD_GD || (n_iter==0 && this->task->solver==METHOD_CGD)  || (n_iter==1 && this->task->solver==METHOD_GBB) )
            doLinearStep(fb, false/*direction*/);
        else if( this->task->solver==METHOD_CGD )
            doConjugateLinearStep(fb);
        else if( this->task->solver==METHOD_GDL )
            doLagrangeStep(fb);
        else if( this->task->solver==METHOD_GBB )
            doBarzilaiBorweinStep(fb);
        else if( this->task->solver==METHOD_BW )
            doBaumWelchStep(fb);
        
        // project to simplex with boundaries if necessary
        fb->projectToSimplex( this->non01constraints, this->active_set_block );
        // check factual convergence (without considering connectivity yet)
        n_conv = fb->checkConvergence(nK, offsets_k, sizes_k, blocks_k, this->active_set_block);

        // decide on active set (propagate dependencies)
        // this->task->connectivities[0] skill-skill
//        NDAT set0to1 = 0; // DEBUG
        for(k=0; k<nK; k++) {
            if( this->active_set_block[k]==1 ) {
                for(NCAT l=0; l<=nK; l++) {
                    if(l!=k && this->task->connectivities[0][k][l]==1) {
//                        if(this->active_set_block[l]==0) set0to1++; // DEBUG
                        this->active_set_block[l] = 1;
                    }
                } // for all potential skills
            } // if the skill is active
        }
//        fprintf(stderr,"[debug] Activated skills %d.\n", set0to1); // DEBUG

        // copy parameter values after we already compared step t-1 with currently computed step t
        // cpy1D(fb->PARAMm1, fb->PARAMm2, fb->nPARAM);
        // cpy1D(fb->PARAM, fb->PARAMm1, fb->nPARAM);
        // for Gradient Descent METHOD_GD, no extra copying
        // if Conjugate Gradient and Barzilain Borwein – copy previous gradient
//        if( this->task->solver==METHOD_CGD || this->task->solver==METHOD_GBB)
//            cpy1D(fb->GRAD, fb->GRADm1, fb->nPARAM);
//        // for congugate gradient descent – work with direction
//        if (this->task->solver==METHOD_CGD) {
//            if( n_iter==0 ) {
//                cpy1D(fb->GRAD, fb->DIRm1, fb->nPARAM);
//                negate1D(fb->DIRm1, fb->nPARAM);
//            }
//            else
//                cpy1D(fb->DIR, fb->DIRm1, fb->nPARAM);// fb->copy(FBS_DIR,  FBS_DIRm1);
//        }

        // move this into the stepper vvvvvv
//        for(k=0; k<=nK; k++) {
//            fb->fit_results[k].iter = n_iter;
//            //frs[k].pOmid = frs[k].pO;
//        }
        // compute p(O|param) post-step after projecting
        if(this->task->solver==METHOD_BW) {
            objval = computeAlphaAndPOParam(NULL); // just alpha and p(O|param)
            computeBeta();
            computeXiGamma();
        } else {
            objval = computeGradients();
            if( this->task->solver==METHOD_GD || this->task->solver==METHOD_CGD || this->task->solver==METHOD_GBB )
                fb->scaleGradients(false, this->active_set_block);
        }
        
        // update objective function & count iterations
        fb->fit_result.pO = objval;
        for(k=0; k<nK; k++) {
            // update objective
            if(this->active_set_block[k]>0) {
                fb->fit_results[k].pO0 = fb->fit_results[k].pO;
                fb->fit_results[k].pO = this->loglik_k[k];
                fb->fit_results[k].iter++;
            }
        }
        n_iter++;
    }// single skill loop
    fb->fit_result.iter = n_iter;
    // this is the case when 2 steps lead to step back to the initial value, i.e. oscillation
    // the code below doesn' make anny sense yet to me.s
//    for(k=0; k<=nK; k++) {
//        if(fb->fit_results[k].iter==2 && fb->fit_results[k].conv && fb->checkConvergenceBitSingle(offsets_k[k], sizes_k[k], &fb->fit_results[0])) {
//            // decrease iteration counter to 1
//            fb->fit_results[k].iter--;
//        }
//        fb->fit_results[k].pO = this->loglik_k[k];
//    }
    free(offsets_k);
    free(sizes_k);
    free(blocks_k);
    return;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x28, %rsp
movq %rsi, %rbx
movq %rdi, %r15
movq 0x68(%rdi), %r14
movslq 0x908(%r14), %r12
movl 0x38(%rdi), %ebp
movl $0x4, %esi
movq %r12, %rdi
callq 0x2110
testq %r12, %r12
jle 0xe849
xorl %ecx, %ecx
movl %ebp, (%rax,%rcx,4)
incq %rcx
cmpq %rcx, %r12
jne 0xe83e
movq %rax, 0x20(%rsp)
movl $0x4, %esi
movq %r12, %rdi
callq 0x2110
movq %rax, %r13
movl $0x4, %esi
movq %r12, %rdi
callq 0x2110
testl %r12d, %r12d
jle 0xe886
xorl %edx, %edx
xorl %ecx, %ecx
movl %edx, (%r13,%rcx,4)
movl %ecx, (%rax,%rcx,4)
incq %rcx
addl %ebp, %edx
cmpq %rcx, %r12
jne 0xe874
movq %rax, 0x10(%rsp)
cmpb $0x1, 0x4f(%r14)
jne 0xe8b3
movq %r15, %rdi
xorl %esi, %esi
callq 0xb7c2
movsd %xmm0, (%rsp)
movq %r15, %rdi
callq 0xc40e
movq %r15, %rdi
callq 0xc716
jmp 0xe8e9
movq (%r15), %rax
movq %r15, %rdi
callq *0x80(%rax)
movq 0x68(%r15), %rax
movzbl 0x4f(%rax), %eax
cmpl $0x5, %eax
ja 0xe8ee
movl $0x2c, %ecx
btl %eax, %ecx
jae 0xe8ee
movq 0x58(%r15), %rdx
movq %rbx, %rdi
xorl %esi, %esi
movsd %xmm0, (%rsp)
callq 0x7fda
movsd (%rsp), %xmm0
movsd %xmm0, 0x70(%rbx)
movsd %xmm0, 0x78(%rbx)
movsd 0x68(%rbx), %xmm1
ucomisd 0x3043(%rip), %xmm1     # 0x11948
jne 0xe90e
jp 0xe90e
movsd %xmm0, 0x68(%rbx)
testl %r12d, %r12d
jle 0xe96e
movq 0x80(%r15), %rax
movq 0x88(%r15), %rcx
movq 0x58(%rbx), %rdx
addq $0x8, %rdx
xorl %esi, %esi
movsd 0x3015(%rip), %xmm0     # 0x11948
movsd (%rax,%rsi,8), %xmm1
movsd %xmm1, 0x8(%rdx)
movsd (%rax,%rsi,8), %xmm1
movsd %xmm1, 0x10(%rdx)
movsd (%rdx), %xmm1
ucomisd %xmm0, %xmm1
jne 0xe962
jp 0xe962
movsd (%rax,%rsi,8), %xmm1
movsd %xmm1, (%rdx)
movl (%rcx,%rsi,4), %edi
movl %edi, 0x1c(%rdx)
incq %rsi
addq $0x28, %rdx
cmpq %rsi, %r12
jne 0xe933
movq %r13, 0x18(%rsp)
testl %r12d, %r12d
je 0xec27
leal 0x1(%r12), %ebp
movl %r12d, %r14d
xorl %r13d, %r13d
movq 0x68(%r15), %rax
cmpl 0x48(%rax), %r13d
jg 0xec2a
movq 0x18(%rbx), %rsi
movq 0x20(%rbx), %rdi
movslq 0xc(%rbx), %rdx
shlq $0x3, %rdx
callq 0x2180
movq 0x10(%rbx), %rsi
movq 0x18(%rbx), %rdi
movslq 0xc(%rbx), %rdx
shlq $0x3, %rdx
callq 0x2180
movq 0x68(%r15), %rax
movzbl 0x4f(%rax), %eax
cmpl $0x5, %eax
je 0xe9d1
cmpl $0x3, %eax
jne 0xe9e6
movq 0x30(%rbx), %rsi
movq 0x38(%rbx), %rdi
movslq 0xc(%rbx), %rdx
shlq $0x3, %rdx
callq 0x2180
movq 0x68(%r15), %rax
cmpb $0x3, 0x4f(%rax)
jne 0xea46
movslq 0xc(%rbx), %rdx
shlq $0x3, %rdx
testl %r13d, %r13d
je 0xea0c
movq 0x48(%rbx), %rsi
movq 0x50(%rbx), %rdi
callq 0x2180
jmp 0xea46
movq 0x30(%rbx), %rsi
movq 0x48(%rbx), %rdi
callq 0x2180
movslq 0xc(%rbx), %rax
testq %rax, %rax
movapd 0x2f78(%rip), %xmm1     # 0x119a0
jle 0xea46
movq 0x48(%rbx), %rcx
xorl %edx, %edx
movsd (%rcx,%rdx,8), %xmm0
xorpd %xmm1, %xmm0
movlpd %xmm0, (%rcx,%rdx,8)
incq %rdx
cmpq %rdx, %rax
jne 0xea30
movq 0x68(%r15), %rax
movzbl 0x4f(%rax), %eax
cmpl $0x2, %eax
je 0xea6e
testl %r13d, %r13d
jne 0xea5c
cmpb $0x3, %al
je 0xea6e
cmpl $0x1, %r13d
jne 0xebbc
cmpb $0x5, %al
jne 0xebbc
movq (%r15), %rax
movq %r15, %rdi
movq %rbx, %rsi
xorl %edx, %edx
callq *0xa0(%rax)
movzbl 0x50(%r15), %esi
movq 0x58(%r15), %rdx
movq %rbx, %rdi
callq 0x808a
movq 0x58(%r15), %r9
movq %rbx, %rdi
movl %r12d, %esi
movq 0x18(%rsp), %rdx
movq 0x20(%rsp), %rcx
movq 0x10(%rsp), %r8
callq 0x7ea6
movl %eax, (%rsp)
testl %r12d, %r12d
jle 0xeaf9
xorl %eax, %eax
movq 0x58(%r15), %rcx
cmpb $0x1, (%rcx,%rax)
jne 0xeaf1
xorl %ecx, %ecx
cmpq %rcx, %rax
je 0xeae9
movq 0x68(%r15), %rdx
movq 0x8f0(%rdx), %rdx
movq (%rdx), %rdx
movq (%rdx,%rax,8), %rdx
cmpb $0x1, (%rdx,%rcx)
jne 0xeae9
movq 0x58(%r15), %rdx
movb $0x1, (%rdx,%rcx)
incq %rcx
cmpq %rcx, %rbp
jne 0xeac4
incq %rax
cmpq %r14, %rax
jne 0xeab8
movq 0x68(%r15), %rax
cmpb $0x1, 0x4f(%rax)
jne 0xeb25
movq %r15, %rdi
xorl %esi, %esi
callq 0xb7c2
movsd %xmm0, 0x8(%rsp)
movq %r15, %rdi
callq 0xc40e
movq %r15, %rdi
callq 0xc716
jmp 0xeb5c
movq (%r15), %rax
movq %r15, %rdi
callq *0x80(%rax)
movq 0x68(%r15), %rax
movzbl 0x4f(%rax), %eax
cmpl $0x5, %eax
ja 0xeb62
movl $0x2c, %ecx
btl %eax, %ecx
jae 0xeb62
movq 0x58(%r15), %rdx
movq %rbx, %rdi
xorl %esi, %esi
movsd %xmm0, 0x8(%rsp)
callq 0x7fda
movsd 0x8(%rsp), %xmm0
movsd %xmm0, 0x78(%rbx)
testl %r12d, %r12d
jle 0xebad
movq 0x58(%r15), %rax
movq 0x80(%r15), %rcx
movl $0x18, %edx
xorl %esi, %esi
cmpb $0x0, (%rax,%rsi)
jle 0xeba1
movq 0x58(%rbx), %rdi
movsd (%rdi,%rdx), %xmm0
movsd %xmm0, -0x8(%rdi,%rdx)
movsd (%rcx,%rsi,8), %xmm0
movsd %xmm0, (%rdi,%rdx)
incl -0x18(%rdi,%rdx)
incq %rsi
addq $0x28, %rdx
cmpq %rsi, %r14
jne 0xeb7e
incl %r13d
cmpl %r12d, (%rsp)
jne 0xe987
jmp 0xec2a
decl %eax
cmpl $0x4, %eax
ja 0xea7f
leaq 0x35e6(%rip), %rcx      # 0x121b4
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq (%r15), %rax
movq %r15, %rdi
movq %rbx, %rsi
callq *0xb8(%rax)
jmp 0xea7f
movq (%r15), %rax
movq %r15, %rdi
movq %rbx, %rsi
callq *0xa8(%rax)
jmp 0xea7f
movq (%r15), %rax
movq %r15, %rdi
movq %rbx, %rsi
callq *0xb0(%rax)
jmp 0xea7f
movq (%r15), %rax
movq %r15, %rdi
movq %rbx, %rsi
callq *0xc0(%rax)
jmp 0xea7f
xorl %r13d, %r13d
movl %r13d, 0x60(%rbx)
movq 0x18(%rsp), %rdi
callq 0x2240
movq 0x20(%rsp), %rdi
callq 0x2240
movq 0x10(%rsp), %rdi
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x2240