/* Copyright (C) 2013 TU Dortmund
 * This file is part of LearnLib, http://www.learnlib.de/.
 * 
 * LearnLib is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 3.0 as published by the Free Software Foundation.
 * 
 * LearnLib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with LearnLib; if not, see
 * <http://www.gnu.de/documents/lgpl.en.html>.
 */
package de.learnlib.eqtests.basic;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.logging.Logger;

import net.automatalib.automata.concepts.OutputAutomaton;
import net.automatalib.automata.fsa.DFA;
import net.automatalib.automata.transout.MealyMachine;
import net.automatalib.words.Word;
import net.automatalib.words.WordBuilder;
import de.learnlib.api.EquivalenceOracle;
import de.learnlib.api.MembershipOracle;
import de.learnlib.oracles.DefaultQuery;

/**
 *
 * @author Maik Merten <maikmerten@googlemail.com>
 */
public class RandomWordsEQOracle<I, O, A extends OutputAutomaton<?, I, ?, O>> implements EquivalenceOracle<A, I, O> {

	public static class DFARandomWordsEQOracle<I> extends RandomWordsEQOracle<I,Boolean,DFA<?,I>>
			implements DFAEquivalenceOracle<I> {
		public DFARandomWordsEQOracle(MembershipOracle<I, Boolean> mqOracle,
				int minLength, int maxLength, int maxTests, Random random) {
			super(mqOracle, minLength, maxLength, maxTests, random);
		}
		public DFARandomWordsEQOracle(MembershipOracle<I, Boolean> mqOracle,
				int minLength, int maxLength, int maxTests, Random random, int batchSize) {
			super(mqOracle, minLength, maxLength, maxTests, random, batchSize);
		}
	}
	
	public static class MealyRandomWordsEQOracle<I,O> extends RandomWordsEQOracle<I,Word<O>,MealyMachine<?,I,?,O>>
			implements MealyEquivalenceOracle<I,O> {
		public MealyRandomWordsEQOracle(MembershipOracle<I, Word<O>> mqOracle,
				int minLength, int maxLength, int maxTests, Random random) {
			super(mqOracle, minLength, maxLength, maxTests, random);
		}
		public MealyRandomWordsEQOracle(MembershipOracle<I, Word<O>> mqOracle,
				int minLength, int maxLength, int maxTests, Random random, int batchSize) {
			super(mqOracle, minLength, maxLength, maxTests, random, batchSize);
		}
	}

	private static final Logger LOGGER = Logger.getLogger(RandomWordsEQOracle.class.getName());

	private MembershipOracle<I, O> oracle;
	private int maxTests, minLength, maxLength;
	private final Random random;
	private final int batchSize;

	public RandomWordsEQOracle(MembershipOracle<I, O> mqOracle, int minLength, int maxLength, int maxTests, Random random, int batchSize) {
		this.oracle = mqOracle;
		this.maxTests = maxTests;
		this.minLength = minLength;
		this.maxLength = maxLength;
		this.random = random;
		this.batchSize = batchSize;
	}

	public RandomWordsEQOracle(MembershipOracle<I, O> mqOracle, int minLength, int maxLength, int maxTests, Random random) {
		this(mqOracle, minLength, maxLength, maxTests, random, 1);
	}

	@Override
	public DefaultQuery<I, O> findCounterExample(A hypothesis, Collection<? extends I> inputs) {
		// Fail fast on empty inputs
		if(inputs.isEmpty()) {
			LOGGER.warning("Passed empty set of inputs to equivalence oracle; no counterexample can be found!");
			return null;
		}
		
		List<? extends I> symbolList;
		if (inputs instanceof List) {
			symbolList = (List<? extends I>) inputs;
		} else {
			symbolList = new ArrayList<>(inputs);
		}
		
		int numSyms = symbolList.size();

		final Collection<DefaultQuery<I,O>> queryBatch = new LinkedList<>();

		for (int i = 0; i < maxTests; ++i) {
			int length = minLength + random.nextInt((maxLength - minLength) + 1);

			WordBuilder<I> testtrace = new WordBuilder<>(length);
			for (int j = 0; j < length; ++j) {
				int symidx = random.nextInt(numSyms);
				I sym = symbolList.get(symidx);
				testtrace.append(sym);
			}

			final DefaultQuery<I, O> query = new DefaultQuery<>(testtrace.toWord());

			final boolean batchNotFilled = queryBatch.size() < batchSize - 1;
			final boolean maxTestsNotReached = i < maxTests - 1;

			if (batchNotFilled && maxTestsNotReached) {
				queryBatch.add(query);
			}
			else {
				// query oracle
				queryBatch.add(query);
				oracle.processQueries(queryBatch);

				for (final DefaultQuery<I, O> ioQuery : queryBatch) {
					O oracleoutput = ioQuery.getOutput();

					// trace hypothesis
					O hypOutput = hypothesis.computeOutput(testtrace.toWord());

					// compare output of hypothesis and oracle
					if (!Objects.equals(oracleoutput, hypOutput)) {
						return ioQuery;
					}
				}

				queryBatch.clear();
			}
		}

		// no counterexample found
		return null;
	}
}

@@ -799,37 +799,8 @@
 on;%0A
-import java.util.LinkedList;%0A
 impo
@@ -3882,21 +3882,29 @@
 new 
-Linked
+Array
 List%3C%3E(
+batchSize
 );%0A%0A

Replace LinkedList with ArrayList