Peter P. Pepper is facing a difficulty.

After fierce battles with the Principality of Croode, the Aaronbarc Kingdom, for which he serves, had the last laugh in the end. Peter had done great service in the war, and the king decided to give him a big reward. But alas, the mean king gave him a hard question to try his intelligence. Peter is given a number of sticks, and he is requested to form a tetrahedral vessel with these sticks. Then he will be, said the king, given as much patas (currency in this kingdom) as the volume of the vessel. In the situation, he has only to form a frame of a vessel.

<image>

Figure 1: An example tetrahedron vessel

The king posed two rules to him in forming a vessel: (1) he need not use all of the given sticks, and (2) he must not glue two or more sticks together in order to make a longer stick.

Needless to say, he wants to obtain as much patas as possible. Thus he wants to know the maximum patas he can be given. So he called you, a friend of him, and asked to write a program to solve the problem.



Input

The input consists of multiple test cases. Each test case is given in a line in the format below:


N a1 a2 . . . aN


where N indicates the number of sticks Peter is given, and ai indicates the length (in centimeters) of each stick. You may assume 6 ≤ N ≤ 15 and 1 ≤ ai ≤ 100.

The input terminates with the line containing a single zero.

Output

For each test case, print the maximum possible volume (in cubic centimeters) of a tetrahedral vessel which can be formed with the given sticks. You may print an arbitrary number of digits after the decimal point, provided that the printed value does not contain an error greater than 10-6.

It is guaranteed that for each test case, at least one tetrahedral vessel can be formed.

Example

Input

7 1 2 2 2 2 2 2
0


Output

0.942809
#include<bits/stdc++.h>

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define EPS (1e-7)
#define equals(a,b) (fabs((a)-(b))<EPS)

using namespace std;

static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;
static const int ONLINE_FRONT = -2;
static const int ON_SEGMENT = 0;
const int IINF = INT_MAX;
const int inf = INT_MAX;
//
class Point{
public:
  double x,y;

  Point(double x = -IINF,double y = -IINF): x(x),y(y){}

  Point operator + (Point p){return Point(x+p.x,y+p.y);}
  Point operator - (Point p){return Point(x-p.x,y-p.y);}
  Point operator * (double a){return Point(a*x,a*y);}
  Point operator / (double a){return Point(x/a,y/a);}
  Point operator * (Point a){ return Point(x * a.x - y * a.y, x * a.y + y * a.x); }

  bool operator < (const Point& p) const{ return !equals(x,p.x)?x<p.x:y<p.y; }

  bool operator == (const Point& p)const{ return fabs(x-p.x) < EPS && fabs(y-p.y) < EPS; }

};

struct Segment{
  Point p1,p2;
  Segment(Point p1 = Point(),Point p2 = Point()):p1(p1),p2(p2){}
  bool operator == (const Segment& p)const { return p.p1 == p1 && p.p2 == p2; }
};

typedef Point Vector;
typedef Segment Line;
typedef vector<Point> Polygon;

ostream& operator << (ostream& os,const Point& a){ os << "(" << a.x << "," << a.y << ")"; }

ostream& operator << (ostream& os,const Segment& a){ os << "( " << a.p1 << " , " << a.p2 << " )"; }

double dot(Point a,Point b){ return a.x*b.x + a.y*b.y; }

double cross(Point a,Point b){ return a.x*b.y - a.y*b.x; }

double norm(Point a){ return a.x*a.x+a.y*a.y; }

double abs(Point a){ return sqrt(norm(a)); }

//rad は角度をラジアンで持たせること
Point rotate(Point a,double rad){ return Point(cos(rad)*a.x - sin(rad)*a.y,sin(rad)*a.x + cos(rad)*a.y); }

// 度をラジアンに変換
double toRad(double agl){ return agl*M_PI/180.0; }

// a => prev, b => cur, c=> next
// prev から cur へ行って next へ行く際の角度を求める
double getArg(Point a,Point b,Point c){
  double arg1 = atan2(b.y-a.y,b.x-a.x);
  double arg2 = atan2(c.y-b.y,c.x-b.x);
  double arg = fabs( arg1 - arg2 );
  while( arg > M_PI ) arg -= 2.0 * M_PI;
  return fabs(arg);
}
//

// CCW
int ccw(Point p0,Point p1,Point p2){
  Point a = p1-p0;
  Point b = p2-p0;
  if(cross(a,b) > EPS)return COUNTER_CLOCKWISE;
  if(cross(a,b) < -EPS)return CLOCKWISE;
  if(dot(a,b) < -EPS)return ONLINE_BACK;
  if(norm(a) < norm(b))return ONLINE_FRONT;
  return ON_SEGMENT;
}
// CCW END

