static Cell_handle findBestCell(const Vector_3 &direction,
		const Vertex_handle &infinity,
		const Cell_handle &startingCell)
{
	DEBUG_START;
	/* FIXME: Maybe some hint will be useful here */
	ASSERT(startingCell->has_vertex(infinity) && "Wrong input");
	Cell_handle bestCell = startingCell;
	Cell_handle nextCell = bestCell;
	double maxProduct = calculateProduct(direction, bestCell, infinity);
	unsigned numIterations = 0;
	do
	{
		++numIterations;
		bestCell = nextCell;
		ASSERT(bestCell->has_vertex(infinity) && "Wrong iteration");
		int infinityIndex = bestCell->index(infinity);
		for (int i = 0; i < NUM_CELL_VERTICES; ++i)
		{
			if (i == infinityIndex)
				continue;
			Cell_handle neighbor = bestCell->neighbor(i);
			double product = calculateProduct(direction,
					neighbor, infinity);
			if (product > maxProduct)
			{
				nextCell = neighbor;
				maxProduct = product;
			}
		}
	} while (nextCell != bestCell);
	ASSERT(bestCell->has_vertex(infinity) && "Wrong result");
	DEBUG_END;
	return bestCell;
}
void DualPolyhedron_3::associateVertex(const Vertex_handle &vertex)
{
	DEBUG_START;
	int iPlane = vertex->info();
	if (isOuterVertex(vertex))
	{
		Cell_handle cell;
		int iVertex, iInfinity;
		bool result = is_edge(vertex, infinite_vertex(), cell,
				iVertex, iInfinity);
		ASSERT(result && "Wrong set");
		
		TDelaunay_3::Edge edge(cell, iVertex, iInfinity);
		auto circulator = incident_cells(edge);
		auto end = circulator;
		do
		{
			Cell_handle currentCell = circulator;
			ASSERT(currentCell->has_vertex(vertex));
			ASSERT(currentCell->has_vertex(infinite_vertex()));
			currentCell->info().associations.insert(iPlane);
			items[iPlane].associations.insert(currentCell);
			items[iPlane].resolved = true;
			++circulator;
		} while (circulator != end);
	}
	else
	{
		Vector_3 direction = items[iPlane].direction;
		Cell_handle bestCell = findBestCell(direction,
				infinite_vertex(), infinite_cell());
		bestCell->info().associations.insert(iPlane);
		/* FIXME: Maybe the association for a plane is singular? */
		items[iPlane].associations.insert(bestCell);
	}
	DEBUG_END;
}
bool DualPolyhedron_3::lift(Cell_handle cell, unsigned iNearest)
{
	DEBUG_START;
	Vector_3 xOld = cell->info().point - CGAL::Origin();
	std::cout << "Old tangient point: " << xOld << std::endl;
	const auto activeGroup = calculateActiveGroup(cell, iNearest, items);
	if (activeGroup.empty())
	{
		DEBUG_END;
		return false;
	}

	Vector_3 xNew = leastSquaresPoint(activeGroup, items);
	std::cout << "New tangient point: " << xNew << std::endl;

	ASSERT(cell->has_vertex(infinite_vertex()));
	unsigned infinityIndex = cell->index(infinite_vertex());
	std::vector<Vertex_handle> vertices;
	Vertex_handle dominator;
	double alphaMax = 0.;
	double alphaMax2 = 0.;
	for (unsigned i = 0; i < NUM_CELL_VERTICES; ++i)
	{
		if (i == infinityIndex)
			continue;

		vertices.push_back(cell->vertex(i));

		Vertex_handle vertex = mirror_vertex(cell, i);
		Plane_3 plane = ::dual(vertex->point());
		double alpha;
	        bool succeeded;
		std::tie(succeeded, alpha) = calculateAlpha(xOld, xNew, plane);
		if (!succeeded)
			return false;
		std::cout << "Alpha #" << i << ": " << alpha << std::endl;
		ASSERT(alpha <= 1. && "Wrongly computed alpha");
		if (alpha > alphaMax)
		{
			alphaMax2 = alphaMax;
			alphaMax = alpha;
			dominator = vertex;
		}
	}
	
	std::cout << "Maximal alpha: " << alphaMax << std::endl;
	std::cout << "Maximal alpha 2nd: " << alphaMax2 << std::endl;
	ASSERT(alphaMax < 1. && "What to do then?");
	if (alphaMax > 0.)
	{
		std::cout << "Full move is impossible, performing partial move"
			<< std::endl;
		partiallyMove(xOld, xNew, vertices, dominator, alphaMax,
				alphaMax2);
	}
	else
	{
		std::cout << "Performing full move" << std::endl;
		if (!fullyMove(vertices, xNew, activeGroup))
		{
			DEBUG_END;
			return false;
		}
	}
	DEBUG_END;
	return true;
}