#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<ctype.h>
//#include<bits/stdc++.h>
using namespace std;
typedef double db;
const db eps=1e-8;
const db PI=acos(-1.0);
const int maxn=3e5+10;
int n;
inline db readb()
{
int f=1;db x=0;char s=getchar();
for( ;!isdigit(s);s=='-' && (f=-1),s=getchar());
for( ;isdigit(s);x=x*10+s-48,s=getchar());
if( s=='.' ) for( db l=0.1,s=getchar();isdigit(s);x=x+(s-48)*l,l/=10,s=getchar() );
return x*f;
}
int sgn( double d ) { return (d>eps)-(d<-eps); }; // 精度判断
struct point{
db x,y;
point(){ x=y=0; }
point(db _x,db _y ){ x=_x,y=_y; }
point operator + ( const point &rhs ) const { return point(x+rhs.x,y+rhs.y); }
point operator - ( const point &rhs ) const { return point(x-rhs.x,y-rhs.y); }
point operator / ( const int &rhs ) const { return point(x/rhs,y/rhs); }
db operator * ( const point &rhs ) const { return 1ll*x*rhs.y-1ll*y*rhs.x; }
point operator * ( const db &rhs ) const { return point(x*rhs,y*rhs);}
db dot( const point &rhs ) const { return 1ll*x*rhs.x+1ll*y*rhs.y; }
void get(){ x=readb(),y=readb(); }
bool operator == ( const point &rhs ) const { return x==rhs.x && y==rhs.y; }
bool operator < ( const point &rhs ) const { return sgn(x-rhs.x)<0 || sgn(x-rhs.x)==0 && sgn(y-rhs.y)<0 ; }
point Rotate( db rad ) { return point(x*cos(rad)-y*sin(rad), x*sin(rad)+y*cos(rad) ); }
db sdis() { return sqrt(x*x+y*y); }
// bool operator < ( const point &rhs ) const { return (*this)*rhs>0 ; } 极角排序
};
db dist( point a,point b ) { return sqrt( (b-a).dot(b-a) ); }
bool getdir( point p[],int n ) // true 逆时针存边
{
db ans=0;
for( int i=0;i<n;i++ ) ans+=p[i]*p[(i+1)%n];
return sgn(ans)>0;
}
struct segment{ // 线段
point s,e;
segment(){}
segment( point _s,point _e ) { s=_s,e=_e; }
void get( point _s,point _e ) { s=_s,e=_e; }
bool inter( const segment &l1, const segment &l2 ) // 两线段是否交 1 交 / 0 不交
{
return
max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&
max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&
max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&
max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&
sgn( (l2.s-l1.e)*(l1.s-l1.e) )*sgn( (l2.e-l1.e)*(l1.s-l1.e) ) <= 0 &&
sgn( (l1.s-l2.e)*(l2.s-l2.e) )*sgn( (l1.e-l2.e)*(l2.s-l2.e) ) <= 0;
}
bool inter1( const segment &l1,const segment &l2 ) // 两线段是否正规相交 1 交 / 0 不交
{
if( inter(l1,l2) )
{
if( l1.s==l2.s && l1.e==l2.e || l1.s==l2.e && l1.e==l2.s ) return true; //重合
else if( l1.s==l2.s || l1.s==l2.e || l1.e==l2.s || l1.e==l2.e ) return false; // 端点相交
return true;
}
return false;
}
bool OnSeg( const point &p ) //判断点在线段上 1 在/ 0 不在
{
return sgn( (s-p)*(e-p) )==0
&& sgn( (p.x-s.x)*(p.x-e.x) )<=0
&& sgn( (p.y-s.y)*(p.y-e.y) )<=0;
}
pair<point,int> operator & ( const segment &rhs ) const{ // 两直线相交问题
point res=s; //交点
if( sgn( (s-e)*(rhs.s-rhs.e) )==0 )
{
if( sgn( (rhs.s-s)*(rhs.e-s) )==0 ) return make_pair(res,0); // 两直线重合
else return make_pair( res,1 ); // 两直线平行
}
db t= ( (s-rhs.s)*(rhs.s-rhs.e) )/ ( (s-e)*(rhs.s-rhs.e) ) ; //单位化
res.x+=(e.x-s.x)*t;
res.y+=(e.y-s.y)*t;
return make_pair(res,2); // 有交点
}
point NearestPointToLineSeg( const point &P,const segment &L ) // 线段 最近点对
{
point result;
db t = ( (P-L.s).dot(L.e-L.s) )/( (L.e-L.s).dot(L.e-L.s) );
if( t >= 0 && t <= 1 )
{
result.x = L.s.x + (L.e.x - L.s.x)*t;
result.y = L.s.y + (L.e.y - L.s.y)*t;
}
else
{
if(dist(P,L.s) < dist(P,L.e))
result = L.s;
else result = L.e;
}
return result;
}
};
//--------------------直线----------------
struct sLine{ // 直线
point s,e;
sLine(){}
sLine( point _s,point _e ) { s=_s,e=_e; }
sLine operator * ( const db &rhs ) const { return sLine(s*rhs,e*rhs); }
pair<point,int> operator & ( const sLine &rhs ) const{ // 两直线相交问题
point res=s; //交点
if( sgn( (s-e)*(rhs.s-rhs.e) )==0 )
{
if( sgn( (rhs.s-s)*(rhs.e-s) )==0 ) return make_pair(res,0); // 两直线重合
else return make_pair( res,1 ); // 两直线平行
}
db t= ( (s-rhs.s)*(rhs.s-rhs.e) )/ ( (s-e)*(rhs.s-rhs.e) ) ; //单位化
res.x+=(e.x-s.x)*t;
res.y+=(e.y-s.y)*t;
return make_pair(res,2); // 有交点
}
int segLineCross( const sLine &s1,const segment &s2 ) // 直线和线段相交
{
point a=s1.s,b=s1.e,c=s2.s,d=s2.e;
int d1,d2;
d1 = sgn( (b-a)*(c-a) );
d2 = sgn( (b-a)*(d-a) );
if( (d1^d2)==-2 ) return 1; // 标准相交
if( d1==0 || d2==0 ) return 2; // 不标准相交
return 0; // 不相交
}
db getDistance( point A ) //点到直线的距离
{
return fabs((A-s)*(A-e)/dist(s,e) );
}
sLine Circle_inter( point o,sLine X ,db R ) //圆与直线交点
{
point o2 = o;
o2.x += X.s.y - X.e.y;
o2.y += X.e.x - X.s.x;
o2 = (sLine(o,o2)&X).first;
db t = sqrt(R * R - (o2 - o).sdis() * (o2 - o).sdis()) / (X.s - X.e).sdis();
point p1 = o2 - (X.e - X.s) * t;
point p2 = o2 + (X.e - X.s) * t;
return sLine(p1,p2);
}
};
//bool Is_covbag( point p[] ,int n )
//{
// for( int i=0;i<=n;i++ )
// if( cross(node[i],node[i+1],node[i+1],node[i+2])<0 )
// return false;
// return true;
//}
//------------凸包-------------------
//判断点在凸多边形内
//点形成一个凸包,而且按逆时针排序(如果是顺时针把里面的<0改为>0)
//点的编号:0~n-1
//返回值:
//-1:点在凸多边形外
//0:点在凸多边形边界上
//1:点在凸多边形内
int inConvexPoly( point a,point p[],int n )
{
for( int i=0;i<n;i++ )
{
if( sgn( ( p[i]-a )*( p[(i+1)%n]-a ) )<0 ) return -1;
else if( segment( p[i],p[(i+1)%n] ).OnSeg(a) ) return 0;
}
return 1;
}
//判断点在任意多边形内---同样适用于凸包
//射线法,poly[]的顶点数要大于等于3,点的编号0~n-1
//返回值
//-1:点在多边形外
//0:点在多边形边界上
//1:点在多边形内
int inPoly( point p, point poly[],int n )
{
int cnt;
segment ray,side;
cnt=0;ray.s=p;ray.e.y=p.y;
ray.e.x=-100000000000.0; // -inf 注意取值防止越界
for( int i=0;i<n;i++ )
{
side.s=poly[i];
side.e=poly[(i+1)%n];
if( side.OnSeg(p) ) return 0;
if( sgn(side.s.y-side.e.y)==0 ) continue;
if( ray.OnSeg(side.s) )
{
if( sgn(side.s.y-side.e.y)>0 ) cnt++;
}
else if( ray.OnSeg(side.e) )
{
if( sgn(side.e.y-side.s.y)>0 ) cnt++;
}
else if( ray.inter(ray,side) ) cnt++;
}
if( cnt%2==1 ) return 1;
else return -1;
}
point st; // 左下角起始点
bool cmp( point a,point b ) // 极角排序
{
db tmp=(a-st)*(b-st);
if( sgn(tmp)==0 ) return dist(st,a)<dist(st,b );
else return sgn(tmp)>0;
}
db Graham( point P[] ,int n ) // 构造凸包 该模板不存在三点共线
{
db res=0;int k=1;
for( int i=1;i<=n;i++ ) // 获得起始点
{
if( sgn(P[k].y-P[i].y)>0 || sgn(P[k].y-P[i].y)==0 && sgn( P[k].x-P[i].y)>0 ) k=i;
}
st=P[k];
sort(P+1,P+1+n,cmp);
int q[maxn],top=2;
q[1]=1;q[2]=2;
for( int i=3;i<=n;i++ ) // 三点共线凸包问题 n^2 check
{
while( top>1 && sgn( ( P[q[top]]-P[q[top-1]] )*( P[i]-P[q[top]] ) )<=0 ) top--;
q[++top]=i;
}
// for(int i=1;i<=top;++i) printf("(%d,%d)\n",P[q[i]].x,P[q[i]].y); // P[ q[1-top] ] 凸包上的点坐标
// for( int i=1;i<=top;i++ ) res+=dist( P[q[i]],P[q[i%top+1]] ); // res ----凸包的周长
for( int i=3;i<=top;i++ ) res+=( P[q[i]]-P[q[i-1]] )*( P[q[1]]-P[q[i-1]] )/2.0; // res -----凸包的面积
return fabs(res);
} // 皮克定理:S=A+B/2-1(S为多边形面积,A为多边形内部整点数,B为多边形边上的整点数) B 等于gcd(abs( xi-x(i-1) ),abs(y i-y(i-1) ) )求和
// 求任意多边形的面积
db CalArea( point P[],int n )
{
db res=0;
for( int i=1;i<=n;i++ )
{
res+=( P[i]*P[i%n+1] )/2.0;
}
return fabs(res);
}
//-----------半平面交---------
struct halfplane:public sLine{
db angle;
halfplane(){}
halfplane( sLine v ){ s=v.s;e=v.e; }
void CalAngle() { angle=atan2(e.y-s.y,e.x-s.x); }
bool operator < ( const halfplane &rhs ) const{ return angle<rhs.angle; }
};
struct polygon{ // 内核面积
int n;
point p[maxn];
db getArea()
{
db sum=0;
for( int i=0;i<n;i++ ) sum+=p[i]*p[(i+1)%n];
return fabs(sum)/2;
}
};
struct halfplanes{
int n; // 记得初始化
halfplane hp[maxn];
point p[maxn];
int que[maxn];int st,ed; // 双端队列
void push( halfplane tmp ) { hp[n++]=tmp; } // sLine(p[(i+1)%n],p[i]) 顺时针存边 / sLine( p[(i-1+n)%n],p[i] ) 逆时针存边
// 去重
void unique()
{
int m=1;
for( int i=1;i<n;i++ )
{
if( sgn( hp[i].angle-hp[i-1].angle )!=0 ) hp[m++]=hp[i];
else if( sgn( ( hp[m-1].e-hp[m-1].s )*( hp[i].s-hp[m-1].s ) )>0 ) hp[m-1]=hp[i];
}
n=m;
}
bool halfplaneInsert() // 判断是否存在内核 ----- 存在一点到与多边形边上任意一点的连线 在多边形内部
{
for( int i=0;i<n;i++ ) hp[i].CalAngle();
sort(hp,hp+n);
unique();
que[st=0]=0;que[ed=1]=1;
p[1]=( hp[0] & hp[1] ).first;
for( int i=2;i<n;i++ )
{
while( st<ed && sgn( ( hp[i].e-hp[i].s )*( p[ed]-hp[i].s ) )<0 ) ed--;
while( st<ed && sgn( ( hp[i].e-hp[i].s )*( p[st+1]-hp[i].s ) )<0 ) st++;
que[++ed]=i;
if( ( hp[i] & hp[ que[ed-1] ] ).second<2 ) return false;
p[ed]=( hp[i] & hp[ que[ed-1] ] ).first;
}
while( st<ed && sgn( ( hp[que[st]].e-hp[que[st]].s )*( p[ed]-hp[que[st]].s ) )<0 ) ed--;
while( st<ed && sgn( ( hp[que[ed]].e-hp[que[ed]].s )*( p[st+1]-hp[que[ed]].s ) )<0 ) st++;
if( st+1>=ed ) return false;
else return true;
}
// 得到最后半平面交的凸多边形 点集
// 得到先调用halfplaneinsert(),且返回true
void getconvex( polygon &con ) //
{
p[st]=( hp[que[st]]&hp[que[ed]] ).first;
con.n=ed-st+1;
for( int j=st,i=0;j<=ed;j++,i++ ) con.p[i]=p[j];
}
};
void change( point p1,point p2,point &a,point &b,db p ) // 凸包边内移 p距离 ---- 可二分求 内切最大圆半径
{
db len=dist(p1,p2);
db dx=(p1.y-p2.y)*p/len,dy=(p2.x-p1.x)*p/len;
a.x=p1.x+dx;a.y=p1.y+dy;
b.x=p2.x+dx;b.y=p2.y+dy;
}
//-------------------圆问题----------------------------
struct qnode{
db angle;
int in;
bool operator < ( const qnode &rhs ) const {
return sgn(angle-rhs.angle)<0 || sgn(angle-rhs.angle)==0 && in>rhs.in;
}
}arc[maxn];
void Min_cir_cover( point p[] ,int n ,double r ) // 最小圆覆盖 最多点 --question 求半径为 r 的圆最多能覆盖平面上多少个点
{
int cnt=0;
int ans=0,maxz=1;
for( int i=1;i<=n;i++ )
{
ans=0,cnt=0;
for( int j=1;j<=n;j++ )
{
if( dist(p[i],p[j])>2*r ) continue;
db angle1=atan2(p[j].y-p[i].y,p[j].x-p[i].x);
db angle2=acos( dist(p[i],p[j])/(2*r) );
arc[++cnt].angle=angle1-angle2;
arc[cnt].in=1;
arc[++cnt].angle=angle1+angle2;
arc[cnt].in=-1;
}
sort(arc+1,arc+cnt+1);
for( int j=1;j<=cnt;j++ )
{
ans+=arc[j].in;
maxz=max(maxz,ans);
}
}
printf("%d\n",maxz);
}
//--------------------最短路----------------------------
const db inf=1e20;
#define pr pair<db,int>
struct node{
int v;
db len;
int next;
}a[maxn*2];
int pre[maxn],cnt=0;
db dis[maxn];
bool vis[maxn];
void add( int u,int v,db len )
{
a[cnt].v=v,a[cnt].len=len,a[cnt].next=pre[u];
pre[u]=cnt++;
}
void init()
{
memset(pre,-1,sizeof(pre)),cnt=0;
}
db dijkstra( int s ,int e ) // 起点
{
for( int i=0;i<maxn;i++ ) dis[i]=inf;
memset( vis,0,sizeof(vis) );
priority_queue<pr,vector<pr>,greater<pr> > q;
q.push( make_pair(0,s) );
dis[s]=0;
while( !q.empty() )
{
int u=q.top().second;
q.pop();
vis[u]=1;
for( int i=pre[u];~i;i=a[i].next )
{
int v=a[i].v;
db len=a[i].len;
if( !vis[v] && dis[v]>dis[u]+len )
{
dis[v]=dis[u]+len;
q.push( make_pair(dis[v],v) );
}
}
}
return dis[e];
}
point p[maxn];
segment s[maxn];
int main()
{
while( ~scanf("%d",&n) && n!=-1 )
{
init();
int p_cnt=0,s_cnt=0;
for( int i=0;i<n;i++ )
{
double x,y[4];
x=readb();
for( int j=0;j<4;j++ )
{
y[j]=readb();
}
for( int j=0;j<4;j++ ) p[p_cnt++]=point(x,y[j]);
s[s_cnt++].get( point(x,0),point(x,y[0]) );
s[s_cnt++].get( point(x,y[1]),point(x,y[2]) );
s[s_cnt++].get( point(x,y[3]),point(x,10.0) );
}
p[p_cnt++]=point(10.0,5.0);
p[p_cnt++]=point(0.0,5.0);
for( int i=0;i<p_cnt;i++ )
{
for( int j=i+1;j<p_cnt;j++ )
{
segment tmp=segment(p[i],p[j]);
int flag=0;
for( int k=0;k<s_cnt;k++ )
{
if( s[k].s.y==p[i].y && s[k].e.y==p[j].y && s[k].s.x==p[i].x && s[k].e.x==p[j].x )
{
flag=1;
break;
}
else if( s[k].s==p[i] || s[k].s==p[j] || s[k].e==p[i] || s[k].e==p[j] ) continue;
if( tmp.inter(tmp,s[k]) )
{
flag=1;
break;
}
}
if( !flag )
{
add( i,j,dist(p[i],p[j]) );
add( j,i,dist(p[i],p[j]) );
}
}
}
db ans=dijkstra(p_cnt-2,p_cnt-1);
printf("%.2f\n",ans);
}
return 0;
}