// intersect
bool intersectLL(Line l, Line m) {
  return abs(cross(l.p2-l.p1, m.p2-m.p1)) > EPS || // non-parallel
         abs(cross(l.p2-l.p1, m.p1-l.p1)) < EPS;   // same line
}
bool intersectLS(Line l, Line s) {
  return cross(l.p2-l.p1, s.p1-l.p1)*       // s[0] is left of l
         cross(l.p2-l.p1, s.p2-l.p1) < EPS; // s[1] is right of l
}
bool intersectLP(Line l,Point p) {
  return abs(cross(l.p2-p, l.p1-p)) < EPS;
}
bool intersectSS(Line s, Line t) {
  return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <= 0 &&
         ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2) <= 0;
}
bool intersectSP(Line s, Point p) {
  return abs(s.p1-p)+abs(s.p2-p)-abs(s.p2-s.p1) < EPS; // triangle inequality
}

Point projection(Line l,Point p) {
  double t = dot(p-l.p1, l.p1-l.p2) / norm(l.p1-l.p2);
  return l.p1 + (l.p1-l.p2)*t;
}
Point reflection(Line l,Point p) {
  return p + (projection(l, p) - p) * 2;
}
double distanceLP(Line l, Point p) {
  return abs(p - projection(l, p));
}
double distanceLL(Line l, Line m) {
  return intersectLL(l, m) ? 0 : distanceLP(l, m.p1);
}

double distanceLS(Line l, Line s) {
  if (intersectLS(l, s)) return 0;
  return min(distanceLP(l, s.p1), distanceLP(l, s.p2));
}
double distanceSP(Line s, Point p) {
  Point r = projection(s, p);
  if (intersectSP(s, r)) return abs(r - p);
  return min(abs(s.p1 - p), abs(s.p2 - p));
}

double distanceSS(Line s, Line t) {
  if (intersectSS(s, t)) return 0;
  return min(min(distanceSP(s, t.p1), distanceSP(s, t.p2)),
             min(distanceSP(t, s.p1), distanceSP(t, s.p2)));
}

/*
same lineの時注意
もしm.p1が中心でなかったときに正しい答えとならない
Point crosspoint(Line l,Line m){
  double A = cross(l.p2-l.p1,m.p2-m.p1);
  double B = cross(l.p2-l.p1,l.p2-m.p1);
  if(abs(A) < EPS && abs(B) < EPS){
    vector<Point> vec;
    vec.push_back(l.p1),vec.push_back(l.p2),vec.push_back(m.p1),vec.push_back(m.p2);
    sort(vec.begin(),vec.end());
    assert(vec[1] == vec[2]);
    return vec[1];
    //return m.p1;
  }
  if(abs(A) < EPS)assert(false);
  return m.p1 + (m.p2-m.p1)*(B/A);
}

*/
Point crosspoint(Line l, Line m) {
  double A = cross(l.p2 - l.p1, m.p2 - m.p1);
  double B = cross(l.p2 - l.p1, l.p2 - m.p1);
  if (abs(A) < EPS && abs(B) < EPS) return m.p1; // same line
  if (abs(A) < EPS) assert(false); // !!!PRECONDITION NOT SATISFIED!!!
  return m.p1 +   (m.p2 - m.p1) * (B / A);
}

// intersect END

int array[6000];

double areaT(double a,double b,double c){
  double s = (a+b+c)/2.0;
  return sqrt(s*(s-a)*(s-b)*(s-c));
}

double areaH(double h,double *a){ return ( areaT(a[0],a[1],a[2]) * h) / 3.0; }

bool LTE(double a,double b){ return equals(a,b) || a < b; }

bool LT(double a,double b){ return !equals(a,b) && a < b; }

bool GTE(double a,double b){ return equals(a,b) || a > b; }

bool check1(double a,double b,double c){ return a + b >= c; }

bool checkT(double a,double b,double c){ return LT(a,b+c) && LT(b,a+c) && LT(c,a+b); }

bool check2(double h,double *a){
  if( LTE(a[3]*a[3]-h*h,0) || LTE(a[4]*a[4]-h*h,0) || LTE(a[5]*a[5]-h*h,0) ) return false;
  double L1 = sqrt(a[3]*a[3]-h*h);
  double L2 = sqrt(a[4]*a[4]-h*h);
  double L3 = sqrt(a[5]*a[5]-h*h);
  if( LTE(L1,0) || LTE(L2,0) || LTE(L3,0) ) return false;
  if( !checkT(L1,L2,a[0]) || !checkT(L2,L3,a[2]) || !checkT(L2,L3,a[2]) ) return false;
  cout << endl;
  cout << L1 << " " << L2 << " " << L3 << endl;
  double tmp_area = areaT(L1,L2,a[0]) + areaT(L2,L3,a[2]) + areaT(L1,L3,a[1]);
  cout << tmp_area << " (" << areaT(L1,L2,a[0]) << "+" << areaT(L2,L3,a[2]) << "+" << areaT(L1,L3,a[1]) << ") "<< " ?= " << areaT(a[0],a[1],a[2]) << endl;
  return equals(tmp_area,areaT(a[0],a[1],a[2]));
}

