ContainmentResult  TestCylinderCylinder ( Cylinder const & A,
                                          Cylinder const & B )
{
	Vector delta = B.getBase() - A.getBase();
	delta.y = 0;

	real dist = delta.magnitude();

	Range AD( dist - A.getRadius(), dist + A.getRadius() );
	Range BD( -B.getRadius(), B.getRadius() );

	ContainmentResult hTest = Containment1d::TestRangeRange( AD, BD );
	ContainmentResult vTest = Containment1d::TestRangeRange( A.getRangeY(), B.getRangeY() );

	// ----------

	return Containment::ComposeAxisTests(hTest,vTest);
}
ContainmentResult     TestCylinderABox ( Cylinder const & C,
									 	 AxialBox const & B )
{
	ContainmentResult test2d = Containment2d::TestCircleABox(C.getBaseCircle(),B);

	ContainmentResult testY = Containment1d::TestRangeRange( C.getRangeY(), B.getRangeY() );

	return Containment::ComposeAxisTests( test2d, testY );
}
ContainmentResult  TestPointCylinder    ( Vector const & V,
                                          Cylinder const & C )
{
	Vector delta = V - C.getBase();
	delta.y = 0;

	real dist = delta.magnitude();
	real radius = C.getRadius();

	ContainmentResult rTest = Containment1d::TestFloatLess(dist,radius);
	ContainmentResult vTest = Containment1d::TestFloatRange(V.y, C.getRangeY());

	// ----------

	return Containment::ComposeAxisTests(rTest,vTest);
}
ContainmentResult  TestSphereCylinder ( Sphere const & S,
									    Cylinder const & C )
{
	Vector delta = C.getBase() - S.getCenter();
	delta.y = 0;

	real dist = delta.magnitude();

	Range SD( dist - S.getRadius(), dist + S.getRadius() );

	Range CD( -C.getRadius(), C.getRadius() );

	ContainmentResult hTest = Containment1d::TestRangeRange( SD, CD );
	ContainmentResult hTest2 = Containment1d::TestFloatRange( dist, CD );

	ContainmentResult vTest = Containment1d::TestRangeRange( S.getRangeY(), C.getRangeY() );
	ContainmentResult cTest = Containment1d::TestFloatRange( S.getCenter().y, C.getRangeY() );

	// ----------

	if(hTest == CR_Outside)
	{
		// Sphere can't possibly touch the cylinder

		return CR_Outside;
	}
	else if((hTest == CR_TouchingOutside) || (hTest == CR_Boundary))
	{
		// Sphere is touching the outside of the cylinder's tube if its
		// center is inside the vertical range of the cylinder

		if((cTest == CR_Inside) || (cTest == CR_Boundary))
		{
			return CR_TouchingOutside;
		}
		else
		{
			return CR_Outside;
		}
	}
	else if((hTest == CR_Inside) || (hTest == CR_TouchingInside))
	{
		// Sphere is in the tube of the cylinder. It touches the cylinder
		// if its vertical range touches the vertical range of the cylinder

		return Containment::ComposeAxisTests(hTest,vTest);
	}
	else
	{
		// hTest == CR_Overlap

		if(vTest == CR_Outside)
		{
			return CR_Outside;
		}
		else if((vTest == CR_Inside) || (vTest == CR_TouchingInside))
		{
			return CR_Overlap;
		}
		else if (vTest == CR_Boundary)
		{
			// This really shouldn't be happening
			return CR_Boundary;
		}
		else if (vTest == CR_TouchingOutside)
		{
			if(hTest2 == CR_Inside)
			{
				// Sphere is touching a cap of the cylinder
				return CR_TouchingOutside;
			}
			else if(hTest2 == CR_Boundary)
			{
				// Sphere is touching the edge of the cap of the cylinder
				return CR_TouchingOutside;
			}
			else
			{
				// Sphere isn't touching the cap
				return CR_Outside;
			}
		}
		else
		{
			// vTest == CR_Overlap

			if((cTest == CR_Inside) || (cTest == CR_Boundary))
			{
				// The ranges overlap vertically and horizontally, and the center of
				// the sphere is inside the vertical range - the sphere overlaps the
				// cylinder

				return CR_Overlap;
			}
			else
			{
				// The sphere is inside both ranges, but its center is outside both
				// ranges. The sphere overlaps the cylinder if the closest point
				// on the cylinder is inside the sphere.

				Vector closestPoint = Distance3d::ClosestPointCylinder(S.getCenter(),C);

				ContainmentResult result = TestPointSphere(closestPoint,S);

				if(result == CR_Outside)
				{
					return CR_Outside;
				}
				else if(result == CR_Boundary)
				{
					return CR_TouchingOutside;
				}
				else
				{
					return CR_Overlap;
				}
			}
		}
	}
}