All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.almondtools.util.map.MinimalPerfectMapBuilder Maven / Gradle / Ivy

There is a newer version: 0.2.22
Show newest version
package com.almondtools.util.map;

import static java.util.Arrays.asList;

import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Random;
import java.util.Set;

/**
 * provides base functionality for a minimal perfect HashMap (no hash collisions, no unused space) with the BMZ algorithm 
 */
public abstract class MinimalPerfectMapBuilder {

	private KeySerializer keySerializer;
	private S defaultValue;
	private Map entries;

	private HashFunction h;
	private int m;
	private int n;

	public MinimalPerfectMapBuilder(S defaultValue) {
		this.defaultValue = defaultValue;
		this.entries = new HashMap();
	}
	
	public MinimalPerfectMapBuilder withKeySerializer(KeySerializer keySerializer) {
		this.keySerializer = keySerializer;
		return this;
	}
	
	public S get(T key) {
		S result = entries.get(key);
		if (result == null) {
			return defaultValue;
		} else {
			return result;
		}
	}

	public void put(T key, S value) {
		entries.put(key, value);
	}

	public S getDefaultValue() {
		return defaultValue;
	}
	
	public MinimalPerfectMapBuilder addEntry(T key, S value) {
		entries.put(key, value);
		return this;
	}

	public Map getEntries() {
		return entries;
	}

	protected void computeFunctions(int maxTries, double c) throws HashBuildException {
		m = entries.size();
		n = 1 + (int) (m * c);

		Random r = new Random(17);
		for (int i = 0; i < maxTries; i++) {
			try {
				int seed1 = r.nextInt();
				int seed2 = r.nextInt();
				h = computeH(seed1, seed2);
				break;
			} catch (HashBuildException e) {
				continue;
			}
		}
		if (h == null) {
			throw new HashBuildException();
		}
	}

	private HashFunction computeH(int seed1, int seed2) throws HashBuildException {
		int[] g = new int[n];
		HashFunction h = new HashFunction(g, seed1, seed2);
		Graph graph = buildGraph(h);
		BitSet assignedNodes = new BitSet(n);
		BitSet assignedEdges = new BitSet(m);
		assignIntegersToCriticalNodes(graph, g, assignedNodes, assignedEdges);
		assignIntegersToNonCriticalNodes(graph, g, assignedNodes, assignedEdges);
		return h;
	}

	private void assignIntegersToCriticalNodes(Graph graph, int[] g, BitSet assignedNodes, BitSet assignedEdges) throws HashBuildException {
		for (int i : graph.findCriticalNodes()) {
			assignIntegersToCriticalNodes(graph, g, i, assignedNodes, assignedEdges);
		}
	}

	private void assignIntegersToCriticalNodes(Graph graph, int[] g, int i, BitSet assignedNodes, BitSet assignedEdges) throws HashBuildException {
		if (!assignedNodes.get(i)) {
			int next = 0;
			g[i] = next;
			assignedNodes.set(i);
			Queue worklist = new LinkedList();
			worklist.add(i);
			while (!worklist.isEmpty()) {
				int v = worklist.remove();
				for (int u : graph.adjacents(v)) {
					if (!assignedNodes.get(u)) {
						next = assignNext(next + 1, graph, g, u, assignedNodes, assignedEdges);
						worklist.add(u);
					}
				}
			}

		}
	}

	private int assignNext(int next, Graph graph, int[] g, int u, BitSet assignedNodes, BitSet assignedEdges) throws HashBuildException {
		while (!isAssignable(next, graph, g, u, assignedNodes, assignedEdges)) {
			next++;
		}
		g[u] = next;
		assignedNodes.set(u);
		for (int w : graph.adjacents(u)) {
			if (assignedNodes.get(w)) {
				int edge = next + g[w];
				assignedEdges.set(edge);
			}
		}
		return next;
	}

	private boolean isAssignable(int next, Graph graph, int[] g, int u, BitSet assignedNodes, BitSet assignedEdges) throws HashBuildException {
		for (int w : graph.adjacents(u)) {
			if (assignedNodes.get(w)) {
				int edge = next + g[w];
				if (edge >= m) {
					throw new HashBuildException();
				}
				if (assignedEdges.get(edge)) {
					return false;
				}
			}
		}
		return true;
	}

	private void assignIntegersToNonCriticalNodes(Graph graph, int[] g, BitSet assignedNodes, BitSet assignedEdges) {
		for (int i : graph.findNonCriticalNodes()) {
			assignIntegersToNonCriticalNodes(graph, g, i, assignedNodes, assignedEdges);
		}
	}

	private void assignIntegersToNonCriticalNodes(Graph graph, int[] g, int i, BitSet assignedNodes, BitSet assignedEdges) {
		if (!assignedNodes.get(i)) {
			int next = 0;
			g[i] = next;
			assignedNodes.set(i);
			Queue worklist = new LinkedList();
			worklist.add(i);
			while (!worklist.isEmpty()) {
				int v = worklist.remove();
				for (int u : graph.adjacents(v)) {
					if (!assignedNodes.get(u)) {
						int edge = assignedEdges.nextClearBit(0);
						g[u] = edge - g[v];
						assignedNodes.set(u);
						assignedEdges.set(edge);
						worklist.add(u);
					}
				}
			}
		}
	}

	private Graph buildGraph(HashFunction h) throws HashBuildException {
		Graph graph = new Graph(n);
		for (T k : entries.keySet()) {
			int[] edge = doubleHash(h, k);
			boolean success = graph.addEdge(edge[0], edge[1]);
			if (!success) {
				throw new HashBuildException();
			}
		}
		return graph;
	}

	private int[] doubleHash(HashFunction h, T k) {
		if (keySerializer == null) {
			return h.doubleHash(k.hashCode());
		} else {
			return h.doubleHash(keySerializer.toLongArray(k));
		}
	}

	protected HashFunction getH() throws HashBuildException {
		return h;
	}

	private static class Graph {

		private int n;
		private List> adjacencyList;

		@SuppressWarnings("unchecked")
		public Graph(int n) {
			this.n = n;
			this.adjacencyList = asList((Set[]) new Set[n]);
		}

		public boolean addEdge(int a, int b) {
			if (adjacents(a).contains(b) || adjacents(b).contains(a)) {
				return false;
			}
			adjacents(a).add(b);
			adjacents(b).add(a);
			return true;
		}

		public Set adjacents(int a) {
			Set adjacents = adjacencyList.get(a);
			if (adjacents == null) {
				adjacents = new LinkedHashSet<>();
				adjacencyList.set(a, adjacents);
			}
			return adjacents;
		}

		public Set findCriticalNodes() {
			int[] degrees = new int[n];
			for (int a = 0; a < degrees.length; a++) {
				degrees[a] += adjacents(a).size();
			}
			Queue todo = new LinkedList<>();
			for (int i = 0; i < degrees.length; i++) {
				if (degrees[i] == 1) {
					todo.add(i);
				}
			}
			while (!todo.isEmpty()) {
				int a = todo.remove();
				for (int b : adjacents(a)) {
					degrees[b]--;
					if (degrees[b] == 1) {
						todo.add(b);
					}
				}
			}
			Set result = new HashSet<>();
			for (int i = 0; i < degrees.length; i++) {
				if (degrees[i] > 1) {
					result.add(i);
				}
			}
			return result;
		}

		public Set findNonCriticalNodes() {
			Set result = new HashSet<>();
			for (int i = 0; i < n; i++) {
				result.add(i);
			}
			result.removeAll(findCriticalNodes());
			return result;
		}

	}

	public abstract U perfectMinimal();

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy