// File: /workspace/github_cpp_makefile_star2_O2/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, 0x10(%rsp)
leaq 0x2f5e(%rip), %rsi      # 0x117ba
leaq 0x58(%rsp), %rbx
leaq 0x28(%rsp), %rdx
movq %rbx, %rdi
callq 0x3e82
pushq $0x2
popq %rsi
movq %rbx, %rdi
callq 0x3d1a
leaq 0x58(%rsp), %rdi
callq 0x3458
xorl %eax, %eax
testq %r14, %r14
je 0xeb36
movq 0x10(%rsp), %rcx
cmpq $0x0, 0x8(%rcx)
je 0xeb36
movq %r14, %rbx
testq %rbx, %rbx
je 0xeb1f
leaq 0x94(%rbx), %r15
xorl %r14d, %r14d
movq %r15, 0x18(%rsp)
cmpq $0x3, %r14
je 0xeb17
movq 0x10(%rbx,%r14,8), %rax
movq %rax, (%rsp)
leaq 0x28(%rsp), %rdi
movq %rbx, %rsi
movl %r14d, %edx
callq 0xf05e
cmpl $0x0, 0x94(%rbx,%r14,4)
je 0xe8f2
cmpl $0x0, 0x10(%r15,%r14,4)
setne %al
jmp 0xe8f4
xorl %eax, %eax
movq (%rsp), %rcx
leaq 0x94(%rcx), %rdx
movb $0x1, %cl
movq %r15, %rsi
xorl %edi, %edi
cmpq $0x2, %rdi
je 0xe951
xorl %r8d, %r8d
cmpq $0x3, %r8
je 0xe948
movslq 0x28(%rsp,%r8,4), %r9
movl (%rsi,%r8,4), %r10d
movq %rdi, %r11
shlq $0x4, %r11
addq %rdx, %r11
movl (%r11,%r9,4), %r9d
movl %r10d, %r11d
orl %r9d, %r11d
sete %r11b
andb %r11b, %cl
addl %r9d, %r10d
sete %r9b
andb %r9b, %al
incq %r8
jmp 0xe90f
incq %rdi
addq $0x10, %rsi
jmp 0xe906
movq %r14, 0x8(%rsp)
testb $0x1, %cl
je 0xe963
cmpq %rbx, (%rsp)
jne 0xe96e
jmp 0xe9df
cmpq %rbx, (%rsp)
setne %cl
testb %cl, %al
je 0xe9df
movq 0x8(%rsp), %rbp
movl 0x28(%rsp,%rbp,4), %r12d
xorl %r15d, %r15d
testq %rbp, %rbp
sete %r15b
pushq $0x3
popq %rax
subl %r15d, %eax
subl %ebp, %eax
movq %rbx, %r13
movq %rbx, %r14
movl %eax, %ebx
leaq 0x38(%rsp), %rdi
movq %r13, %rsi
movl %r15d, %edx
callq 0xf05e
movslq %r15d, %rax
movq 0x10(%r13,%rax,8), %r13
movslq %ebp, %rcx
movl 0x38(%rsp,%rcx,4), %ebp
movl 0x38(%rsp,%rax,4), %eax
movslq %ebx, %rcx
movq %r14, %rbx
movl 0x38(%rsp,%rcx,4), %r15d
cmpq %r14, %r13
jne 0xe9cc
cmpq %rbp, 0x8(%rsp)
je 0xe9d9
cmpq (%rsp), %r13
jne 0xe98d
cmpl %r12d, %ebp
jne 0xe98d
jmp 0xe9df
cmpq (%rsp), %rbx
jne 0xe9f1
movq 0x8(%rsp), %r14
incq %r14
movq 0x18(%rsp), %r15
jmp 0xe8b9
movq 0x8(%rbx), %rax
cmpq (%rsp), %rax
jne 0xea03
movq (%rsp), %rax
movq 0x8(%rax), %rax
movq %rax, 0x48(%rsp)
xorl %r13d, %r13d
movq 0x8(%rsp), %r12
cmpq $0x4, %r13
je 0xeaf3
cmpq %r13, %r12
je 0xeaeb
movq 0x10(%rbx,%r13,8), %rax
movq %rax, 0x18(%rsp)
movslq 0x28(%rsp,%r13,4), %rax
movq (%rsp), %r15
movq 0x10(%r15,%rax,8), %rax
movq %rax, 0x50(%rsp)
leaq 0x38(%rsp), %r14
movq %r14, %rdi
movq %rbx, %rsi
movl %r13d, %edx
callq 0xf05e
movl 0x38(%rsp,%r13,4), %eax
movl %eax, 0x24(%rsp)
leaq 0x98(%rsp), %r12
movq %r12, %rdi
movq %rbx, %rsi
movl %r13d, %edx
callq 0xf05e
movq %r14, %rdi
movq %r12, %rsi
callq 0xa396
movl 0x28(%rsp,%r13,4), %edx
movq %r12, %rdi
movq %r15, %rsi
callq 0xf05e
leaq 0x78(%rsp), %rbp
movq %rbp, %rdi
leaq 0x28(%rsp), %rsi
movq %r14, %rdx
callq 0xa3c4
leaq 0x88(%rsp), %r14
movq %r14, %rdi
movq %r12, %rsi
movq %rbp, %rdx
movq 0x8(%rsp), %r12
callq 0xa3c4
movl 0x28(%rsp,%r13,4), %esi
movq %r15, %rdi
callq 0xa698
movq %rbx, %rdi
movl %r13d, %esi
callq 0xa698
movq 0x18(%rsp), %rdi
movq 0x50(%rsp), %rsi
movl 0x24(%rsp), %edx
movq %r14, %rcx
callq 0xa5d2
incq %r13
jmp 0xea10
movq %rbx, %r14
movq 0x10(%rsp), %rbx
movq %rbx, %rdi
movq (%rsp), %rsi
callq 0xd83a
movq %rbx, %rdi
movq %r14, %rsi
callq 0xd83a
movq 0x48(%rsp), %rbx
movq (%rbx), %rbx
jmp 0xe8a1
movq 0x10(%rsp), %rax
movq (%rax), %rax
testq %rax, %rax
je 0xeb34
cmpl $0x3, 0x70(%rax)
jne 0xeb24
jmp 0xeb36
xorl %eax, %eax
addq $0xa8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, %rbx
leaq 0x58(%rsp), %rdi
callq 0x3458
movq %rbx, %rdi
callq 0x33d0
nop