/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: GlobalRouterV3.java
 * Written by: Alexander Herzog (Team 4)
 *
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.tool.routing.experimentalLeeMoore2;

import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CyclicBarrier;

import com.sun.electric.tool.routing.RoutingFrame.RoutingSegment;
import com.sun.electric.tool.routing.experimentalLeeMoore2.RoutingFrameLeeMoore.Coordinate;

public class GlobalRouterV3 {
	
	/** Threads */
	int num_threads;
	
	/** barrier to synchronize stages */
	CyclicBarrier barrier;
	
	/** minimum tile size of detailed router */
	double tileSize;

	/** maximum detour */
	int max_detour = 6;
	
	/** cost for crossing a tile from horizontal to vertical or vice versa */
	double via_cost = 0.2;
	
	/** Global routing output. This represents a view on a whole route.  */
	public class RouteToStitch{
		public int id = -1;
		public List<SegPart> coarse_route = null;
		public ArrayList<Vector2i> region_positions;
		
		public RoutingSegment seg_head_tail = null;
		public int start_layer = Integer.MIN_VALUE;
		public int finish_layer = Integer.MIN_VALUE;
		
		public RouteToStitch(RoutingSegment routing_seg){
			seg_head_tail = routing_seg;
			
			start_layer = routing_seg.getStartLayers().get(0).getMetalNumber();
			finish_layer = routing_seg.getFinishLayers().get(0).getMetalNumber();
		}
		
		public ArrayList<RegionToRoute> SelectPassedRegionsFrom(RegionToRoute[] all_regions){
			ArrayList<RegionToRoute> ret = new ArrayList<RegionToRoute>();
			
			Iterator<Vector2i> it = region_positions.iterator();
			while(it.hasNext()){
				Vector2i pos = it.next();
				ret.add(all_regions[pos.y * regions_x + pos.x]);
			}
			
			return ret;
		}
	}
	public HashMap<Integer, RouteToStitch> output_coarse_routes;

	/** Adds a whole route to output data */
	public synchronized void OfferCoarseRoute(List<SegPart> route, int seg_id, ArrayList<Vector2i> passed_regions){

		RouteToStitch rts = new RouteToStitch(electric_segments.get(seg_id));
		rts.coarse_route = route;
		rts.id = seg_id;
		rts.region_positions = passed_regions;
		
		output_coarse_routes.put(new Integer(rts.id), rts);
	}
	
	/** Output data for further processing in a detailed router. */
	public class RegionToRoute{
		Rectangle2D bounds;
		List<SegPart> segments_to_route;
		
		public RegionToRoute(Rectangle2D b){
			bounds = b;
			segments_to_route = new ArrayList<SegPart>();
		}
	}
	public RegionToRoute[] output_regions;
	
	/** Adds a route patch to a output region representation */
	public synchronized void AddRouteToOutReg(Vector2i reg_pos, SegPart segment){
		RegionToRoute region = OutputRegionAt(reg_pos.x, reg_pos.y);
		region.segments_to_route.add(segment);
	}
	
	/** Access to output */
	public RegionToRoute OutputRegionAt(int x, int y){
		return output_regions[y * regions_x + x];
	}
	
	public RegionToRoute OutputRegionAt(int x, int y, RegionDirection dir){
		
		switch(dir){
		case rd_left:
			--x;
			break;
		case rd_right:
			++x;
			break;
		case rd_down:
			--y;
			break;
		case rd_up:
			++y;
			break;
		case rd_undefined:
			break;
		}
		
		if(IsCoordinateValid(x, y)){
			return OutputRegionAt(x, y);
		}else{
			return null;
		}
	}
	
	/** Information for a GlobalRouter worker to do his job */
	class SegmentInfo{
		public boolean is_initialized = false;
		public boolean was_part_of_bt = false;
		public boolean is_on_min_path = false;
		
		/** for each direction you can reach the source with several
		 * length-minimum pairs 
		 */
		private ArrayList<WeightLengthPair> left_back_scores = 
			new ArrayList<WeightLengthPair>();
		private ArrayList<WeightLengthPair> right_back_scores = 
			new ArrayList<WeightLengthPair>();
		private ArrayList<WeightLengthPair> up_back_scores = 
			new ArrayList<WeightLengthPair>();
		private ArrayList<WeightLengthPair> down_back_scores = 
			new ArrayList<WeightLengthPair>();
		
