#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;
#define MAXN 65536
vector<int> g[MAXN];
int visited[MAXN], vIdx, back[MAXN], depth[MAXN];
int cutPoint[MAXN];
void tarjan(int u, int p, int root) {
back[u] = depth[u] = ++vIdx;
visited[u] = 1;
int son = 0;
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (!visited[v]) {
tarjan(v, u, root);
back[u] = min(back[u], back[v]);
son++;
if ((u == root && son > 1) || (u != root && back[v] >= depth[u]))
cutPoint[u]++;
} else if (v != p) {
back[u] = min(back[u], depth[v]);
}
}
}
set<int> adjCutPt;
int comSize = 0;
void dfs(int u) {
visited[u] = 1, comSize++;
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (cutPoint[v]) adjCutPt.insert(v);
if (cutPoint[v] || visited[v])
continue;
dfs(v);
}
}
int main() {
int n, m, x, y;
int cases = 0;
while(scanf("%d", &m) == 1 && m) {
int used[MAXN] = {}, usedn = 0;
for (int i = 0; i < MAXN; i++)
g[i].clear();
n = 0;
for (int i = 0; i < m; i++) {
scanf("%d %d", &x, &y);
n = max(n, max(x, y));
x--, y--;
used[x] = used[y] = 1;
g[x].push_back(y);
g[y].push_back(x);
}
for (int i = 0; i < n; i++)
usedn += used[i];
vIdx = 0;
for (int i = 0; i < n; i++)
visited[i] = cutPoint[i] = 0;
for (int i = 0; i < n; i++) {
if (!visited[i] && used[i])
tarjan(i, -1, i);
}
vector<int> D;
for (int i = 0; i < n; i++)
visited[i] = 0;
for (int i = 0; i < n; i++) {
if (!visited[i] && !cutPoint[i]) {
comSize = 0, adjCutPt.clear();
dfs(i);
if (adjCutPt.size() == 1)
D.push_back(comSize);
}
}
long long ways = 1, mn = D.size();
for (int i = 0; i < D.size(); i++)
ways *= D[i];
if (D.size() == 0) ways = (long long) usedn * (usedn-1) / 2, mn = 2;
printf("Case %d: %lld %lld\n", ++cases, mn, ways);
}
return 0;
}