As a proud hero, you are finally about to reach the Demon King. However, in order to reach the throne where the Demon King is, you must clear the final map prepared by the Demon King.

In this map, everything changes like a two-dimensional plan view from the sky. Humans transform into just points on this map. The map is divided into two areas by a straight line. Then, when there is a person in one area, a mirror image of the person is projected at a position in the other area that is line-symmetric with respect to a straight line.

There are multiple buildings in each of the two areas. Even though it is a building, in this map, it is a polygon made by connecting several points, and the inside surrounded by the sides and the top of the side are the inside of the building. Buildings, unlike humans, do not create mirror images in other areas.

When you break into this map, the Demon King's magic will lock you inside one of the buildings. You can only clear the map by moving to one escape point inside the detained building. In addition, you are cursed by the Demon King and restricted in your movements. The limitation is that your location and the location of your mirror image must be inside one of the buildings. As long as you follow this restriction, you are free to move inside the building.

Now, you must clear this map as soon as possible and defeat the Demon King who is destroying the world. Given your initial position, find the shortest path distance to get to the escape point. However, if you cannot move to the escape point, you will not be able to escape from the eternal map and will only decay.

Input

The input consists of multiple datasets. The format of each data set is as follows.


N
BUILDING1
...
BUILDINGN
SX SY GX GY
LX1 LY1 LX2 LY2


All inputs are integer values. Moreover, it may be assumed that all the coordinate information is -10,000 <= x, y <= 10,000.

N (1 <= N <= 100) is the number of buildings. Next, information on N buildings is entered. BUILDINGi is a polygon that is input in the following format.


M
X1 Y1
...
XM YM


M (3 <= M <= 50) represents the number of vertices of the polygon. Then M vertices, (Xi, Yi) are input counterclockwise. The line segment connecting the vertices of (Xi, Yi) and (Xi + 1, Yi + 1) is one side of the polygon. However, the first vertex is connected to the M-th vertex. It can be assumed that the three consecutive vertices of a polygon do not line up. In addition, each side of the polygon is input so that it does not touch or intersect with any side other than the adjacent sides. Each polygon does not touch, intersect, or contain other polygons.

After the building information is entered, the start point (SX, SY) and escape point (GX, GY) information is entered. Both the starting point and the escape point are inside the same building. However, please note that the mirror images of the starting point and the escape point do not always exist inside the building. In this case, of course, the map cannot be cleared. The start point and the escape point always exist at different positions.

Finally, the straight line information is input. A straight line is represented by two points (LX1, LY1) and (LX2, LY2) existing on the straight line. These two points are always in different positions. It may be assumed that the straight line does not touch or intersect any building.

The end of the input is given on a line with only one 0.

Output

For each dataset, output the shortest distance from the start point to the escape point. The error in the answer must not exceed 0.00000001 (10-8). Any number of digits after the decimal point may be output as long as the precision conditions are met.

If you can't reach the escape point, print "impossible".

Sample Input


2
3
1 1
twenty two
1 2
3
-1 1
-1 2
-twenty two
1 1 2 2
0 0 0 1
2
Ten
1 1
7 1
7 5
5 5
5 3
6 3
6 2
3 2
3 5
1 5
Ten
-1 1
-1 5
-3 5
-3 3
-twenty three
-twenty two
-5 2
-5 5
-7 5
-7 1
2 4 6 4
0 0 0 1
2
7
-7 0
-5 0
-4 3
-3 0
-Ten
-14
-7 4
3
Ten
7 0
7 4
3 1 5 2
0 0 0 1
0


Output for Sample Input


1.4142135623730951
8.0 8.0
impossible impossible


The following figures show the arrangement of each sample. In Figures G-1 and G-2, the red path shows the hero's own path, and the blue path shows the hero's mirror image path. In Figure G-3, it is not possible to reach the escape point from the start point.

<image>

Figure G-1: First sample

<image>

Figure G-2: Second sample

<image>

Figure G-3: Third sample





Example

Input

2
3
1 1
2 2
1 2
3
-1 1
-1 2
-2 2
1 1 2 2
0 0 0 1
2
10
1 1
7 1
7 5
5 5
5 3
6 3
6 2
3 2
3 5
1 5
10
-1 1
-1 5
-3 5
-3 3
-2 3
-2 2
-5 2
-5 5
-7 5
-7 1
2 4 6 4
0 0 0 1
2
7
-7 0
-5 0
-4 3
-3 0
-1 0
-1 4
-7 4
3
1 0
7 0
7 4
3 1 5 2
0 0 0 1
0


