import java.util.*;
import java.io.*;

public class TestGen implements Runnable {
    private ArrayList<String> data;
    private int done;

    private Random rnd;

    private void print() throws IOException {
        String filename = "" + (done + 1);
        if (done < 9) {
            filename = "0" + filename;
        }
        PrintWriter out = new PrintWriter(new File(curOut + "\\" + filename));
        for (String aData : data) {
            out.println(aData.trim());
        }
        out.close();
        done++;
        System.out.println(done);
        data.clear();
    }

    private int nextInt(int low, int high) {
        return Math.abs(rnd.nextInt()) % (high - low + 1) + low;
    }

    private long nextLong(long low, long high) {
        return Math.abs(rnd.nextLong()) % (high - low + 1) + low;
    }


    String curOut;

    public void initOut(String folderName) {
        done = 0;
        curOut = folderName;
        File testDir = new File(curOut);
        if (!testDir.exists()) {
            testDir.mkdir();
        }
    }

    private final int VERYSMALL = 10;
    private final int SMALL = 1000;
    private final int MEDIUM = (int) 1e5;
    private final int LARGE = (int) 1e6;

    private final long TINY = (long) 1e6;
    private final long HUGE = (long) 1e18;


    public void run() {
        try {
            data = new ArrayList<String>();
            rnd = new Random(9436756);

            initOut("..\\preliminary");
            genSample1();


            initOut("..\\tests");

            // small tests: n <= 1000
            genRandom(3, 3, TINY, false);
            genRandom(3, 3, HUGE, false);
            genRandom(VERYSMALL / 3, VERYSMALL, TINY, false);
            genRandom(VERYSMALL / 3, VERYSMALL, HUGE, false);

            for (int i = 0; i < 3; i++) {
                genRandom(SMALL / 3, SMALL, TINY, false);
                genRandom(SMALL / 3, SMALL, HUGE, false);
            }

            // medium tests for n * log n solutions

            for (int i = 0; i < 7; i++) {
                genRandom(MEDIUM / 2, MEDIUM, HUGE, false);
                genRandom(MEDIUM / 2, MEDIUM, HUGE, true);
            }
            genRandom(MEDIUM / 2, MEDIUM, HUGE, false);

        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    public void genMax() throws IOException {
        int n = LARGE;
        long maxC = HUGE / 3;
        long l = maxC * 2 + 1;
        Polygon p = new Polygon();
        p.size = 4;

        Point a = new Point(1, maxC);
        Point b = new Point(0, maxC);
        Point c = new Point(0, 0);
        Point d = new Point(maxC, 0);
        p.points.add(a);
        p.points.add(b);
        p.points.add(c);
        p.points.add(d);
        p.missing.add(0);
        p.missing.add(2);
        p.noSplit.add(true);
        p.noSplit.add(true);
        p.noSplit.add(true);
        p.noSplit.add(false);
        p.buildRandom(n, maxC, c);

        p.makeHoles(true);
        p.lineLength = l;
        p.dump();
        print();
        p.reverse();
        p.dump();
        print();
    }

    public void genRandom(int minN, int maxN, long maxC, boolean lotsOfHoles) throws IOException {
        Polygon p = new Polygon(nextInt(minN, maxN), maxC, lotsOfHoles);
        if (maxN == MEDIUM && p.lineLength == 0) {
            p.lineLength = (long) (Math.min(HUGE, p.totalLen / 2));
        }
        if (maxC == TINY && nextInt(0, 1) == 0) {
            p.lineLength = HUGE;
        }
        p.dump();
        print();
        p.reverse();
        p.dump();
        print();
    }


    public class Point {
        double x;
        double y;

        public Point(double xx, double yy) {
            x = xx;
            y = yy;
        }

        public Point(long maxC) {
            x = nextLong(-maxC, maxC);
            y = nextLong(-maxC, maxC);
        }

        public Point add(Point other) {
            return new Point(x + other.x, y + other.y);
        }

        public Point divide(double d) {
            return new Point(x / d, y / d);
        }

        public Point multiply(double d) {
            return this.divide(1.0 / d);
        }

        @Override
        public Point clone() {
            return new Point(x, y);
        }

        public Point subtract(Point other) {
            return this.add(other.multiply(-1));
        }

        public void normalise() {
            x /= this.length();
            y /= this.length();
        }

        private double length() {
            return Math.sqrt(x * x + y * y);
        }

        private void trunc() {
            x = Math.round(x);
            y = Math.round(y);
        }
    }

    private boolean checkLeft(Point a, Point b, Point c) {
        return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x) > 0;
    }

    private boolean outOfBox(Point p, long maxC) {
        return (Math.abs(p.x) > maxC || Math.abs(p.y) > maxC);
    }

    private double dist(Point a, Point b) {
//        return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
        Point v = a.subtract(b);
        if (Math.abs(v.x) < 1e9 && Math.abs(v.y) < 1e9) {
            return Math.sqrt(v.x * v.x + v.y * v.y);
        }
        double norm = v.x;
        if (v.x == 0 || Math.abs(v.y) > Math.abs(v.x)) {
            norm = v.y;
        }
        v = v.divide(norm);
        v.normalise();
        return Math.abs((a.y - b.y) / v.y);
    }

    public class Polygon {
        int size;
        ArrayList<Point> points;
        ArrayList<Boolean> noSplit;
        ArrayList<Integer> missing;

        double totalLen;
        double totalMissing;
        long lineLength;

        public Polygon() {
            points = new ArrayList<Point>();
            noSplit = new ArrayList<Boolean>();
            missing = new ArrayList<Integer>();
        }

        public Polygon(int n, long maxC, boolean lotsOfHoles) {
            size = 3;
            Point a = new Point(-maxC, -maxC);
            Point b = new Point(maxC, 0);
            Point c = new Point(0, maxC);
            Point inside = a.add(b).add(c).divide(3.0);
            points = new ArrayList<Point>();
            missing = new ArrayList<Integer>();
            noSplit = new ArrayList<Boolean>();
            noSplit.add(false);
            noSplit.add(false);
            noSplit.add(false);
            noSplit.set(nextInt(0, 2), true);

            points.add(a);
            points.add(b);
            points.add(c);

            buildRandom(n, maxC, inside);
            makeHoles(lotsOfHoles);
        }

        private void makeHoles(boolean lotsOfHoles) {
            int last = -1;
            if (missing.size() > 0) {
                last = missing.get(missing.size() - 1);
            }
            while (last < size - 1) {
                int next = nextInt(last + 1, size - 1);
                if (lotsOfHoles) {
                    next = last + 2;
                }
                if (next >= size) {
                    break;
                }
                if (missing.size() > 0 && missing.get(0) == 0 && next == size - 1) {
                    break;
                }
                missing.add(next);
                totalMissing += dist(points.get(next), points.get((next + 1) % size));
                last = next + 1;
            }

            for (int i = 0; i < size; i++) {
                totalLen += dist(points.get(i), points.get((i + 1) % size));
            }
            lineLength = nextLong((long) (totalMissing), (long) totalLen - 100);

            int val = nextInt(0, 5);
            if (val == 0) {
                lineLength = 0;
            } else if (val == 1) {
                lineLength = (long) (totalLen) + 10;
            }

            lineLength = Math.min(lineLength, HUGE);
        }

        private void buildRandom(int n, long maxC, Point inside) {
            while (size < n) {
//                System.err.println("Generating polygon : " + size + " of " + n + " sides done");
                boolean ok = false;
                int deformate = 0;
                Point newPoint = new Point(0, 0);
//                int cnt = 0;
                while (!ok) {
//                    cnt++;
//                    System.err.println("# " + cnt);
                    deformate = nextInt(0, size - 1);
                    while (noSplit.get(deformate)) {
                        deformate = nextInt(0, size - 1);
                    }
                    Point first = points.get(deformate);
                    Point second = points.get((deformate + 1) % size);
                    double shift = rnd.nextDouble();
                    Point between = (first.multiply(shift)).add(second.multiply(1 - shift));
                    Point moveVector = between.subtract(inside);
                    moveVector.normalise();

                    newPoint = getNewPoint(points.get((deformate - 1 + size) % size), points.get(deformate),
                            points.get((deformate + 1) % size), points.get((deformate + 2) % size), between, moveVector, maxC, 1);
                    newPoint.trunc();
                    ok = checkLeft(points.get((deformate - 1 + size) % size), points.get(deformate), newPoint) &&
                            checkLeft(newPoint, points.get((deformate + 1) % size), points.get((deformate + 2) % size))
                            && checkLeft(points.get(deformate), newPoint, points.get((deformate + 1) % size));
                }
                size++;
                points.add((deformate + 1) % size, newPoint);
                noSplit.add((deformate + 1) % size, false);
            }
        }

        private Point getNewPoint(Point prePrev, Point prev, Point next, Point nextNext, Point onSide,
                                  Point moveVector, long maxC, int maxCnt) {
            double left = 0;
            double right = maxC;

            int cnt = 0;
            for (int i = 0; i < 100; i++) {
                double mid = (left + right) / 2;
                Point toCheck = onSide.add(moveVector.multiply(mid));
                if (outOfBox(toCheck, maxC) || !checkLeft(prePrev, prev, toCheck) || !checkLeft(toCheck, next, nextNext)) {
                    right = mid;
                } else {
                    left = mid;
                    cnt++;
                    if (cnt == maxCnt) {
                        break;
                    }
                }
            }
            return onSide.add(moveVector.multiply(left / 4));
        }

        public void reverse() {
            ArrayList<Point> newPoints = new ArrayList<Point>();
            for (int i = size - 1; i >= 0; i--) {
                newPoints.add(points.get(i));
            }
            ArrayList<Integer> newMissing = new ArrayList<Integer>();
            for (int holeNum : missing) {
                newMissing.add((size - 1 - holeNum - 1 + size) % size);
            }
            points = newPoints;
            missing = newMissing;
        }

        public void dump() {
            data.add(size + " " + lineLength + " " + missing.size());
            StringBuilder sb = new StringBuilder();
            Collections.sort(missing);
            for (int i = 0; i < missing.size(); i++) {
                sb.append(missing.get(i) + 1);
                sb.append(" ");
            }
            data.add(sb.toString());
            for (int i = 0; i < size; i++) {
                data.add((long) (points.get(i).x) + " " + (long) (points.get(i).y));
            }
        }

    }

    public void genSample1() throws IOException {
        data.add("6 4 3");
        data.add("1 3 5");
        data.add("0 0");
        data.add("3 0");
        data.add("4 1");
        data.add("3 2");
        data.add("0 2");
        data.add("-1 1");
        print();
    }

    public static void main(String[] args) {
        new TestGen().run();
    }
}