// File: /workspace/github_cpp_makefile_star2_O3/wichtounet[P]articles/plf_colony_alpha/plf_colony.h
// Function: plf::colony<Trivial<8>, std::allocator<Trivial<8>>, unsigned short>::consolidate_erased_locations(plf::colony<Trivial<8>, std::allocator<Trivial<8>>, unsigned short>::group*)
void consolidate_erased_locations(const group_pointer_type the_group_pointer)
	{
		// Function: Remove all entries in the stack containing memory locations from the supplied colony group, and incidentally remove unused trailing groups from the stack.

		// Process explanation:
		// First, remove any trailing unused groups from the stack. These may be present if a stack has pushed then popped a lot, as plf::stack never deallocates.
		// If there're no pointers to memory locations from the supplied colony group within stack groups, preserve those particular stack groups with no alterations in the new chain.
		// If there are pointers to memory locations from the supplied colony group in any stack group, copy these locations to the new stack group and remove the old group.
		// If a stack group is at the end of the old stack but is partial, and there have been some copies of memory locations made from old groups (see sentence immediately above this one), copy the locations from this group to the new group and remove it (otherwise you would end up with two partially-full groups at the end of the new chain - a new partially-full group following a partially-full old group). If there're no copies of memory locations (just old groups), then preserve the old end group.
		// At the end, if the new group has copies in it, add it to the new chain. Alternatively if the stack is now empty, reinitialize it. Or if there're no old groups remaining make the new group the first group. Or if there are only old groups, link the new chain (of old groups) into the stack.
		// Complicated but faster than any other alternative.

		typedef typename reduced_stack::stack_group_pointer_type stack_group_pointer;
		typedef typename reduced_stack::stack_element_pointer_type stack_element_pointer;

		#ifdef PLF_COLONY_ALLOCATOR_TRAITS_SUPPORT
			typedef typename reduced_stack::stack_group_allocator_type stack_group_allocator_type; // Not used by PLF_COLONY_DESTROY etc when std::allocator_traits not supported
		#endif

		// Remove trailing stack groups (not removed in general 'pop' usage in reduced_stack for performance reasons)
		{
			stack_group_pointer temp_group = erased_locations.current_group->next_group, temp_group2;
			erased_locations.current_group->next_group = NULL; // Set to NULL regardless of whether it is already NULL (avoids branching). Cuts off rest of groups from this group.

			while (temp_group != NULL)
			{
				temp_group2 = temp_group;
				temp_group = temp_group->next_group;
				PLF_COLONY_DESTROY(stack_group_allocator_type, erased_locations.group_allocator_pair, temp_group2);
				PLF_COLONY_DEALLOCATE(stack_group_allocator_type, erased_locations.group_allocator_pair, temp_group2, 1);
			}
		}

		// Determine what the size of the first new group in erased_locations should be, based on the size of the first colony group:
		const size_type temp_size = (min_elements_per_group < 8) ? min_elements_per_group : (min_elements_per_group >> 7) + 8;

		// Note: All groups from here onwards refer to erased_location's stack groups, not colony groups, unless stated otherwise

		// Use either the size determined above or the current total number of elements in stack as an estimate of how large the first group in it should be:
		// The current total number of elements in the stack is a good estimate for how large it might become in future, but if it's smaller than the first_group determination, we should probably use the first_group determination.
		const size_type new_group_size = (erased_locations.total_number_of_elements < temp_size) ? temp_size : erased_locations.total_number_of_elements;

		stack_group_pointer current_old_group = erased_locations.first_group,
							new_group = PLF_COLONY_ALLOCATE(stack_group_allocator_type, erased_locations.group_allocator_pair, 1, erased_locations.current_group),
							first_new_chain = NULL,
							current_new_chain = NULL;

		try
		{
			#ifdef PLF_COLONY_VARIADICS_SUPPORT
				PLF_COLONY_CONSTRUCT(stack_group_allocator_type, erased_locations.group_allocator_pair, new_group, new_group_size, erased_locations.current_group);
			#else
				PLF_COLONY_CONSTRUCT(stack_group_allocator_type, erased_locations.group_allocator_pair, new_group, typename reduced_stack::group(new_group_size, erased_locations.current_group));
			#endif
		}
		catch (...)
		{
			PLF_COLONY_DEALLOCATE(stack_group_allocator_type, erased_locations.group_allocator_pair, new_group, 1);
			throw;
		}


		stack_element_pointer iterator_pointer, source_end, destination_begin = new_group->elements, the_end = NULL;
		const stack_element_pointer destination_start = destination_begin;
		const element_pointer_type group_elements_start = the_group_pointer->elements, group_elements_end = reinterpret_cast<element_pointer_type>(the_group_pointer->skipfield);
		size_type total_number_of_copies = 0, number_to_be_copied = 0;

		do // per old group of stack
		{
			if (current_old_group->next_group != NULL)
			{
				source_end = current_old_group->end + 1;
			}
			else
			{
				source_end = erased_locations.top_element + 1;
			}

			iterator_pointer = current_old_group->elements;
			const size_type number_of_group_elements = static_cast<const size_type>(source_end - iterator_pointer);

			for (; iterator_pointer != source_end; ++iterator_pointer) // per old group element
			{
				if (*iterator_pointer < group_elements_start || *iterator_pointer >= group_elements_end) // ie. this stack element doesn't match the skipfield)) // ie. this stack element doesn't match the group pointer, doesn't need to be removed
				{
					++number_to_be_copied;
				}
				else if (number_to_be_copied != 0) // Indicates a break in a run of elements matching the group_pointer, so copy time:
				{
					// Use the fastest method for moving iterators, while perserving values if allocator provides non-trivial pointers - unused if/else branches will be optimised out by any decent compiler:
					#ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
						if (std::is_trivially_copyable<element_pointer_type>::value && std::is_trivially_destructible<element_pointer_type>::value) // Avoid iteration for trivially-destructible iterators ie. all iterators, unless allocator returns non-trivial pointers
						{
							std::memcpy(&*destination_begin, &*(iterator_pointer - number_to_be_copied), number_to_be_copied * sizeof(element_pointer_type)); // &* to avoid problems with non-trivial pointers that are trivially-copyable
						}
						#ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
							else if (std::is_move_constructible<element_pointer_type>::value)
							{
								std::uninitialized_copy(std::make_move_iterator(iterator_pointer - number_to_be_copied), std::make_move_iterator(iterator_pointer), destination_begin);
							}
						#endif
						else
					#endif
					{
						std::uninitialized_copy(iterator_pointer - number_to_be_copied, iterator_pointer, destination_begin);

						// The following loop is necessary because the allocator may return non-trivially-destructible pointer types, making iterator a non-trivially-destructible type:
						for (stack_element_pointer element_pointer = iterator_pointer - number_to_be_copied; element_pointer != iterator_pointer; ++element_pointer)
						{
							PLF_COLONY_DESTROY(element_pointer_allocator_type, erased_locations, element_pointer);
						}
					}

					destination_begin += number_to_be_copied;
					total_number_of_copies += number_to_be_copied;
					number_to_be_copied = 0;
				}
			}

			const bool test = (number_to_be_copied == static_cast<const size_type>((current_old_group->end + 1) - current_old_group->elements));

			if (test || (number_to_be_copied == number_of_group_elements && destination_begin == destination_start))
			{
				if (test)
				{
					the_end = current_old_group->end;
				}
				else
				{
					the_end = source_end - 1;
				}

				// No deletions, preserve group
				if (current_new_chain == NULL)
				{
					// First in chain
					current_new_chain = first_new_chain = current_old_group;
					current_old_group->previous_group = NULL;
				}
				else
				{
					// Add to chain
					current_new_chain->next_group = current_old_group;
					current_old_group->previous_group = current_new_chain;
					current_new_chain = current_old_group;
				}

				total_number_of_copies += number_to_be_copied;
				number_to_be_copied = 0;
				current_old_group = current_old_group->next_group;
			}
			else
			{
				if (number_to_be_copied != 0)
				{
					// move remainder:
					#ifdef PLF_COLONY_TYPE_TRAITS_SUPPORT
						if (std::is_trivially_copyable<element_pointer_type>::value && std::is_trivially_destructible<element_pointer_type>::value)
						{ // Dereferencing here in order to deal with smart pointer situations ie. obtaining the raw pointer from the smart pointer
							std::memcpy(&*destination_begin, &*(iterator_pointer - number_to_be_copied), number_to_be_copied * sizeof(element_pointer_type));
						}
						#ifdef PLF_COLONY_MOVE_SEMANTICS_SUPPORT
							else if (std::is_move_constructible<element_pointer_type>::value)
							{
								std::uninitialized_copy(std::make_move_iterator(iterator_pointer - number_to_be_copied), std::make_move_iterator(iterator_pointer), destination_begin);
							}
						#endif
						else
					#endif
					{
						std::uninitialized_copy(iterator_pointer - number_to_be_copied, iterator_pointer, destination_begin);

						// The following loop is necessary because the allocator may return non-trivially-destructible pointer types, making iterator a non-trivially-destructible type:
						for (stack_element_pointer element_pointer = iterator_pointer - number_to_be_copied; element_pointer != iterator_pointer; ++element_pointer)
						{
							PLF_COLONY_DESTROY(element_pointer_allocator_type, erased_locations, element_pointer);
						}
					}

					destination_begin += number_to_be_copied;
					total_number_of_copies += number_to_be_copied;
					number_to_be_copied = 0;
				}

				// Remove old group:
				const stack_group_pointer prev_group = current_old_group;
				current_old_group = current_old_group->next_group;
				PLF_COLONY_DESTROY(stack_group_allocator_type, erased_locations.group_allocator_pair, prev_group);
				PLF_COLONY_DEALLOCATE(stack_group_allocator_type, erased_locations.group_allocator_pair, prev_group, 1);
			}
		} while (current_old_group != NULL);


		switch((current_new_chain != NULL) | ((destination_begin == destination_start) << 1))
		{
			case 0: // if (current_new_chain == NULL && destination_begin != destination_start) - No old groups remaining, some copies
			{
				new_group->previous_group = NULL;
				erased_locations.first_group = erased_locations.current_group = new_group;
				erased_locations.top_element = destination_begin - 1;
				erased_locations.start_element = new_group->elements;
				erased_locations.end_element = new_group->end;
				erased_locations.total_number_of_elements = total_number_of_copies;
				break;
			}
			case 1: // else if (current_new_chain != NULL && destination_begin != destination_start) - Some deletions occured ie. some elements copied, some old groups remaining - current_new_chain != NULL implied by previous if
			{
				new_group->previous_group = current_new_chain;
				erased_locations.current_group = current_new_chain->next_group = new_group;
				erased_locations.first_group = first_new_chain;
				erased_locations.top_element = destination_begin - 1;
				erased_locations.start_element = new_group->elements;
				erased_locations.end_element = new_group->end;
				erased_locations.total_number_of_elements = total_number_of_copies;
				break;
			}
			case 3: // else if (current_new_chain != NULL && destination_begin == destination_start) - No copies, some old groups - destination_begin == destination_start implied by previous if's
			{
				PLF_COLONY_DESTROY(stack_group_allocator_type, erased_locations.group_allocator_pair, new_group);
				PLF_COLONY_DEALLOCATE(stack_group_allocator_type, erased_locations.group_allocator_pair, new_group, 1);

				current_new_chain->next_group = NULL;
				erased_locations.current_group = current_new_chain;
				erased_locations.first_group = first_new_chain;
				erased_locations.top_element = the_end;
				erased_locations.start_element = current_new_chain->elements;
				erased_locations.end_element = current_new_chain->end;
				erased_locations.total_number_of_elements = total_number_of_copies;
				break;
			}
			default: // else (current_new_chain == NULL && destination_begin == destination_start) - No elements remaining - least likely
			{
				// Instead of removing the newly-constructed group and returning to zero,
				// we bank on the idea that if an element has been removed, more elements are likely to be removed later, and so keep the group rather than deleting it.

				erased_locations.current_group = new_group;
				erased_locations.first_group = new_group;
				erased_locations.top_element = new_group->elements - 1;
				erased_locations.start_element = new_group->elements;
				erased_locations.end_element = new_group->end;
				erased_locations.total_number_of_elements = 0;
				break;
			}
		}
	}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x68, %rsp
