
com.almondtools.util.map.MinimalPerfectMapBuilder Maven / Gradle / Ivy
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) (((double) 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("assignment failed");
}
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("duplicate edge in graph");
}
}
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 - 2025 Weber Informatics LLC | Privacy Policy