using System;
public class UpperTangentFinder
{
static int Quad(int x, int y)
{
if (x >= 0 && y >= 0) return 1;
if (x <= 0 && y >= 0) return 2;
if (x <= 0 && y <= 0) return 3;
return 4;
}
static int Orientation(int[] a, int[] b, int[] c)
{
int res = (b[1] - a[1]) * (c[0] - b[0]) -
(c[1] - b[1]) * (b[0] - a[0]);
if (res == 0) return 0;
return res > 0 ? 1 : -1;
}
static bool Compare(int[] p1, int[] p2, int[] mid)
{
int[] p = { p1[0] - mid[0], p1[1] - mid[1] };
int[] q = { p2[0] - mid[0], p2[1] - mid[1] };
int quadP = Quad(p[0], p[1]);
int quadQ = Quad(q[0], q[1]);
if (quadP != quadQ)
return quadP < quadQ;
return (p[1] * q[0]) < (q[1] * p[0]);
}
static int[,] SortPoints(int[,] polygon)
{
int n = polygon.GetLength(0);
int[] mid = { 0, 0 };
for (int i = 0; i < n; i++)
{
mid[0] += polygon[i, 0];
mid[1] += polygon[i, 1];
polygon[i, 0] *= n;
polygon[i, 1] *= n;
}
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
int[] p1 = { polygon[i, 0], polygon[i, 1] };
int[] p2 = { polygon[j, 0], polygon[j, 1] };
if (!Compare(p1, p2, mid))
{
int tempX = polygon[i, 0], tempY = polygon[i, 1];
polygon[i, 0] = polygon[j, 0];
polygon[i, 1] = polygon[j, 1];
polygon[j, 0] = tempX;
polygon[j, 1] = tempY;
}
}
}
for (int i = 0; i < n; i++)
{
polygon[i, 0] /= n;
polygon[i, 1] /= n;
}
return polygon;
}
static int[,] FindUpperTangent(int[,] a, int[,] b)
{
int n1 = a.GetLength(0);
int n2 = b.GetLength(0);
int maxa = int.MinValue;
for (int i = 0; i < n1; i++)
maxa = Math.Max(maxa, a[i, 0]);
int minb = int.MaxValue;
for (int i = 0; i < n2; i++)
minb = Math.Min(minb, b[i, 0]);
a = SortPoints(a);
b = SortPoints(b);
if (minb < maxa)
{
int[,] temp = a;
a = b;
b = temp;
n1 = a.GetLength(0);
n2 = b.GetLength(0);
}
int ia = 0, ib = 0;
for (int i = 1; i < n1; i++)
if (a[i, 0] > a[ia, 0])
ia = i;
for (int i = 1; i < n2; i++)
if (b[i, 0] < b[ib, 0])
ib = i;
int inda = ia, indb = ib;
bool done = false;
while (!done)
{
done = true;
while (Orientation(
new int[] { b[indb, 0], b[indb, 1] },
new int[] { a[inda, 0], a[inda, 1] },
new int[] { a[(inda + 1) % n1, 0],
a[(inda + 1) % n1, 1] }) > 0){
inda = (inda + 1) % n1;
}
while (Orientation(
new int[] { a[inda, 0], a[inda, 1] },
new int[] { b[indb, 0], b[indb, 1] },
new int[] { b[(n2 + indb - 1) % n2, 0],
b[(n2 + indb - 1) % n2, 1] }) < 0){
indb = (n2 + indb - 1) % n2;
done = false;
}
}
int[,] result = new int[2, 2];
result[0, 0] = a[inda, 0];
result[0, 1] = a[inda, 1];
result[1, 0] = b[indb, 0];
result[1, 1] = b[indb, 1];
return result;
}
public static void Main(string[] args)
{
int[,] a = new int[,] {
{2, 2},
{3, 1},
{3, 3},
{5, 2},
{4, 0}
};
int[,] b = new int[,] {
{0, 1},
{1, 0},
{0, -2},
{-1, 0}
};
int[,] tangent = FindUpperTangent(a, b);
for (int i = 0; i < 2; i++)
{
Console.WriteLine(tangent[i, 0] + " " + tangent[i, 1]);
}
}
}