		public ArrayList<WeightLengthPair> GetScores(RegionDirection dir){
			ArrayList<WeightLengthPair> ret = null;
			switch(dir){
			case rd_left:
				ret = left_back_scores;
				break;
			case rd_right:
				ret = right_back_scores;
				break;
			case rd_up:
				ret = up_back_scores;
				break;
			case rd_down:
				ret = down_back_scores;
				break;
			case rd_undefined:
				ret = null;
				break;
			}
			
			return ret;
		}
		
		/** checks if segment is possibly part of the best backtrace and refreshes data */
		public boolean RefreshSegment(int length, double weight, RegionDirection min_dir){
			
			// first check if detour limit is reached
			if(max_detour < length){
				return false;
			}else if(!is_initialized){
				is_initialized = true;
			}
			
			if(min_dir != RegionDirection.rd_undefined){
				// drop this path if there is another path with lower cost AND lower length
					ArrayList<WeightLengthPair> scores = GetScores(min_dir);
					for(WeightLengthPair wlp : scores){
						if(wlp.length <= length && wlp.weight <= weight){
							return false;
						}
					}
				// if not returned false, insert weight-length pair
				GetScores(min_dir).add(new WeightLengthPair(weight, length));
			}else{
				// broadcast weight-length pair to all directions (needed for source segment)
				for(RegionDirection dir : RegionDirection.values()){
					if(dir == RegionDirection.rd_undefined){
						continue;
					}
					ArrayList<WeightLengthPair> scores = GetScores(dir);
					scores.add(new WeightLengthPair(weight, length));
				}
			}
			
			return true;		
		}
		
		/** Information for backtracking to find its way back to the start point */
		public BacktraceState GetMin(int max_path_length){
			if(!is_initialized){
				return null;
			}

			BacktraceState min = null;
			
				for(RegionDirection dir : RegionDirection.values()){
					if(dir == RegionDirection.rd_undefined){
						continue;
					}
					
					ArrayList<WeightLengthPair> scores = GetScores(dir);
					for(WeightLengthPair cur_wlp : scores){
						if(cur_wlp.length > max_path_length){
							continue;
						}
						if(min == null){
							min = new BacktraceState(cur_wlp.weight, dir, cur_wlp.length);
						}else if(cur_wlp.weight < min.weight){
							min.weight = cur_wlp.weight;
							min.dir = dir;
							min.path_length = cur_wlp.length;
						}
					}
				}
				
			return min;
		}
	}
	
	/** This represents a sub-region, passed by routes */
	class RegionRepresentation{
		public double weight;
		public SegmentInfo[] segment_infos;

		public RegionBorder left_border;
		public RegionBorder right_border;
		public RegionBorder lower_border;
		public RegionBorder upper_border;
		
		public RegionRepresentation(double weight, int num_segs){
			this.weight = weight;
			segment_infos = new SegmentInfo[num_segs];
		}
		
		public double getCongestionWeight(){
			double congestion = 0;

			if(left_border != null && left_border.GetWeight() != Double.MAX_VALUE)
				congestion += left_border.GetWeight();	
			else
				congestion += 10;
			
			if(right_border != null && right_border.GetWeight() != Double.MAX_VALUE)
				congestion += right_border.GetWeight();
			else
				congestion += 10;
			
			if(lower_border != null && lower_border.GetWeight() != Double.MAX_VALUE)
				congestion += lower_border.GetWeight();
			else
				congestion += 10;
			
			if(upper_border != null && upper_border.GetWeight() != Double.MAX_VALUE)
				congestion += upper_border.GetWeight();
			else
				congestion += 10;
			
			return congestion;
		}
		
		public RegionBorder GetRegionBorder(RegionDirection dir){
			switch(dir){
			case rd_left:
				return left_border;
			case rd_right:
				return right_border;
			case rd_down:
				return lower_border;
			case rd_up:
				return upper_border;
			default:
				return null;
			}
		}
	}
	RegionRepresentation[] regions;
	int regions_x, regions_y;
	double region_width, region_height;
	double offset_x, offset_y;
	
