Problem: 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

Solution (in CPP):

Solution:
#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-8)
#define COUNTER_CLOCKWISE 1
#define CLOCKWISE -1 
#define ONLINE_BACK 2
#define ONLINE_FRONT -2
#define ON_SEGMENT 0
#define equals(a,b) (fabs((a)-(b)) < EPS)

using namespace std;

const int IINF = INT_MAX;
// template - begin
class Point{
public:
  double x,y;

  Point(double x = 0,double y = 0): 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 ) || ( p.p1 == p2 && p.p2 == p1 ); }
  bool operator < (const Segment& s) const {
    if( !( p1 == s.p1 ) ) return p1 < s.p1;
    return p2 < s.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);
}
// template - end

// ccw - begin
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 - begin
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)));
}
bool DEBUG = false;
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());
    // while(DEBUG);
    cerr << l << " ! " << m << endl;
    assert(vec[1] == vec[2]); //同じセグメントかもよ
    return vec[1];
  }
  if(abs(A) < EPS)assert(false);
  return m.p1 + (m.p2-m.p1)*(B/A);
}
// intersect - end

// 　点の内包判定 - begin
double cross3p(Point p,Point q,Point r){ return (r.x-q.x) * (p.y -q.y) - (r.y - q.y) * (p.x - q.x); }
bool collinear(Point p,Point q,Point r){ return fabs(cross3p(p,q,r)) < EPS; }
bool ccwtest(Point p,Point q,Point r){ return cross3p(p,q,r) > 0; }
bool onSegment(Point p,Point q,Point r){
  return collinear(p,q,r) && equals(sqrt(pow(p.x-r.x,2)+pow(p.y-r.y,2)) + sqrt(pow(r.x-q.x,2) + pow(r.y-q.y,2) ),sqrt(pow(p.x-q.x,2)+pow(p.y-q.y,2)) ) ;
}

double angle(Point a,Point b,Point c){
  double ux = b.x - a.x, uy = b.y - a.y;
  double vx = c.x - a.x, vy = c.y - a.y;
  return acos((ux*vx + uy*vy)/sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)));
}  
  
//多角形poly内（線分上も含む）に点pが存在するかどうは判定する  
bool inPolygon(Polygon poly,Point p){
  if((int)poly.size() == 0)return false;

  rep(i,poly.size())
    if(onSegment(poly[i],poly[(i+1)%poly.size()],p))return true;
  double sum = 0;
  for(int i=0; i < (int)poly.size() ;i++)
    {
      if( equals(cross(poly[i]-p,poly[(i+1)%poly.size()]-p),0.0) ) continue;
      if(cross3p(p,poly[i],poly[(i+1)%poly.size()]) < 0) sum -= angle(p,poly[i],poly[(i+1)%poly.size()]);
      else sum += angle(p,poly[i],poly[(i+1)%poly.size()]);
    }
  const double eps = 1e-5;
  return (fabs(sum - 2*M_PI) < eps || fabs(sum + 2*M_PI) < eps);
}  
// 点の内包判定 - end

// 線分の内包判定 - begin
bool inPolygon(const Polygon &poly,Segment seg){
  vector<Point> vp;
  vp.push_back(seg.p1);
  vp.push_back(seg.p2);
  rep(i,(int)poly.size()) {
    Segment edge = Segment(poly[i],poly[(i+1)%(int)poly.size()]);
    if( equals(cross(seg.p1-seg.p2,edge.p1-edge.p2),0.0) ) { // 後で変更
      if( onSegment(seg.p1,seg.p2,edge.p1) ) vp.push_back(edge.p1);
      if( onSegment(seg.p1,seg.p2,edge.p2) ) vp.push_back(edge.p2);
    } else {
      DEBUG = true;
      if( intersectSS(seg,edge) ) vp.push_back(crosspoint(seg,edge));
      DEBUG = false;
    }
  }
  sort(vp.begin(),vp.end());
  vp.erase(unique(vp.begin(),vp.end()),vp.end());
  rep(i,(int)vp.size()-1) {
    Point middle_point = ( vp[i] + vp[i+1] ) / 2.0;
    if( !inPolygon(poly,middle_point) ) return false;
  }
  return true;  
}
// 線分の内包判定 - end

struct Node{
  int cur;
  double weight;
  bool operator < (const Node& node) const { return weight > node.weight; }
};

struct Edge{
  int from,to;
  double cost;
  Edge(int from=IINF,int to=IINF,double cost=IINF):from(from),to(to),cost(cost){}
  bool operator < (const Edge& a)const{ return cost < a.cost; }
};


const string NO = "impossible";
const int MAX_N = 110;
const int MAX_V = 100000;
int N;
Polygon polies[MAX_N];
Point sp,ep;
Line mirror;
double mindist[MAX_V];

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

//Point pを含む多角形のインデックスを返す,存在しない場合は-1
int selectGraph(Point p){
  rep(i,N) if( inPolygon(polies[i],p) ) return i;
  return -1;
}

