/*
* Kodkod -- Copyright (c) 2005-present, Emina Torlak
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package kodkod.examples.csp;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A simple graph implementation for parsing and storing graphs in DIMACS and ASP formats.
* @specfield nodes: set N
* @specfield edges: nodes->nodes
* @specfield start: nodes+null
* @invariant all e: edges | e.from + e.to in nodes
* @author Emina Torlak
*/
public final class Graph<N> {
private final Map<N,Set<N>> graph;
private final N start;
/**
* Constructs a graph that stores the given sets of nodes and edges,
* along with the specified distinguished start node, if any.
* @requires start in graph.keySet() + null
* @ensures this.start' = start && this.nodes = graph.keySet() && this.edges = graph
*/
private Graph(N start, Map<N,Set<N>> graph) {
this.start = start;
this.graph = Collections.unmodifiableMap(graph);
}
/**
* Returns the nodes.
* @return this.nodes
*/
public Set<N> nodes() { return graph.keySet(); }
/**
* Returns the start node.
* @return this.start
*/
public N start() { return start; }
/**
* Returns this.edges[n]
* @return this.edges[n]
*/
public Set<N> edges(Object node) { return graph.containsKey(node) ? Collections.unmodifiableSet(graph.get(node)) : null; }
/**
* Supported graph file formats.
* @author Emina Torlak
*/
public static enum Format {
/**
* DIMACS graph format.
*/
DIMACS {
/**
* {@inheritDoc}
* @see kodkod.examples.csp.Graph.Format#parse(java.lang.String)
*/
public Graph<Integer> parse(String file) {
try(BufferedReader in = new BufferedReader(new FileReader(file))) {
final Map<Integer,Set<Integer>> graph = new LinkedHashMap<Integer,Set<Integer>>();
int vertices = -1;
final Pattern p = Pattern.compile("p\\s+edge\\s+(\\d+)\\s+(\\d+)\\s*");
final Matcher mp = p.matcher("");
for(String line = in.readLine(); line != null; line = in.readLine()) {
mp.reset(line);
if(mp.matches()) {
vertices = Integer.parseInt(mp.group(1));
break;
}
}
if (vertices<0) throw new IllegalArgumentException("Bad header line: " + file);
for(int i = 0; i < vertices; i++) {
graph.put(i+1, new LinkedHashSet<Integer>(4));
}
final Pattern e = Pattern.compile("e\\s+(\\d+)\\s+(\\d+)\\s*");
final Matcher me = e.matcher("");
for(String line = in.readLine(); line != null; line = in.readLine()) {
me.reset(line);
if(me.matches()) {
final int from = Integer.parseInt(me.group(1)), to = Integer.parseInt(me.group(2));
if (!graph.containsKey(from)) throw new IllegalArgumentException("Bad vertex: " + from + " in " + line);
if (!graph.containsKey(to)) throw new IllegalArgumentException("Bad vertex: " + to + " in " + line);
graph.get(from).add(to);
graph.get(to).add(from);
}
}
return new Graph<Integer>(null, graph);
} catch (IOException e) {
throw new IllegalArgumentException("Bad file: " + file);
}
}
},
/**
* Full ASP graph format; used for Hamiltonian path instances (http://asparagus.cs.uni-potsdam.de/?action=instances&id=30).
*/
ASP {
/**
* {@inheritDoc}
* @see kodkod.examples.csp.Graph.Format#parse(java.lang.String)
*/
public Graph<String> parse(String file) {
try(BufferedReader in = new BufferedReader(new FileReader(file))) {
final Map<String,Set<String>> graph = new LinkedHashMap<String,Set<String>>();
String start = null;
final Pattern v = Pattern.compile("vtx\\((\\S+)\\)\\.");
final Matcher mv = v.matcher("");
final Pattern e = Pattern.compile("edge\\((\\S+),(\\S+)\\)\\.");
final Matcher me = e.matcher("");
final Pattern s = Pattern.compile("start\\((\\S+)\\)\\.");
final Matcher ms = s.matcher("");
for(String line = in.readLine(); line != null; line = in.readLine()) {
mv.reset(line);
while(mv.find()) {
graph.put(mv.group(1), new LinkedHashSet<String>(4));
}
me.reset(line);
while(me.find()) {
final String from = me.group(1), to = me.group(2);
if (!graph.containsKey(from)) throw new IllegalArgumentException("Bad vertex: " + from + " in " + line);
if (!graph.containsKey(to)) throw new IllegalArgumentException("Bad vertex: " + to + " in " + line);
graph.get(from).add(to);
graph.get(to).add(from);
}
ms.reset(line);
if (ms.find()) { start = ms.group(1); }
}
if (graph.isEmpty()) throw new IllegalArgumentException("No vertices or edges found in " + file);
return new Graph<String>(start, graph);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
},
/**
* ASP graph format where only edges are specified; used for coloring instances (http://asparagus.cs.uni-potsdam.de/?action=instances&id=30).
*/
ASP_EDGES {
/**
* {@inheritDoc}
* @see kodkod.examples.csp.Graph.Format#parse(java.lang.String)
*/
public Graph<String> parse(String file) {
try(BufferedReader in = new BufferedReader(new FileReader(file))) {
final Map<String,Set<String>> graph = new LinkedHashMap<String,Set<String>>();
String start = null;
final Pattern e = Pattern.compile("edge\\((\\S+),(\\S+)\\)\\.");
final Matcher me = e.matcher("");
for(String line = in.readLine(); line != null; line = in.readLine()) {
me.reset(line);
while(me.find()) {
final String from = me.group(1), to = me.group(2);
if (!graph.containsKey(from)) {
graph.put(from, new LinkedHashSet<String>(4));
}
if (!graph.containsKey(to)) {
graph.put(to, new LinkedHashSet<String>(4));
}
graph.get(from).add(to);
// graph.get(to).add(from);
}
}
if (graph.isEmpty()) throw new IllegalArgumentException("No vertices or edges found in " + file);
return new Graph<String>(start, graph);
} catch (IOException e) {
throw new IllegalArgumentException("Bad file: " + file);
}
}
};
/**
* Parses the given file into a graph
* @requires the file must be in the format specified by this
* @return a graph parsed out of the given file.
*/
public abstract Graph<?> parse(String file);
}
}