	/** Access to regions */
	public RegionRepresentation RegionAt(int x, int y){
		return regions[y * regions_x + x];
	}
	
	public RegionRepresentation RegionAt(int x, int y, RegionDirection dir){
		
		switch(dir){
		case rd_left:
			--x;
			break;
		case rd_right:
			++x;
			break;
		case rd_down:
			--y;
			break;
		case rd_up:
			++y;
			break;
		case rd_undefined:
			break;
		}
		
		if(IsCoordinateValid(x, y)){
			return RegionAt(x, y);
		}else{
			return null;
		}
	}
	
	/** Representation of a border between two regions */
	class RegionBorder{
		private int demand;
		private int supply;
		private int min_supply = 4;
		
		private Vector<Integer> priv_soft_passes;
		private ArrayList<Integer> soft_passes = new ArrayList<Integer>();
		private Vector<Integer> hard_passes = new Vector<Integer>();
		
		public RegionBorder(int demand, int supply, int passes){
			this.demand = demand;
			this.supply = supply;
			
			priv_soft_passes = new Vector<Integer>();
			for(int i = 0; i < passes; ++i){
				priv_soft_passes.add(null);
				hard_passes.add(null);
			}
			
		}
		
		boolean IsBlocked(){
			synchronized(this){
				return supply <= this.min_supply;
			}
		}
		
		public int GetDemand(){
			return demand;
		}
		
		public void IncDemand(){
			++ demand;
		}
		
		public boolean DecDemand(){
			if(demand > 0){
				--demand;
				return true;
			}else{
				return false;
			}
		}
		
		public int GetSupply(){
			return supply;
		}
		
		public int GetHardPos(int seg_id){
			return hard_passes.indexOf(new Integer(seg_id));
		}
		
		/** see: "Global Wiring on a Wire Routing Machine" Ravi Nair et al. */
		public double GetWeight(){
			double estd = (double)(demand + 1) / 16.0d;
			double weight = Double.MAX_VALUE;
			if(estd <= supply - min_supply){
				weight = estd * 8 / Math.pow(2, 
						((double)supply - min_supply - estd)/2);
			}else if(estd > supply - min_supply 
					&& supply - min_supply >= 3){
				weight = estd * 8 * 2 / Math.pow(2, 
						(double)supply - min_supply / 2);
			}else if(estd > supply - min_supply 
					&& supply - min_supply == 2){
				weight = estd * 8 * 3;
			}else if(estd > supply - min_supply 
					&& supply - min_supply == 1){
				weight = estd * 8 * 9;
			}else if(supply - min_supply == 0){
				weight = Double.MAX_VALUE;
			}
			
			return weight;
		}
		
		/** Route passes border temporarily */
		public boolean SoftPassBorder(int seg_id){
			synchronized(this){
				if(supply > this.min_supply){
					--supply;
					soft_passes.add(seg_id);
					return true;
				}else{
					return false;
				}
			}
		}
		
		/** Set a privileged position in this border */
		public boolean FromSoftToPriv(int seg_id, int priv_pos){
			synchronized(this){
				if(soft_passes.contains(seg_id)){
					soft_passes.remove(soft_passes.indexOf(seg_id));
					
					int delta = 1;
					int length = priv_soft_passes.size();
					int lower = priv_pos - delta;
					int higher = priv_pos + delta;
					
					while(((lower >= 0) && (lower < length))
						|| ((higher >= 0) && (higher < length))){
						
						if(((lower >= 0) && (lower < length)) && priv_soft_passes.get(lower) == null){
							priv_soft_passes.set(lower, seg_id);
							return true;
						}else if(((higher >= 0) && (higher < length)) && priv_soft_passes.get(higher) == null){
							priv_soft_passes.set(higher, seg_id);
							return true;
						}
						
						++delta;
						lower = priv_pos - delta;
						higher = priv_pos + delta;
					}
					
					// nothing free (should not happen)
					soft_passes.add(seg_id);
					return false;
				}else{
					return false;
				}
			}
		}
		
		
		public boolean SoftPassBorder(int seg_id, int priv_pos){
			synchronized(this){
				if(supply > this.min_supply){
					--supply;
					
					if(priv_soft_passes.get(priv_pos) == null){
						priv_soft_passes.set(priv_pos, seg_id);
						return true;
					}else{
						int delta = 1;
						int length = priv_soft_passes.size();
						int lower = priv_pos - delta;
						int higher = priv_pos + delta;
						
						while(((lower >= 0) && (lower < length))
							|| ((higher >= 0) && (higher < length))){
							
							if(((lower >= 0) && (lower < length)) && priv_soft_passes.get(lower) == null){
								priv_soft_passes.set(lower, seg_id);
								return true;
							}else if(((higher >= 0) && (higher < length)) && priv_soft_passes.get(higher) == null){
								priv_soft_passes.set(higher, seg_id);
								return true;
							}
							
							++delta;
							lower = priv_pos - delta;
							higher = priv_pos + delta;
						}
						
						// nothing free (should not happen)
						soft_passes.add(seg_id);
						return true;
					}
				}else{
					return false;
				}
			}
		}
		