void dijkstra(const vector<vector<Edge> > &G,const vector<Point> &ps){
  priority_queue<Node> Q;
  int start = -1, goal = -1, V = ps.size();
  assert( V < MAX_V );
  rep(i,V) {
    if( start == -1 && ps[i] == sp ) start = i;
    if( goal  == -1 && ps[i] == ep ) goal  = i;
    if( start != -1 && goal != -1  ) break;
  }
  //assert( start != -1 && goal != -1 );
  rep(i,V) mindist[i] = 1e20;
  Q.push((Node){start,0});
  mindist[start] = 0;
  while( !Q.empty() ) {
    Node node = Q.top(); Q.pop();
    int cur = node.cur;
    double weight = node.weight;

    if( cur == goal ) {
      printf("%.12lf\n",weight);
      return;
    }

    if( LT(mindist[cur],weight) ) continue;
    rep(i,G[cur].size()){
      int to = G[cur][i].to;
      double new_weight =  weight + G[cur][i].cost;
      if( LT(new_weight,mindist[to]) ) {
        mindist[to] = new_weight;
        Q.push((Node){to,mindist[to]});
      }
    }
  }
  puts(NO.c_str());
}

void compute(){
  int indices[4] = {selectGraph(sp),selectGraph(ep),selectGraph(reflection(mirror,sp)),selectGraph(reflection(mirror,ep))};

  bool failed = false;
  rep(i,4) if( indices[i] == -1 ) { failed = true; break; }
  if( indices[0] != indices[1] || indices[2] != indices[3] ) failed = true;
  if( failed ) { puts(NO.c_str()); return; } 
  rep(i,N) if( i != indices[0] && i != indices[2] ) polies[i].clear();

  Polygon Real = polies[indices[0]];
  Polygon Mirr = polies[indices[2]];
  Polygon Mixi = Real;
  rep(i,(int)Mirr.size()) Mixi.push_back(reflection(mirror,Mirr[i]));
  Mixi.push_back(sp), Mixi.push_back(ep);  
  sort(Mixi.begin(),Mixi.end());
  Mixi.erase(unique(Mixi.begin(),Mixi.end()),Mixi.end());
 
  vector<Segment> segment_buffer;
  rep(i,(int)Mixi.size()) REP(j,i+1,(int)Mixi.size()) {
     Line line = Segment(Mixi[i],Mixi[j]);
    vector<Point> vp;
    vp.push_back(Mixi[i]);
    vp.push_back(Mixi[j]);
    rep(k,Real.size()) {
      Segment s = Segment(Real[k],Real[(k+1)%Real.size()]);
      if( equals(cross(s.p1-s.p2,line.p1-line.p2),0.0) ) {
        if( onSegment(line.p1,line.p2,s.p1) ) vp.push_back(s.p1);
        if( onSegment(line.p1,line.p2,s.p2) ) vp.push_back(s.p2);
      } else {
        if( intersectLS(line,s) )vp.push_back(crosspoint(line,s));
      }
    }
    sort(vp.begin(),vp.end());
    vp.erase(unique(vp.begin(),vp.end()),vp.end());
    rep(k,(int)vp.size()-1) {
      Segment  s = Segment(vp[k],vp[k+1]);
      Segment rs = Segment(reflection(mirror,s.p1),reflection(mirror,s.p2));
      if( inPolygon(Real,s) && inPolygon(Mirr,rs) ) segment_buffer.push_back(s);
    }
  }

  sort(segment_buffer.begin(),segment_buffer.end());
  segment_buffer.erase(unique(segment_buffer.begin(),segment_buffer.end()),segment_buffer.end());
  int dex = 0;
  map<Point,int> mp;
  vector<Point> ps;
  rep(i,segment_buffer.size()) {
    if( !mp.count(segment_buffer[i].p1) ) {
      mp[segment_buffer[i].p1] = dex++;
      ps.push_back(segment_buffer[i].p1);
    }
    if( !mp.count(segment_buffer[i].p2) ) {
      mp[segment_buffer[i].p2] = dex++;
      ps.push_back(segment_buffer[i].p2);
    }
  }

  vector<vector<Edge> > G(dex,vector<Edge>());
  rep(i,segment_buffer.size()) {
    G[mp[segment_buffer[i].p1]].push_back(Edge(mp[segment_buffer[i].p1],mp[segment_buffer[i].p2],abs(segment_buffer[i].p1-segment_buffer[i].p2)));
    G[mp[segment_buffer[i].p2]].push_back(Edge(mp[segment_buffer[i].p2],mp[segment_buffer[i].p1],abs(segment_buffer[i].p1-segment_buffer[i].p2)));
  }
  dijkstra(G,ps);
}

int main(){
  while( cin >> N, N ){  
    rep(i,N){
      int M;
      cin >> M;
      polies[i].resize(M);
      rep(j,M) cin >> polies[i][j].x >> polies[i][j].y;
    }
    cin >> sp.x >> sp.y >> ep.x >> ep.y;
    cin >> mirror.p1.x >> mirror.p1.y >> mirror.p2.x >> mirror.p2.y;
    compute();
  }
  return 0;
}
/*
スタートとゴールがある多角形を選ぶ
鏡の向こうの多角形も１つ選ぶ
鏡に対称にして多角形を移動する
鏡の向こうの選ばれし多角形を鏡を基準に折りたたむ
こうしてできたものの各線分で移動可能なものをつめこみグラフ化する
できあがったグラフをつかってdijkstra
 */