As you know, Bob's brother lives in Flatland. In Flatland there are n cities, connected by n - 1 two-way roads. The cities are numbered from 1 to n. You can get from one city to another moving along the roads.

The «Two Paths» company, where Bob's brother works, has won a tender to repair two paths in Flatland. A path is a sequence of different cities, connected sequentially by roads. The company is allowed to choose by itself the paths to repair. The only condition they have to meet is that the two paths shouldn't cross (i.e. shouldn't have common cities).

It is known that the profit, the «Two Paths» company will get, equals the product of the lengths of the two paths. Let's consider the length of each road equals 1, and the length of a path equals the amount of roads in it. Find the maximum possible profit for the company.

Input

The first line contains an integer n (2 ≤ n ≤ 200), where n is the amount of cities in the country. The following n - 1 lines contain the information about the roads. Each line contains a pair of numbers of the cities, connected by the road ai, bi (1 ≤ ai, bi ≤ n).

Output

Output the maximum possible profit.

Examples

Input

4
1 2
2 3
3 4


Output

1


Input

7
1 2
1 3
1 4
1 5
1 6
1 7


Output

0


Input

6
1 2
2 3
2 4
5 4
6 4


Output

4
#include <bits/stdc++.h>
using namespace std;
void init() {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);
}
int random(int min, int max) {
  static bool first = true;
  if (first) {
    srand(time(NULL));
    first = false;
  }
  return min + rand() % ((max + 1) - min);
}
class GraphAdj {
 public:
  int vertices;
  int edges;
  vector<int> *adjL;
  GraphAdj(int n) {
    this->vertices = n;
    this->edges = 0;
    this->adjL = new vector<int>[n];
  }
  GraphAdj(int n, int e) {
    this->vertices = n;
    this->edges = e;
    this->adjL = new vector<int>[n];
  }
  void addEdge(int x, int y) { adjL[x].push_back(y); }
  void printGraph() {
    for (int i = 0; i < vertices; ++i) {
      for (int x : adjL[i]) {
        cout << i + 1 << " " << x + 1 << '\n';
      }
    }
  }
  vector<vector<int>> bfs(int start) {
    vector<vector<int>> res;
    vector<bool> visited(vertices, 0);
    queue<int> q;
    visited[start] = 1;
    q.push(start);
    while (!q.empty()) {
      int count = q.size();
      vector<int> t;
      while (count--) {
        int top = q.front();
        q.pop();
        t.push_back(top);
        for (int x : adjL[top]) {
          if (!visited[x]) {
            visited[x] = 1;
            q.push(x);
          }
        }
      }
      res.push_back(t);
    }
    return res;
  }
  void dfsSource(int s, vector<bool> &visited, vector<int> &v) {
    visited[s] = 1;
    v.push_back(s);
    for (int x : adjL[s]) {
      if (!visited[x]) dfsSource(x, visited, v);
    }
  }
  void dfsAll(vector<int> &v) {
    vector<bool> visited(vertices, 0);
    for (int i = 0; i < vertices; ++i) {
      if (!visited[i]) dfsSource(i, visited, v);
    }
  }
  void dfsTimeStamps(int s, int &time, vector<int> &in, vector<int> &out,
                     vector<bool> &visited) {
    visited[s] = 1;
    in[s] = ++time;
    for (int x : adjL[s]) {
      if (!visited[x]) dfsTimeStamps(x, time, in, out, visited);
    }
    out[s] = ++time;
  }
  int findMotherVertex() {
    vector<int> v;
    vector<bool> visited(vertices, 0);
    int ans = 0;
    for (int i = 0; i < vertices; ++i) {
      if (!visited[i]) {
        dfsSource(i, visited, v);
        ans = i;
      }
    }
    v.clear();
    fill(visited.begin(), visited.end(), 0);
    dfsSource(ans, visited, v);
    for (int i = 0; i < vertices; ++i) {
      if (!visited[i]) return -1;
    }
    return ans;
  }
  int MinNumEdges_or_ShortPthBw2Vert(int src, int dest) {
    vector<bool> visited(vertices, 0);
    vector<int> dist(vertices, 0);
    queue<int> q;
    dist[src] = 0;
    visited[src] = 1;
    q.push(src);
    while (!q.empty()) {
      int top = q.front();
      q.pop();
      for (int x : adjL[top]) {
        if (!visited[x]) {
          dist[x] = dist[top] + 1;
          q.push(x);
          visited[x] = 1;
        }
      }
    }
    return dist[dest];
  }
  bool findDirCycleHelper(int s, vector<bool> &visited,
                          vector<bool> &recStack) {
    visited[s] = 1;
    recStack[s] = 1;
    for (int x : adjL[s]) {
      if ((!visited[x]) and findDirCycleHelper(x, visited, recStack)) {
        return 1;
      } else if (recStack[x]) {
        return 1;
      }
    }
    recStack[s] = 0;
    return 0;
  }
  bool findDirCycle() {
    vector<bool> visited(vertices, 0), recStack(vertices, 0);
    for (int i = 0; i < vertices; ++i)
      if (!visited[i] and findDirCycleHelper(i, visited, recStack)) return 1;
    return 0;
  }
  bool findUnDirCycleHelper(int s, vector<bool> &visited, int par) {
    visited[s] = 1;
    for (int x : adjL[s]) {
      if (visited[x] and x != par) {
        return 1;
      }
      if ((!visited[x]) and findUnDirCycleHelper(x, visited, s)) {
        return 1;
      }
    }
    return 0;
  }
  bool findUnDirCycle() {
    vector<bool> visited(vertices, 0);
    for (int i = 0; i < vertices; ++i) {
      if (!visited[i] and findUnDirCycleHelper(i, visited, -1)) {
        return 1;
      }
    }
    return 0;
  }
  void topSortHelper(int s, vector<bool> &visited, stack<int> &st) {
    visited[s] = 1;
    for (int x : adjL[s]) {
      if (!visited[x]) {
        topSortHelper(x, visited, st);
      }
    }
    st.push(s);
  }
  vector<int> topSort() {
    vector<bool> visited(vertices, 0);
    vector<int> v;
    stack<int> st;
    for (int i = 0; i < vertices; ++i) {
      if (!visited[i]) {
        topSortHelper(i, visited, st);
      }
    }
    while (!st.empty()) {
      v.push_back(st.top());
      st.pop();
    }
    return v;
  }
  int findUnDirCycleNLengthHelper(int s, int par, int n,
                                  vector<bool> &visited) {
    visited[s] = 1;
    int count = 0;
    if (n == 0) {
      visited[s] = 0;
      if (find(adjL[s].begin(), adjL[s].end(), par) != adjL[s].end()) {
        count++;
      }
      return count;
    }
    for (int x : adjL[s]) {
      if (!visited[x]) {
        count += findUnDirCycleNLengthHelper(x, par, n - 1, visited);
      }
    }
    visited[s] = 0;
    return count;
  }
  int findUnDirCycleNLength(int n) {
    vector<bool> visited(vertices, 0);
    int count = 0;
    for (int i = 0; i < vertices - (n - 1); ++i) {
      count += findUnDirCycleNLengthHelper(i, i, n - 1, visited);
      visited[i] = 1;
    }
    return count / 2;
  }
  pair<int, vector<int>> BestFirstSearch(int src, int dest,
                                         vector<vector<int>> &dist) {
    int cost = 0;
    vector<bool> visited(vertices, 0);
    priority_queue<pair<int, int>, vector<pair<int, int>>,
                   greater<pair<int, int>>>
        pq;
    pq.push({0, src});
    visited[src] = 1;
    vector<int> v;
    while (!pq.empty()) {
      pair<int, int> p = pq.top();
      pq.pop();
      cost += p.first;
      v.push_back(p.second);
      if (p.second == dest) break;
      for (int x : adjL[p.second]) {
        if (!visited[x]) {
          visited[x] = 1;
          pq.push({dist[p.second][x], x});
        }
      }
    }
    return {cost, v};
  }
  vector<int> KahnsTopSort() {
    vector<int> indegree(vertices, 0), res;
    for (int i = 0; i < vertices; ++i) {
      for (int x : adjL[i]) indegree[x]++;
    }
    queue<int> q;
    int count = 0;
    for (int i = 0; i < vertices; ++i) {
      if (indegree[i] == 0) {
        q.push(i);
      }
    }
    while (!q.empty()) {
      int top = q.front();
      q.pop();
      res.push_back(top);
      for (int x : adjL[top]) {
        if (--indegree[x] == 0) q.push(x);
      }
      count++;
    }
    if (count != vertices) {
      return {};
    }
    return res;
  }
  void AlienDictionary(vector<string> &words, int MaxChar) {
    GraphAdj g(MaxChar);
    int n = words.size();
    string word1, word2;
    for (int i = 0; i < n - 1; ++i) {
      word1 = words[i], word2 = words[i + 1];
      for (int j = 0; j < min(word1.size(), word2.size()); ++j) {
        if (word1[j] != word2[j]) {
          g.addEdge(word1[j] - 'a', word2[j] - 'a');
          break;
        }
      }
    }
    vector<int> v = g.topSort();
    for (int x : v) cout << (char)('a' + x) << " ";
  }
};
class GraphMat {
 public:
  int vertices;
  int edges;
  vector<vector<int>> adjMat;
  GraphMat(int n) {
    this->vertices = n;
    this->edges = 0;
    this->adjMat.resize(n, vector<int>(n, 0));
  }
  GraphMat(int n, int e) {
    this->vertices = n;
    this->edges = e;
    this->adjMat.resize(n, vector<int>(n, 0));
  }
  void addEdge(int x, int y) { this->adjMat[x][y] = 1; }
  void printGraph() {
    for (int i = 0; i < vertices; ++i) {
      for (int j = 0; j < vertices; ++j) {
        if (adjMat[i][j]) cout << i + 1 << " " << j + 1 << '\n';
      }
    }
  }
  vector<vector<int>> bfs(int start) {
    vector<vector<int>> res;
    vector<bool> visited(vertices, 0);
    queue<int> q;
    visited[start] = 1;
    q.push(start);
    while (!q.empty()) {
      int count = q.size();
      vector<int> t;
      while (count--) {
        int top = q.front();
        q.pop();
        t.push_back(top);
        for (int i = 0; i < adjMat[top].size(); ++i) {
          if (!visited[i] and adjMat[top][i]) {
            visited[i] = 1;
            q.push(i);
          }
        }
      }
      res.push_back(t);
    }
    return res;
  }
  void dfsSource(int s, vector<bool> &visited, vector<int> &v) {
    visited[s] = 1;
    v.push_back(s);
    for (int i = 0; i < vertices; ++i) {
      if (!visited[i] and adjMat[s][i]) dfsSource(i, visited, v);
    }
  }
  void dfsAll(vector<int> &v) {
    vector<bool> visited(vertices, 0);
    for (int i = 0; i < vertices; ++i) {
      if (!visited[i]) dfsSource(i, visited, v);
    }
  }
  int findMotherVertex() {
    vector<int> v;
    vector<bool> visited(vertices, 0);
    int ans = 0;
    for (int i = 0; i < vertices; ++i) {
      if (!visited[i]) {
        dfsSource(i, visited, v);
        ans = i;
      }
    }
    v.clear();
    fill(visited.begin(), visited.end(), 0);
    dfsSource(ans, visited, v);
    for (int i = 0; i < vertices; ++i) {
      if (!visited[i]) return -1;
    }
    return ans;
  }
  int MinNumEdges_or_ShortPthBw2Vert(int src, int dest) {
    vector<bool> visited(vertices, 0);
    vector<int> dist(vertices, 0);
    queue<int> q;
    dist[src] = 0;
    visited[src] = 1;
    q.push(src);
    while (!q.empty()) {
      int top = q.front();
      q.pop();
      for (int i = 0; i < adjMat[top].size(); ++i) {
        if (!visited[i] and adjMat[top][i]) {
          dist[i] = dist[top] + 1;
          q.push(i);
          visited[i] = 1;
        }
      }
    }
    return dist[dest];
  }
  bool findDirCycleHelper(int s, vector<bool> &visited,
                          vector<bool> &recStack) {
    visited[s] = 1;
    recStack[s] = 1;
    for (int x = 0; x < adjMat[s].size(); ++x) {
      if ((!visited[x] and adjMat[s][x]) and
          findDirCycleHelper(x, visited, recStack)) {
        return 1;
      } else if (adjMat[s][x] and recStack[x]) {
        return 1;
      }
    }
    recStack[s] = 0;
    return 0;
  }
  bool findDirCycle() {
    vector<bool> visited(vertices, 0), recStack(vertices, 0);
    for (int i = 0; i < vertices; ++i)
      if (!visited[i] and findDirCycleHelper(i, visited, recStack)) return 1;
    return 0;
  }
  bool findUnDirCycleHelper(int s, vector<bool> &visited, int par) {
    visited[s] = 1;
    for (int x = 0; x < adjMat[s].size(); ++x) {
      if (visited[x] and adjMat[s][x] and x != par) {
        return 1;
      }
      if ((!visited[x]) and adjMat[s][x] and
          findUnDirCycleHelper(x, visited, s)) {
        return 1;
      }
    }
    return 0;
  }
  bool findUnDirCycle() {
    vector<bool> visited(vertices, 0);
    for (int i = 0; i < vertices; ++i) {
      if (!visited[i] and findUnDirCycleHelper(i, visited, -1)) {
        return 1;
      }
    }
    return 0;
  }
  void topSortHelper(int s, vector<bool> &visited, stack<int> &st) {
    visited[s] = 1;
    for (int x = 0; x < adjMat[s].size(); ++x) {
      if (!visited[x] and adjMat[s][x]) {
        topSortHelper(x, visited, st);
      }
    }
    st.push(s);
  }
  vector<int> topSort() {
    vector<bool> visited(vertices, 0);
    vector<int> v;
    stack<int> st;
    for (int i = 0; i < vertices; ++i) {
      if (!visited[i]) {
        topSortHelper(i, visited, st);
      }
    }
    while (!st.empty()) {
      v.push_back(st.top());
      st.pop();
    }
    return v;
  }
  int findUnDirCycleNLengthHelper(int s, int par, int n,
                                  vector<bool> &visited) {
    visited[s] = 1;
    int count = 0;
    if (n == 0) {
      visited[s] = 0;
      if (adjMat[s][par]) {
        count++;
      }
      return count;
    }
    for (int x = 0; x < adjMat[s].size(); ++x) {
      if (!visited[x] and adjMat[s][x]) {
        count += findUnDirCycleNLengthHelper(x, par, n - 1, visited);
      }
    }
    visited[s] = 0;
    return count;
  }
  int findUnDirCycleNLength(int n) {
    vector<bool> visited(vertices, 0);
    int count = 0;
    for (int i = 0; i < vertices - (n - 1); ++i) {
      count += findUnDirCycleNLengthHelper(i, i, n - 1, visited);
      visited[i] = 1;
    }
    return count / 2;
  }
  bool findNegCycleFloydWarshall(vector<vector<int>> &dist) {
    for (int k = 0; k < vertices; ++k) {
      for (int i = 0; i < vertices; ++i) {
        for (int j = 0; j < vertices; ++j) {
          if (dist[i][k] + dist[k][j] < dist[i][j]) {
            dist[i][j] = dist[i][k] + dist[k][j];
          }
        }
      }
    }
    for (int i = 0; i < vertices; ++i) {
      if (dist[i][i] < 0) return 1;
    }
    return 0;
  }
  vector<int> KahnsTopSort() {
    vector<int> indegree(vertices, 0), res;
    for (int i = 0; i < vertices; ++i) {
      for (int x = 0; x < vertices; ++x)
        if (adjMat[i][x]) indegree[x]++;
    }
    queue<int> q;
    int count = 0;
    for (int i = 0; i < vertices; ++i) {
      if (indegree[i] == 0) {
        q.push(i);
      }
    }
    while (!q.empty()) {
      int top = q.front();
      q.pop();
      res.push_back(top);
      for (int x = 0; x < adjMat[top].size(); ++x) {
        if (adjMat[top][x])
          if (--indegree[x] == 0) q.push(x);
      }
      count++;
    }
    if (count != vertices) {
      return {};
    }
    return res;
  }
  pair<int, vector<int>> BestFirstSearch(int src, int dest,
                                         vector<vector<int>> &dist) {
    int cost = 0;
    vector<bool> visited(vertices, 0);
    priority_queue<pair<int, int>, vector<pair<int, int>>,
                   greater<pair<int, int>>>
        pq;
    pq.push({0, src});
    visited[src] = 1;
    vector<int> v;
    while (!pq.empty()) {
      pair<int, int> p = pq.top();
      pq.pop();
      cost += p.first;
      v.push_back(p.second);
      if (p.second == dest) break;
      for (int x = 0; x < vertices; ++x) {
        if (!visited[x] and adjMat[p.second][x]) {
          visited[x] = 1;
          pq.push({dist[p.second][x], x});
        }
      }
    }
    return {cost, v};
  }
};
void dfs(GraphAdj g, int root, vector<int> &dp, vector<bool> &visited) {
  visited[root] = 1;
  for (int x : g.adjL[root]) {
    if (!visited[x]) {
      dfs(g, x, dp, visited);
      dp[root] = max(dp[root], dp[x] + 1);
    }
  }
}
int maxDiameter(int root, GraphAdj g, int no, vector<bool> &visited, int &h) {
  visited[root] = 1;
  int ans1 = 0, ans2 = 0;
  for (int x : g.adjL[root]) {
    if (!visited[x] and x != no) {
      int ans = maxDiameter(x, g, no, visited, h);
      if (ans > ans1)
        ans2 = ans1, ans1 = ans;
      else if (ans > ans2)
        ans2 = ans;
    }
  }
  h = max(h, ans1 + ans2);
  return 1 + max(ans1, ans2);
}
int solve(GraphAdj g, int root) {
  int n = g.vertices;
  vector<int> dp(n, 0);
  vector<bool> visited(n, 0);
  dfs(g, root, dp, visited);
  int path1 = 0, path2 = 0;
  int pOne_Idx = -1, pSec_Idx = -1;
  int sum = 0;
  for (int x : g.adjL[root]) {
    if (pOne_Idx == -1) {
      pOne_Idx = x;
      path1 = dp[x];
    } else if (dp[pOne_Idx] < dp[x]) {
      pOne_Idx = x;
      path1 = dp[x];
    }
  }
  for (int x : g.adjL[root]) {
    if (pSec_Idx == -1) {
      if (pOne_Idx != x) {
        pSec_Idx = x;
        path2 = dp[x];
      }
    } else if (x != pOne_Idx and dp[pSec_Idx] < dp[x]) {
      pSec_Idx = x;
      path2 = dp[x];
    }
  }
  if (pOne_Idx == -1 or pSec_Idx == -1) {
    return 0;
  }
  int ans = 0;
  for (int x : g.adjL[root]) {
    int h = 0, k = 0;
    fill(visited.begin(), visited.end(), 0);
    maxDiameter(root, g, x, visited, h);
    int maxSeq = h;
    fill(visited.begin(), visited.end(), 0);
    maxDiameter(x, g, root, visited, k);
    int maxSeqChild = k;
    ans = max(ans, maxSeqChild * maxSeq);
  }
  return max(ans, max((path1 + 1) * path2, path1 * (path2 + 1)));
}
int main() {
  init();
  int n, e, x, y;
  cin >> n;
  e = n - 1;
  GraphAdj g(n, e);
  while (e--) {
    cin >> x >> y;
    g.addEdge(x - 1, y - 1);
    g.addEdge(y - 1, x - 1);
  }
  if (n <= 3) {
    cout << 0 << '\n';
    return 0;
  }
  int root = 0;
  for (int i = 0; i < n; ++i) {
    if (g.adjL[i].size() > g.adjL[root].size()) {
      root = i;
    }
  }
  vector<int> roots;
  for (int i = 0; i < n; ++i) {
    if (g.adjL[i].size() == g.adjL[root].size()) roots.push_back(i);
  }
  int ans = 0;
  for (int i = 0; i < n; ++i) {
    ans = max(solve(g, i), ans);
  }
  cout << ans << '\n';
  return 0;
}