		/** Remove a route, which passed this border earlier */
		public boolean RevertSoftPass(int seg_id){
			synchronized(this){
				if(soft_passes.contains(seg_id)){
					++supply;
					soft_passes.remove(soft_passes.indexOf(seg_id));
					return true;
				}else if(priv_soft_passes.contains(seg_id)){
					++supply;
					int index = priv_soft_passes.indexOf(seg_id);
					priv_soft_passes.set(index, null);
					
					return true;
				}else{
					return false;
				}
			}
		}
		
		/** Pay attention using these two functions because of concurrency.
		 *  Reverting a pass, set in this function will only be done, if 
		 *  detailed will not be able to route this wire.
		 */
		public Vector<Integer> HardPassBorderV2(){
			HardPassBorder();
			return new Vector<Integer>(hard_passes);
		}
		
		public Vector<Integer> HardPassBorderV2(Vector<Integer> passes){
			for(int i = 0; i < passes.size(); ++i){
				Integer id = passes.get(i);
				if(id != null && soft_passes.contains(id)){
					FromSoftToPriv(id, i);
				}
			}
			
			HardPassBorder();
			return new Vector<Integer>(hard_passes);
		}
		
		public void HardPassBorder(){
			synchronized(this){
				/** first sort passes */
				Collections.sort(soft_passes);
				
				/** insert previliged passes */
				for(int priv_i = 0; priv_i < priv_soft_passes.size(); ++priv_i){
	
					if(priv_soft_passes.get(priv_i) == null){
						continue;
					}
					
					int id = priv_soft_passes.get(priv_i);
					
					if(hard_passes.get(priv_i) == null){
						hard_passes.set(priv_i, id);
					}else{
						int delta = 1;
						int length = hard_passes.size();
						int lower = priv_i - delta;
						int higher = priv_i + delta;
						
						while(((lower >= 0) && (lower < length))
							|| ((higher >= 0) && (higher < length))){
							
							if(((lower >= 0) && (lower < length)) && hard_passes.get(lower) == null){
								hard_passes.set(lower, id);
								break;
							}else if(((higher >= 0) && (higher < length)) && hard_passes.get(higher) == null){
								hard_passes.set(higher, id);
								break;
							}
							
							++delta;
							lower = priv_i - delta;
							higher = priv_i + delta;
						}
					}
				}
				
				/** then insert soft passes to hard passes */
				int hi = this.min_supply / 2;
				for(int si = 0; si < soft_passes.size(); ++ si){
					for(; hi < hard_passes.size() && hard_passes.get(hi) != null && hard_passes.get(hi) >= 0; ++hi){
					};
					if(hi < hard_passes.size()){
						hard_passes.set(hi, soft_passes.get(si));
					}else{
						hard_passes.add(soft_passes.get(si));
					}
	
					hi++;
				}
					
				/** and at the end reset soft_passes */
				soft_passes = new ArrayList<Integer>();
				int passes = priv_soft_passes.size();
				priv_soft_passes = new Vector<Integer>();
				for(int i = 0; i < passes; ++i){
					priv_soft_passes.add(null);
				}
			}
		}
			
		/** Reverts a pass through this border, if the detailed router could 
		 * not route the segment.
		 */
		public boolean RevertHardPass(int seg_id){
			synchronized(this){
				int index = hard_passes.indexOf(new Integer(seg_id));
				if(index >= 0){
					++supply;
					hard_passes.set(index, null);
					return true;
				}else{
					return false;
				}
			}			
		}
		
