#include <bits/stdc++.h>
using namespace std;
#define el '\n'
#define sz(x) (int)x.size()
#define all(x) x.begin(), x.end()
#define ll long long
#define double long double
#define ff first
#define ss second
#define lll __int128
const long long N = 5e3 + 1, mod = 1e9 + 7;
const ll inf = 3e18;
double EPS = 1e-14;
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
// ● order_of_key (k) : Number of items strictly smaller than k .
// ● find_by_order(k) : K-th element in a set (counting from zero).
struct line {
double x1 , x2 , y1 , y2 ;
double sx , sy , slop , c;
line (double x1 , double y1 , double x2 , double y2 ){
this->x1 = x1 ;
this->x2 = x2 ;
this->y1= y1 ;
this->y2 = y2 ;
sx = (x1-x2) ;
sy = (y1-y2) ;
int g = __gcd((int)sx,(int)sy) ;
sx/=g , sy/=g ;
slop = sy/sx ;
c = y1 - slop*x1 ;
}
// for not perpendicular
// let l_slop = sy1/sx1 , sy2/sx2
};
pair<double , double> get_point1 (line l1 , line l2 ){
// if l1 is perp
// y = mx +c
double retx = l1.x1 , rety = l2.slop*retx + l2.c ;
return {retx , rety} ;
}
pair<double ,double> get_point2 (line l1 , line l2 ){
// (sy1/sx1)*x +c = (sy2/sx2)*x +b
// b-c = [(sy1/sx1) - (sy2/sx2) ] *x
// y = (slop1 * x + c )
double retx = (l1.c-l2.c)/ (l2.slop-l1.slop) , rety = l1.slop * retx + l1.c ;
return {retx, rety} ;
};
double get_dist (pair<double , double> p1 , pair<double ,double>p2){
return sqrtl((p1.first-p2.first)*(p1.first-p2.first) +(p1.second-p2.second) *(p1.second-p2.second)) ;
}
void acc() {
vector<line> perp , noprep ;
int n ;
cin>>n ;
for (int i =0 ; i < n ; i++){
double x1 ,x2 , y1,y2 ;
cin >>x1>>y1>>x2>>y2 ;
line l (x1,y1,x2,y2) ;
if (l.sx==0)perp.push_back(l) ;
else noprep.push_back(l) ;
}
double ans = 0 ;
for (int i = 0 ; i < sz(perp) ; i++){
for (int j = 0 ; j < sz(noprep) ; j++){
for (int k = j+1 ; k< sz(noprep) ; k++){
if (noprep[j].slop == noprep[k].slop)continue ;
pair <double , double > p1 = get_point1(perp[i] , noprep[j]) ;
pair <double , double > p2 = get_point1(perp[i] , noprep[k]) ;
pair <double , double > p3 = get_point2(noprep[k] , noprep[j]) ;
ans = max (ans , get_dist(p1,p2) + get_dist(p2,p3) + get_dist(p3,p1)) ;
}
}
}
for (int i = 0 ; i < sz(noprep) ; i++){
for (int j = i+1 ; j < sz(noprep) ; j++){
for (int k = j+1 ; k< sz(noprep) ; k++){
if (noprep[j].slop == noprep[k].slop)continue ;
if (noprep[i].slop == noprep[k].slop)continue ;
if (noprep[i].slop == noprep[j].slop)continue ;
pair <double , double > p1 = get_point2(noprep[i] , noprep[j]) ;
pair <double , double > p2 = get_point2(noprep[i] , noprep[k]) ;
pair <double , double > p3 = get_point2(noprep[k] , noprep[j]) ;
ans = max (ans , get_dist(p1,p2) + get_dist(p2,p3) + get_dist(p3,p1)) ;
}
}
}
if (ans<=0){
cout <<"no triangle"<<'\n' ;
}else{
cout <<setprecision(7)<<fixed<<ans<<'\n' ;
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int t = 1;
//cin >> t;
while (t--)
acc();
}
// direction 0 left , 1 right , 2 down , 3 up