#include <utility>
#include <iostream>
#include <vector>

using namespace std;

/* Maximum vertex coordinate value. Used for error checking */
#define MAXPOS 100

/* A 2D point (or 2D vector) is represented as a pair of doubles */
typedef pair<double, double> point;

/* A line/ray/segment is uniquely defined by two points */
typedef pair<point, point> line;

/* Polygons are represented as a list of vertex points */
typedef vector<point> polygon;

/* A dataset consists of a polygon list */
typedef vector<polygon> dataset;

/* A pair of integers used to keep track of intersecting polygon numbers */
typedef pair<int, int> intpair;

/* List of integer pairs keeps track of all intersecting polygon numbers */
typedef vector<intpair> resultset;

/* Adding/Subtracting 2D points/vectors together adds/subtracts dimensions */
inline point operator+(point const &a, point const &b) {
    return point(a.first + b.first, a.second + b.second);
}
inline point operator-(point const &a, point const &b) {
    return point(a.first - b.first, a.second - b.second);
}

/* Multiplying by a scalar, simply scales length of the vector */
inline point const operator*(double i, point const &o) {
    return point(i * o.first, i * o.second);
}

/* Multiplying 2D vectors together computes dot product */
inline double operator*(point const &a, point const &b)
{
    return (a.first * b.first) + (a.second * b.second);
}

/* The ~ is a "perp operator". Rotates vector 90 degrees to the left */
inline point operator~(point const &a)
{
    return point(-a.second, a.first);
}

/* Extraction operator for a 2D point reads coordinates of the form "x,y" */
istream &operator>>(istream &in, point &p)
{
    char c;

    /* Parse the coordinate points */
    cin >> p.first >> c >> p.second;

    /* Check for invalid coordinate points */
    if(p.first < 0 || p.second < 0 || p.first > MAXPOS || p.second > MAXPOS ||
      c != ',') {
        throw "Invalid coordinate";
    }
    
    return in;
}

/*
 * Generic extraction operator to read a list of arbitrary items. First we read
 * an integer item count from input. Then we read that many items of type T
 * from the input. Finally, we store all of the items in a STL vector. Each
 * item is read by using it's own >> operator..
 */
template<class T> istream &operator>>(istream &in, vector<T> &list)
{
    int item_idx, item_num;
    
    /* Read in the item count */
    in >> item_num;
    
    /* Read in each individual item and append to list */
    for(item_idx = 0; item_idx < item_num; item_idx++) {
        T item;        
        in >> item;
        list.push_back(item);
    }
    
    return in;
}

/*
 * Computes the intersection between two lines segments and returns the
 * parametric values for the two vector equations at the intersection
 * point. If both values are between 0 and 1, then the intersection
 * occurs within the line segment ( otherwise the lines intersect but
 * not the segments). If the line are parallel we get a division by 0
 * and infinities are returned. If the segments are collinear, we get 0
 * divided by 0 and NotANumbers are returned.
 *
 * For reference see:
 * http://www.geometryalgorithms.com/Archive/algorithm_0104/algorithm_0104B.htm
 * http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
 */
point intersect(line const &p, line const &q)
{
    point param;

    /* Compute the w, u, and v vectors and common denominator */
    point w = p.first - q.first;
    point u = p.second - p.first;
    point v = q.second - q.first;
    double denom = ~v * u;
    
    /* Compute the parametric value for each line at point of intersection */
    param.first = (~w * v) / denom;
    param.second = (~u * w) / -denom;
    
    return param;
}

/*
 * Return true if point pt in inside or on the edge of polygon poly.
 * For debug purposes, we return a -1 if the point is on edge and a
 * 1 if it's strickly inside the poly.
 * 
 * This algorithm casts a ray in the right direction from point pt and
 * counts how many line segments the ray intersects. Since the polygon
 * is a closed curve, a point outside the polygon will intersect an
 * even number of line segments. There are a few special cases to
 * handle:
 *
 * Rules #1 and #4 check if the point is on a line segment. Without
 * these, a point of the left edge of a poly would be counted as
 * inside and a point on the right side as being outside.
 *
 * Rules #2 and #3 check if the ray passes through the end/start
 * of a line segment. Without these rules, both line segments
 * would be incorrectly counted.
 *
 * For reference see:
 * http://www.geometryalgorithms.com/Archive/algorithm_0103/algorithm_0103.htm
 * http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/
 */
int check_point(point const &pt, polygon const &poly)
{
    int counter = 0;

    /* Create a ray originating at "pt" and going to the right (+1, 0) */
    line r1( pt, point(pt.first + 1, pt.second) );
    
    /* Check intersection of ray with each line segment in poly */
    for(int i = 0; i < poly.size(); i++) {
        
        /* Current line segment from polygon being checked */
        line l2( poly[i], poly[ (i + 1) % poly.size() ] );
        
        /* Get parametric values at point of intersection */
        point v = intersect(r1, l2);

        /* Count number of intersections between ray and inside of segment */
        if(v.first >= 0 && v.second >= 0 && v.second <= 1) {

            /* Rule #1: A point on edge */
            if(v.first < 0.001) {
                return -1;
            }

            /* Rule #2: Upward edge excludes end point */
            if(v.second > 0.999 && l2.first.second > l2.second.second) {
                continue;
            }
        
            /* Rule #3: Downward edge excludes start point */
            if(v.second < 0.001 && l2.first.second < l2.second.second) {
                continue;
            }
                
            counter++;
        }
        
        /* Rule #4: Ray collinear with horizontal edge; v.first == NaN */
        if( !(v.first <= 0) && !(v.first > 0) ) {

            /* Check if the point is actually within the horizontal edge */
            if(pt.first >= l2.first.first && pt.first <= l2.second.first) {            
                return -1;
            }
            
            /* Same check but for edge with start/end reversed */
            if(pt.first >= l2.second.first && pt.first <= l2.first.first) {
                return -1;
            }
        }
    }
    
    /* If ray intersects odd number of segments, then pt must be inside */
    return (counter % 2) ? 1 : 0;
}

