// File: /workspace/github_cpp_makefile_star2_O3/big-data-lab-team[P]OrpailleCC/./src/mondrian_coarse.hpp
// Function: CoarseMondrianForest<double, funct, KappaMetrics<2, false>, 4, 2, 6000>::train(double const*, int)
bool train(feature_type const* features, int const label){
	bool fully_trained = true;

	TreeBase* base = tree_bases();

	if(print_nodes){
		scory(features, label);
	}

	static bool checked = false;
	if(has_been_full > 1 && !checked){
		//cout << "Full memory!" << endl;
		checked = true;
	}
	//Fade the count of the average point
	for(int i = 0; i < feature_count; ++i)
		sum_features[i] = sum_features[i]*fading_count + features[i];
	count_points = count_points*fading_count + 1;

	int tree_order[tree_count];
	for(int i = 0; i < tree_count; ++i)
		tree_order[i] = i;

	if(trim_type == TRIM_FADING)
		tree_fade_counts();
	if(trim_type != TRIM_NONE && node_available <= 1 && total_count%100 == 0){
		tree_trim();
	}
	for(int i = 0; i < tree_count; ++i){
		bool has_trained = train_tree(features, label, tree_order[i]);
		if(!has_trained)
			fully_trained = false;
        #ifdef UNBOUND_OPTIMIZE
		update_posterior_count(tree_order[i], features, label);
        #endif
	}
	if(node_available <= 1)
		has_been_full += 1;

	//otter(features, label);
    #ifdef DEBUG
	if(!fully_trained)
		cout << "[WARNING] Not fully trained. (" << __FILE__ << ":" << __LINE__ << ")" << endl;
    #endif
	total_count += 1;
	TreeBase* bases = tree_bases();
	bool all_tree_grown = true;
	//for(int i = 0; i < tree_count; ++i){
		//int node_count = 0;
		//int depth = tree_depth(i, &node_count);
		//if(!bases[i].is_grown(tree_management))
			//all_tree_grown = false;
		//#ifdef DEBUG
		//double score_debug;
		//if(bases[i].statistics.ratio()){
			//score_debug = bases[i].statistics.score(false, bases[i].size);
		//}
		//else {
			//const std::type_info& ti1 = typeid(Statistic);
			//const std::type_info& ti2 = typeid(ReservoirSamplingMetrics);
			//if(ti1.hash_code() == ti2.hash_code()) //If RS
				//score_debug = bases[i].statistics.score(false, tree_count-1);
			//else
				//score_debug = bases[i].statistics.score(false);
		//}
		//#endif
		//if(size_type == DEPTH_SIZE){
			//bases[i].size = depth;
		//}
		//else if(size_type == NODE_SIZE){
			//bases[i].size = node_count;
		//}
	//}
    #ifdef DEBUG
	cout << "Nodes remaining:" << total_count << "," << node_available << endl;
	cout << "Tree count:" << tree_count << endl;
	cout << "Tree Management:" << tree_management << endl;
	#endif
	if(print_nodes && ((total_count > 0 && total_count <= 500) || (total_count > 0 && total_count%5 == 0))){
		printy();
	}
	if(node_available <= 1){ //Remove trees
		if(false && total_count > 0 && total_count%500 == 0 && tree_count < 5){
			int const index_tree_base = max_size - tree_count * sizeof(TreeBase);
			int const new_index_tree_base = index_tree_base - sizeof(TreeBase);
			int const index_last_node = node_count * sizeof(Node);
			int const space_to_free = index_last_node - new_index_tree_base;
			int const number_of_node_to_move = space_to_free < 0 ? 0 : (space_to_free - space_to_free%sizeof(Node)) / sizeof(Node) + ((space_to_free%sizeof(Node)) > 0);

			int virtual_node_count = node_count - number_of_node_to_move;
			int virtual_node_per_tree = (virtual_node_count - (virtual_node_count%(tree_count+1)))/(tree_count+1);
			int virtual_remaining = (virtual_node_count%(tree_count+1));
			for(int i = 0; i < tree_count; ++i){
				int count;
				int depth_i = tree_depth(i, &count);
				while(count > virtual_node_per_tree){
					tree_chop(i, depth_i-1);
					depth_i = tree_depth(i, &count);
				}
			}
			if(node_available > 2){
				tree_add();
				tree_bases()[tree_count-1].node_count_limit = node_available;
			}
			for(int i = 0; i < tree_count; ++i){
				tree_bases()[i].node_count_limit = virtual_node_per_tree + (i < virtual_remaining);
			}
		}
		if(false && dont_delete == DO_DELETE && total_count%500 == 0 && total_count > 0 && tree_count > 30){
			int idx_smallest_contribution = 0;
			int highest_ds = 0;
			for(int i = 0; i < tree_count; ++i){
				double const contribution = bases[i].sum_contribution/bases[i].count_contribution;
				double const best_contrib = bases[idx_smallest_contribution].sum_contribution/bases[idx_smallest_contribution].count_contribution;

				if(contribution < best_contrib)
					idx_smallest_contribution = i;
			}
			if(tree_count > 2){
				tree_delete(idx_smallest_contribution);
			}
			int node_per_trees = (node_count - (node_count%tree_count))/tree_count;
			int remaining = (node_count%tree_count);
			for(int i = 0; i < tree_count; ++i)
				bases[i].node_count_limit = node_per_trees + (i < remaining);
		}
	}
    #ifdef DEBUG
	for(int i = 0; i < tree_count; ++i){
		if(!tree_checker(i)){
			cout << "Error in CoarseMondrianForest::tree_checker for tree " << i << "." << endl;
			exit(-1);
		}
	}
	#endif
	return fully_trained;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
pushq %rax
movl %edx, %r14d
movq %rsi, %r15
movq %rdi, %rbx
cmpb $0x1, 0x10(%rdi)
jne 0x1553
movq %rbx, %rdi
movq %r15, %rsi
movl %r14d, %edx
callq 0x1818
cmpl $0x2, 0x1840(%rbx)
jl 0x156c
cmpb $0x0, 0x6d1e(%rip)      # 0x8281
jne 0x156c
movb $0x1, 0x6d15(%rip)      # 0x8281
movl $0x300, %eax            # imm = 0x300
movsd (%rbx,%rax,8), %xmm0
mulsd 0x1828(%rbx), %xmm0
addsd -0x1800(%r15,%rax,8), %xmm0
movsd %xmm0, (%rbx,%rax,8)
incq %rax
cmpq $0x304, %rax            # imm = 0x304
jne 0x1571
movsd 0x1820(%rbx), %xmm0
mulsd 0x1828(%rbx), %xmm0
addsd 0x4a68(%rip), %xmm0     # 0x6018
movsd %xmm0, 0x1820(%rbx)
movl 0x17dc(%rbx), %eax
movq %rsp, %r13
leaq 0xf(,%rax,4), %rcx
andq $-0x10, %rcx
subq %rcx, %r13
movq %r13, %rsp
testl %eax, %eax
jle 0x169c
leal 0x1(%rax), %ecx
andl $-0x2, %ecx
leaq -0x1(%rax), %rdx
movq %rdx, %xmm0
pshufd $0x44, %xmm0, %xmm0     # xmm0 = xmm0[0,1,0,1]
movdqa 0x4a49(%rip), %xmm1     # 0x6040
xorl %edx, %edx
movdqa 0x4a4f(%rip), %xmm2     # 0x6050
pxor %xmm2, %xmm0
pcmpeqd %xmm3, %xmm3
movdqa 0x4a4f(%rip), %xmm4     # 0x6060
movdqa %xmm1, %xmm5
pxor %xmm2, %xmm5
movdqa %xmm5, %xmm6
pcmpgtd %xmm0, %xmm6
pcmpeqd %xmm0, %xmm5
pshufd $0xf5, %xmm5, %xmm7     # xmm7 = xmm5[1,1,3,3]
pand %xmm6, %xmm7
pshufd $0xf5, %xmm6, %xmm5     # xmm5 = xmm6[1,1,3,3]
por %xmm7, %xmm5
movd %xmm5, %esi
notl %esi
testb $0x1, %sil
je 0x1648
movl %edx, (%r13,%rdx,4)
pxor %xmm3, %xmm5
pextrw $0x4, %xmm5, %esi
testb $0x1, %sil
je 0x165f
leal 0x1(%rdx), %esi
movl %esi, 0x4(%r13,%rdx,4)
addq $0x2, %rdx
paddq %xmm4, %xmm1
cmpq %rdx, %rcx
jne 0x1611
testl %eax, %eax
setle %dl
movl 0x40(%rbx), %ecx
cmpl $0x2, %ecx
setne %sil
orb %dl, %sil
jne 0x169f
xorl %r12d, %r12d
movq %rbx, %rdi
movl %r12d, %esi
callq 0x210c
incl %r12d
movl 0x17dc(%rbx), %eax
cmpl %eax, %r12d
jl 0x1683
movl 0x40(%rbx), %ecx
testl %ecx, %ecx
je 0x16cf
cmpl $0x1, 0x17d8(%rbx)
jg 0x16cf
imull $0xc28f5c29, 0x17b4(%rbx), %ecx # imm = 0xC28F5C29
rorl $0x2, %ecx
cmpl $0x28f5c28, %ecx        # imm = 0x28F5C28
ja 0x16cf
movq %rbx, %rdi
callq 0x1ab0
movl 0x17dc(%rbx), %eax
movb $0x1, %r12b
testl %eax, %eax
jle 0x1706
movq %r15, -0x30(%rbp)
movl %r14d, %r15d
xorl %r14d, %r14d
movl (%r13,%r14,4), %ecx
movq %rbx, %rdi
movq -0x30(%rbp), %rsi
movl %r15d, %edx
callq 0x1b20
andb %al, %r12b
incq %r14
movslq 0x17dc(%rbx), %rax
cmpq %rax, %r14
jl 0x16e0
cmpl $0x2, 0x17d8(%rbx)
jge 0x1715
incl 0x1840(%rbx)
movl 0x17b4(%rbx), %ecx
leal 0x1(%rcx), %eax
movl %eax, 0x17b4(%rbx)
cmpb $0x1, 0x10(%rbx)
jne 0x1754
cmpl $0x1f4, %ecx            # imm = 0x1F4
jb 0x174c
testl %eax, %eax
je 0x1754
movl %eax, %ecx
movl $0xcccccccd, %edx       # imm = 0xCCCCCCCD
imulq %rcx, %rdx
shrq $0x22, %rdx
leal (%rdx,%rdx,4), %ecx
subl %ecx, %eax
jne 0x1754
movq %rbx, %rdi
callq 0x1cca
movl %r12d, %eax
leaq -0x28(%rbp), %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq