
// Given an edge e and Minkowski factor f of a polygon poly, determine whether e determines an edge of f.
function IsEdgeInducedByFactor(e,f,poly)

	// We consider two cases, depending on the dimension of f. If f is a segment
	// we check whether e is parallel to f.
	if Dimension(f) eq 1 then
			E := Explode(Edges(f));
			if AreProportional(Vertices(E)[1] - Vertices(E)[2], Vertices(e)[1] - Vertices(e)[2]) or
			   AreProportional(Vertices(E)[1] - Vertices(E)[2], Vertices(e)[2] - Vertices(e)[1]) then
				return true;
			end if;
	end if;

	// If f is two dimensional, then check whether the normal vector of e in poly is a ray generator
	// of the normal fan of f.
	if Dimension(f) eq 2 then
		if Explode(Rays(NormalCone(poly,e))) in Rays(NormalFan(f)) then
			return true;
		end if;
	end if;

	return false;
end function;

// Increment the index vector, incrementing the first entry less than its possible maximum
// and resetting all smaller values. Example: sizes = [2,3] generates the following sequence:
// [1,1], [2,1], [1,2], [2,2], [1,3], [2,3].
function increment_index_vector(index_vector,sizes)

for pos in [1..#sizes] do
     if index_vector[pos] lt sizes[pos] then
     	index_vector[pos] := index_vector[pos]+1;
		for j in [1..pos-1] do
			index_vector[j] := 1;
		end for;
       	 break;
    	end if;
	end for;

	return index_vector;
end function;

function GetOrientation(edge,face)

	// Put 'face' in a 2D lattice. This implicitly fixes an orientation of 'face'.
	flat_face,emb,shift := PolyhedronInSublattice(face);
	flat_edge := Preimage(emb,edge-shift);

	// We construct an interior point of 'flat_face' which we treat as the origin
	// to construct an orientation of its boundary
	interior_point := (Vertices(flat_face)[1] + Vertices(flat_face)[2] + Vertices(flat_face)[3])/3;

	// Return the sign of the determinant of the 2x2 matrix formed by the vertices of 'edge'
	// after projection to the minimal sublattice containing 'face'.
	return Sign(Determinant(Matrix(Rationals(),2,2,[Eltseq(Vertices(flat_edge - interior_point)[i]) : i in [1,2]])));
end function;

// Return the dual face of a reflexive polytope.
function DualFace(P,face)
	nc := NormalCone(P,face);
	return Polytope(RGenerators(nc));
end function;

// Start here

QQ := Rationals();
interval := Polytope([[-1],[1]]);
polygon4 := Polytope([[-1,0],[0,1],[1,0],[0,-1]]);
polygon5 := Polytope([[-1,-1],[-1,0],[0,1],[1,0],[0,-1]]);
polygon6 := Polytope([[1,0],[1,1],[0,1],[-1,0],[-1,-1],[0,-1]]);
polygon7 := Polytope([[-1,-1],[1,-1],[-1,1],[1,0],[0,1]]);
polygon8 := Polytope([[-1,-1],[1,-1],[-1,1],[1,1]]);
polygon9 := Polytope([[-1,-1],[2,-1],[-1,2]]);

P := polygon6*polygon6;

// Cache all the dual face information.
DualFaces := AssociativeArray();
for i in [1,2] do
	for face in Faces(P,i) do
		DualFaces[face] := DualFace(P,face);
	end for;
end for;

// A sequence of all all the Minkowski decompositions possible on each face.
all_decompositions := [[[D : D in M | Dimension(D) gt 0] : M in MinkowskiDecomposition((NumberOfPoints(DualFaces[F])-1)*F)] : F in Faces(P,2)];
sizes := [#M : M in all_decompositions];

// The Minkowski decompositions for each face are stored in index_vector.
index_vector := [1 : M in [1..#all_decompositions]];

// A fixed list of vertices for polygon6.
hex_vertices := [[1,0],[1,1],[0,1],[-1,0],[-1,-1],[0,-1]];

// Test sets of Minkowski decompositions.
interesting_values := [
[[1],[]]
];
results := [1 : i in [1..#interesting_values]];

for choice in interesting_values do
	index_vector := [1 : M in [1..#all_decompositions]];
	fail := false;

	for val_a in choice[1] do
		index_vector[Index(Faces(P,2),polygon6*Polytope([hex_vertices[val_a]]))] := 2;
	end for;
	for val_b in choice[2] do
		index_vector[Index(Faces(P,2),Polytope([hex_vertices[val_b]])*polygon6)] := 2;
	end for;

	index_vector;

	// The sequence of Minkowski decompositions determined by index_vector.
	decomps := [all_decompositions[i][index_vector[i]] : i in [1..#all_decompositions]];

	// Solutions of the linear conditions imposed on the slopes of the PL function are realised as 
	// the kernel of the matrix 'adjacency'.
	adjacency := Matrix(QQ,#Edges(P),0,[]);

	for i in [1..#Faces(P,2)] do

		// Store the face indexed by i
		face := Faces(P,2)[i];

		// adjacency has a block decomposition. Each block corresponds to a 2D face F of P.
		// Columns of each block are indexed by summands of F in the chosen decomposition.
		tall_block := ZeroMatrix(QQ,#Edges(P),#decomps[i]);

		// k indexes columns of tall_block, while j indexes rows.
	    for k in [1..#decomps[i]] do
		    for j in [1..#Edges(P)] do

		    	// Check two conditions:
		    	// 1) whether the edge E of P indexed by is contained in the edges of the face F indexed by i,
		    	// 2) if so, whether E is an edge of F corresponding to the Minkowski factor indexed by i and k.
				if Edges(P)[j] in Edges(face) and IsEdgeInducedByFactor(Edges(P)[j],decomps[i][k],face)  then
					tall_block[j,k] := GetOrientation(DualFaces[face],DualFaces[Edges(P)[j]]);
		   		end if;
			end for;
    	end for;

		adjacency := HorizontalJoin(adjacency,tall_block);
	end for;

	// We test the kernel of adjacency. Each vector in the kernel of adjacency corresponds to a tuple of slopes
	// for a PL function. To construct a strictly convex function we need to be able to choose different slopes
	// for the various Minkowski factors of a single 2D face of P.

	// 'position' records the location in each vector in the kernel corresponding to a 2D face F.
	position := 1;
	for i in [1..#Faces(P,2)] do

		// 'zeroes' records the pairs of Minkowski factors which have the same slope for every member of a basis
		// of the kernel of adjacency so far tested. We start assuming no pair of slopes can be seperated.
		zeroes := {1..#decomps[i]*(#decomps[i]-1)/2};

		// store the indices of the factors of F in 'indices'
		indices := [position..position+#decomps[i]-1];

		// Iterate over elements in a basis of the kernel of 'adjacency'.
		for b in Basis(Kernel(Transpose(adjacency))) do

			// Form the differences of slopes corresponding to various Minkowski factors of F. 
			seq := [Eltseq(b)[j] - Eltseq(b)[k] : j in indices, k in indices | j lt k];

			// Reduce the list of inseperable pairs of slopes using seq.
			zeroes := zeroes meet {j : j in [1..#seq] | seq[j] eq 0};
		end for;

		// If there is at least one pair of Minkowski factors for which slopes must coincide, record failure as a zero.
		if #zeroes ge 1 then
			fail := true;
		end if;

   		position := position + #decomps[i];
	end for;

	// If this choice of Minkowski decompositions was succesful then print the configuration of faces.
	if not fail then
		print choice, #choice[1], #choice[2];
	end if;
end for;