Output

1.4142135623730951
8.0
impossible
#include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define SIZE 105
#define G_SIZE 2005

struct Point{
	Point(double arg_x,double arg_y){
		x = arg_x;
		y = arg_y;
	}

	Point(){
		x = y = 0.0;
	}

	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); }

	double abs(){ return sqrt(norm()); }
	double norm(){ return x*x + y*y; }

	bool operator<(const Point &p) const{
		return 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;
	}

	double x,y;
};

typedef Point Vector;
typedef vector<Point> Polygon;

double calc_dist(Point A,Point B){

	return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}

struct Line{
	Line(){

	}
	Line(Point a,Point b){
		p[0] = a;
		p[1] = b;
	}
	Point p[2];
};

struct Edge{
	Edge(int arg_to,double arg_cost){
		to = arg_to;
		cost = arg_cost;
	}
	int to;
	double cost;
};

struct Info{
	Info(int arg_node_id,double arg_sum_dist){
		node_id = arg_node_id;
		sum_dist = arg_sum_dist;
	}
	bool operator<(const struct Info &arg) const{

		return sum_dist > arg.sum_dist; //総距離の昇順(PQ)
	}

	int node_id;
	double sum_dist;
};


int N;
int base_index,rev_index;
double min_dist[G_SIZE];
Polygon POLYGON[SIZE],REV,BASE;
Point start,goal,rev_start,rev_goal;
Line LINE;
vector<Edge> G[G_SIZE];



Point calc_Reflection_Point(double x1,double y1,double x2,double y2,double xp,double yp){

	Point ret;

	bool X_FLG = false,Y_FLG = false;
	double slope;

	if(y1 == y2){
		X_FLG = true;
	}else if(x1 == x2){
		Y_FLG = true;
	}else{
		slope = (y2-y1)/(x2-x1);
	}

	if(X_FLG){
		ret.x = xp,ret.y=y1;
	}else if(Y_FLG){
		ret.x = x1,ret.y = yp;
	}else{
		ret.x = (yp*(x2-x1)*(y2-y1)+xp*(x2-x1)*(x2-x1)-y1*(y2-y1)*(x2-x1)+x1*(y2-y1)*(y2-y1))/((y2-y1)*(y2-y1)+(x2-x1)*(x2-x1));
		ret.y = ((x1-x2)*ret.x+yp*(y2-y1)+xp*(x2-x1))/(y2-y1);
	}
	ret.x = 2*ret.x-xp;
	ret.y = 2*ret.y-yp;

	return ret;
}

Point calc_Reflection_Point(Line line,Point point){

	return calc_Reflection_Point(line.p[0].x,line.p[0].y,line.p[1].x,line.p[1].y,point.x,point.y);
}




int func(double x1,double y1,double x2, double y2, double xp, double yp){
	double naiseki,norm1,norm2,gaiseki;
	norm1 = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
	norm2 = sqrt((xp-x1)*(xp-x1)+(yp-y1)*(yp-y1));
	naiseki = (xp-x1)*(x2-x1)+(yp-y1)*(y2-y1);
	gaiseki = (x2-x1)*(yp-y1)-(xp-x1)*(y2-y1);
	if(gaiseki > EPS){
		return 1;
	}else if(gaiseki < -EPS){
		return -1;
	}
	if(naiseki < -EPS){
		return 2;
	}

	if(norm1 < norm2){
		return -2;
	}
	return 0;
}


//★★直線ではなく、線分の交差判定★★
bool is_Cross(Line a,Line b){

	if(func(a.p[0].x,a.p[0].y,a.p[1].x,a.p[1].y,b.p[0].x,b.p[0].y)*
			func(a.p[0].x,a.p[0].y,a.p[1].x,a.p[1].y,b.p[1].x,b.p[1].y) <= 0 &&
			func(b.p[0].x,b.p[0].y,b.p[1].x,b.p[1].y,a.p[0].x,a.p[0].y)*
			func(b.p[0].x,b.p[0].y,b.p[1].x,b.p[1].y,a.p[1].x,a.p[1].y) <= 0){
		return true;
	}
	return false;
}


Point calc_minus(Point a,Point b){
    Point ret;

    ret.x = a.x-b.x;
    ret.y = a.y-b.y;

    return ret;
}

