function [ d, cost, avgRT, cdfRTindex, cdfRTvalue, evaluations, time, nrefactorings ] =  ...
	refactor(qn, scAlternatives, mergeAlternatives, maxDuplications, ...
					maxJobReassignments, constraints, resources, seed)		
	% REFACTOR
	%
	% This function combines the three types of software refactorings and the
	% deployment actions according to the following algorithm:
	%
	% 1. Apply all speed-increasing substitutions
	% 2. Apply all speed-increasing merges
	% 3. If at least a modification has been done, go back to step 1
	% 4. Calculate optimal resource rates of the resulting queueing network
	% 5. Apply Job reassignment
	% 6. Recalculate optimal resource rates
	% 7. Calculate the set of resources
	% 8. Calculate the deployment matrix
	% 9. If the constraints are violated, scale-up the optimal resource rates
	%    of the bottleneck node and go back to step 7
	% 10. Return the result
	%
	% INPUT
	% qn: queuing network
	% 
	% scAlternatives: array of cells in which each cell contains a matrix. Each
	% row of the internal matrix contains alternative rates for each class, columns are the classes.
	% The indexes of the array are the ones of the replaceable components.
	% components.
	%
	% mergeAlternatives: matrix of cells in which each cell contains a matrix. Each
	% row of the internal matrix contains alternative rates for each class, columns are the classes.
	% The indexes of the matrix are the ones of the mergeable components.
	% components.
	%
   % maxDuplications: maximum number of additional duplications for the current queuing
	% center (vector of qn.M size)
	% maxJobReassignments: max number of iterations for reassigning jobs
	%
	% constraints: SLO constraints
	% resources: resource model
	% seed: seed for experiment repeatibility
	%
	% OUTPUT:
	% d: allocation matrix
	% cost: total cost of the result
	% avgRT: average response time of the result
	% cdfRTindex: indexes of the response time distribution
	% cdfRTvalue: values of the response time distribution
	% evaluations: total number of QN evaluations
	% time: total time to calculate the result
	% nrefactorings: total number of QN refactorings
	
	
	time = cputime;
	evaluations = 0;
	nrefactorings = 0;
	%currentRefactorings = 1;
	myqn = qn;
	selected = true(1,qn.M);
	
	if isempty(scAlternatives)
		scAlternatives{1,qn.M} = [];
	end
	if isempty(mergeAlternatives)
		mergeAlternatives{qn.M,qn.M} = [];
	end
	
	% STEP 0: calculate optimal rates
	rng(seed);
	startingRates = findFeasibleRates(myqn, constraints);
	for ii=1:numel(startingRates)
		startingRates(ii) = startingRates(ii)*(1+rand());
	end
	
	[ rates, ~, ~, ~, ~, ievaluations ] = ...
								findOptimalRates(myqn, startingRates, constraints);
	evaluations = evaluations + ievaluations;
% 	
% 	
% 	% STEP 0.1: apply optimal rates
% 	for ii=1:numel(rates)
% 		myqn.rates(ii,:) = myqn.rates(ii,:)*rates(ii);
% 		if ~isempty(scAlternatives{ii})
% 			scAlternatives{ii}=scAlternatives{ii}*rates(ii);
% 			evaluations = evaluations + 1;
% 		end
% 		
% 		for jj=1:numel(rates)
% 			if ~isempty(mergeAlternatives{ii,jj})
% 				mergeAlternatives{ii,jj}=mergeAlternatives{ii,jj}*rates(ii);
% 				evaluations = evaluations + 1;
% 			end
% 		end
% 	end
	
	
	%while currentRefactorings > 0
		% STEPS 1 and 2: substitutions and merges
		%currentRefactorings = 0;
		myScAlt = scAlternatives(selected);
		myMgAlt = mergeAlternatives(selected,selected);
		
		[myqn, nref] = softwareRefactorSCReplacement(myqn, myScAlt, constraints, rates);
		nrefactorings = nrefactorings + nref;
		%currentRefactorings = currentRefactorings + nref;
		
		[myqn, nref] = softwareRefactorSCMerge(myqn, myMgAlt, constraints, rates);
		nrefactorings = nrefactorings + nref;
		%currentRefactorings = currentRefactorings + nref;
		
		% remove queueing centers that have been merged
