/*******************************************************************************
 * Copyright (C) 2014  Stefan Schroeder
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 *
 * This library 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 this library.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package jsprit.core.algorithm.ruin;

import jsprit.core.problem.VehicleRoutingProblem;
import jsprit.core.problem.job.Job;
import jsprit.core.problem.solution.route.VehicleRoute;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;


/**
 * Ruin strategy that ruins current solution randomly. I.e.
 * customer are removed randomly from current solution.
 * 
 * @author stefan schroeder
 * 
 */

public final class RuinRandom extends AbstractRuinStrategy {
	
	private Logger logger = LogManager.getLogger(RuinRandom.class);

	private VehicleRoutingProblem vrp;

	private double fractionOfAllNodes2beRuined;

	/**
	 * Constructs ruinRandom.
	 * 
	 * @param vrp
	 * @param fraction which is the fraction of total c
	 */
	public RuinRandom(VehicleRoutingProblem vrp, double fraction) {
		super(vrp);
		this.vrp = vrp;
		this.fractionOfAllNodes2beRuined = fraction;
		setRuinShareFactory(new RuinShareFactory() {
			@Override
			public int createNumberToBeRemoved() {
				return selectNuOfJobs2BeRemoved();
			}
		});
        logger.info("initialise " + this);
		logger.info("done");
	}

	/**
	 * Removes a fraction of jobs from vehicleRoutes. 
	 * 
	 * <p>The number of jobs is calculated as follows: Math.ceil(vrp.getJobs().values().size() * fractionOfAllNodes2beRuined).
	 */
	@Override
	public Collection<Job> ruinRoutes(Collection<VehicleRoute> vehicleRoutes) {
        List<Job> unassignedJobs = new ArrayList<Job>();
		int nOfJobs2BeRemoved = getRuinShareFactory().createNumberToBeRemoved();
		ruin(vehicleRoutes, nOfJobs2BeRemoved, unassignedJobs);
        return unassignedJobs;
	}

	/**
	 * Removes nOfJobs2BeRemoved from vehicleRoutes, including targetJob.
	 */
	@Override
	public Collection<Job> ruinRoutes(Collection<VehicleRoute> vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved) {
        throw new IllegalStateException("not supported");
	}

	@Deprecated
	public void setRuinFraction(double fractionOfAllNodes2beRuined) {
		this.fractionOfAllNodes2beRuined = fractionOfAllNodes2beRuined;
		logger.info("fraction set " + this);
	}

	private void ruin(Collection<VehicleRoute> vehicleRoutes, int nOfJobs2BeRemoved, List<Job> unassignedJobs) {
		LinkedList<Job> availableJobs = new LinkedList<Job>(vrp.getJobs().values());
		for (int i = 0; i < nOfJobs2BeRemoved; i++) {
			if(availableJobs.isEmpty()) break;
			Job job = pickRandomJob(availableJobs);
			if(removeJob(job,vehicleRoutes)) {
				unassignedJobs.add(job);
				availableJobs.remove(job);
			}
		}
	}

		
	@Override
	public String toString() {
		return "[name=randomRuin][noJobsToBeRemoved="+selectNuOfJobs2BeRemoved()+"]";
	}
	
	private Job pickRandomJob(LinkedList<Job> availableJobs) {
		int randomIndex = random.nextInt(availableJobs.size());
		return availableJobs.get(randomIndex);
	}

	private int selectNuOfJobs2BeRemoved() {
		return (int) Math.ceil(vrp.getJobs().values().size() * fractionOfAllNodes2beRuined);
	}


}

@@ -1206,26 +1206,27 @@
 va.util.
-LinkedList
+Collections
 ;%0Aimport
@@ -2870,197 +2870,8 @@
 %09%7D%0A%0A
-%09@Deprecated%0A%09public void setRuinFraction(double fractionOfAllNodes2beRuined) %7B%0A%09%09this.fractionOfAllNodes2beRuined = fractionOfAllNodes2beRuined;%0A%09%09logger.info(%22fraction set %22 + this);%0A%09%7D%0A%0A
 %09pri
@@ -2978,22 +2978,21 @@
 bs) %7B%0A%09%09
-Linked
+Array
 List%3CJob
@@ -3013,22 +3013,21 @@
 s = new 
-Linked
+Array
 List%3CJob
@@ -3059,133 +3059,145 @@
 ;%0A%09%09
-for (int i = 0; i %3C nOfJobs2BeRemoved; i++) %7B%0A%09%09%09if(availableJobs.isEmpty()) break;%0A%09%09%09Job job = pickRandomJob(availableJobs)
+Collections.shuffle(availableJobs,random);%0A%09%09int removed = 0;%0A%09%09for (Job job : availableJobs) %7B%0A%09%09%09if(removed == nOfJobs2BeRemoved) break
 ;%0A%09%09
@@ -3268,40 +3268,23 @@
 %0A%09%09%09
-%09availableJobs.remove(job);%0A%09%09%09%7D
+%7D%0A%09%09%09removed++;
 %0A%09%09%7D
@@ -3292,11 +3292,8 @@
 %09%7D%0A%0A
-%09%09%0A
 %09@Ov
@@ -3413,172 +3413,8 @@
 ;%0A%09%7D
-%0A%09%0A%09private Job pickRandomJob(LinkedList%3CJob%3E availableJobs) %7B%0A%09%09int randomIndex = random.nextInt(availableJobs.size());%0A%09%09return availableJobs.get(randomIndex);%0A%09%7D
 %0A%0A%09p

optimize ruin random