// File: /workspace/github_cpp_makefile_star2_O1/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, %r15
movq %rdi, 0x10(%rsp)
leaq 0x78(%rsp), %r14
movq %r14, -0x10(%r14)
leaq 0x49b9(%rip), %rsi      # 0x17822
leaq 0x49b9(%rip), %rdx      # 0x17829
leaq 0x68(%rsp), %rbx
movq %rbx, %rdi
callq 0x86fe
movq %rbx, %rdi
movl $0x2, %esi
callq 0x4209
movq 0x68(%rsp), %rdi
cmpq %r14, %rdi
je 0x12ea1
movq 0x78(%rsp), %rsi
incq %rsi
callq 0x3220
xorl %eax, %eax
testq %r15, %r15
je 0x13185
movq 0x10(%rsp), %rcx
cmpq $0x0, 0x8(%rcx)
je 0x13185
xorl %r14d, %r14d
xorl %r12d, %r12d
movq 0x10(%r15,%r12,8), %rdx
movq %r12, %rax
shlq $0x4, %rax
movups 0x30(%r15,%rax), %xmm0
movaps %xmm0, 0x20(%rsp)
leaq 0x94(%r15), %rax
cmpl $0x0, 0x94(%r15,%r12,4)
je 0x12ef6
cmpl $0x0, 0x10(%rax,%r12,4)
setne %cl
jmp 0x12ef8
xorl %ecx, %ecx
leaq 0x30(%r15), %rsi
movq %rsi, 0x58(%rsp)
movq %rdx, 0x18(%rsp)
addq $0x94, %rdx
movb $0x1, %sil
xorl %edi, %edi
movq %rdi, %r8
shlq $0x4, %r8
addq %rdx, %r8
xorl %r9d, %r9d
movslq 0x20(%rsp,%r9,4), %r10
movl (%rax,%r9,4), %r11d
testl %r11d, %r11d
jne 0x12f34
cmpl $0x0, (%r8,%r10,4)
je 0x12f36
xorl %esi, %esi
addl (%r8,%r10,4), %r11d
movzbl %cl, %ecx
cmovnel %r14d, %ecx
incq %r9
cmpq $0x3, %r9
jne 0x12f1f
leaq 0x1(%rdi), %r8
addq $0x10, %rax
testq %rdi, %rdi
movq %r8, %rdi
je 0x12f12
testb $0x1, %sil
je 0x12f71
movq 0x18(%rsp), %r9
cmpq %r15, %r9
jne 0x12f8a
movb $0x1, %al
jmp 0x1314c
movb $0x1, %al
testb $0x1, %cl
movq 0x18(%rsp), %r9
je 0x1314c
cmpq %r15, %r9
je 0x1314c
movl 0x20(%rsp,%r12,4), %eax
xorl %edx, %edx
testq %r12, %r12
sete %dl
movl $0x3, %edi
subl %edx, %edi
subl %r12d, %edi
movq %r15, %rcx
movl %r12d, %esi
movslq %edx, %rdx
movq %rdx, %r8
shlq $0x4, %r8
movups 0x30(%rcx,%r8), %xmm0
movaps %xmm0, (%rsp)
movq 0x10(%rcx,%rdx,8), %rcx
movslq %esi, %rsi
movl (%rsp,%rsi,4), %esi
movslq %edi, %r8
movl (%rsp,%rdx,4), %edi
movl (%rsp,%r8,4), %edx
cmpq %r15, %rcx
jne 0x12fda
cmpq %rsi, %r12
je 0x12fe3
cmpq %r9, %rcx
jne 0x12fa7
cmpl %eax, %esi
jne 0x12fa7
cmpq %r9, %rcx
sete %al
je 0x1314c
movq 0x8(%r15), %rax
cmpq %r9, %rax
jne 0x12ffc
movq 0x8(%r9), %rax
movq %rax, 0x40(%rsp)
leaq 0x30(%r9), %rax
movq %rax, 0x48(%rsp)
xorl %r13d, %r13d
movq %r12, 0x50(%rsp)
cmpq %r12, %r13
je 0x1311d
movq %r15, %rbp
movq 0x10(%r15,%r13,8), %rax
movq %rax, 0x60(%rsp)
movslq 0x20(%rsp,%r13,4), %rsi
movq 0x10(%r9,%rsi,8), %rbx
movq %r13, %rax
shlq $0x4, %rax
movq 0x58(%rsp), %rcx
movups (%rcx,%rax), %xmm0
movaps %xmm0, (%rsp)
movl (%rsp,%r13,4), %r12d
movups (%rcx,%rax), %xmm0
movaps %xmm0, 0x30(%rsp)
xorl %eax, %eax
movl %eax, (%rsp,%rax,4)
incq %rax
cmpq $0x4, %rax
jne 0x13055
xorl %eax, %eax
movslq 0x30(%rsp,%rax,4), %rcx
movl %eax, (%rsp,%rcx,4)
incq %rax
cmpq $0x4, %rax
jne 0x13063
movq %rsi, %rax
shlq $0x4, %rax
movq 0x48(%rsp), %rcx
movups (%rcx,%rax), %xmm0
movaps %xmm0, 0x30(%rsp)
xorl %eax, %eax
movl %eax, 0x88(%rsp,%rax,4)
incq %rax
cmpq $0x4, %rax
jne 0x1308b
xorl %eax, %eax
movslq (%rsp,%rax,4), %rcx
movl 0x20(%rsp,%rcx,4), %ecx
movl %ecx, 0x88(%rsp,%rax,4)
incq %rax
cmpq $0x4, %rax
jne 0x1309d
xorl %eax, %eax
movl %eax, 0x98(%rsp,%rax,4)
incq %rax
cmpq $0x4, %rax
jne 0x130b7
xorl %eax, %eax
movslq 0x88(%rsp,%rax,4), %rcx
movl 0x30(%rsp,%rcx,4), %ecx
movl %ecx, 0x98(%rsp,%rax,4)
incq %rax
cmpq $0x4, %rax
jne 0x130c9
movq %r9, %rdi
callq 0xdf6a
movq %rbp, %rdi
movl %r13d, %esi
callq 0xdf6a
movq 0x60(%rsp), %rdi
movq %rbx, %rsi
movl %r12d, %edx
movq %rbp, %r15
leaq 0x98(%rsp), %rcx
callq 0xde78
movq 0x18(%rsp), %r9
movq 0x50(%rsp), %r12
incq %r13
cmpq $0x4, %r13
jne 0x13012
movq 0x10(%rsp), %rbx
movq %rbx, %rdi
movq %r9, %rsi
callq 0x1187a
movq %rbx, %rdi
movq %r15, %rsi
callq 0x1187a
xorl %eax, %eax
movq 0x40(%rsp), %r15
cmpq $0x2, %r12
leaq 0x1(%r12), %r12
setb %dl
testb %dl, %al
jne 0x12ec2
movq (%r15), %r15
movl $0x0, %r12d
testq %r15, %r15
jne 0x12ec2
movq 0x10(%rsp), %rax
movq (%rax), %rax
testq %rax, %rax
je 0x13185
cmpl $0x3, 0x70(%rax)
jne 0x13177
addq $0xa8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, %rbx
movq 0x68(%rsp), %rdi
cmpq %r14, %rdi
je 0x131b1
movq 0x78(%rsp), %rsi
incq %rsi
callq 0x3220
movq %rbx, %rdi
callq 0x33e0
nop