		/** Removes segments failed to route in the global router. */
		public void CleanBorder(){
			synchronized(this){
				for(Integer i : not_routed){
					if(soft_passes.contains(i)){
						soft_passes.remove(soft_passes.indexOf(i));
					}
					if(priv_soft_passes.contains(i)){
						priv_soft_passes.set(priv_soft_passes.indexOf(i), null);
					}
				}
			}
		}
	}
	int max_supply_vertical, max_supply_horizontal;
	
	/** Checks and s to assure a correct coordinate access */
	public boolean IsCoordinateValid(int x, int y){
		return 0 <= x && x < regions_x && 0 <= y && y < regions_y;
	}

	public int ConvertX(double x){
		return (int)(x / region_width);
	}
	
	public int ConvertY(double y){
		return (int)(y / region_height);
	}
	
	
	public RegionDirection DirFromTo(Vector2i from, Vector2i to){
		int x = to.x - from.x;
		int y = to.y - from.y;
		
		RegionDirection dir;
		if(x == 1 && y == 0){
			dir = RegionDirection.rd_right;
		}else if(x == -1 && y == 0){
			dir = RegionDirection.rd_left;
		}else if(x == 0 && y == 1){
			dir = RegionDirection.rd_up;
		}else if(x == 0 && y == -1){
			dir = RegionDirection.rd_down;
		}else{
			dir = RegionDirection.rd_undefined;
		}
		
		return dir;
	}
	
	public int GetNeighborX(int x, RegionDirection dir){
		switch(dir){
		case rd_left:
			--x;
			break;
		case rd_right:
			++x;
			break;
		}
		
		return x;
	}
	
	public int GetNeighborY(int y, RegionDirection dir){
		switch(dir){
		case rd_down:
			--y;
			break;
		case rd_up:
			++y;
			break;
		}
		
		return y;
	}
	
	public Vector2i GetNeighborPos(int x, int y, RegionDirection dir){

		switch(dir){
		case rd_left:
			--x;
			break;
		case rd_right:
			++x;
			break;
		case rd_down:
			--y;
			break;
		case rd_up:
			++y;
			break;
		case rd_undefined:
			break;
		}
		
		return new Vector2i(x, y);
	}
	
	public Vector2i GetNeighborPos(Vector2i curr_pos, RegionDirection dir){
		return GetNeighborPos(curr_pos.x, curr_pos.y, dir);
	}
	
	static public RegionDirection GetOppositeDir(RegionDirection dir){
		
		switch(dir){
		case rd_left:
			return RegionDirection.rd_right;
		case rd_right:
			return RegionDirection.rd_left;
		case rd_down:
			return RegionDirection.rd_up;
		case rd_up:
			return RegionDirection.rd_down;
		default:
			return RegionDirection.rd_undefined;
		}
	}

	/** Structure to implement job-stealing. This queue offers not yet routed segments. */
	private ArrayList<JobMessage> segment_jobs;
	
	public synchronized JobMessage PollSegmentJob(){
			if(segment_jobs.isEmpty()){
				return null;
			}else{
				JobMessage job = segment_jobs.get(0);
				segment_jobs.remove(0);
				return job;
			}
	}
	
	/** Interface between data from electric and global router */
	class SegmentRepresentation{
		public Vector2i start;
		public Vector2i end;
		public double d_start_x;
		public double d_start_y;
		public double d_end_x;
		public double d_end_y;
		SegmentRepresentation(Vector2i start, Vector2i end){
			this.start = new Vector2i(start.x, start.y);
			this.end = new Vector2i(end.x, end.y);
		}
	}
	SegmentRepresentation[] segments;
	ArrayList<RoutingSegment> electric_segments;
	
	/** job stealing for demand estimation */
	Integer demand_seg_index;
	
	public synchronized SegmentRepresentation PollDemandEstimationJob(){
		SegmentRepresentation ret = null;
			if(to_reroute.isEmpty()){
				if(demand_seg_index < segments.length){
					ret = segments[demand_seg_index];
					++demand_seg_index;
				}
			}else{
				if(demand_seg_index < to_reroute.size()){
					ret = segments[to_reroute.get(demand_seg_index)];
					++demand_seg_index;
				}
			}
		
		return ret;
	}
	