% 		selected = false(1, qn.M);
% 		
% 		for ii=1:qn.M
% 			for jj=1:myqn.M
% 				if strcmp(qn.nodeNames{ii},myqn.nodeNames{jj})
% 					selected(ii) = true;
% 				end
% 			end
% 		end
	%end
	% STEP 3: repeat STEP 1 and 2 if needed REMOVED
	evaluations = evaluations + nrefactorings;

	
	% STEP 4: Calculate and apply optimal rates again
		
	if nrefactorings>0
		rng(seed);
		startingRates = findFeasibleRates(myqn, constraints);
		for ii=1:numel(startingRates)
			startingRates(ii) = startingRates(ii)*(1+rand());
		end
		[ rates, ~, ~, ~, ~, ievaluations ] = ...
									findOptimalRates(myqn, startingRates, constraints);

		evaluations = evaluations + ievaluations;
	end
	
	myqnspeedup = myqn;
	
	for ii=1:numel(rates)
		myqn.rates(ii,:) = myqn.rates(ii,:)*rates(ii);
	end
	
	
	% STEP 5: Calculate Job reassignment
	jobreassigned = false;
	myMaxDuplications = zeros(myqn.M, 1);
	for ii=1:qn.M
		currentLabel = qn.nodeNames{ii};
		duplications = maxDuplications(ii);
		safeCondition = 50;
		jj=0;
		
		while duplications>0 && safeCondition>0
			jj = mod(jj, myqn.M)+1;
			safeCondition = safeCondition - 1;
			
			if strcmp(myqn.nodeNames{jj}, currentLabel)
				myMaxDuplications(jj) = myMaxDuplications(jj) + 1;
				duplications = duplications - 1;
			end
		end
	end
	
	[qnRefactored, nref] = softwareRefactorJobReassignment(myqn, myMaxDuplications, ...
		maxJobReassignments);
	
	nrefactorings = nrefactorings + nref;
	
	if nref>0
		jobreassigned = true;
	end
	
	% CLEAN ME, recalculate maximum duplications (this contains duplicated code
	% that can be optimized
	myMaxDuplications = zeros(qnRefactored.M, 1);
	
	for ii=1:qn.M
		currentLabel = qn.nodeNames{ii};
		duplications = maxDuplications(ii);
		safeCondition = 50;
		jj=0;
		
		while duplications>0 && safeCondition>0
			jj = mod(jj, qnRefactored.M)+1;
			safeCondition = safeCondition - 1;
			
			if strcmp(qnRefactored.nodeNames{jj}, currentLabel)
				myMaxDuplications(jj) = myMaxDuplications(jj) + 1;
				duplications = duplications - 1;
			end
		end
	end
	maxRep = myMaxDuplications;
	
	% STEP6: Recalculate optimal resource rates
	for ii=1:myqnspeedup.M
		currentLabel = myqnspeedup.nodeNames{ii};
		currentRates = myqnspeedup.rates(ii,:);
		
		for jj=1:qnRefactored.M
			if strcmp(qnRefactored.nodeNames{jj}, currentLabel)
				qnRefactored.rates(ii,:) = currentRates;
			end
		end
	end
	% CLEAN ME, some code duplication
	if jobreassigned
		rng(seed);
		startingRates = findFeasibleRates(qnRefactored, constraints);
		for ii=1:numel(startingRates)
			startingRates(ii) = startingRates(ii)*(1+rand());
		end
		[ rates, ~, ~, ~, ~, ievaluations ] = ...
									findOptimalRates(qnRefactored, startingRates, constraints);
		evaluations = evaluations + ievaluations;
	end
	
	% STEP 7, 8, 9, 10
	[ d, cost, avgRT, cdfRTindex, cdfRTvalue, ievaluations] = ...
				findOptimalAllocationFullLimit(qnRefactored, rates, ...
				constraints, resources, maxRep, seed);
	evaluations = evaluations + ievaluations;
	time = cputime - time;
end