double calc_len(Vector a){
    return sqrt(a.x*a.x+a.y*a.y);
}

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

double calc_slope(Line A){

	if(fabs(A.p[0].x-A.p[1].x) < EPS){

		return DBL_MAX;

	}else if(fabs(A.p[0].y-A.p[1].y) < EPS){

		return 0;

	}else{

		return (A.p[0].y-A.p[1].y)/(A.p[0].x-A.p[1].x);
	}
}

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

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

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


double getDistanceLP(Line l,Point p){
    return fabs(cross(calc_minus(l.p[1],l.p[0]),calc_minus(p,l.p[0]))/calc_len(calc_minus(l.p[1],l.p[0])));
}

//★★点と線分の距離★★
double getDistanceSP(Line l,Point p){
	if(dot(l.p[1]-l.p[0],p-l.p[0]) < 0.0) return abs(p-l.p[0]);
	if(dot(l.p[0]-l.p[1],p-l.p[1]) < 0.0) return abs(p-l.p[1]);
	return getDistanceLP(l,p);
}

//交点を求める関数
Point calc_Cross_Point(double x1,double x2,double x3,double x4,double y1,double y2,double y3,double y4){
	Point ret;
	ret.x = ((x2-x1)*(y3*(x4-x3)+x3*(y3-y4))-(x4-x3)*(y1*(x2-x1)+x1*(y1-y2)))/((y2-y1)*(x4-x3)-(y4-y3)*(x2-x1));
	if(x1 != x2){
		ret.y = ((y2-y1)*ret.x+y1*(x2-x1)+x1*(y1-y2))/(x2-x1);
	}else{
		ret.y = ((y4-y3)*ret.x+y3*(x4-x3)+x3*(y3-y4))/(x4-x3);
	}
	return ret;
}

//インタフェース関数
Point calc_Cross_Point(Point a,Point b,Point c,Point d){
	return calc_Cross_Point(a.x,b.x,c.x,d.x,a.y,b.y,c.y,d.y);
}

Point calc_Cross_Point(Line A,Line B){

	if(getDistanceSP(B,A.p[0]) < EPS){

		return A.p[0];

	}else if(getDistanceSP(B,A.p[1]) < EPS){

		return A.p[1];

	}else if(getDistanceSP(A,B.p[0]) < EPS){

		return B.p[0];

	}else if(getDistanceSP(A,B.p[1]) < EPS){

		return B.p[1];
	}

	return calc_Cross_Point(A.p[0],A.p[1],B.p[0],B.p[1]);
}

/*
 * IN 2
 * ON 1
 * OUT 0
 *
 */
int contains(Polygon g,Point p){
	int n = g.size();
	bool x = false;
	for(int i = 0; i < n; i++){
		Point a = g[i]-p,b = g[(i+1)%n]-p;
		if(abs(cross(a,b)) < EPS && dot(a,b) < EPS)return 1;
		if(a.y > b.y)swap(a,b);
		if(a.y < EPS && EPS < b.y && cross(a,b) > EPS) x = !x;
	}
	return (x ? 2:0);
}


bool contains(Polygon g,Line l){

	if(contains(g,l.p[0]) == 0 || contains(g,l.p[1]) == 0){

		return false;
	}

	Polygon TMP;

	TMP.push_back(l.p[0]);
	TMP.push_back(l.p[1]);

	for(int i = 0; i < g.size(); i++){
		Line tmp_line = Line(g[i],g[(i+1)%g.size()]);

		if(is_Cross(tmp_line,l)){

			TMP.push_back(calc_Cross_Point(l,tmp_line));
		}
	}

	sort(TMP.begin(),TMP.end());
	TMP.erase(unique(TMP.begin(),TMP.end()),TMP.end());

	for(int i = 0; i < TMP.size()-1; i++){

		Point mid = Point((TMP[i].x+TMP[i+1].x)/2,(TMP[i].y+TMP[i+1].y)/2);
		if(contains(g,mid) == 0){

			return false;
		}
	}

	return true;
}

bool is_OK(Line A){

	return contains(POLYGON[base_index],A) && contains(REV,A);
}

