/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: AStarOpenListCheapList.java * Written by: Christian Harnisch, Ingo Besenfelder, Michael Neumann (Team 3) * * 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 Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */ package com.sun.electric.tool.routing.experimentalAStar2.storage; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import com.sun.electric.tool.routing.experimentalAStar2.algorithm.AStarMapBase; import com.sun.electric.tool.routing.experimentalAStar2.algorithm.AStarNodeBase; import com.sun.electric.tool.routing.experimentalAStar2.algorithm.AStarOpenListBase; public class AStarOpenListCheapList<T extends AStarNodeBase<T>> implements AStarOpenListBase<T> { /** * Find best value how many elements to be inserted into CheapList initially. */ private int CHEAPLIST_INIT_SIZE = 15; LinkedList<T> openList = new LinkedList<T>(); CheapList cheapList = new CheapList(); AStarMapBase<T> map; public void setMap(AStarMapBase<T> map) { this.map = map; } /** * Reinitializes the CheapList. Either sort the openList in O(n log n) or run * through it <code>CHEAPLIST_INIT_SIZE</code> times to find (and remove) the * best ones. Then insert the found elements into the CheapList in O( * <code>CHEAPLIST_INIT_SIZE</code>²) * * @param fillingSize number of elements to be move to CheapList. */ public void reinitializeCheapList(int fillingSize) { Collections.sort(this.openList); int i = 0; while (i < fillingSize && !this.openList.isEmpty()) { this.cheapList.forcedInsert(this.openList.removeFirst()); i++; } } public void addNodeToOpenList(T node) { if (this.cheapList.isEmpty()) reinitializeCheapList(CHEAPLIST_INIT_SIZE); if (this.cheapList.isEmpty() || !this.cheapList.cheapInsert(node)) // lazy eval is important for correctness! this.openList.add(node); node.markAsOpen(); } public Collection<T> dumpOpenList() { Collection<T> dump = new ArrayList<T>(this.cheapList.dump()); dump.addAll(this.openList); this.openList.clear(); return dump; } /* * (non-Javadoc) * * @seecom.sun.electric.tool.routing.astar.t3.algorithm.AStarOpenListBase# * clearOpenList() */ public void clearOpenList() { this.cheapList.clear(); for (T node : this.openList) { node.markAsNoList(); } this.openList.clear(); } public T findOpenNode(int x, int y, int z) { T node = map.nodeAt(x, y, z); if (node != null && node.isOpen()) return node; return null; } public boolean isOpenListEmpty() { if (this.cheapList.head != null) return false; return this.openList.isEmpty(); } public T removeCheapestOpenNode() { if (this.cheapList.isEmpty()) reinitializeCheapList(this.CHEAPLIST_INIT_SIZE); T result = this.cheapList.pop(); if (this.cheapList.isEmpty()) reinitializeCheapList(this.CHEAPLIST_INIT_SIZE); if (result != null) result.markAsNoList(); return result; } public void removeNodeFromOpenList(T node) { if (this.cheapList.remove(node)) { if (this.cheapList.isEmpty()) reinitializeCheapList(CHEAPLIST_INIT_SIZE); } else this.openList.remove(node); node.markAsNoList(); } /** * The cheapList initially contains the <code>CHEAPLIST_INIT_SIZE</code> * cheapest nodes of the openList. It will be filled up in the initialization * phase and only filled again when it is empty.<br> * <br> * <b>Runtime assumptions:</b><br> * <b>insert():</b> O(k) with k = a relatively small constant. O(1) for all * elements with higher cost than tail.<br> * <b>getCheapestNode():</b> O(1).<br> * <b>isEmpty():</b> O(1)<br> */ class CheapList extends ADoubleLinkedList<T> { /** * Sorted insertion in O(k). Node is only inserted if better than tail => * Small list of cheapest Nodes. */ public boolean cheapInsert(T node) { AElement elementToInsert = new AElement(node); if (this.head == null && this.tail == null) { // initial insert this.head = elementToInsert; this.tail = elementToInsert; return true; } else if (node.getTotalCost() > this.tail.data.getTotalCost()) { return false; } else { AElement current = this.head; while (current != null) { if (node.getTotalCost() < current.data.getTotalCost()) /* * TODO: optimize with this perhaps: || (node.getTotalCost() == * current.data.getTotalCost() && node.getCostToGoal() < * current.data.getCostToGoal())) */ { if (current.hasPrev()) { current.prev.next = elementToInsert; elementToInsert.prev = current.prev; } else { // no prev means: current is head this.head = elementToInsert; } current.prev = elementToInsert; elementToInsert.next = current; return true; } current = current.next; } if (node.getTotalCost() == this.tail.data.getTotalCost()) { this.tail.next = elementToInsert; elementToInsert.prev = this.tail; this.tail = elementToInsert; return true; } } return false; } /** * Sorted insertion in O(k). Node is only inserted if better than tail => * Small list of cheapest Nodes. */ public boolean forcedInsert(T node) { AElement elementToInsert = new AElement(node); if (this.head == null && this.tail == null) { // initial insert this.head = elementToInsert; this.tail = elementToInsert; return true; } if (node.getTotalCost() >= this.tail.data.getTotalCost()) { this.tail.next = elementToInsert; elementToInsert.prev = this.tail; this.tail = elementToInsert; return true; } AElement current = this.head; while (current != null) { if (node.getTotalCost() < current.data.getTotalCost()) { if (current.hasPrev()) { current.prev.next = elementToInsert; elementToInsert.prev = current.prev; } else { // no prev means: current is head this.head = elementToInsert; } current.prev = elementToInsert; elementToInsert.next = current; return true; } current = current.next; } return false; } public void print() { AElement current = head; while (current != null) { System.err.print("(" + current.data.getX() + "," + current.data.getY() + "),"); current = current.next; } } public ArrayList<T> dump() { ArrayList<T> list = new ArrayList<T>(); while (this.head != null) { T currentHead = this.head.data; list.add(currentHead); this.remove(currentHead); } return list; } public void clear() { while (this.head != null) { T currentHead = this.head.data; currentHead.markAsNoList(); this.remove(currentHead); } } public T pop() { if (this.head == null) return null; T currentHead = this.head.data; this.remove(currentHead); return currentHead; } public boolean isEmpty() { return this.head == null; } /** * Retrieves an Object in O(n). * * @return the found element, or null. */ public T getNode(int x, int y, int z) { AElement current = this.head; while (current != null) { if (current.data.getX() == x && current.data.getY() == y && current.data.getZ() == z) { return current.data; } current = current.next; } return null; } } class ADoubleLinkedList<S> { // head and tail AElement head = null; AElement tail = null; /** * Inserts an Object in O(1). * * @param data the Object which contains the actual data to be stored. * @return true if inserted successfully. */ public boolean insert(S data) { AElement elementToInsert = new AElement(data); if (this.head == null && this.tail == null) { // initial insert this.head = elementToInsert; this.tail = elementToInsert; this.head.next = this.tail; this.tail.prev = this.head; } else { // append to tail this.tail.next = elementToInsert; this.tail = elementToInsert; } return true; } /** * Removes an Object in O(n) after finding it. * * @return true if element was found, else false. */ public boolean remove(S data) { AElement current = this.head; boolean found = false; while (current != null) { if (current.data.equals(data)) { found = true; if (current.hasPrev()) { current.prev.next = current.next; if (!current.hasNext()) this.tail = current.prev; } if (current.hasNext()) { current.next.prev = current.prev; if (!current.hasPrev()) this.head = current.next; } if (!current.hasNext() && !current.hasPrev()) { this.head = null; this.tail = null; } break; } current = current.next; } return found; } /** * The wrapping Element. This practically only adds two pointers to the * actual data object, that has to be stored in the list. * */ class AElement { AElement prev = null; AElement next = null; S data; public AElement(S data) { this.data = data; } public boolean hasNext() { return this.next != null; } public boolean hasPrev() { return this.prev != null; } } } }