public Pair<Double, LMSaver> trainAndMerge(LMSaver saver) {

        List<File> splittedFiles = new ArrayList<File>();
        try {
            splittedFiles = split();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Logger.info(String.format("[ParallelTrainer] split %s \n-> %s", conf.trainFile.toString(), splittedFiles.toString()));

        ExecutorService ex = Executors.newFixedThreadPool(cores);
        List<ParallelTrainRunner> runners = new ArrayList<ParallelTrainRunner>();
        for (File part: splittedFiles) {
            RNNLMConfiguration partConf = conf.clone();
            partConf.trainFile = part;
            ParallelTrainRunner runner = new ParallelTrainRunner(partConf, saver);
            runners.add(runner);
            ex.execute(runner);
        }

        ex.shutdown();
        try {
            ex.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Logger.info("[ParallelTrainer] Interrupted");
        }

        Logger.info("[ParallelTrainer] Finalize");

        // Merge
        StringBuilder scoreBuilder = new StringBuilder("[ParallelTrainer] scores: ");
        double entropySum = 0;
        for (int i = 0; i < runners.size(); ++i) {
            double finalEntropy = runners.get(i).finalEntropy;
            scoreBuilder.append(String.format("%d:%.4f ", i + 1, finalEntropy));
            entropySum += finalEntropy;
        }
        Logger.info(scoreBuilder.toString());

        LMSaver outputSaver = new LMSaver();
        outputSaver.zero(conf);
        for (int i = 0; i < runners.size(); ++i) {
            // double alpha = runners.get(i).finalEntropy / entropySum;
            double alpha = 1 / cores;
            outputSaver.mergeSynapse(runners.get(i).lm, alpha);
        }
        return new Pair<Double, LMSaver>(entropySum / cores, outputSaver);
    }