// File: CominLab[P]QCluster/em.cc
// Function: em_routine(int, int, int, double**, double**, double*, double**, double*, int, double**, int*, int*, double**, double**, double**, double**, char, int, int)
static double em_routine(int K, int N, int row_length, double** freq, 
		double **quality, double *expected_qual, double **quality_1, double *expected_freq, 
		int num_nt, double** freq_1, int* assignment, int* numMembers,
		double** centroids, double** centroids_tilde, double** tmp_data,
		double**tmp_data_1, char dist_type,
		int verbose=0, int allow_empty_clusters = 0)
{
	double **tmp_qfreq = new double*[N];
	double ** tmp_qfreq_1 = new double*[N];

	
	double distortion = 0;
	initialize_cache(N);
	// choose auxiliary functions
	double (*distf)(double*, double*, int, double*, double *, double*, double *);
	void (*eval_centroid)(int, int, double**, int, double**, double*, 
						  double*, double **, double *, double**, double*);
	select_dists_cent(dist_type, &distf, &eval_centroid);

	while (true){ // repeat clustering attempts till we get a result
restart:
		if (verbose > 0) {
			cerr<<"\tEM routine: starting clustering attempt!"<<endl;
		}
		
		memset(assignment, 0, N*sizeof(*assignment));
		memset(numMembers, -1, K*sizeof(*numMembers));
		// initial centroid assignment --- initialize with randomly chosen 
		// freq items
		for (int k=0; k<K; ++k) {
			int point_number = rand()% N;
			eval_centroid(1, row_length, freq + point_number, num_nt, freq_1 +
			point_number, centroids[k], centroids_tilde[k], 
			quality + point_number, expected_qual, 
			quality_1 + point_number, expected_freq);
		}
		
		
		// start iterations of EM clustering
		while (true){
			// compute the distances to the new centroids 
			// and compute the new assignments
			bool assignmentChanged = false;  // set to true if 
			// at least one element changes its cluster assignment
			for(int n=0; n<N; n++){ // update assignment of each element
				int new_assignment=-1;  // new assignment
				double min_dist=-1;  // distance to the closest centroid
				// initial value set here is irrelevant as it 
				// gets changed later
				for(int k=0; k<K; k++){ // loop through centroids to 
				// find the closest one
					if (!allow_empty_clusters || numMembers[k]){
						// evaluate the distance; note the order of 
						// arguments: centroid followed by freq --- it's 
						// important for non-symmetric distances
						double dist = (*distf)(centroids[k], freq[n], 
							row_length, centroids_tilde[k], quality[n], 
							expected_qual, expected_freq);
						if (new_assignment==-1 || dist < min_dist) {
							min_dist = dist;
							new_assignment = k;
						}
					}
				}
				// check for assignment change
				assignmentChanged = assignmentChanged || (assignment[n] != 
					new_assignment);
				// update assignment
				assignment[n] = new_assignment;
			}
			
			// evaluate the number of members in each cluster
			memset(numMembers, 0, K*sizeof(*numMembers));
			for(int n=0; n<N; n++){
				numMembers[assignment[n]] += 1;
			}

			// check for empty clusters
			if (!allow_empty_clusters){
				for(int k=0; k<K; k++){
					if(!numMembers[k]){
						if (verbose > 0) {
							cerr<<"\tEM routine: empty cluster"<<endl;
						}
						// got an empty cluster; continue from the beginning
						deallocate_cache();
						initialize_cache(N);
						goto restart;
					}
				}
			}
			
			distortion = 0;
			for (int n=0; n<N; n++) {
				distortion += distf(centroids[assignment[n]], freq[n],
				row_length, centroids_tilde[assignment[n]], quality[n], 
				expected_qual, expected_freq);
			}
			if (!assignmentChanged){
				// clustering succeded; return
				if (verbose > 0) {
					cerr<<"\tEM routine: attempt succeded"<<endl;
				}
				deallocate_cache();
				delete [] tmp_qfreq;
				delete [] tmp_qfreq_1;
				return distortion;
			}
			
			//If the number of iterations has excedeed a maximum, the 
			//algorithm stops and the best solution found is returned.
			if (exceed_max_iterations(N, K, assignment, distortion)){
				if (verbose > 0) {
					cerr<<"\tEM routine: Cycling. Returning"<<endl;
				}
				deallocate_cache();
				delete [] tmp_qfreq;
				delete [] tmp_qfreq_1;
				return distortion;
			}			

			// evaluate the new centroids
			for(int k=0; k<K; k++){
				if(!allow_empty_clusters || numMembers[k]){
					int elems_in_cluster = 0;
					for(int n=0; n<N; ++n){
						if(assignment[n] == k){
							tmp_data[elems_in_cluster] = freq[n];
							tmp_data_1[elems_in_cluster] = freq_1[n];
							
							tmp_qfreq[elems_in_cluster] = quality[n];
							tmp_qfreq_1[elems_in_cluster] = quality_1[n];
							
							elems_in_cluster++;
						}
					}
					eval_centroid(elems_in_cluster, row_length, tmp_data,
						num_nt, tmp_data_1, centroids[k], centroids_tilde[k],
						tmp_qfreq, expected_qual, tmp_qfreq_1,expected_freq);
				}
			}
		}
	}
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %rbx
subq $0xf8, %rsp
movl 0x70(%rbp), %eax
movl 0x68(%rbp), %eax
movb 0x60(%rbp), %al
movq 0x58(%rbp), %rax
movq 0x50(%rbp), %rax
movq 0x48(%rbp), %rax
movq 0x40(%rbp), %rax
movq 0x38(%rbp), %rax
movq 0x30(%rbp), %rax
movq 0x28(%rbp), %rax
movl 0x20(%rbp), %eax
movq 0x18(%rbp), %rax
movq 0x10(%rbp), %rax
movl %edi, -0x24(%rbp)
movl %esi, -0x28(%rbp)
movl %edx, -0x2c(%rbp)
movq %rcx, -0x38(%rbp)
movq %r8, -0x40(%rbp)
movq %r9, -0x48(%rbp)
movslq -0x28(%rbp), %rax
movl $0x8, %ecx
mulq %rcx
movq %rax, %rdi
seto %cl
movq $-0x1, %rax
testb $0x1, %cl
cmovneq %rax, %rdi
callq 0x3030
movq %rax, -0x50(%rbp)
movslq -0x28(%rbp), %rax
movl $0x8, %ecx
mulq %rcx
movq %rax, %rdi
seto %cl
movq $-0x1, %rax
testb $0x1, %cl
cmovneq %rax, %rdi
callq 0x3030
movq %rax, -0x58(%rbp)
xorps %xmm0, %xmm0
movsd %xmm0, -0x60(%rbp)
movl -0x28(%rbp), %edi
callq 0x5c60
leaq -0x68(%rbp), %rsi
leaq -0x70(%rbp), %rdx
movsbl 0x60(%rbp), %edi
callq 0x5e10
jmp 0x658a
cmpl $0x0, 0x68(%rbp)
jle 0x65b2
movq 0x8a59(%rip), %rdi      # 0xeff0
leaq 0x56c7(%rip), %rsi      # 0xbc65
callq 0x3260
movq %rax, %rdi
movq 0x8a03(%rip), %rsi      # 0xefb0
callq 0x32a0
movq 0x30(%rbp), %rdi
movslq -0x28(%rbp), %rdx
shlq $0x2, %rdx
xorl %esi, %esi
callq 0x3150
movq 0x38(%rbp), %rdi
movslq -0x24(%rbp), %rdx
shlq $0x2, %rdx
movl $0xff, %esi
callq 0x3150
movl $0x0, -0x74(%rbp)
movl -0x74(%rbp), %eax
cmpl -0x24(%rbp), %eax
jge 0x668d
callq 0x30c0
cltd
idivl -0x28(%rbp)
movl %edx, -0x78(%rbp)
movq -0x70(%rbp), %rax
movl -0x2c(%rbp), %esi
movq -0x38(%rbp), %rdx
movslq -0x78(%rbp), %rcx
shlq $0x3, %rcx
addq %rcx, %rdx
movl 0x20(%rbp), %ecx
movq 0x28(%rbp), %r8
movslq -0x78(%rbp), %rdi
shlq $0x3, %rdi
addq %rdi, %r8
movq 0x40(%rbp), %rdi
movslq -0x74(%rbp), %r9
movq (%rdi,%r9,8), %r9
movq 0x48(%rbp), %rdi
movslq -0x74(%rbp), %r10
movq (%rdi,%r10,8), %r15
movq -0x40(%rbp), %r14
movslq -0x78(%rbp), %rdi
shlq $0x3, %rdi
addq %rdi, %r14
movq -0x48(%rbp), %rbx
movq 0x10(%rbp), %r11
movslq -0x78(%rbp), %rdi
shlq $0x3, %rdi
addq %rdi, %r11
movq 0x18(%rbp), %r10
movl $0x1, %edi
movq %r15, (%rsp)
movq %r14, 0x8(%rsp)
movq %rbx, 0x10(%rsp)
movq %r11, 0x18(%rsp)
movq %r10, 0x20(%rsp)
callq *%rax
movl -0x74(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x74(%rbp)
jmp 0x65e2
jmp 0x668f
movb $0x0, -0x79(%rbp)
movl $0x0, -0x80(%rbp)
movl -0x80(%rbp), %eax
cmpl -0x28(%rbp), %eax
jge 0x67ec
movl $0xffffffff, -0x84(%rbp) # imm = 0xFFFFFFFF
movsd 0x54c0(%rip), %xmm0     # 0xbb78
movsd %xmm0, -0x90(%rbp)
movl $0x0, -0x94(%rbp)
movl -0x94(%rbp), %eax
cmpl -0x24(%rbp), %eax
jge 0x679a
cmpl $0x0, 0x70(%rbp)
je 0x66f4
movq 0x38(%rbp), %rax
movslq -0x94(%rbp), %rcx
cmpl $0x0, (%rax,%rcx,4)
je 0x6784
movq -0x68(%rbp), %rax
movq 0x40(%rbp), %rcx
movslq -0x94(%rbp), %rdx
movq (%rcx,%rdx,8), %rdi
movq -0x38(%rbp), %rcx
movslq -0x80(%rbp), %rdx
movq (%rcx,%rdx,8), %rsi
movl -0x2c(%rbp), %edx
movq 0x48(%rbp), %rcx
movslq -0x94(%rbp), %r8
movq (%rcx,%r8,8), %rcx
movq -0x40(%rbp), %r8
movslq -0x80(%rbp), %r9
movq (%r8,%r9,8), %r8
movq -0x48(%rbp), %r9
movq 0x18(%rbp), %r10
movq %r10, (%rsp)
callq *%rax
movsd %xmm0, -0xa0(%rbp)
cmpl $-0x1, -0x84(%rbp)
je 0x6766
movsd -0xa0(%rbp), %xmm1
movsd -0x90(%rbp), %xmm0
ucomisd %xmm1, %xmm0
jbe 0x6782
movsd -0xa0(%rbp), %xmm0
movsd %xmm0, -0x90(%rbp)
movl -0x94(%rbp), %eax
movl %eax, -0x84(%rbp)
jmp 0x6784
jmp 0x6786
movl -0x94(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x94(%rbp)
jmp 0x66ca
movb $0x1, %al
testb $0x1, -0x79(%rbp)
movb %al, -0xb9(%rbp)
jne 0x67c2
movq 0x30(%rbp), %rax
movslq -0x80(%rbp), %rcx
movl (%rax,%rcx,4), %eax
cmpl -0x84(%rbp), %eax
setne %al
movb %al, -0xb9(%rbp)
movb -0xb9(%rbp), %al
andb $0x1, %al
movb %al, -0x79(%rbp)
movl -0x84(%rbp), %edx
movq 0x30(%rbp), %rax
movslq -0x80(%rbp), %rcx
movl %edx, (%rax,%rcx,4)
movl -0x80(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x80(%rbp)
jmp 0x669a
movq 0x38(%rbp), %rdi
movslq -0x24(%rbp), %rdx
shlq $0x2, %rdx
xorl %esi, %esi
callq 0x3150
movl $0x0, -0xa4(%rbp)
movl -0xa4(%rbp), %eax
cmpl -0x28(%rbp), %eax
jge 0x6841
movq 0x38(%rbp), %rax
movq 0x30(%rbp), %rcx
movslq -0xa4(%rbp), %rdx
movslq (%rcx,%rdx,4), %rcx
movl (%rax,%rcx,4), %edx
addl $0x1, %edx
movl %edx, (%rax,%rcx,4)
movl -0xa4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xa4(%rbp)
jmp 0x6809
cmpl $0x0, 0x70(%rbp)
jne 0x68bc
movl $0x0, -0xa8(%rbp)
movl -0xa8(%rbp), %eax
cmpl -0x24(%rbp), %eax
jge 0x68ba
movq 0x38(%rbp), %rax
movslq -0xa8(%rbp), %rcx
cmpl $0x0, (%rax,%rcx,4)
jne 0x68a7
cmpl $0x0, 0x68(%rbp)
jle 0x6895
movq 0x8776(%rip), %rdi      # 0xeff0
leaq 0x540e(%rip), %rsi      # 0xbc8f
callq 0x3260
movq %rax, %rdi
movq 0x8720(%rip), %rsi      # 0xefb0
callq 0x32a0
callq 0x5de0
movl -0x28(%rbp), %edi
callq 0x5c60
jmp 0x658a
jmp 0x68a9
movl -0xa8(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xa8(%rbp)
jmp 0x6851
jmp 0x68bc
xorps %xmm0, %xmm0
movsd %xmm0, -0x60(%rbp)
movl $0x0, -0xac(%rbp)
movl -0xac(%rbp), %eax
cmpl -0x28(%rbp), %eax
jge 0x6958
movq -0x68(%rbp), %rax
movq 0x40(%rbp), %rcx
movq 0x30(%rbp), %rdx
movslq -0xac(%rbp), %rsi
movslq (%rdx,%rsi,4), %rdx
movq (%rcx,%rdx,8), %rdi
movq -0x38(%rbp), %rcx
movslq -0xac(%rbp), %rdx
movq (%rcx,%rdx,8), %rsi
movl -0x2c(%rbp), %edx
movq 0x48(%rbp), %rcx
movq 0x30(%rbp), %r8
movslq -0xac(%rbp), %r9
movslq (%r8,%r9,4), %r8
movq (%rcx,%r8,8), %rcx
movq -0x40(%rbp), %r8
movslq -0xac(%rbp), %r9
movq (%r8,%r9,8), %r8
movq -0x48(%rbp), %r9
movq 0x18(%rbp), %r10
movq %r10, (%rsp)
callq *%rax
addsd -0x60(%rbp), %xmm0
movsd %xmm0, -0x60(%rbp)
movl -0xac(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xac(%rbp)
jmp 0x68ce
testb $0x1, -0x79(%rbp)
jne 0x69d4
cmpl $0x0, 0x68(%rbp)
jle 0x6986
movq 0x8685(%rip), %rdi      # 0xeff0
leaq 0x5338(%rip), %rsi      # 0xbcaa
callq 0x3260
movq %rax, %rdi
movq 0x862f(%rip), %rsi      # 0xefb0
callq 0x32a0
callq 0x5de0
movq -0x50(%rbp), %rax
movq %rax, -0xc8(%rbp)
cmpq $0x0, %rax
je 0x69a8
movq -0xc8(%rbp), %rdi
callq 0x3350
movq -0x58(%rbp), %rax
movq %rax, -0xd0(%rbp)
cmpq $0x0, %rax
je 0x69c5
movq -0xd0(%rbp), %rdi
callq 0x3350
movsd -0x60(%rbp), %xmm0
movsd %xmm0, -0x20(%rbp)
jmp 0x6bec
movl -0x28(%rbp), %edi
movl -0x24(%rbp), %esi
movq 0x30(%rbp), %rdx
leaq -0x60(%rbp), %rcx
callq 0x5cf0
testb $0x1, %al
jne 0x69ed
jmp 0x6a63
cmpl $0x0, 0x68(%rbp)
jle 0x6a15
movq 0x85f6(%rip), %rdi      # 0xeff0
leaq 0x52c7(%rip), %rsi      # 0xbcc8
callq 0x3260
movq %rax, %rdi
movq 0x85a0(%rip), %rsi      # 0xefb0
callq 0x32a0
callq 0x5de0
movq -0x50(%rbp), %rax
movq %rax, -0xd8(%rbp)
cmpq $0x0, %rax
je 0x6a37
movq -0xd8(%rbp), %rdi
callq 0x3350
movq -0x58(%rbp), %rax
movq %rax, -0xe0(%rbp)
cmpq $0x0, %rax
je 0x6a54
movq -0xe0(%rbp), %rdi
callq 0x3350
movsd -0x60(%rbp), %xmm0
movsd %xmm0, -0x20(%rbp)
jmp 0x6bec
movl $0x0, -0xb0(%rbp)
movl -0xb0(%rbp), %eax
cmpl -0x24(%rbp), %eax
jge 0x6be7
cmpl $0x0, 0x70(%rbp)
je 0x6a97
movq 0x38(%rbp), %rax
movslq -0xb0(%rbp), %rcx
cmpl $0x0, (%rax,%rcx,4)
je 0x6bd1
movl $0x0, -0xb4(%rbp)
movl $0x0, -0xb8(%rbp)
movl -0xb8(%rbp), %eax
cmpl -0x28(%rbp), %eax
jge 0x6b71
movq 0x30(%rbp), %rax
movslq -0xb8(%rbp), %rcx
movl (%rax,%rcx,4), %eax
cmpl -0xb0(%rbp), %eax
jne 0x6b5b
movq -0x38(%rbp), %rax
movslq -0xb8(%rbp), %rcx
movq (%rax,%rcx,8), %rdx
movq 0x50(%rbp), %rax
movslq -0xb4(%rbp), %rcx
movq %rdx, (%rax,%rcx,8)
movq 0x28(%rbp), %rax
movslq -0xb8(%rbp), %rcx
movq (%rax,%rcx,8), %rdx
movq 0x58(%rbp), %rax
movslq -0xb4(%rbp), %rcx
movq %rdx, (%rax,%rcx,8)
movq -0x40(%rbp), %rax
movslq -0xb8(%rbp), %rcx
movq (%rax,%rcx,8), %rdx
movq -0x50(%rbp), %rax
movslq -0xb4(%rbp), %rcx
movq %rdx, (%rax,%rcx,8)
movq 0x10(%rbp), %rax
movslq -0xb8(%rbp), %rcx
movq (%rax,%rcx,8), %rdx
movq -0x58(%rbp), %rax
movslq -0xb4(%rbp), %rcx
movq %rdx, (%rax,%rcx,8)
movl -0xb4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xb4(%rbp)
jmp 0x6b5d
movl -0xb8(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xb8(%rbp)
jmp 0x6aab
movq -0x70(%rbp), %rax
movl -0xb4(%rbp), %edi
movl -0x2c(%rbp), %esi
movq 0x50(%rbp), %rdx
movl 0x20(%rbp), %ecx
movq 0x58(%rbp), %r8
movq 0x40(%rbp), %r9
movslq -0xb0(%rbp), %r10
movq (%r9,%r10,8), %r9
movq 0x48(%rbp), %r10
movslq -0xb0(%rbp), %r11
movq (%r10,%r11,8), %r15
movq -0x50(%rbp), %r14
movq -0x48(%rbp), %rbx
movq -0x58(%rbp), %r11
movq 0x18(%rbp), %r10
movq %r15, (%rsp)
movq %r14, 0x8(%rsp)
movq %rbx, 0x10(%rsp)
movq %r11, 0x18(%rsp)
movq %r10, 0x20(%rsp)
callq *%rax
jmp 0x6bd3
movl -0xb0(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xb0(%rbp)
jmp 0x6a6d
jmp 0x668f
movsd -0x20(%rbp), %xmm0
addq $0xf8, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
nop