/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: PriorityQueue.java
 * Written by: Christian Julg, Jonas Thedering (Team 1)
 *
 * 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.experimentalAStar1;

import java.util.Random;

/** 
 * This class implements a priority queue by using a splay tree and forming a
 * doubly linked list for entries with the same key
 * 
 * this list is ordered (numbers represent add order:
 * (0, n-1, n-2, ..., 1)
 * remove order will happen reverse to addition, 
 * i.e. the double linked list works as a LIFO / stack data structure 
 * 
 * Node.children[] is reused:
 * 0: prev, 1: next
 * 
 * @author Jonas Thedering
 * @author Christian Jülg
 */
public class PriorityQueue {
	SplayTree tree = new SplayTree();
	
	public void clear() {
		tree.clear();
	}
	
	public void add(Node node) {
		Node equalNode = tree.find(node);
		if(equalNode == null) {
			node.children[0] = null;
			node.children[1] = null;
			tree.insert(node);
		}
		else {
			node.children[1] = equalNode.children[1];
			if(equalNode.children[1] != null)
				equalNode.children[1].children[0] = node;
			equalNode.children[1] = node;
			node.children[0] = equalNode;
		}
	}
	
	public Node remove() {
		Node firstNode = tree.findMin();
		if(firstNode == null)
			return null;
		else if(firstNode.children[1] == null) {
			tree.remove(firstNode);
			return firstNode;
		}
		else {
			Node result = firstNode.children[1];
			firstNode.children[1] = result.children[1];
			if(result.children[1] != null)
				result.children[1].children[0] = firstNode;
			
			return result;
		}
	}
	
	public boolean isEmpty() {
		return tree.isEmpty();
	}
	
	/** Updates the f value for the given node and updates its position in the queue */
	public void decreaseKey(Node node, int newKey) {
		// Unfortunately, there is no decreaseKey method in the splay tree
		if(node.children[0] != null) {
			node.children[0].children[1] = node.children[1];
			if(node.children[1] != null)
				node.children[1].children[0] = node.children[0];
		}
		else {
			tree.remove(node);
			
			Node next = node.children[1];
			if(next != null) {
				next.children[0] = null;
				tree.insert(next);
			}
		}
		node.f = newKey;
		add(node);
	}

	
	// "Unit test"
	public static void main(String[] args) throws Exception {
		PriorityQueue queue = new PriorityQueue();
		long seed = System.currentTimeMillis();
		//seed= 1275391773870l;
		System.out.printf("Seed: %d\n", seed);
		Random r = new Random(seed);
		int[] testValues = new int[50000];
		Node[] testNodes = new Node[testValues.length];
		for(int i = 0; i < testValues.length; ++i)
			testValues[i] = r.nextInt(1000000);
		
		for(int i = 0; i < testValues.length; ++i) {
			Node node = new Node();
			node.initialize(1, 2, 3);
			node.childCount = -1;
			node.children[0] = node;
			node.children[1] = node;
			node.f = testValues[i];
			queue.add(node);
			
			testNodes[i] = node;
		}
		
		for(int i = 100; i < testValues.length; i += 19) {
			queue.decreaseKey(testNodes[i], testNodes[i].f - 1);
			
			queue.decreaseKey(testNodes[i-1], testNodes[i-1].f - 12345);
		}
		
		int previousValue = Integer.MIN_VALUE;
		int count = 0;
		while(!queue.isEmpty()) {
			++count;
			Node node = queue.remove();
			//System.out.printf("%d, ", node.f);
			if(node.f < previousValue) throw new Exception("Wrong order");
			previousValue = node.f;
		}
		
		if(count != testValues.length)
			throw new Exception("Wrong value count: " + count);
		
		System.out.println("ok");
	}	
}