	/** job stealing for removing failed segments */
	Integer remove_failed_index;
	
	public synchronized int PollFailedId(){
			if(remove_failed_index < to_reroute.size()){
				int ret = to_reroute.get(remove_failed_index);
				++remove_failed_index;
				return ret;
			}else{
				return -1;
			}
		
	}
	
	public synchronized void ResetOutputRoutes(){
			this.output_coarse_routes = new HashMap<Integer, RouteToStitch>();
	}
	
	/** backtraces from global router workers */
	class SegBtPair{
		public int seg_id;
		public Vector<RegionDirection> backtrace;
	}
	ArrayList<SegBtPair> seg_backtraces;
	
	public synchronized void OfferBacktrace(Vector<RegionDirection> bt, int seg_id){
		SegBtPair pair = new SegBtPair();
		pair.backtrace = bt;
		pair.seg_id = seg_id;

		seg_backtraces.add(pair);
	}
	
	public synchronized SegBtPair PollBacktrace(){
		
		SegBtPair ret;

		if(seg_backtraces.isEmpty()){
			ret = new SegBtPair();
			ret.seg_id = -1;
			ret.backtrace = null;
		}else{
			ret = seg_backtraces.get(0);
			seg_backtraces.remove(0);
		}

		return ret;
	}
	
	/** routes which could not be routed */
	public ArrayList<Integer> not_routed;
	
	public synchronized void OfferUnrouted(ArrayList<Integer> list){
			not_routed.addAll(list);
	}
	
	/** synchronization of second phase */
	Integer regions_in_work;
	public synchronized RegionRepresentation PollNextRegionToSort(){
		RegionRepresentation ret = null;

		if(regions_in_work < regions_x * regions_y){
			ret =  regions[regions_in_work];
			++regions_in_work;
		}
		
		return ret;
	}
	
	/** list with id's of segments which need to be rerouted (feedback from detailed)*/
	ArrayList<Integer> to_reroute;

	/** Transforms Coordinates back after the whole routing process is finished. */
	public void Retransform(List<Coordinate> list){
		Coordinate co;
		for (Iterator<Coordinate> i = list.iterator(); i.hasNext();) {
			co = i.next();
			co.x += offset_x;
			co.y += offset_y;
		}
	}
	
