import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import java.util.List;

public class Tests {
    static final boolean GK_VERBOSE = false;
    static final boolean MB_VERBOSE = true;

    final int MIN_N = 3;
    final int MAX_N = 100000;
    final int MAX_C = 1000_000_000;

    int testNo = 0;
    PrintWriter out;
    static PrintWriter descriptions;
    Random random = new Random(7023842358235832895L);

    void beginTest(String description, Object... args) {
        ++testNo;
        descriptions.format("%03d: %s\n", testNo, String.format(description, args));
        descriptions.flush();
        System.out.format("%03d: %s\n", testNo, String.format(description, args));
        try {
            out = new PrintWriter(String.format("tests/%03d", testNo));
        } catch (FileNotFoundException e) {
            throw new AssertionError(e);
        }
    }

    class Vector {
        final int x, y, gcd;

        Vector(int x, int y) {
            assert x != 0 && y != 0;
            this.x = x;
            this.y = y;
            gcd = Math.abs(gcd(x, y));
        }


        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Vector that = (Vector) o;
            return this.x / this.gcd == that.x / that.gcd && this.y / this.gcd == that.y / that.gcd;
        }

        @Override
        public int hashCode() {
            return 31 * (x / gcd) + (y / gcd);
        }

        double length() {
            return Math.hypot(x, y);
        }

        double cos(Vector that) {
            return (this.x * (double) that.x + this.y * (double) that.y) / this.length() / that.length();
        }

