@Override
    public O train(I input) throws Exception {
        Object i = input;
        for(final Processor p : processors){
            p.setContext(context);
            context.getProgressReporter().reportProgress(p.getClass().getSimpleName() + " train: ", 0);
            long t0 = System.currentTimeMillis();
            if (p instanceof ParallelProcessor) {
                final ParallelProcessor pp = (ParallelProcessor) p;
                pp.setChunks(cores);
                final Object o = pp.initTrain(i);
                Queue<Future> futures = new LinkedList<Future>();
                for(int c=0; c<cores; c++){
                    final Object finalI = i;
                    final int finalC = c;
                    futures.add(executorService.submit(new Callable<Object>() {
                        @Override
                        public Object call() throws Exception {
                            pp.train(finalI, o, finalC);
                            return null;
                        }
                    }));
                    while(futures.size() > cores * 2) {
                        futures.poll().get();
                    }
                }
                while(futures.size() > 0) {
                    futures.poll().get();
                }
                i = o;
            } else {
                System.err.println(p + " ( " + i.getClass() + " )");
                i = p.train(i);
            }
            long t1 = System.currentTimeMillis();
            context.getProgressReporter().reportProgress(p.getClass().getSimpleName() + " train complete. Time spent " + (t1-t0), 1);
        }
        return (O) i;
    }