	public GlobalRouterV3(Rectangle2D rect, int num_regions, List<RoutingSegment> segmentsToRoute, int num_threads, double tileSize){
		
		this.tileSize = tileSize;
		this.num_threads = num_threads;
		barrier = new CyclicBarrier(num_threads);
		
		/* calculate region width and height */
		double w = rect.getWidth() / num_regions;
		double h = rect.getHeight() / num_regions;
		int num_tiles_w_in_region = (int)Math.ceil(w / this.tileSize);
		int num_tiles_h_in_region = (int)Math.ceil(h / this.tileSize);
		
		this.region_width = num_tiles_w_in_region * this.tileSize;
		this.region_height = num_tiles_h_in_region * this.tileSize;
		this.regions_x = num_regions;
		this.regions_y = num_regions;
		this.offset_x = rect.getMinX() - Math.signum(rect.getMinX()) * Math.abs(rect.getMinX()) % 0.01;
		this.offset_y = rect.getMinY() - Math.signum(rect.getMinY()) * Math.abs(rect.getMinY()) % 0.01;
		
		regions = new RegionRepresentation[regions_x * regions_y];
		electric_segments = new ArrayList<RoutingSegment>(segmentsToRoute);
		this.segments = new SegmentRepresentation[segmentsToRoute.size()];
		
		Iterator<RoutingSegment> it = segmentsToRoute.iterator();
		for(int i = 0; i < segments.length; ++i){
			RoutingSegment r_seg = it.next();
			double start_x = r_seg.getStartEnd().getLocation().getX() - offset_x;
			double start_y = r_seg.getStartEnd().getLocation().getY() - offset_y;
			double end_x = r_seg.getFinishEnd().getLocation().getX() - offset_x;
			double end_y = r_seg.getFinishEnd().getLocation().getY() - offset_y;
			Vector2i i_start = new Vector2i((int)(start_x / this.region_width),
					(int)(start_y / this.region_height));
			Vector2i i_end = new Vector2i((int)(end_x / this.region_width), 
					(int)(end_y / this.region_height));
			if(i_start.x < 0){
				i_start.x = 0;
			}
			if(i_start.x >= regions_x){
				i_start.x = regions_x - 1;
			}
			if(i_start.y < 0){
				i_start.y = 0;
			}
			if(i_start.y >= regions_y){
				i_start.y = regions_y - 1;
			}
			if(i_end.x < 0){
				i_end.x = 0;
			}
			if(i_end.x >= regions_x){
				i_end.x = regions_x - 1;
			}
			if(i_end.y < 0){
				i_end.y = 0;
			}
			if(i_end.y >= regions_y){
				i_end.y = regions_y - 1;
			}
			this.segments[i] = new SegmentRepresentation(i_start, i_end);
			this.segments[i].d_start_x = start_x;
			this.segments[i].d_start_y = start_y;
			this.segments[i].d_end_x = end_x;
			this.segments[i].d_end_y = end_y;
		}
		
		/** setup RegionRepresentations  */
		for(int i = 0; i < this.regions_x * this.regions_y; ++i){
			regions[i] = new RegionRepresentation(0d, segments.length);
		}
		
		/** create borders */
		this.max_supply_horizontal = (int)(region_width / tileSize) - 1;
		int supply_x = this.max_supply_horizontal;
		this.max_supply_vertical = (int)(region_height / tileSize) - 1;
		int supply_y = this.max_supply_vertical;
		for(int x = 0; x < this.regions_x; ++x){
			for(int y = 0; y < this.regions_y; ++y){
				if(x > 0){
					RegionAt(x, y).left_border = new RegionBorder(0, supply_y, max_supply_vertical);
				}
				if(y > 0){
					RegionAt(x, y).lower_border = new RegionBorder(0, supply_x, max_supply_horizontal);
				}
			}
		}
		/** set upper and right border */
		for(int x = 0; x < this.regions_x; ++x){
			for(int y = 0; y < this.regions_y; ++y){
				if(x < this.regions_x - 1){
					RegionAt(x, y).right_border = RegionAt(x, y, RegionDirection.rd_right).left_border;
				}
				if(y < this.regions_y - 1){
					RegionAt(x, y).upper_border = RegionAt(x, y, RegionDirection.rd_up).lower_border;
				}
			}
		}
		
		/** initialize for first run of global router */
		to_reroute = new ArrayList<Integer>();
		segment_jobs = new ArrayList<JobMessage>();
		demand_seg_index = 0;
		regions_in_work = 0;
		not_routed = new ArrayList<Integer>();
		remove_failed_index = 0;
		this.seg_backtraces = new ArrayList<SegBtPair>();

		for(int i = 0; i < segments.length; ++i){
			JobMessage job = new JobMessage(i, segments[i].start, 0, 0.0d, RegionDirection.rd_undefined);
			segment_jobs.add(job);
		}

		this.output_coarse_routes = new HashMap<Integer, RouteToStitch>();
		this.output_regions = new RegionToRoute[regions_x * regions_y];
		for(int x = 0; x < regions_x; ++x){
			for(int y = 0; y < regions_y; ++y){
				Rectangle2D rec = new Rectangle2D.Double();
				rec.setFrameFromDiagonal(x * this.region_width, y * this.region_height,
						(x + 1) * this.region_width, (y + 1) * this.region_height);
				output_regions[y * regions_x + x] = new RegionToRoute(rec);
			}	
		}
	}
	
	/** List of routes, which could be completed by the detailed router */
	List<Integer> wiried_routes = new LinkedList<Integer>();
	
