// File: /workspace/github_cpp_makefile_star2_O3/MarkCBell[P]twister/./twister/kernel/twister.cpp
// Function: manifold::foldoff(tetra*)
tetra *manifold::foldoff(tetra *capoff_tetra)
{
	output_debugging("foldoff", 2);
	
	if (!last_tetra)  // Empty manifold?
		return NULL;
	
	if (capoff_tetra == NULL)  // There's no boundary faces.
		return capoff_tetra;
	
	// Definition: Suppose that two boundary triangles T_1 = ABC and T_2 = A'B'C'
	// are glued along the edge BC ~ B'C' then T is said to have a valid fold in 
	// direction A iff A and A' are not in the same vertex class.
	
	// Definition: With T_1, T_2 as above in a manifold M, the manifold obtained 
	// by performing the 'close the book' move on T_1 in direction A is M / ~. 
	// Here ~ is the gluing of T_1 to T_2 by A ~ A', B ~ B' and C ~ C' extended 
	// linearly over T_1 and T_2.
	
	// Lemma: If T is a boundary triangle of the 3-manifold M which has a valid 
	// fold in direction A then the manifold obtained by performing the 'close the book' 
	// move on T in the direction A is homeomorphic to M.
	
	// Proof: See L122 of close_cusps.c in the SnapPea kernel. //
	
	// Lemma: If 2 boundary triangles each have no valid folds in any direction and,
	// after doing a valid fold elsewhere, they are now are adjacent to each other then 
	// they both still do not have a valid fold.
	
	// Proof: Omitted. //
	
	// Corollary: Only 1 pass through the list of tetra is ever needed.
	
	// We only foldoff capping tetra when it will preserve the meridian or longitude information.
	for (tetra *current = capoff_tetra; current != NULL; current = current->get_next())
	{
		// Let's find a direction in which we can foldoff.
		for (int dir = 0; dir < 3; dir++)  // Direction 3 is down into the manifold so don't bother trying that way.
		{
			tetra *adjacent = current->get_gluedto(dir);
			perm current_to_adjacent = current->get_gluing(dir);
			
			// Check we're not going to mess up the longitudes or meridians.
			bool no_peripheral_curves = true;  // Peripheral curve free?
			bool side_to_side = ((current->get_peripheral_curves(longitude, dir) != 0) && (current->get_peripheral_curves(meridian, dir) != 0));
			for (int i = 0; i < 2; i++)
			{
				Curves c = (Curves) i;  // Which type of curve we're looking at.
				for (int j = 0; j < 3; j++)
				{
					int k = current_to_adjacent[j];
					if ((current->get_peripheral_curves(c, j) != 0) || (adjacent->get_peripheral_curves(c, k) != 0)) no_peripheral_curves = false;
					if (current->get_peripheral_curves(c, j) != -adjacent->get_peripheral_curves(c, k)) side_to_side = false;
				}
			}
			
			if (!no_peripheral_curves && !side_to_side)
				continue;
			
			if (adjacent == current)
				continue;  // This would be caught by the A == A' test later, but it's an easy catch here.
			
			int A = dir;  // These are the 'opposite vertices'.
			int A_prime = current_to_adjacent[A];
			
			// Now we check A != A'. If this is the case then we have sufficent grounds to foldoff this way.
			tetra *walking = current;
			
			// Let's label the vertices of the current triangle.
			int curr_a = A, curr_b = (curr_a == 0) ? 1 : 0, curr_c = 3 - curr_a - curr_b;
			
			// Walk around the edge between A and vertex 3.
			do
			{
				perm current_gluing = walking->get_gluing(curr_b);
				
				walking = walking->get_gluedto(curr_b);
				
				curr_a = current_gluing[curr_a];
				curr_b = current_gluing[curr_b];
				curr_c = current_gluing[curr_c];
				
				curr_b ^= curr_c; curr_c ^= curr_b; curr_b ^= curr_c;  // Interchange curr_b & curr_c.
			} while (((walking != current) || (curr_a != A)) && ((walking != adjacent) || (curr_a != A_prime)));
			// Stop only when we get back to current or adjacent with the CORRECT orientation.
			
			if (walking == adjacent)  // A == A'. So give up and try a different direction.
				continue;
			
			// A != A'. All's good, we definitely can foldoff this way.
			
			// Better record the previous one so we can take a step forward once we delete current.
			tetra *prev = (current->get_prev() != adjacent) ? current->get_prev() : adjacent->get_prev();
			
			// Unglue pairs of corresponding faces (one pair from current & one pair from adjacent)
			// and glue them together to allow us to remove current and adjacent from the manifold,
			// reducing the number of free faces by 2.
			// Note: This process maintains the cappoff, so no recapping is required.
			for (int i = 0; i < 4; i++)
			{
				if (i == dir)
					continue;  // Now it would be a waste of time to even think about gluing that way.
				
				tetra *from = current->get_gluedto(i);
				tetra *to = adjacent->get_gluedto(current_to_adjacent[i]);
				int glueout = current->get_gluing(i)[i];
				
				perm from_to_current = (current->get_gluing(i)).inverse();
				perm adjacent_to_to = adjacent->get_gluing(current_to_adjacent[i]);
				perm composed = adjacent_to_to.of(current_to_adjacent.of(from_to_current));
				
				adjacent->ungluesym(current_to_adjacent[i]);
				current->ungluesym(i);
				
				from->gluesym(to, glueout, composed);
			}
			
			// Delete current & adjacent as they are now disconnected from the manifold.
			oneless(adjacent);
			oneless(current);
			
			current = prev;  // Take a step back.
			break;
		}
	}
	
	// The capoff has changed, so better re-find the capoff_tetra.
	for (capoff_tetra = first_tetra; capoff_tetra != NULL; capoff_tetra = capoff_tetra->get_next())
		if (capoff_tetra->get_category() == cap)
			break;
	
	return capoff_tetra;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xa8, %rsp
movq %rsi, %r14
movq %rdi, 0x18(%rsp)
leaq 0x98(%rsp), %r15
movq %r15, -0x10(%r15)
leaq 0x48ea(%rip), %rsi      # 0x17862
leaq 0x48ea(%rip), %rdx      # 0x17869
leaq 0x88(%rsp), %rbx
movq %rbx, %rdi
callq 0x892e
movq %rbx, %rdi
movl $0x2, %esi
callq 0x4229
movq 0x88(%rsp), %rdi
cmpq %r15, %rdi
je 0x12fb9
movq 0x98(%rsp), %rsi
incq %rsi
callq 0x3220
xorl %eax, %eax
testq %r14, %r14
je 0x1324d
movq 0x18(%rsp), %rcx
cmpq $0x0, 0x8(%rcx)
je 0x1324d
movaps 0x44d5(%rip), %xmm1     # 0x174b0
leaq 0x30(%r14), %rbp
leaq 0x94(%r14), %rax
xorl %ebx, %ebx
movq 0x10(%r14,%rbx,8), %r12
movq %rbx, %rcx
shlq $0x4, %rcx
movups (%rbp,%rcx), %xmm0
movaps %xmm0, 0x20(%rsp)
cmpl $0x0, 0x94(%r14,%rbx,4)
je 0x13017
cmpl $0x0, 0xa4(%r14,%rbx,4)
setne %cl
jmp 0x13019
xorl %ecx, %ecx
leaq 0x94(%r12), %rdx
movb $0x1, %sil
xorl %r8d, %r8d
movb $0x1, %dil
shlq $0x4, %r8
leaq (%rax,%r8), %r9
addq %rdx, %r8
xorl %r10d, %r10d
movslq 0x20(%rsp,%r10,4), %r11
movl (%r9,%r10,4), %r15d
movl (%r8,%r11,4), %r11d
movl %r15d, %r13d
orl %r11d, %r13d
sete %r13b
andb %r13b, %dil
addl %r11d, %r15d
sete %r11b
andb %r11b, %cl
incq %r10
cmpq $0x3, %r10
jne 0x13038
movl $0x1, %r8d
testb $0x1, %sil
movl $0x0, %esi
jne 0x1302a
testb %dil, %dil
je 0x13082
cmpq %r14, %r12
jne 0x1308c
jmp 0x130ee
cmpq %r14, %r12
setne %dl
testb %dl, %cl
je 0x130ee
movl 0x20(%rsp,%rbx,4), %ecx
xorl %esi, %esi
testq %rbx, %rbx
sete %sil
movl $0x3, %r8d
subl %esi, %r8d
subl %ebx, %r8d
movq %r14, %rdx
movl %ebx, %edi
movslq %esi, %rsi
movq %rsi, %r9
shlq $0x4, %r9
movups 0x30(%rdx,%r9), %xmm0
movaps %xmm0, (%rsp)
movq 0x10(%rdx,%rsi,8), %rdx
movslq %edi, %rdi
movl (%rsp,%rdi,4), %edi
movslq %r8d, %r9
movl (%rsp,%rsi,4), %r8d
movl (%rsp,%r9,4), %esi
cmpq %r14, %rdx
jne 0x130de
cmpq %rdi, %rbx
je 0x130e9
cmpq %r12, %rdx
jne 0x130aa
cmpl %ecx, %edi
jne 0x130aa
jmp 0x130ee
cmpq %r12, %r14
jne 0x13100
incq %rbx
cmpq $0x3, %rbx
jne 0x12fe8
jmp 0x1322a
movq 0x8(%r14), %rax
cmpq %r12, %rax
jne 0x1310e
movq 0x8(%r12), %rax
movq %rax, 0x40(%rsp)
leaq 0x30(%r12), %rax
movq %rax, 0x48(%rsp)
xorl %r13d, %r13d
cmpq %rbx, %r13
je 0x131f6
movq 0x10(%r14,%r13,8), %rax
movq %rax, 0x58(%rsp)
movslq 0x20(%rsp,%r13,4), %rsi
movq 0x10(%r12,%rsi,8), %rax
movq %rax, 0x50(%rsp)
movq %r13, %rax
shlq $0x4, %rax
movups (%rbp,%rax), %xmm0
movaps %xmm0, (%rsp)
movl (%rsp,%r13,4), %r15d
movups (%rbp,%rax), %xmm0
movaps %xmm0, 0x30(%rsp)
movaps %xmm1, (%rsp)
xorl %eax, %eax
movslq 0x30(%rsp,%rax,4), %rcx
movl %eax, (%rsp,%rcx,4)
incq %rax
cmpq $0x4, %rax
jne 0x13166
movq %rsi, %rax
shlq $0x4, %rax
movq 0x48(%rsp), %rcx
movups (%rcx,%rax), %xmm0
movaps %xmm0, 0x30(%rsp)
movaps %xmm1, 0x60(%rsp)
xorl %eax, %eax
movslq (%rsp,%rax,4), %rcx
movl 0x20(%rsp,%rcx,4), %ecx
movl %ecx, 0x60(%rsp,%rax,4)
incq %rax
cmpq $0x4, %rax
jne 0x13193
movaps %xmm1, 0x70(%rsp)
xorl %eax, %eax
movslq 0x60(%rsp,%rax,4), %rcx
movl 0x30(%rsp,%rcx,4), %ecx
movl %ecx, 0x70(%rsp,%rax,4)
incq %rax
cmpq $0x4, %rax
jne 0x131af
movq %r12, %rdi
callq 0xe13e
movq %r14, %rdi
movl %r13d, %esi
callq 0xe13e
movq 0x58(%rsp), %rdi
movq 0x50(%rsp), %rsi
movl %r15d, %edx
leaq 0x70(%rsp), %rcx
callq 0xe04e
movaps 0x42ba(%rip), %xmm1     # 0x174b0
incq %r13
cmpq $0x4, %r13
jne 0x13120
movq 0x18(%rsp), %rbx
movq %rbx, %rdi
movq %r12, %rsi
callq 0x11992
movq %rbx, %rdi
movq %r14, %rsi
callq 0x11992
movaps 0x428b(%rip), %xmm1     # 0x174b0
movq 0x40(%rsp), %r14
movq (%r14), %r14
testq %r14, %r14
jne 0x12fdb
movq 0x18(%rsp), %rax
movq (%rax), %rax
testq %rax, %rax
je 0x1324b
cmpl $0x3, 0x70(%rax)
jne 0x1323b
jmp 0x1324d
xorl %eax, %eax
addq $0xa8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, %rbx
movq 0x88(%rsp), %rdi
cmpq %r15, %rdi
je 0x1327f
movq 0x98(%rsp), %rsi
incq %rsi
callq 0x3220
movq %rbx, %rdi
callq 0x33e0
nop