Segment gen(Point p1,Point p2,double a,double b,double c,bool &error){
  double S = areaT(a,b,c);
  cout << "S = " << S << endl;
  double h = 2 * S / a;
  if( LTE(h,0) ) error = true;
  if( LTE(b*b-h*h,0) ) error = true;
  if( error ) return Segment();
  cout << c * c << " - " << h*h << endl;  
double x = sqrt(c*c-h*h);
    cout << h << "," << x << endl;
  Segment seg;
  seg.p1 = p1;
  Vector v = p2 - p1;
  Vector ve = v / abs(v);
  seg.p2 = p1 + ve * x;
  cout << v << endl;
  v = rotate(v,toRad(90));
  cout << v << endl;
  ve = v / abs(v);
  seg.p2 = seg.p2 + ve * h;
  return seg;
}

//
  typedef vector<int> VI;
  typedef vector<VI> VVI;
  typedef VVI matrix;



matrix identity(int n)
{
  matrix A(n,vector<int>(n));
  for(int i=0;i<n;i++) A[i][i] = 1;
  return A;
}


vector<int> multi(const matrix& A,const vector<int>& B)
{
  vector<int> ret(A.size());
  for(int i=0;i<A.size();++i)
    for(int j=0;j<A[0].size();++j)
      ret[i] += A[i][j]*B[j];
  return ret;
}


matrix multi(const matrix& A,const matrix& B)
{
  matrix C(A.size(),vector<int>(B[0].size()));
  for(int i=0;i<C.size();++i)
    for(int j=0;j<C[i].size();++j)
      for(int k=0;k<A[i].size();++k)
	C[i][j] += A[i][k]*B[k][j];
  return C;
}



matrix pow(const matrix& A,int e)
{
  return !e?identity(A.size()):
    (e%2?multi(A,pow(A,e-1)):pow(multi(A,A),e/2));
}
/*
冪乗に関するちょいと解説
行列をAする
A^2 = A*A, A^4 = (A^2)*(A^2), A^8 = (A^4)*(A^4) ・・・・と計算している
行列に限ったことでなく、普通の値の冪乗でも同じように考えることができる
*/


int extgcd(int a,int b,int& x,int& y)
{
  int d = a;
  if(b != 0){
    d = extgcd(b,a%b,y,x);
    y -= (a/b)*x;
  }
  else
    x = 1,y = 0;
  return d;
}

int mod_inv(int a,int m)
{
  int x,y;
  extgcd(a,m,x,y);
  return (m+x%m)%m;
}


int rank(vector<vector<int> > A,int mod)
{
  int H = A.size(), W = A[0].size();
  int r = 0;
  rep(x,W)
    {  
      int pivot = inf;
      REP(y,r,H)
	if(A[y][x])
	  {
	    pivot = y;
	    break;
	  }
      if(pivot == inf)continue;
      swap(A[r],A[pivot]);

      REP(y,r+1,H)
	{
	  int value = A[y][x] * mod_inv(A[r][x],mod) % mod;
	  if(value)REP(x2,x,W)
		     A[y][x2] = ((A[y][x2] - A[r][x2] * value)%mod + mod ) % mod; 
	}
      r++;
    }
  return r;
}


/*

Ax=bが解を持つ必要十分条件は、
rank(A) = rank(A|b)
であり、
Ax=bが一意の解を持つ必要十分条件は、
rank(A) = rank(A|b) = n
*/

