public static void main(String[] args) throws Exception {
        conf = Configuration.fromFile(args[0]);
        
        int threads;
        
        if(args.length > 1 && args[1] != null) {
            threads = Integer.parseInt(args[1]);
        }
        else {
            threads = Runtime.getRuntime().availableProcessors();
        }
       
        workQueue = new LinkedBlockingQueue<>();
        doneQueue = new LinkedBlockingQueue<>();

        List<TriangleWorker> workers = new ArrayList<>(threads);
        for (int i = 0; i < threads; i++) {
            workers.add(new TriangleWorker(i, conf));
        }
        for (TriangleWorker tw : workers) {
            tw.start();
        }

        ArrayList<Triangle> triangles = new ArrayList<>();

        Picture p = new Picture(conf.pic.getWidth(), conf.pic.getHeight());
        double pDist = p.distance(conf.pic);
        double bestDist = pDist;

        for (int i = 0; i < conf.maxTriangles; i++) {
            doneQueue = new LinkedBlockingQueue<>();

            int workUnits = 0;

            for (int point1 = 0; point1 < conf.numPoints; point1++) {
                Point p1 = new Point(xFromPoint(point1), yFromPoint(point1));

                CalcState state = new CalcState(p, pDist, i, point1, p1, bestDist, null);
                workQueue.add(state);
                workUnits++;
            }

            while (doneQueue.size() < workUnits) {
                if (!doneQueue.isEmpty()) {
                    CalcState bestState = Collections.min(doneQueue);

                    System.out.println(now() + bestState.bestTriangle + " " + Arrays.toString(bestState.bestTriangle.rgba) + " " + bestState.bestDist);
                }
                System.out.println(now() + "Triangle: " + i + " done: " + doneQueue.size() + "/" + workUnits
                        + String.format(" (%.4f%%) (%.4f%%) cache size: %d",
                                (1.0 * doneQueue.size() / workUnits) * 100,
                                (1.0 * doneQueue.size() / workUnits) / conf.maxTriangles * 100,
                                p.getCacheSize()));

                Thread.sleep(60000);
            }

            System.out.println(now() + "Triangle: " + i + " done: " + doneQueue.size() + "/" + workUnits
                    + String.format(" (%.4f%%) (%.4f%%) cache size: %d",
                            (1.0 * doneQueue.size() / workUnits) * 100,
                            (1.0 * doneQueue.size() / workUnits) / conf.maxTriangles * 100,
                            p.getCacheSize()));

            CalcState bestState = Collections.min(doneQueue);
            Triangle bestTriangle = bestState.bestTriangle;

            p.addTriangle(bestTriangle);
            triangles.add(bestTriangle);

            pDist = p.distance(conf.pic);
            bestDist = pDist;

            Rasterizer.writeTga(p, conf.filenamePrefix + "_" + triangles.size());
            TriangleWriter.writeTriangles(conf.filenamePrefix, p.getWidth(), p.getHeight(), triangles);
        }

        for (TriangleWorker tw : workers) {
            tw.interrupt();
        }
    }