void func(){

	REV.clear();
	BASE.clear();

	for(int i = 0; i < SIZE; i++){

		POLYGON[i].clear();
	}

	for(int i = 0; i < G_SIZE; i++){

		G[i].clear();
	}

	int num_points;
	double X,Y;

	for(int i = 0; i < N; i++){

		scanf("%d",&num_points);

		for(int loop = 0; loop < num_points; loop++){

			scanf("%lf %lf",&X,&Y);

			POLYGON[i].push_back(Point(X,Y));
		}
	}

	scanf("%lf %lf %lf %lf",&start.x,&start.y,&goal.x,&goal.y);
	scanf("%lf %lf %lf %lf\n",&LINE.p[0].x,&LINE.p[0].y,&LINE.p[1].x,&LINE.p[1].y);

	//startがあるポリゴンを探す
	for(int i = 0; i < N; i++){

		if(contains(POLYGON[i],start) != 0){

			base_index = i;
			break;
		}
	}

	//少なくとも、rev_startとrev_goalが同じポリゴンの中にあるか調べる
	rev_start = calc_Reflection_Point(LINE,start);
	rev_goal = calc_Reflection_Point(LINE,goal);

	int rev_start_index = -1,rev_goal_index = -1;

	for(int i = 0; i < N; i++){

		if(contains(POLYGON[i],rev_start) != 0){

			rev_start_index = i;
			break;
		}
	}

	if(rev_start_index == -1){

		printf("impossible\n");
		return;
	}

	for(int i = 0; i < N; i++){

		if(contains(POLYGON[i],rev_goal) != 0){

			rev_goal_index = i;
			break;
		}
	}

	if(rev_start_index != rev_goal_index){

		printf("impossible\n");
		return;
	}

	rev_index = rev_start_index;

	//POLYGON[rev_index]の線対称ポリゴン作成
	for(int i = 0; i < POLYGON[rev_index].size(); i++){

		REV.push_back(calc_Reflection_Point(LINE,POLYGON[rev_index][i]));
	}

	BASE.push_back(start);
	BASE.push_back(goal);

	for(int i = 0; i < POLYGON[base_index].size(); i++){

		BASE.push_back(POLYGON[base_index][i]);
	}

	for(int i = 0; i < REV.size(); i++){

		BASE.push_back(REV[i]);
	}

	//POLYGON[base_index]と、REVの交点を追加する
	for(int i = 0; i < POLYGON[base_index].size(); i++){
		Line A = Line(POLYGON[base_index][i],POLYGON[base_index][(i+1)%POLYGON[base_index].size()]);
		for(int k = 0; k < REV.size(); k++){
			Line B = Line(REV[k],REV[(k+1)%REV.size()]);

			if(!is_Cross(A,B))continue;

			BASE.push_back(calc_Cross_Point(A,B));
		}
	}

	sort(BASE.begin(),BASE.end());
	BASE.erase(unique(BASE.begin(),BASE.end()),BASE.end());

	int V = BASE.size();

	int start_index,goal_index;

	for(int i = 0; i < V; i++){

		if(BASE[i] == start){

			start_index = i;

		}else if(BASE[i] == goal){

			goal_index = i;
		}
	}

	//エッジをあらかじめ計算しておく
	for(int i = 0; i < V-1; i++){
		for(int k = i+1; k < V; k++){

			double tmp_dist = calc_dist(BASE[i],BASE[k]);

			if(is_OK(Line(BASE[i],BASE[k]))){

				G[i].push_back(Edge(k,tmp_dist));
				G[k].push_back(Edge(i,tmp_dist));
			}
		}
	}

	for(int i = 0; i < V; i++){

		min_dist[i] = HUGE_NUM;
	}

	min_dist[start_index] = 0;

	priority_queue<Info> Q;
	Q.push(Info(start_index,0));

	int next_node;
	double next_dist;

	while(!Q.empty()){

		if(Q.top().sum_dist > min_dist[Q.top().node_id]+EPS){

			Q.pop();

		}else if(Q.top().node_id == goal_index){

			printf("%.10lf\n",Q.top().sum_dist);
			return;

		}else{

			for(int i = 0; i < G[Q.top().node_id].size(); i++){

				next_node = G[Q.top().node_id][i].to;
				next_dist = Q.top().sum_dist+G[Q.top().node_id][i].cost;

				if(min_dist[next_node] > next_dist+EPS){
					min_dist[next_node] = next_dist;
					Q.push(Info(next_node,next_dist));
				}
			}

			Q.pop();
		}
	}

	printf("impossible\n");
}

int main(){

	while(true){

		scanf("%d",&N);
		if(N == 0)break;

		func();
	}

	return 0;
}

