/*
 * Copyright (C) 2016 Stefan Henß
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.insightml.models.meta;

import java.util.Random;

import javax.annotation.Nonnull;

import com.insightml.data.samples.ISamples;
import com.insightml.data.samples.Sample;
import com.insightml.models.ILearner;
import com.insightml.models.IModel;
import com.insightml.models.LearnerArguments;
import com.insightml.models.LearnerInput;
import com.insightml.models.meta.VoteModel.VoteStrategy;
import com.insightml.utils.Arguments;
import com.insightml.utils.IArguments;
import com.insightml.utils.jobs.ParallelFor;

public class Bagging<I extends Sample> extends AbstractEnsembleLearner<I, Object, Double> {
	private final VoteStrategy strategy;

	public Bagging(final IArguments arguments, final ILearner<I, ? extends Object, Double>... learner) {
		super(arguments, learner);
		this.strategy = VoteStrategy.AVERAGE;
	}

	public Bagging(final int bags, final double isample, final double fsample, final VoteStrategy strategy,
			final ILearner<I, ? extends Object, Double>... learner) {
		super(new Arguments("bags", bags, "isample", isample, "fsample", fsample), learner);
		this.strategy = strategy;
	}

	@Override
	public LearnerArguments arguments() {
		final LearnerArguments args = new LearnerArguments();
		args.add("bags", 10.0, 2, 1000);
		args.add("isample", 0.8, 0.1, 1.0);
		args.add("fsample", 0.9, 0.05, 1.0);
		return args;
	}

	@Override
	protected IModel<I, Double> createModel(final ISamples<I, Object> instances,
			final ILearner<I, ? extends Object, Double>[] learner, final int labelIndex) {
		final ISamples<I, Object> samples = preprocess(instances);
		final int bags = (int) argument("bags");
		final double instancesSample = argument("isample");
		final double featureSample = argument("fsample");
		final IModel<I, Double>[] models = new IModel[bags];
		final double[] weights = new double[bags];
		ParallelFor.run(i -> {
			final Random random = new Random((long) Math.pow(i + 2, 2));
			final ISamples<I, Object> sampled = sample(samples, instancesSample, featureSample, random);
			models[i] = learner[i % learner.length].run(new LearnerInput(sampled, null, null, labelIndex));
			weights[i] = 1;
			return 1;
		}, 0, bags, 3);
		return new VoteModel<>(models, weights, strategy);
	}

	protected ISamples<I, Object> sample(final ISamples<I, Object> samples, final double instancesSample,
			final double featureSample, final Random random) {
		final ISamples<I, Object> sub = instancesSample < 1 ? samples.sample(instancesSample, random).getFirst()
				: samples;
		return featureSample < 1 ? sub.sampleFeatures(featureSample, random) : sub;
	}

	protected ISamples<I, Object> preprocess(final @Nonnull ISamples<I, Object> instances) {
		return instances;
	}

}

@@ -648,16 +648,100 @@
 .Random;
+%0Aimport java.util.concurrent.ExecutorService;%0Aimport java.util.concurrent.Executors;
 %0A%0Aimport
@@ -1142,32 +1142,75 @@
 ils.IArguments;%0A
+import com.insightml.utils.ResourceCloser;%0A
 import com.insig
@@ -1331,16 +1331,174 @@
 uble%3E %7B%0A
+%0A%09private static final ExecutorService executor;%0A%0A%09static %7B%0A%09%09executor = Executors.newFixedThreadPool(16);%0A%09%09ResourceCloser.register(executor::shutdown);%0A%09%7D%0A%0A
 %09private
@@ -3037,16 +3037,26 @@
  bags, 3
+, executor
 );%0A%09%09ret

use own executor