        @Override
        public String toString() {
            return "(" + x + " -> " + y + "| " + gcd + ")";
        }
    }

    class Ratio {
        int nom;
        int den;

        Ratio(int nom, int den) {
            this.nom = nom;
            this.den = den;
        }

        @Override
        public String toString() {
            return nom + "/" + den;
        }
    }

    private int gcd(int a, int b) {
        if (a == 0) {
            return Math.abs(b);
        } else {
            return gcd(b % a, a);
        }
    }

    static class Point {
        final int x, y;

        Point(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public Point minus(Point that) {
            return new Point(this.x - that.x, this.y - that.y);
        }

        public long times(Point that) {
            return this.x * (long) that.y - this.y * (long) that.x;
        }

        @Override
        public String toString() {
            return "(" + x + ", " + y + ")";
        }
    }


    Point p(int x, int y) {
        return new Point(x, y);
    }

    private void assertBetween(int min, long value, int max) {
        assert min <= value && value <= max : value + " not between "  + min + " and " + max;
    }

    public void run() throws IOException {
        sampleTests();
        handmadeTests();
        randomTests();
    }

    public void sampleTests() {
        writeTest("Sample test 1", false, p(5, 1), p(2, 4), p(3, 7), p(7, 3));
        writeTest("Sample test 2", false, p(1, 1), p(1, 4), p(4, 7), p(7, 4), p(7, 1));
    }

    public void handmadeTests() {
        writeTest("Rectangle", true, p(0, 0), p(0, 20), p(10, 20), p(10, 0));
        writeTest("Square, rotated 45deg", true, p(0, -3), p(-3, 0), p(0, 3), p(3, 0));
        writeTest("Rectangle, rotated 45deg", true, p(0, 1), p(1, 0), p(-1, -2), p(-2, -1));
        writeTest("Tall symmetric triangle", true, p(-1, 0), p(0, 10), p(1, 0));
        writeTest("Wide symmetric triangle", true, p(-10, 0), p(0, 1), p(10, 0));
        writeTest("Tall asymmetric triangle", true, p(-1, 0), p(0, 10), p(2, 0));
        writeTest("Wide asymmetric triangle", true, p(-10, 0), p(0, 1), p(20, 0));
        writeTest("Tall symmetric trapezoid", true, p(-1, 0), p(-2, 10), p(2, 10), p(1, 0));
        writeTest("Wide symmetric trapezoid", true, p(-10, 0), p(-1, 1), p(1, 1), p(10, 0));
        writeTest("Tall asymmetric trapezoid", true, p(-1, 0), p(-2, 10), p(4, 10), p(2, 0));
        writeTest("Tall right asymmetric trapezoid", true, p(-1, 0), p(-2, 10), p(2, 10), p(2, 0));
        writeTest("Wide asymmetric trapezoid", true, p(-10, 0), p(-1, 1), p(1, 1), p(20, 0));
        writeTest("Rotated symmetric trapezoid", true, p(4, 0), p(0, 4), p(4, 12), p(12, 4));
        writeTest("Rotated tall symmetric trapezoid", true, p(4, 0), p(0, 4), p(4, 20), p(20, 4));
        writeTest("Rotated wide symmetric trapezoid", true, p(4, 0), p(0, 4), p(3, 12), p(12, 3));
        writeTest("Rotated asymmetric trapezoid", true, p(4, 0), p(0, 4), p(3, 10), p(10, 6));
        writeTest("Rotated elongated hexagon", true, p(0, 0), p(1, 2), p(3, 4), p(5, 5), p(4, 3), p(2, 1));
        writeTest("Square, rotated", true, p(9, 3), p(3, -9), p(-9, -3), p(-3, 9));
        writeTest("Rectangle, rotated", true, p(6, 3), p(3, -6), p(-6, -3), p(-3, 6));
    }

    private void randomTestsMaximizingDifference() {
        int maxC = Math.min(MAX_C, 1000000);
        for (int n = 10; n <= 20; n += 1) {
            List<Point> thePoints = null;
            double delta = 0;

            beginTest("Best of 5000 random tests according to difference in answers between mb and gk_exact, n=%d, maxC=%d, boxed", n, MAX_C);
            for (int times = 0; times < 5000; ++times) {
                List<Point> pts = box(randomPolygon(n, maxC), maxC);
                int nn = pts.size();
                int[] x = new int[nn];
                int[] y = new int[nn];
                for (int i = 0; i < nn; ++i) {
                    x[i] = pts.get(i).x;
                    y[i] = pts.get(i).y;
                }
                easy_gk_wa_exact.Result gk = new easy_gk_wa_exact().solve(nn, x, y);
                double[] mb = easy_mb.solve(nn, x, y);
                double diff = Math.abs(gk.area - (mb[2] - mb[0]) * (mb[3] - mb[1])) / gk.area;
                if (diff > delta) {
                    delta = diff;
                    thePoints = pts;
                    if (MB_VERBOSE) {
                        System.err.println("    delta => " + delta);
                    }
                }
            }

            endTest(thePoints, false);
        }
    }

    private void randomTests() {
        for (int n : new int[]{4, 10, 100, MAX_N / 200}) {
            int maxC = MAX_C / MAX_N * n;
            randomVectorTest(n, maxC, 0.99);
            randomVectorTest(n, maxC, 0.95);
            randomVectorTest(n, maxC, 0.5);
            randomTest(n, maxC, false, false);
            randomTest(n, maxC, false, true);

            if (n >= 10) {
                randomTest(n, maxC, true, false);
                randomTest(n, maxC, true, true);
            }
            regularPolygonTest(n, maxC, false);
            regularPolygonTest(n, maxC, true);
        }
        randomTestsMaximizingDifference();
        writeTest("Max rectangle", true, p(-MAX_C, -MAX_C), p(-MAX_C, MAX_C), p(MAX_C, MAX_C), p(MAX_C, -MAX_C));
        for (int n : new int[]{4, 10, 100, MAX_N / 100, MAX_N / 10, MAX_N}) {
            if (n < 10000) {
                randomTest(n, MAX_C, false, false);
                randomTest(n, n == MAX_N ? MAX_C / 2 : MAX_C / 5, false, true);
                if (n >= 10) {
                    randomTest(n, MAX_C, true, false);
                    randomTest(n, n == MAX_N ? MAX_C / 2 : MAX_C / 5, true, true);
                }
                regularPolygonTest(n, MAX_C, false);
                regularPolygonTest(n, MAX_C / 5, true);
            }
            if (16 <= n) {
                fareyTest(n);
            }
            randomVectorTest(n, MAX_C, 0.5);
            randomVectorTest(n, MAX_C, 0.95);
            randomVectorTest(n, MAX_C, 0.99);
        }
    }

    private void fareyTest(int n) {
    	System.out.println("farey");
        List<Point> points = genFarey(n);
        beginTest("Farey-sequence based test test n=%d", n);
        endTest(points, true);
    }

    private void randomVectorTest(int n, int maxC, double cosThreshold) {
        List<Point> points = genRandomVectors(n, maxC, cosThreshold);
        beginTest("Random vectors test n=%d, maxC=%d, similarity=%f", n, maxC, cosThreshold);
        endTest(points, true);
    }

    void randomTest(int n, int maxC, boolean boxed, boolean elongated) {
        List<Point> points = randomPolygon(n, maxC);
        if (boxed) {
            points = box(points, maxC);
        }
        if (elongated) {
            points = elongate(points, Math.min(MAX_C / maxC, 10));
        }

        beginTest("Random test n=%d, maxC=%d%s%s", n, maxC, boxed ? ", boxed" : "", elongated ? ", elongated" : "");
        endTest(points, true);
    }

    private List<Point> elongate(List<Point> points, int times) {
        while (true) {
            double r = (random.nextDouble() + 1) / 2 * times;
            double a = random.nextDouble() * Math.PI;
            double cos2 = Math.cos(a) * Math.cos(a);
            double sin2 = Math.sin(a) * Math.sin(a);
            double cs = Math.cos(a) * Math.sin(a);

            List<Point> result = new ArrayList<>();
            for (Point point : points) {
                result.add(new Point(
                        (int) (point.x * (r * cos2 + sin2) + point.y * (1 - r) * cs),
                        (int) (point.x * ((1 - r) * cs) + point.y * (cos2 + r * sin2))
                ));
            }
            try {
                validateTest(result);
                return result;
            } catch (AssertionError e) {
//                continue;
            }
        }
    }

    private List<Point> box(List<Point> points, int maxC) {
        List<Point> result;
        do {
            double magic = 10;
            int minx = (int) (-maxC * magicRandom(magic));
            int maxx = (int) (maxC * magicRandom(magic));
            int miny = (int) (-maxC * magicRandom(magic));
            int maxy = (int) (maxC * magicRandom(magic));
            result = new ArrayList<>();
            for (Point point : points) {
                if (minx <= point.x && point.x <= maxx && miny <= point.y && point.y <= maxy) {
                    result.add(point);
                }
            }
        } while (result.size() <= points.size() / 2 || points.size() * 3 / 4 <= result.size());
        if (GK_VERBOSE) {
            System.out.println("+");
        }
        return result;
    }

    private double magicRandom(double magic) {
        return (1 + magic * random.nextDouble()) / (1 + magic);
    }

    void regularPolygonTest(int n, int maxC, boolean elongated) {
        beginTest("Regular polygon n=%d, maxC=%d%s", n, maxC, elongated ? ", elongated" : "");
        List<Point> points = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            points.add(polarPoint(maxC, -i * 2 * Math.PI / n));
        }
        if (elongated) {
            points = elongate(points, Math.min(10, MAX_C / maxC));
        }
        endTest(points, true);
    }

    private List<Point> regularPolygon(int n, int maxC, boolean elongated) {
        List<Point> points = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            points.add(polarPoint(maxC, -i * 2 * Math.PI / n));
        }
        return points;
    }

    private List<Point> randomPolygon(int n, int maxC) {
        double magic = n != MAX_N ? 0.0013 * n / MAX_N : 0.001;
        if (GK_VERBOSE) {
            System.out.println("n = " + n + ", maxC = " + maxC + ", magic = " + magic);
        }
        loop: while (true) {
            double[] angles = new double[n];
            double base = 0;
            for (int i = 0; i < angles.length; i++) {
                angles[i] = base;
                base += (random.nextDouble() + 0.5);
            }
            base = 2 * Math.PI / base;
            for (int i = 0; i < angles.length; i++) {
                angles[i] *= base;
            }

            for (int i = 0; i < angles.length - 1; i++) {
                if (angles[i + 1] - angles[i] > Math.PI * 0.9) {
                    continue loop;
                }
            }
            if (angles[0] + Math.PI * 2 - angles[n - 1] > Math.PI * 0.9) {
                continue loop;
            }
            List<Point> points = new ArrayList<>();
            points.add(polarPoint((random.nextDouble() * 3 * magic + 1) / (1 + 3 * magic) * maxC, -angles[0]));
            points.add(polarPoint((random.nextDouble() * 3 * magic + 1) / (1 + 3 * magic) * maxC, -angles[1]));
            for (int i = 2; i < angles.length; i++) {
                Point p0 = points.get(i - 2);
                Point p1 = points.get(i - 1);
                Point p2;
                double l = 0;
                double r = maxC;
                while (r - l > 1e-2) {
                    double m = (r + l) / 2;
                    p2 = polarPoint(m, -angles[i]);
                    if (clockwise(p0, p1, p2)) {
                        l = m;
                    } else {
                        r = m;
                    }
                }
                assert(angles[i] > angles[i - 1]);
                double distance = -1;
                for (int j = 0; j < 100; j++) {
                    double d = (random.nextDouble() * magic + 1) * l / (1 + magic);
                    p2 = polarPoint(d, -angles[i]);
                    if (clockwise(p0, p1, p2) && clockwise(new Point(0, 0), p1, p2) && distance < d) {
                        distance = d;
                    }
                }
                if (distance == -1) {
                    if (GK_VERBOSE) {
                        System.out.print("." + i);
                    }
                    continue loop;
                }
                points.add(polarPoint(distance, -angles[i]));
            }
            if (GK_VERBOSE) {
                System.out.print("+");
            }
            if (
                    clockwise(points.get(n - 2), points.get(n - 1), points.get(0)) &&
                            clockwise(points.get(n - 1), points.get(0), points.get(1))
                    ) {
                if (GK_VERBOSE) {
                    System.out.println();
                }
                return points;
            }
        }
    }

    Point polarPoint(double r, double a) {
        return new Point((int) Math.round(r * Math.cos(a)), (int) Math.round(r * Math.sin(a)));
    }

    private void writeTest(String description, boolean shuffle, Point... points) {
        writeTest(description, shuffle, Arrays.asList(points));
    }

    private void writeTest(String description, boolean shuffle, List<Point> points) {
        beginTest(description);
        endTest(points, shuffle);
    }

    private void endTest(List<Point> points, boolean shuffle) {
        if (shuffle) {
            points = rotate(shift(points, random.nextInt(points.size())), random.nextInt(4));
        }

        validateTest(points);

        out.println(points.size());
        for (Point point : points) {
            out.println(point.x + " " + point.y);
        }
        out.close();

        drawTest(points);
    }

    private void drawTest(List<Point> points) {
        easy_gk_tl_square.Result result = solve(points);
//        easy_gk.Result resultMB = solveMB(points);

        int size = 1000;
        int border = 5;
        new File("images").mkdirs();
        long minx = points.get(0).x;
        long miny = points.get(0).y;
        long maxx = points.get(0).x;
        long maxy = points.get(0).y;
        for (Point point : points) {
            minx = Math.min(minx, point.x);
            miny = Math.min(miny, point.y);
            maxx = Math.max(maxx, point.x);
            maxy = Math.max(maxy, point.y);
        }
        long midx = (maxx + minx) / 2;
        long midy = (maxy + miny) / 2;
//        midx = 0;
//        midy = 0;
        double scale = (size - 2 * border) / (double) Math.max(maxx - minx, maxy - miny);
        BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = (Graphics2D) img.getGraphics();
        g.setBackground(Color.BLACK);
        g.clearRect(0, 0, size, size);

        g.setColor(Color.YELLOW.darker().darker());
        for (Point point : points) {
            int y = (int) (-(point.y - midy) * scale + size / 2);
            int x = (int) ((point.x - midx) * scale + size / 2);
            g.drawLine(0, y, size, y);
            g.drawLine(x, 0, x, size);
        }

        g.setColor(Color.BLUE);
        g.fillRect(
                (int) ((result.minx - midx) * scale + size / 2),
                (int) (-(result.maxy - midy) * scale + size / 2),
                (int) ((result.maxx - result.minx) * scale),
                (int) ((result.maxy - result.miny) * scale + 0.99999999)
        );
        g.setColor(Color.GREEN);
//        g.drawRect(
//                (int) ((resultMB.minx - midx) * scale + size / 2),
//                (int) (-(resultMB.maxy - midy) * scale + size / 2),
//                (int) ((resultMB.maxx - resultMB.minx) * scale),
//                (int) ((resultMB.maxy - resultMB.miny) * scale + 0.99999999)
//        );
//        System.out.println(result.minx + " " + result.miny);

        g.setColor(Color.WHITE);
        for (int i = 0; i < points.size(); i++) {
            Point p0 = points.get(i);
            Point p1 = points.get((i + 1) % points.size());
            int x = p0.x;
            g.drawLine(
                    (int) ((x - midx) * scale) + size / 2,
                    (int) (-(p0.y - midy) * scale) + size / 2,
                    (int) ((p1.x - midx) * scale) + size / 2,
                    (int) (-(p1.y - midy) * scale) + size / 2
            );
        }
        g.fillOval((int) (-midx * scale + size / 2) - 3, (int) (-midy * scale + size / 2) - 3, 7, 7);
        try {
            ImageIO.write(img, "png", new File(String.format("images/%03d.png", testNo)));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private easy_gk_tl_square.Result solve(List<Point> points) {
        int[] x = new int[points.size()];
        int[] y = new int[points.size()];
        for (int i = 0; i < x.length; i++) {
            x[i] = points.get(i).x;
            y[i] = points.get(i).y;
        }
        easy_gk triple = new easy_gk();
        triple.solve(x.length, x, y);
        return new easy_gk_tl_square.Result(triple.minX, triple.minY, triple.maxX, triple.maxY);
    }

    private easy_gk_tl_square.Result solveMB(List<Point> points) {
        int[] x = new int[points.size()];
        int[] y = new int[points.size()];
        for (int i = 0; i < x.length; i++) {
            x[i] = points.get(i).x;
            y[i] = points.get(i).y;
        }
        double[] rv = easy_mb.solve(x.length, x, y);
        return new easy_gk_tl_square.Result(rv[0], rv[1], rv[2], rv[3]);
    }

    private List<Point> rotate(List<Point> points, double k) {
        for (int i = 0; i < k; i++) {
            List<Point> result = new ArrayList<>();
            for (Point point : points) {
                result.add(p(-point.y, point.x));
            }
            points = result;
        }
        return points;
    }

    private List<Point> shift(List<Point> points, int shift) {
        List<Point> result = new ArrayList<>();
        for (int i = 0; i < points.size(); i++) {
            result.add(points.get((i + shift) % points.size()));
        }
        return result;
    }

    private void validateTest(List<Point> points) {
        assertBetween(MIN_N, points.size(), MAX_N);
        for (Point point : points) {
            assertBetween(-MAX_C, point.x, MAX_C);
            assertBetween(-MAX_C, point.y, MAX_C);
        }

        for (int i = 0; i < points.size(); i++) {
            Point p0 = points.get(i);
            Point p1 = points.get((i + 1) % points.size());
            Point p2 = points.get((i + 2) % points.size());

            assert p0.x != p1.x || p0.y != p1.y;
            assert clockwise(p0, p1, p2) : "Not clockwise 1: " + p1.minus(p0).times(p2.minus(p0));
        }

        double angle = angle(points);
        assert Math.abs(angle - 2 * Math.PI) <= 1e-5 : "Invalid angle " + angle;
    }

    private double angle(List<Point> points) {
        double cx = 0;
        double cy = 0;
        for (Point point : points) {
            cx += point.x;
            cy += point.y;
        }
        cx /= points.size();
        cy /= points.size();

        double angle = 0;
        for (int i = 0; i < points.size(); i++) {
            Point p0 = points.get(i);
            Point p1 = points.get((i + 1) % points.size());
            double dx0 = p0.x - cx;
            double dy0 = p0.y - cy;
            double dx1 = p1.x - cx;
            double dy1 = p1.y - cy;
//            assert clockwise(new Point(cx, cy), p1, p0) : "Not clockwise 2";
            if (dy0 * dx1 - dx0 * dy1 <= 0) {
                return 0;
            }
            angle += Math.atan2(dy0 * dx1 - dx0 * dy1, dx0 * dx1 + dy0 * dy1);
        }
        return angle;
    }

    private boolean clockwise(Point p0, Point p1, Point p2) {
        return p1.minus(p0).times(p2.minus(p0)) < 0;
    }

    int randomInt(int from, int to) {
        return random.nextInt(to - from) + from;
    }

    List<Point> genRandomVectors(int n, int maxC, double cosThreshold) {
        int minYX = randomInt(-maxC + 1, maxC);
        int minXY = randomInt(-maxC + 1, maxC);
        int maxYX = randomInt(-maxC + 1, maxC);
        int maxXY = randomInt(-maxC + 1, maxC);

        Set<Vector> vectors = new HashSet<>();
        vectors.add(new Vector(maxYX - -maxC, maxC - minXY));
        vectors.add(new Vector(maxC - maxYX, maxXY - maxC));
        vectors.add(new Vector(minYX - maxC, -maxC - maxXY));
        vectors.add(new Vector(-maxC - minYX, minXY - -maxC));

        Set<Vector> good = new HashSet<>(vectors);

        while (vectors.size() < n) {
            if (vectors.size() > 100000) {
                System.out.println(vectors.size());
            }
            Iterator<Vector> iterator = good.iterator();
            Set<Vector> newGood = new HashSet<>();
            while (iterator.hasNext() && vectors.size() < n) {
                final Vector vector = iterator.next();

                if (Math.abs(vector.x) == 1 || Math.abs(vector.y) == 1) {
                    continue;
                }

                for (int i = 0; i < 20; i++) {
                    int x = randomInt(1, Math.abs(vector.x)) * (int) Math.signum(vector.x);
                    int y = randomInt(1, Math.abs(vector.y)) * (int) Math.signum(vector.y);
                    Vector v1 = new Vector(x, y);
                    Vector v2 = new Vector(vector.x - x, vector.y - y);
                    if (v1.cos(vector) > cosThreshold && v2.cos(vector) > cosThreshold && !vectors.contains(v1) && !vectors.contains(v2)) {
                        iterator.remove();
                        vectors.remove(vector);
                        vectors.add(v1);
                        vectors.add(v2);
                        newGood.add(v1);
                        newGood.add(v2);
                        break;
                    }
                }
                good = newGood;
            }
        }

        List<Vector> vs = new ArrayList<>(vectors);
        Collections.sort(vs, new Comparator<Vector>() {
            @Override
            public int compare(Vector v1, Vector v2) {
                return -Double.compare(Math.atan2(v1.y, v1.x), Math.atan2(v2.y, v2.x));
            }
        });

        int x = minYX;
        int y = -maxC;
        List<Point> points = new ArrayList<>();
        for (Vector vector : vs) points.add(new Point((x += vector.x), (y += vector.y)));
        return points;
    }

    private List<Point> genFarey(int n) {
        List<Ratio> list = Arrays.asList(new Ratio(0, 1), new Ratio(1, 1));
        for (int i = 2; list.size() <= n / 8 + 2; i++) {
            List<Ratio> next = new ArrayList<>();
            Ratio prev = null;
            for (Ratio curr : list) {
                if (prev != null) {
                    int den = prev.den + curr.den;
                    if (den <= i) {
                        int nom = prev.nom + curr.nom;
                        next.add(new Ratio(nom, den));
                    }
                }
                next.add(curr);
                prev = curr;
            }
            list = next;
        }
        list = new ArrayList<>(list.subList(2, n / 8 + 2));
        List<Ratio> revl = new ArrayList<>(list);
        Collections.reverse(revl);

        List<Point> points = new ArrayList<>();
        int x = 0;
        int y = 0;
        for (Ratio ratio : list) points.add(new Point(x += ratio.nom, y += ratio.den));
        for (Ratio ratio : revl) points.add(new Point(x += ratio.den, y += ratio.nom));
        for (Ratio ratio : list) points.add(new Point(x += ratio.den, y -= ratio.nom));
        for (Ratio ratio : revl) points.add(new Point(x += ratio.nom, y -= ratio.den));
        for (Ratio ratio : list) points.add(new Point(x -= ratio.nom, y -= ratio.den));
        for (Ratio ratio : revl) points.add(new Point(x -= ratio.den, y -= ratio.nom));
        for (Ratio ratio : list) points.add(new Point(x -= ratio.den, y += ratio.nom));
        for (Ratio ratio : revl) points.add(new Point(x -= ratio.nom, y += ratio.den));
        return points;
    }


    public static void main(String[] args) throws IOException {
        descriptions = new PrintWriter("tests/tests.lst");
        new Tests().run();
        descriptions.close();
    }
}