bool modGaussElimination(vector<vector<int> > &A, vector<int> &b, int m, vector<int> &res) {
  int n = A.size();
  vector<vector<int> > B(n, vector<int>(n+1));
  rep(i,n) rep(j,n)
    B[i][j] = A[i][j];
  rep(i, n) B[i][n] = b[i];
  
  int nowy = 0;
  rep(x, n) {
    int pivot = -1;
    for (int j=nowy; j<n; ++j)
      if (B[j][x]) {
        pivot = j;break;
      }
    if (pivot == -1) continue;
    swap(B[nowy], B[pivot]);

    for (int j=nowy+1; j<n; ++j) {
      int t = B[j][x] * mod_inv(B[nowy][x], m) % m;
      if (t)
        for (int k=x; k<=n; ++k)
          B[j][k] = ((B[j][k] - B[nowy][k] * t)%m + m )%m;
    }
    nowy++;
    
  }

  res.clear();
  for (int y=nowy; y<n; ++y)
    if (B[y][n])                // rank(A) != rank(A|b)
      return 0;
  if (nowy != n) {              // rank(A) == rank(A|b) != n
    // 解が一意でない。ひとつ求める
    res = vector<int>(n,inf);           // INFは任意を表す
    for (int y=n-1; y>=0; --y) {
      int x;
      for (x=y; x<n; ++x) {
        if (B[y][x])
          break;
      }
      if (x==n) continue;
      int sum = B[y][n];
      
      for (int i=x+1; i<n; ++i) {
        if (res[i] == inf) res[i] = 0; // この時点でres[i]が決まってなかったら0とする
        sum = ((sum - res[i] * B[y][i])%m + m)%m;
      }
      res[x] = sum * mod_inv(B[y][x], m) % m;
    }
    rep(i, n) if (res[i]==inf) res[i] = 0;
    return 0;
  }
  // 解が一意に決まる。普通に後退代入
  res.resize(n);
  for (int x=n-1; x>=0; --x) {
    int sum = B[x][n];
    for (int i=n-1; i>x; --i) {
      sum = ((sum - res[i] * B[x][i])%m + m)%m; 
    }
    res[x] = sum * mod_inv(B[x][x], m) % m;
  }
  return 1;
}

double det(vector<vector<double> > A) {
  const int n = A.size();
  double D = 1;
  for (int i = 0; i < n; ++i) {
    int pivot = i;
    for (int j = i+1; j < n; ++j)
      if (abs(A[j][i]) > abs(A[pivot][i])) pivot = j;
    swap(A[pivot], A[i]);
    D *= A[i][i] * (i != pivot ? -1 : 1);
    if (abs(A[i][i]) < EPS) break;
    for(int j = i+1; j < n; ++j)
      for(int k = n-1; k >= i; --k)
        A[j][k] -= A[i][k] * A[j][i] / A[i][i];
  }
  return D;
}


//

double getAreaTetrahedra(double *A){
  double a = A[3], b = A[1], c = A[0], p = A[5], q = A[4], r = A[2];

  vector<vector<double> > mat(5,vector<double>(5,0));
  mat[0][1] = p*p, mat[0][2] = q*q, mat[0][3] = a*a, mat[0][4] = 1;
  mat[1][0] = p*p, mat[1][2] = r*r, mat[1][3] = b*b, mat[1][4] = 1;
  mat[2][0] = q*q, mat[2][1] = r*r, mat[2][3] = c*c, mat[2][4] = 1;
  mat[3][0] = a*a, mat[3][1] = b*b, mat[3][2] = c*c, mat[3][4] = 1;
  mat[4][0] = 1,   mat[4][1] = 1,   mat[4][2] = 1,   mat[4][3] = 1;

  return det(mat)/288.0;
}

double calc(double *a){
  double ret = 0;
  do{
  if( !checkT(a[0],a[1],a[2]) || !checkT(a[0],a[3],a[4]) || !checkT(a[1],a[3],a[5]) || !checkT(a[2],a[4],a[5]) ) continue;
  /*
    Point p1(0,0);
    Point p2(a[1],0);
    double theta = acos( ( a[0]*a[0] + a[1]*a[1] - a[2]*a[2] ) / ( 2*a[0]*a[1] ) );
    Point p3(a[0]*cos(theta),a[0]*sin(theta));
    //cout << p1 << " " << p2 << " " << p3 << endl;
    double A = a[1], b =a[0], c = a[2];
    double x0 = (A*A-b*b-c*c)/(2*A);
    //cout << "(" << (A*A-b*b-c*c)/(2*A) << "," << sqrt(b*b-x0*x0)<<")"<<endl;
    */
    //if( error ) continue;
    //if( error ) continue;
    //if( LTE(a[3]*a[3]-x*x,0) ) continue;
    //if( LTE(h,0) ) continue;
    ret = max(ret,sqrt(getAreaTetrahedra(a)));
    }while(next_permutation(a,a+6));
    return ret;
}

int main(){
  /*
  double a[6] = {2,2,2,2,2,2};
  cout << calc(a) << endl;
  return 0;
  */
  int dex = 0;
  rep(i,(1<<15)) if( __builtin_popcount(i) == 6 ) array[dex++] = i;
  int N,input[16];
  while( scanf("%d",&N), N ){
    rep(i,N) scanf("%d",input+i);
    sort(input,input+N);
    double maxi = 0;
    rep(k,dex){
      int bitmask = array[k];
      if( bitmask >= (1<<N) ) break;
      double buf[6];
      int cur = 0;
      rep(j,N) if( (bitmask>>j) & 1 ) buf[cur++] = input[j];
      maxi = max(maxi,calc(buf));
    }
    printf("%.10f\n",maxi);
  }
  return 0;
}