// File: 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
subq $0x70, %rsp
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movl %edx, -0x14(%rbp)
movq -0x8(%rbp), %rdi
movq %rdi, -0x60(%rbp)
movb $0x1, -0x15(%rbp)
callq 0x1a50
movq %rax, %rcx
movq -0x60(%rbp), %rax
movq %rcx, -0x20(%rbp)
testb $0x1, 0x10(%rax)
je 0x16e5
movq -0x60(%rbp), %rdi
movq -0x10(%rbp), %rsi
movl -0x14(%rbp), %edx
callq 0x1c50
movq -0x60(%rbp), %rax
cmpl $0x1, 0x1840(%rax)
jle 0x1702
testb $0x1, 0xbb78(%rip)      # 0xd271
jne 0x1702
movb $0x1, 0xbb6f(%rip)      # 0xd271
movl $0x0, -0x24(%rbp)
cmpl $0x4, -0x24(%rbp)
jge 0x1755
movq -0x60(%rbp), %rax
movslq -0x24(%rbp), %rcx
movsd 0x1800(%rax,%rcx,8), %xmm0
movsd 0x1828(%rax), %xmm2
movq -0x10(%rbp), %rcx
movslq -0x24(%rbp), %rdx
movsd (%rcx,%rdx,8), %xmm1
mulsd %xmm2, %xmm0
addsd %xmm1, %xmm0
movslq -0x24(%rbp), %rcx
movsd %xmm0, 0x1800(%rax,%rcx,8)
movl -0x24(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x24(%rbp)
jmp 0x1709
movq -0x60(%rbp), %rax
movsd 0x1820(%rax), %xmm0
movsd 0x1828(%rax), %xmm1
mulsd %xmm1, %xmm0
movsd 0x98a3(%rip), %xmm1     # 0xb018
addsd %xmm1, %xmm0
movsd %xmm0, 0x1820(%rax)
movl 0x17dc(%rax), %eax
movq %rsp, %rcx
movq %rcx, -0x30(%rbp)
leaq 0xf(,%rax,4), %rdx
andq $-0x10, %rdx
movq %rsp, %rcx
subq %rdx, %rcx
movq %rcx, -0x68(%rbp)
movq %rcx, %rsp
movq %rax, -0x38(%rbp)
movl $0x0, -0x3c(%rbp)
movq -0x60(%rbp), %rcx
movl -0x3c(%rbp), %eax
cmpl 0x17dc(%rcx), %eax
jge 0x17da
movq -0x68(%rbp), %rax
movl -0x3c(%rbp), %edx
movslq -0x3c(%rbp), %rcx
movl %edx, (%rax,%rcx,4)
movl -0x3c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x3c(%rbp)
jmp 0x17b2
movq -0x60(%rbp), %rax
cmpl $0x2, 0x40(%rax)
jne 0x17ed
movq -0x60(%rbp), %rdi
callq 0x1ec0
movq -0x60(%rbp), %rax
cmpl $0x0, 0x40(%rax)
je 0x1825
movq -0x60(%rbp), %rax
cmpl $0x1, 0x17d8(%rax)
jg 0x1825
movq -0x60(%rbp), %rax
movl 0x17b4(%rax), %eax
movl $0x64, %ecx
xorl %edx, %edx
divl %ecx
cmpl $0x0, %edx
jne 0x1825
movq -0x60(%rbp), %rdi
callq 0x1f10
movl $0x0, -0x40(%rbp)
movq -0x60(%rbp), %rcx
movl -0x40(%rbp), %eax
cmpl 0x17dc(%rcx), %eax
jge 0x1872
movq -0x60(%rbp), %rdi
movq -0x68(%rbp), %rax
movq -0x10(%rbp), %rsi
movl -0x14(%rbp), %edx
movslq -0x40(%rbp), %rcx
movl (%rax,%rcx,4), %ecx
callq 0x1fb0
andb $0x1, %al
movb %al, -0x41(%rbp)
testb $0x1, -0x41(%rbp)
jne 0x1865
movb $0x0, -0x15(%rbp)
jmp 0x1867
movl -0x40(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x40(%rbp)
jmp 0x182c
movq -0x60(%rbp), %rax
cmpl $0x1, 0x17d8(%rax)
jg 0x1892
movq -0x60(%rbp), %rax
movl 0x1840(%rax), %ecx
addl $0x1, %ecx
movl %ecx, 0x1840(%rax)
movq -0x60(%rbp), %rdi
movl 0x17b4(%rdi), %eax
addl $0x1, %eax
movl %eax, 0x17b4(%rdi)
callq 0x1a50
movq %rax, %rcx
movq -0x60(%rbp), %rax
movq %rcx, -0x50(%rbp)
movb $0x1, -0x51(%rbp)
testb $0x1, 0x10(%rax)
je 0x190a
movq -0x60(%rbp), %rax
cmpl $0x0, 0x17b4(%rax)
jbe 0x18dc
movq -0x60(%rbp), %rax
cmpl $0x1f4, 0x17b4(%rax)    # imm = 0x1F4
jbe 0x1901
movq -0x60(%rbp), %rax
cmpl $0x0, 0x17b4(%rax)
jbe 0x190a
movq -0x60(%rbp), %rax
movl 0x17b4(%rax), %eax
movl $0x5, %ecx
xorl %edx, %edx
divl %ecx
cmpl $0x0, %edx
jne 0x190a
movq -0x60(%rbp), %rdi
callq 0x21f0
movq -0x60(%rbp), %rax
cmpl $0x1, 0x17d8(%rax)
jg 0x1919
jmp 0x1919
movb -0x15(%rbp), %al
movq -0x30(%rbp), %rcx
movq %rcx, %rsp
andb $0x1, %al
movq %rbp, %rsp
popq %rbp
retq
nopw (%rax,%rax)