	public void Reinitialize(List<Integer> failed_to_route){
		to_reroute = new ArrayList<Integer>(failed_to_route);
		if(max_detour < Math.max(regions_x, regions_y))
		{
			max_detour += 2;
		}	
		
		//mark rest of output_coarse_routes that are not in failed_to_route as wiried
		for(RouteToStitch rts : output_coarse_routes.values()){
			if(!failed_to_route.contains(rts.id)){
				wiried_routes.add(rts.id);
			}
		}
		
		to_reroute.addAll(not_routed);
		not_routed = new ArrayList<Integer>();
		segment_jobs = new ArrayList<JobMessage>();
		demand_seg_index = 0;
		regions_in_work = 0;
		remove_failed_index = 0;
		this.seg_backtraces = new ArrayList<SegBtPair>();

		for(int i = 0; i < to_reroute.size(); ++i){
			int seg_id = to_reroute.get(i);
			JobMessage job = new JobMessage(seg_id, segments[seg_id].start, 0, 0.0d, RegionDirection.rd_undefined);
			segment_jobs.add(job);
		}

		this.output_regions = new RegionToRoute[regions_x * regions_y];
		for(int x = 0; x < regions_x; ++x){
			for(int y = 0; y < regions_y; ++y){
				Rectangle2D rec = new Rectangle2D.Double();
				rec.setFrameFromDiagonal(x * this.region_width, y * this.region_height,
						(x + 1) * this.region_width, (y + 1) * this.region_height);
				output_regions[y * regions_x + x] = new RegionToRoute(rec);
			}	
		}
		
		/** reset demand estimate */
		for(int x = 0; x < this.regions_x; ++x){
			for(int y = 0; y < this.regions_y; ++y){
				if(x > 0){
					RegionAt(x, y).left_border.demand = 0;
				}
				if(y > 0){
					RegionAt(x, y).lower_border.demand = 0;
				}
			}
		}
	}
	
	public void StartGlobalRouting(){
		Thread[] slave_threads;
		GlobalRouterThreadV3[] slaves;
		slave_threads = new Thread[num_threads - 1];
		slaves = new GlobalRouterThreadV3[num_threads];
		
		/** start parallel global routing */
		for(int i = 0; i < num_threads - 1; ++i){
			slaves[i] = new GlobalRouterThreadV3(this, i);
			slave_threads[i] = new Thread(slaves[i]);
			slave_threads[i].setName("GlobalRouterSlave_" + i);
			slave_threads[i].start();
		}
		slaves[num_threads - 1] = new GlobalRouterThreadV3(this, 
				num_threads - 1);
		slaves[num_threads - 1].run();
	}
}


/** routing segments representation declarations */
/** Position in coordinate system of global router */
class Vector2i{
	public int x;
	public int y;
	
	public Vector2i(int x, int y){
		this.x = x;
		this.y = y;
	}
	public Vector2i(Vector2i p){
		this.x = p.x;
		this.y = p.y;
	}
	 public boolean equals(Vector2i v){
		 return (v.x == x && v.y == y);
	 }
}

/** region declaration */
enum RegionDirection{
	rd_left, rd_right, rd_up, rd_down, rd_undefined
}

/** job queues declaration */
class JobMessage{
	public int seg_id = Integer.MIN_VALUE;
	public Vector2i position = null;
	public int step_num = Integer.MIN_VALUE;
	public double weight = Double.MIN_VALUE;
	public RegionDirection min_dir = RegionDirection.rd_undefined;
	
	public JobMessage(int seg_id, Vector2i pos, int min_length, double weight, RegionDirection min_dir){
		position = new Vector2i(pos.x, pos.y);
		this.seg_id = seg_id;
		this.step_num = min_length;
		this.weight = weight;
		this.min_dir = min_dir;
	}
	
	public JobMessage(int seg_id, Vector2i pos, int step_num){
		position = new Vector2i(pos.x, pos.y);
		this.seg_id = seg_id;
		this.step_num = step_num;
	}
	
	public JobMessage(int seg_id, Vector2i pos, double weight, int step_num){
		position = new Vector2i(pos.x, pos.y);
		this.seg_id = seg_id;
		this.weight = weight;
		this.step_num = step_num;
	}
}

/** SegmentInfoHelper classes */
class WeightLengthPair{
	public double weight;
	public int length;
	public WeightLengthPair(double w, int l){
		weight = w;
		length = l;
	}
}

/** Structure holding information for backtracing procedure */
class BacktraceState{
	public double weight;
	public RegionDirection dir;
	public int path_length;
	public BacktraceState(double w, RegionDirection d, int l){
		weight = w;
		dir = d;
		path_length = l;
	}
}

/** A segment patch, inside of a region. */
class SegPart{
	public List<Coordinate> segment_part;
	public int id;
}