movq 0x48(%rdi), %rax
movq %rsi, %r12
movq %rdi, %r15
movq 0x8(%rax), %r14
movq $0x0, 0x8(%rax)
testq %r14, %r14
je 0x51fff
movq (%r14), %rdi
movq 0x8(%r14), %rbx
callq 0x41a0
movq %r14, %rdi
callq 0x41a0
movq %rbx, %r14
testq %rbx, %rbx
jne 0x51fe3
movzwl 0x40(%r15), %eax
movq 0x70(%r15), %rcx
movq 0x50(%r15), %rdx
movl $0x20, %edi
movl %eax, %ebp
shrl $0x7, %ebp
movq %rdx, 0x8(%rsp)
addl $0x8, %ebp
cmpl $0x8, %eax
cmovbl %eax, %ebp
cmpq %rbp, %rcx
cmovaq %rcx, %rbp
callq 0x41c0
movq 0x48(%r15), %rbx
movq %rax, %r14
testq %rbx, %rbx
je 0x52041
movq (%rbx), %rdx
jmp 0x52043
xorl %edx, %edx
movq %r15, 0x10(%rsp)
movq %r14, %rdi
movq %rbp, %rsi
callq 0xc3f8
movq 0x10(%rsp), %rdx
movq %rax, %rcx
leaq -0x8(%rcx,%rbp,8), %rsi
movq %rax, (%r14)
movq %r14, %rax
movq $0x0, 0x8(%r14)
movq %rbx, 0x10(%r14)
addq $0x18, %rax
movq %r14, 0x18(%rsp)
movq $0x0, 0x38(%rsp)
movq %rcx, 0x30(%rsp)
xorl %r15d, %r15d
xorl %r8d, %r8d
movq $0x0, 0x20(%rsp)
movq %rax, 0x28(%rsp)
movq %rsi, 0x18(%r14)
movq 0x18(%r12), %r9
movq 0x10(%r12), %r13
movq %rcx, %r12
leaq 0x58(%rdx), %rax
movq 0x8(%rsp), %rdx
movq %rax, 0x48(%rsp)
movq %r9, 0x40(%rsp)
cmpq $0x0, 0x8(%rdx)
leaq 0x18(%rdx), %rax
movq (%rdx), %rdi
movq %rdx, 0x8(%rsp)
movq %rax, 0x60(%rsp)
cmoveq 0x48(%rsp), %rax
movq (%rax), %rbp
leaq 0x8(%rbp), %rax
subq %rdi, %rax
movq %rax, 0x58(%rsp)
je 0x52159
movq %r15, 0x50(%rsp)
xorl %r14d, %r14d
movq %rdi, %rsi
movq (%rsi), %rax
movq %rsi, %r15
cmpq %r13, %rax
jb 0x5213e
cmpq %r9, %rax
jae 0x5213e
testq %r14, %r14
je 0x52139
leaq (,%r14,8), %rdx
movq %r15, %rsi
movq %r12, %rdi
movq %r12, %rbx
movq %r8, %r12
subq %rdx, %rsi
callq 0x4160
movq 0x40(%rsp), %r9
movq %r12, %r8
leaq (%rbx,%r14,8), %r12
addq %r14, %r8
xorl %r14d, %r14d
jmp 0x52141
incq %r14
leaq 0x8(%r15), %rsi
cmpq %rbp, %r15
jne 0x520f9
movq 0x8(%rsp), %rax
movq 0x50(%rsp), %r15
movq (%rax), %rdi
jmp 0x5215f
movq %rdi, %rsi
xorl %r14d, %r14d
movq 0x60(%rsp), %rax
movq (%rax), %rax
movq %rax, %rcx
subq %rdi, %rcx
addq $0x8, %rcx
sarq $0x3, %rcx
cmpq %rcx, %r14
je 0x52192
movq 0x58(%rsp), %rax
sarq $0x3, %rax
cmpq %rax, %r14
jne 0x521c2
movq %rbp, %rax
cmpq 0x30(%rsp), %r12
jne 0x521c2
movq 0x8(%rsp), %rdx
movq %rdx, %rcx
testq %r15, %r15
je 0x521a8
movq 0x20(%rsp), %rcx
movq %rdx, 0x8(%r15)
movq %r15, 0x10(%rdx)
movq %rdx, %r15
addq %r14, %r8
movq %rax, 0x38(%rsp)
movq %rcx, 0x20(%rsp)
movq 0x8(%rdx), %rdx
jmp 0x5221d
movq %r15, %rbx
movq %r8, %r15
testq %r14, %r14
je 0x521f2
leaq (,%r14,8), %rdx
movq %r12, %rdi
subq %rdx, %rsi
callq 0x4160
leaq (%r12,%r14,8), %rbp
movq 0x8(%rsp), %r12
addq %r14, %r15
movq (%r12), %rdi
jmp 0x521fa
movq %r12, %rbp
movq 0x8(%rsp), %r12
movq 0x8(%r12), %r14
callq 0x41a0
movq %r12, %rdi
callq 0x41a0
movq 0x40(%rsp), %r9
movq %r15, %r8
movq %r14, %rdx
movq %rbp, %r12
movq %rbx, %r15
testq %rdx, %rdx
jne 0x520c1
xorl %eax, %eax
testq %r15, %r15
setne %al
xorl %ecx, %ecx
cmpq 0x30(%rsp), %r12
sete %cl
leal (%rax,%rcx,2), %eax
leaq 0x6587e(%rip), %rcx     # 0xb7ac0
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq 0x18(%rsp), %rax
movq 0x10(%rsp), %rdx
movq $0x0, 0x10(%rax)
movq %rax, 0x48(%rdx)
movq %rax, 0x50(%rdx)
jmp 0x522f9
movq 0x10(%rsp), %rdx
movq 0x18(%rsp), %rax
xorl %r8d, %r8d
movq %rax, 0x48(%rdx)
movq %rax, 0x50(%rdx)
movq (%rax), %rax
leaq -0x8(%rax), %rcx
movq %rcx, 0x58(%rdx)
jmp 0x52304
movq 0x18(%rsp), %rbx
movq %r8, %r14
movq (%rbx), %rdi
callq 0x41a0
movq %rbx, %rdi
callq 0x41a0
movq 0x10(%rsp), %rdx
movq 0x20(%rsp), %rax
movq 0x38(%rsp), %rcx
movq $0x0, 0x8(%r15)
movq %r14, %r8
movq %r15, 0x48(%rdx)
movq %rax, 0x50(%rdx)
movq %rcx, 0x58(%rdx)
movq (%r15), %rax
addq $0x18, %r15
movq %rax, 0x60(%rdx)
movq %r15, %rax
jmp 0x5230d
movq 0x18(%rsp), %rax
movq 0x10(%rsp), %rdx
movq 0x20(%rsp), %rcx
movq %r15, 0x10(%rax)
movq %rax, 0x8(%r15)
movq %rax, 0x48(%rdx)
movq %rcx, 0x50(%rdx)
addq $-0x8, %r12
movq %r12, 0x58(%rdx)
movq (%rax), %rax
movq %rax, 0x60(%rdx)
movq 0x28(%rsp), %rax
movq (%rax), %rax
movq %rax, 0x68(%rdx)
movq %r8, 0x70(%rdx)
addq $0x68, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, %rdi
callq 0x40c0
movq %r14, %rdi
callq 0x41a0
callq 0x4270
movq %rax, %rbx
callq 0x42c0
movq %rbx, %rdi
callq 0x42d0
movq %rax, %rdi
callq 0xaa79