// File: /workspace/github_cpp_makefile_star2_O2/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, %r14
movq 0x68(%rdi), %rax
movl 0x908(%rax), %r15d
movl 0x38(%rdi), %esi
movl %r15d, %edi
callq 0xef2f
movq %rax, 0x18(%rsp)
movl %r15d, %edi
callq 0xec25
movq %rax, %r13
movl %r15d, %edi
callq 0xec25
xorl %edx, %edx
testl %r15d, %r15d
movl $0x0, %r12d
cmovgl %r15d, %r12d
cmpq %rdx, %r12
je 0xd1f2
movl 0x38(%r14), %ecx
imull %edx, %ecx
movl %ecx, (%r13,%rdx,4)
movl %edx, (%rax,%rdx,4)
incq %rdx
jmp 0xd1d9
movq %rax, 0x10(%rsp)
movq 0x68(%r14), %rax
cmpb $0x1, 0x4f(%rax)
jne 0xd222
movq %r14, %rdi
xorl %esi, %esi
callq 0xa6be
movsd %xmm0, (%rsp)
movq %r14, %rdi
callq 0xb29c
movq %r14, %rdi
callq 0xb56a
jmp 0xd256
movq (%r14), %rax
movq %r14, %rdi
callq *0x80(%rax)
movq 0x68(%r14), %rax
movzbl 0x4f(%rax), %eax
cmpl $0x5, %eax
ja 0xd25b
pushq $0x2c
popq %rcx
btl %eax, %ecx
jae 0xd25b
movq 0x58(%r14), %rdx
movq %rbx, %rdi
xorl %esi, %esi
movsd %xmm0, (%rsp)
callq 0x7404
movsd (%rsp), %xmm0
movsd %xmm0, 0x70(%rbx)
movsd %xmm0, 0x78(%rbx)
movsd 0x68(%rbx), %xmm1
ucomisd 0x36d6(%rip), %xmm1     # 0x10948
jne 0xd27b
jp 0xd27b
movsd %xmm0, 0x68(%rbx)
movq 0x80(%r14), %rax
movq 0x88(%r14), %rcx
pushq $0x4
popq %rdx
xorl %esi, %esi
movsd 0x36b2(%rip), %xmm0     # 0x10948
cmpq %rsi, %r12
je 0xd2d9
movsd (%rax,%rsi,8), %xmm1
movq 0x58(%rbx), %rdi
movsd %xmm1, 0x8(%rdi,%rdx,2)
movsd %xmm1, 0x10(%rdi,%rdx,2)
movsd (%rdi,%rdx,2), %xmm1
ucomisd %xmm0, %xmm1
jne 0xd2d0
jp 0xd2d0
movsd (%rax,%rsi,8), %xmm1
movsd %xmm1, (%rdi,%rdx,2)
movl (%rcx,%rsi,4), %r8d
movl %r8d, 0x1c(%rdi,%rdx,2)
incq %rsi
addq $0x14, %rdx
jmp 0xd296
movq %r13, 0x8(%rsp)
movq %r15, 0x20(%rsp)
leal 0x1(%r15), %r13d
xorl %ebp, %ebp
xorl %r15d, %r15d
movq 0x20(%rsp), %rax
cmpl %eax, %r15d
je 0xd566
movq %rax, %r15
movq 0x68(%r14), %rax
cmpl 0x48(%rax), %ebp
jg 0xd566
movq 0x18(%rbx), %rsi
movq 0x20(%rbx), %rdi
movslq 0xc(%rbx), %rdx
shlq $0x3, %rdx
callq 0x3180
movq 0x10(%rbx), %rsi
movq 0x18(%rbx), %rdi
movslq 0xc(%rbx), %rdx
shlq $0x3, %rdx
callq 0x3180
movq 0x68(%r14), %rax
movzbl 0x4f(%rax), %eax
cmpl $0x5, %eax
je 0xd346
cmpl $0x3, %eax
jne 0xd362
movq 0x30(%rbx), %rsi
movq 0x38(%rbx), %rdi
movslq 0xc(%rbx), %rdx
shlq $0x3, %rdx
callq 0x3180
movq 0x68(%r14), %rax
movb 0x4f(%rax), %al
cmpb $0x3, %al
jne 0xd39a
movslq 0xc(%rbx), %rdx
shlq $0x3, %rdx
testl %ebp, %ebp
je 0xd381
movq 0x48(%rbx), %rsi
movq 0x50(%rbx), %rdi
callq 0x3180
jmp 0xd39a
movq 0x30(%rbx), %rsi
movq 0x48(%rbx), %rdi
callq 0x3180
movq 0x48(%rbx), %rdi
movl 0xc(%rbx), %esi
callq 0xef61
movq 0x68(%r14), %rax
movzbl 0x4f(%rax), %eax
cmpl $0x2, %eax
je 0xd3c0
testl %ebp, %ebp
jne 0xd3af
cmpb $0x3, %al
je 0xd3c0
cmpl $0x1, %ebp
jne 0xd4fb
cmpb $0x5, %al
jne 0xd4fb
movq (%r14), %rax
movq %r14, %rdi
movq %rbx, %rsi
xorl %edx, %edx
callq *0xa0(%rax)
movzbl 0x50(%r14), %esi
movq 0x58(%r14), %rdx
movq %rbx, %rdi
callq 0x74d0
movq 0x58(%r14), %r9
movq %rbx, %rdi
movl %r15d, %esi
movq 0x8(%rsp), %rdx
movq 0x18(%rsp), %rcx
movq 0x10(%rsp), %r8
callq 0x72d4
movl %eax, %r15d
xorl %eax, %eax
cmpq %r12, %rax
je 0xd44a
movq 0x58(%r14), %rcx
cmpb $0x1, (%rcx,%rax)
jne 0xd445
xorl %ecx, %ecx
cmpq %rcx, %r13
je 0xd445
cmpq %rcx, %rax
je 0xd440
movq 0x68(%r14), %rdx
movq 0x8f0(%rdx), %rdx
movq (%rdx), %rdx
movq (%rdx,%rax,8), %rdx
cmpb $0x1, (%rdx,%rcx)
jne 0xd440
movq 0x58(%r14), %rdx
movb $0x1, (%rdx,%rcx)
incq %rcx
jmp 0xd416
incq %rax
jmp 0xd405
movq 0x68(%r14), %rax
cmpb $0x1, 0x4f(%rax)
jne 0xd475
movq %r14, %rdi
xorl %esi, %esi
callq 0xa6be
movsd %xmm0, (%rsp)
movq %r14, %rdi
callq 0xb29c
movq %r14, %rdi
callq 0xb56a
jmp 0xd4a9
movq (%r14), %rax
movq %r14, %rdi
callq *0x80(%rax)
movq 0x68(%r14), %rax
movzbl 0x4f(%rax), %eax
cmpl $0x5, %eax
ja 0xd4ae
pushq $0x2c
popq %rcx
btl %eax, %ecx
jae 0xd4ae
movq 0x58(%r14), %rdx
movq %rbx, %rdi
xorl %esi, %esi
movsd %xmm0, (%rsp)
callq 0x7404
movsd (%rsp), %xmm0
movsd %xmm0, 0x78(%rbx)
movq 0x58(%r14), %rax
movq 0x80(%r14), %rcx
pushq $0x18
popq %rdx
xorl %esi, %esi
cmpq %rsi, %r12
je 0xd4f4
cmpb $0x0, (%rax,%rsi)
jle 0xd4eb
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
jmp 0xd4c3
incl %ebp
jmp 0xd2ec
decl %eax
cmpl $0x4, %eax
ja 0xd3d1
leaq 0x3d37(%rip), %rcx      # 0x11244
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq (%r14), %rax
movq %r14, %rdi
movq %rbx, %rsi
callq *0xb8(%rax)
jmp 0xd3d1
movq (%r14), %rax
movq %r14, %rdi
movq %rbx, %rsi
callq *0xa8(%rax)
jmp 0xd3d1
movq (%r14), %rax
movq %r14, %rdi
movq %rbx, %rsi
callq *0xb0(%rax)
jmp 0xd3d1
movq (%r14), %rax
movq %r14, %rdi
movq %rbx, %rsi
callq *0xc0(%rax)
jmp 0xd3d1
movl %ebp, 0x60(%rbx)
movq 0x8(%rsp), %rdi
callq 0x3230
movq 0x18(%rsp), %rdi
callq 0x3230
movq 0x10(%rsp), %rdi
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x3230
nop