/*
 * Check if line segment l1 intersects any of the line sefments from
 * poly. The num1 and num2 arguments are only for printing of debug
 * output.
 */
bool check_line(line const &l1, polygon const &poly, int num1, int num2)
{
    /* Assume no intersections until proven otherwise */
    bool result = false;

    /* Check intersection of line segment l1 with each line segment in poly */
    for(int i = 0; i < poly.size(); i++) {
        
        /* Current line segment from polygon being checked */
        line l2( poly[i], poly[ (i + 1) % poly.size() ] );
        
        /* Get parametric values at point of intersection */
        point v = intersect(l1, l2);

        /* Check if intersection is within line segments (but not on edge)*/
        if(v.first > 0 && v.first < 1 && v.second > 0 && v.second < 1) {
#ifdef DEBUG
            /* Print coordinates of segments being intersected */
            cerr << "(" << l1.first.first << "," << l1.first.second << "),";
            cerr << "(" << l1.second.first << "," << l1.second.second << ") x ";
            cerr << "(" << l2.first.first << "," << l2.first.second << "),";
            cerr << "(" << l2.second.first << "," << l2.second.second << ") @ ";
            
            /* Print coordinates of intersection point */
            point pi = l1.first + (v.first * (l1.second - l1.first));
            cerr << "(" << pi.first << "," << pi.second << ") ";
            cerr << "from poly #" << num1 + 1 << " and #" << num2 + 1 << endl;
#endif
            result |= true;
        }
    }
    
    return result;
}

/*
 * Return true if polygons num1 and num2 from the dataset intersect
 * with each other. This does both a line segment intersection test
 * and a point in polygon test.
 * 
 * Both tests are necessary. The line segment intersection will not
 * catch the case of one polygon completely enclosed in another and
 * the point in polygon test will not catch the case of two rectangles
 * intersecting each other.
 */
bool check(dataset const &dataset, int num1, int num2)
{
    /* Assume polygons don't overlap until proven otherwise */
    bool result = false;

    /* The polygons to check for intersection */
    polygon const p1 = dataset[num1];
    polygon const p2 = dataset[num2];

    /* Intersect each segment of poly1 against all segments of poly2 */
    for(int i = 0; i < p1.size(); i++) {
        
        /*
         * Each line segment is defined by two polygon vertices. Using
         * "(index + 1) % size", ensures that we consider the implied
         * segment between the first and last vertex of a polygon.
         */
        line l1( p1[i], p1[ (i + 1) % p1.size() ] );

        /* Check if line segment l1 intersects any segments in polygon p2 */
        if(check_line(l1, p2, num1, num2)) {
            result |= true;
        }
    }
    
    /* Check if any vertex of poly1 lies inside the area of poly2 */
    for(int i = 0; i < p1.size(); i++) {
        if(int rc = check_point(p1[i], p2)) {
#ifdef DEBUG
            cerr << "(" << p1[i].first << "," << p1[i].second << ") ";
            cerr << "from poly #" << num1 + 1 << " ";
            cerr << (rc > 0 ? "inside #" : "on edge of #");
            cerr << num2 + 1 << endl;
#endif
            result |= true;
        }
    }

    return result;
}

/* Main body of program */
void process(void)
{
    int data_num, data_idx;

    /* Read how many data sets to process */
    cin >> data_num;
    
    /* Process each data set separately */
    for(data_idx = 0; data_idx < data_num; data_idx++) {
        resultset output;
        dataset input;

        /* Read in the list of polygons */
        cin >> input;
        cout << "Data Set #" << data_idx + 1 << endl;

        /* Check all polygons for intersection with each other */
        for(int i = 0; i < input.size(); i++) {
            for(int j = 0; j < input.size(); j++) {
            
                /* Do not check polygon for intersection with itself */
                if(i != j) {
                
                    /* If polys interst, store their (1 based) numbers */                
                    if(check(input, i, j)) {
                        if(i < j) {
                            output.push_back(intpair(i + 1, j + 1));
                        } else {
                            output.push_back(intpair(j + 1, i + 1));
                        }
                    }
                }
            }
        }
       
        /* Vector must be sorted for unique() to work correctly */
        sort(output.begin(), output.end());
       
        /* Print the collision list without any duplicates */
        if(output.size()) {
            resultset::iterator end = unique(output.begin(), output.end());
            
            for(resultset::iterator i = output.begin(); i != end; ++i) {
                cout << i->first << " " << i->second << endl;
            }
        }
        
        /* Or print a message if data set has no collisions */
        else {
            cout << "no collisions" << endl;
        }
    }
}

/* Run program and print out any exceptions that occur */
int main(void)
{
    /* Throw exceptions on EOF or failed data extraction in >> operator */
    cin.exceptions(ios::eofbit | ios::failbit);
    
    /* For debug only when printing intersection points */
    cerr.precision(3);

    /* Run main body of code */
    try {
        process();
    }
    
    /* Catch any internally generated exceptions */
    catch(char const *e) {
        cerr << "Exception: " << e << endl;
    }
    
    /* Catch unexpected EOF or bad input data */
    catch(ios::failure const &e) {
        cerr << "Unexpected EOF or data type mismatch on input" << endl;
    }

    return 0;
}