// File: 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
movq %rsp, %rbp
subq $0x60, %rsp
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movq -0x8(%rbp), %rax
movq %rax, -0x50(%rbp)
movq 0x68(%rax), %rcx
movl 0x908(%rcx), %ecx
movl %ecx, -0x18(%rbp)
movl $0x0, -0x1c(%rbp)
movl $0x0, -0x20(%rbp)
movl -0x18(%rbp), %edi
movl 0x38(%rax), %esi
callq 0x19650
movq %rax, -0x28(%rbp)
movl -0x18(%rbp), %edi
callq 0x190c0
movq %rax, -0x30(%rbp)
movl -0x18(%rbp), %edi
callq 0x190c0
movq %rax, -0x38(%rbp)
xorps %xmm0, %xmm0
movsd %xmm0, -0x40(%rbp)
movl $0x0, -0x14(%rbp)
movl -0x14(%rbp), %eax
cmpl -0x18(%rbp), %eax
jge 0x16510
movq -0x50(%rbp), %rax
movl -0x14(%rbp), %edx
imull 0x38(%rax), %edx
movq -0x30(%rbp), %rax
movslq -0x14(%rbp), %rcx
movl %edx, (%rax,%rcx,4)
movl -0x14(%rbp), %edx
movq -0x38(%rbp), %rax
movslq -0x14(%rbp), %rcx
movl %edx, (%rax,%rcx,4)
movl -0x14(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x14(%rbp)
jmp 0x164d9
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x1, %eax
jne 0x16547
movq -0x50(%rbp), %rdi
xorl %eax, %eax
movl %eax, %esi
callq 0x12530
movq -0x50(%rbp), %rdi
movsd %xmm0, -0x40(%rbp)
callq 0x13630
movq -0x50(%rbp), %rdi
callq 0x13a20
jmp 0x165a1
movq -0x50(%rbp), %rdi
movq (%rdi), %rax
callq *0x80(%rax)
movq -0x50(%rbp), %rax
movsd %xmm0, -0x40(%rbp)
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x2, %eax
je 0x1658c
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x3, %eax
je 0x1658c
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x5, %eax
jne 0x1659f
movq -0x50(%rbp), %rax
movq -0x10(%rbp), %rdi
movq 0x58(%rax), %rdx
xorl %esi, %esi
callq 0xb570
jmp 0x165a1
movsd -0x40(%rbp), %xmm0
movq -0x10(%rbp), %rax
movsd %xmm0, 0x70(%rax)
movsd -0x40(%rbp), %xmm0
movq -0x10(%rbp), %rax
movsd %xmm0, 0x78(%rax)
movq -0x10(%rbp), %rax
movsd 0x68(%rax), %xmm0
movsd 0x5482(%rip), %xmm1     # 0x1ba50
ucomisd %xmm1, %xmm0
jne 0x165e4
jp 0x165e4
movsd -0x40(%rbp), %xmm0
movq -0x10(%rbp), %rax
movsd %xmm0, 0x68(%rax)
movl $0x0, -0x14(%rbp)
movl -0x14(%rbp), %eax
cmpl -0x18(%rbp), %eax
jge 0x166df
cmpl $0x0, -0x20(%rbp)
jne 0x166cf
movq -0x50(%rbp), %rax
movq 0x80(%rax), %rcx
movslq -0x14(%rbp), %rdx
movsd (%rcx,%rdx,8), %xmm0
movq -0x10(%rbp), %rcx
movq 0x58(%rcx), %rcx
movslq -0x14(%rbp), %rdx
imulq $0x28, %rdx, %rdx
addq %rdx, %rcx
movsd %xmm0, 0x10(%rcx)
movq 0x80(%rax), %rax
movslq -0x14(%rbp), %rcx
movsd (%rax,%rcx,8), %xmm0
movq -0x10(%rbp), %rax
movq 0x58(%rax), %rax
movslq -0x14(%rbp), %rcx
imulq $0x28, %rcx, %rcx
addq %rcx, %rax
movsd %xmm0, 0x18(%rax)
movq -0x10(%rbp), %rax
movq 0x58(%rax), %rax
movslq -0x14(%rbp), %rcx
imulq $0x28, %rcx, %rcx
addq %rcx, %rax
movsd 0x8(%rax), %xmm0
movsd 0x53db(%rip), %xmm1     # 0x1ba50
ucomisd %xmm1, %xmm0
jne 0x166cd
jp 0x166cd
movq -0x50(%rbp), %rax
movq 0x80(%rax), %rcx
movslq -0x14(%rbp), %rdx
movsd (%rcx,%rdx,8), %xmm0
movq -0x10(%rbp), %rcx
movq 0x58(%rcx), %rcx
movslq -0x14(%rbp), %rdx
imulq $0x28, %rdx, %rdx
addq %rdx, %rcx
movsd %xmm0, 0x8(%rcx)
movq 0x88(%rax), %rax
movslq -0x14(%rbp), %rcx
movl (%rax,%rcx,4), %ecx
movq -0x10(%rbp), %rax
movq 0x58(%rax), %rax
movslq -0x14(%rbp), %rdx
imulq $0x28, %rdx, %rdx
addq %rdx, %rax
movl %ecx, 0x24(%rax)
jmp 0x166cf
jmp 0x166d1
movl -0x14(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x14(%rbp)
jmp 0x165eb
jmp 0x166e1
movl -0x1c(%rbp), %ecx
xorl %eax, %eax
cmpl -0x18(%rbp), %ecx
movb %al, -0x51(%rbp)
je 0x16702
movq -0x50(%rbp), %rcx
movl -0x20(%rbp), %eax
movq 0x68(%rcx), %rcx
cmpl 0x48(%rcx), %eax
setle %al
movb %al, -0x51(%rbp)
movb -0x51(%rbp), %al
testb $0x1, %al
jne 0x1670e
jmp 0x16b04
movq -0x10(%rbp), %rax
movq 0x18(%rax), %rdi
movq -0x10(%rbp), %rax
movq 0x20(%rax), %rsi
movq -0x10(%rbp), %rax
movl 0xc(%rax), %edx
callq 0x9dd0
movq -0x10(%rbp), %rax
movq 0x10(%rax), %rdi
movq -0x10(%rbp), %rax
movq 0x18(%rax), %rsi
movq -0x10(%rbp), %rax
movl 0xc(%rax), %edx
callq 0x9dd0
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x3, %eax
je 0x16768
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x5, %eax
jne 0x16784
movq -0x10(%rbp), %rax
movq 0x30(%rax), %rdi
movq -0x10(%rbp), %rax
movq 0x38(%rax), %rsi
movq -0x10(%rbp), %rax
movl 0xc(%rax), %edx
callq 0x9dd0
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x3, %eax
jne 0x167eb
cmpl $0x0, -0x20(%rbp)
jne 0x167cd
movq -0x10(%rbp), %rax
movq 0x30(%rax), %rdi
movq -0x10(%rbp), %rax
movq 0x48(%rax), %rsi
movq -0x10(%rbp), %rax
movl 0xc(%rax), %edx
callq 0x9dd0
movq -0x10(%rbp), %rax
movq 0x48(%rax), %rdi
movq -0x10(%rbp), %rax
movl 0xc(%rax), %esi
callq 0x196b0
jmp 0x167e9
movq -0x10(%rbp), %rax
movq 0x48(%rax), %rdi
movq -0x10(%rbp), %rax
movq 0x50(%rax), %rsi
movq -0x10(%rbp), %rax
movl 0xc(%rax), %edx
callq 0x9dd0
jmp 0x167eb
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x2, %eax
je 0x1682a
cmpl $0x0, -0x20(%rbp)
jne 0x16813
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x3, %eax
je 0x1682a
cmpl $0x1, -0x20(%rbp)
jne 0x16842
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x5, %eax
jne 0x16842
movq -0x50(%rbp), %rdi
movq -0x10(%rbp), %rsi
movq (%rdi), %rax
xorl %edx, %edx
callq *0xa0(%rax)
jmp 0x168d8
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x3, %eax
jne 0x16866
movq -0x50(%rbp), %rdi
movq -0x10(%rbp), %rsi
movq (%rdi), %rax
callq *0xb0(%rax)
jmp 0x168d6
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x4, %eax
jne 0x1688a
movq -0x50(%rbp), %rdi
movq -0x10(%rbp), %rsi
movq (%rdi), %rax
callq *0xa8(%rax)
jmp 0x168d4
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x5, %eax
jne 0x168ae
movq -0x50(%rbp), %rdi
movq -0x10(%rbp), %rsi
movq (%rdi), %rax
callq *0xc0(%rax)
jmp 0x168d2
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x1, %eax
jne 0x168d0
movq -0x50(%rbp), %rdi
movq -0x10(%rbp), %rsi
movq (%rdi), %rax
callq *0xb8(%rax)
jmp 0x168d2
jmp 0x168d4
jmp 0x168d6
jmp 0x168d8
movq -0x50(%rbp), %rcx
movq -0x10(%rbp), %rdi
movb 0x50(%rcx), %al
movq 0x58(%rcx), %rdx
andb $0x1, %al
movzbl %al, %esi
callq 0xb6b0
movq -0x50(%rbp), %rax
movq -0x10(%rbp), %rdi
movl -0x18(%rbp), %esi
movq -0x30(%rbp), %rdx
movq -0x28(%rbp), %rcx
movq -0x38(%rbp), %r8
movq 0x58(%rax), %r9
callq 0xb3b0
movl %eax, -0x1c(%rbp)
movl $0x0, -0x14(%rbp)
movl -0x14(%rbp), %eax
cmpl -0x18(%rbp), %eax
jge 0x169a9
movq -0x50(%rbp), %rax
movq 0x58(%rax), %rax
movslq -0x14(%rbp), %rcx
movsbl (%rax,%rcx), %eax
cmpl $0x1, %eax
jne 0x16999
movl $0x0, -0x44(%rbp)
movl -0x44(%rbp), %eax
cmpl -0x18(%rbp), %eax
jg 0x16997
movl -0x44(%rbp), %eax
cmpl -0x14(%rbp), %eax
je 0x1698a
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movq 0x8f0(%rax), %rax
movq (%rax), %rax
movslq -0x14(%rbp), %rcx
movq (%rax,%rcx,8), %rax
movslq -0x44(%rbp), %rcx
movsbl (%rax,%rcx), %eax
cmpl $0x1, %eax
jne 0x1698a
movq -0x50(%rbp), %rax
movq 0x58(%rax), %rax
movslq -0x44(%rbp), %rcx
movb $0x1, (%rax,%rcx)
jmp 0x1698c
movl -0x44(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x44(%rbp)
jmp 0x16943
jmp 0x16999
jmp 0x1699b
movl -0x14(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x14(%rbp)
jmp 0x1691b
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x1, %eax
jne 0x169e0
movq -0x50(%rbp), %rdi
xorl %eax, %eax
movl %eax, %esi
callq 0x12530
movq -0x50(%rbp), %rdi
movsd %xmm0, -0x40(%rbp)
callq 0x13630
movq -0x50(%rbp), %rdi
callq 0x13a20
jmp 0x16a3a
movq -0x50(%rbp), %rdi
movq (%rdi), %rax
callq *0x80(%rax)
movq -0x50(%rbp), %rax
movsd %xmm0, -0x40(%rbp)
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x2, %eax
je 0x16a25
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x3, %eax
je 0x16a25
movq -0x50(%rbp), %rax
movq 0x68(%rax), %rax
movsbl 0x4f(%rax), %eax
cmpl $0x5, %eax
jne 0x16a38
movq -0x50(%rbp), %rax
movq -0x10(%rbp), %rdi
movq 0x58(%rax), %rdx
xorl %esi, %esi
callq 0xb570
jmp 0x16a3a
movsd -0x40(%rbp), %xmm0
movq -0x10(%rbp), %rax
movsd %xmm0, 0x78(%rax)
movl $0x0, -0x14(%rbp)
movl -0x14(%rbp), %eax
cmpl -0x18(%rbp), %eax
jge 0x16af6
movq -0x50(%rbp), %rax
movq 0x58(%rax), %rax
movslq -0x14(%rbp), %rcx
movsbl (%rax,%rcx), %eax
cmpl $0x0, %eax
jle 0x16ae6
movq -0x50(%rbp), %rax
movq -0x10(%rbp), %rcx
movq 0x58(%rcx), %rcx
movslq -0x14(%rbp), %rdx
imulq $0x28, %rdx, %rdx
addq %rdx, %rcx
movsd 0x18(%rcx), %xmm0
movq -0x10(%rbp), %rcx
movq 0x58(%rcx), %rcx
movslq -0x14(%rbp), %rdx
imulq $0x28, %rdx, %rdx
addq %rdx, %rcx
movsd %xmm0, 0x10(%rcx)
movq 0x80(%rax), %rax
movslq -0x14(%rbp), %rcx
movsd (%rax,%rcx,8), %xmm0
movq -0x10(%rbp), %rax
movq 0x58(%rax), %rax
movslq -0x14(%rbp), %rcx
imulq $0x28, %rcx, %rcx
addq %rcx, %rax
movsd %xmm0, 0x18(%rax)
movq -0x10(%rbp), %rax
movq 0x58(%rax), %rax
movslq -0x14(%rbp), %rcx
imulq $0x28, %rcx, %rcx
addq %rcx, %rax
movl (%rax), %ecx
addl $0x1, %ecx
movl %ecx, (%rax)
jmp 0x16ae8
movl -0x14(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x14(%rbp)
jmp 0x16a4f
movl -0x20(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x20(%rbp)
jmp 0x166e1
movl -0x20(%rbp), %ecx
movq -0x10(%rbp), %rax
movl %ecx, 0x60(%rax)
movq -0x30(%rbp), %rdi
callq 0x3320
movq -0x28(%rbp), %rdi
callq 0x3320
movq -0x38(%rbp), %rdi
callq 0x3320
addq $0x60, %rsp
popq %rbp
retq
nop