Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* (C) Copyright 2017-2017, by Joris Kinable and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* This program and the accompanying materials are dual-licensed under
* either
*
* (a) the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation, or (at your option) any
* later version.
*
* or (per the licensee's choosing)
*
* (b) the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation.
*/
package org.jgrapht.io;
import java.io.*;
import java.util.*;
import org.jgrapht.*;
/**
* Importer which reads graphs in graph6 or sparse6 format. A description of the format can be found
* here. graph6 and sparse6 are formats for storing
* undirected graphs in a compact manner, using only printable ASCII characters. Files in these formats have text Format
* and contain one line per graph. graph6 is suitable for small graphs, or large dense graphs. sparse6 is more
* space-efficient for large sparse graphs. Typically, files storing graph6 graphs have the 'g6' extension. Similarly,
* files storing sparse6 graphs have a 's6' file extension. sparse6 graphs support loops and multiple edges, graph6
* graphs do not.
*
* @author Joris Kinable
*
* @param graph vertex type
* @param graph edge type
*/
public class Graph6Sparse6Importer extends AbstractBaseImporter implements GraphImporter{
enum Format{GRAPH6, SPARSE6}
private final double defaultWeight;
/* byte representation of the input string */
private byte[] bytes;
/* pointers which index a specific byte/bit in the vector bytes */
private int byteIndex, bitIndex=0;
private Format format=Format.GRAPH6;
// ~ Constructors ----------------------------------------------------------
/**
* Construct a new Graph6Sparse6Importer
*
* @param vertexProvider provider for the generation of vertices. Must not be null.
* @param edgeProvider provider for the generation of edges. Must not be null.
* @param defaultWeight default edge weight
*/
public Graph6Sparse6Importer(
VertexProvider vertexProvider, EdgeProvider edgeProvider, double defaultWeight)
{
super(vertexProvider, edgeProvider);
this.defaultWeight = defaultWeight;
}
/**
* Construct a new Graph6Sparse6Importer
*
* @param vertexProvider provider for the generation of vertices. Must not be null.
* @param edgeProvider provider for the generation of edges. Must not be null.
*/
public Graph6Sparse6Importer(VertexProvider vertexProvider, EdgeProvider edgeProvider)
{
this(vertexProvider, edgeProvider, Graph.DEFAULT_EDGE_WEIGHT);
}
@Override
public void importGraph(Graph g, Reader input) throws ImportException {
// convert to buffered
BufferedReader in;
if (input instanceof BufferedReader) {
in = (BufferedReader) input;
} else {
in = new BufferedReader(input);
}
String g6="";
try {
g6=in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if(g6.isEmpty())
throw new ImportException("Failed to read graph");
this.importGraph(g, g6);
}
/**
* Import the graph represented by a String in graph6 or sparse6 encoding.
* @param g the graph
* @param g6 String representation of a graph either in graph6 or sparse6 format. WARNING: a g6/s6 string may contain
* backslashes '\'. Escaping is required when invoking this method directly. E.g.
importgraph(g,":?@MnDA\oi")
may
* result in undefined behavior. This should have been:
importgraph(g,":?@MnDA\\oi")
* @throws ImportException in case any error occurs, such as I/O or parse error
*/
public void importGraph(Graph g, String g6) throws ImportException {
g6=g6.replace("\n", "").replace("\r", ""); //remove any new line characters
//Strip header. By default we assume the input Format is GRAPH6, unless stated otherwise
if(g6.startsWith(":")) {
g6=g6.substring(1, g6.length());
this.format=Format.SPARSE6;
}else if(g6.startsWith(">>sparse6<<:")) {
g6 = g6.substring(12, g6.length());
this.format = Format.SPARSE6;
}else if(g6.startsWith(">>graph6<<"))
g6 = g6.substring(10, g6.length());
bytes = g6.getBytes();
validateInput();
byteIndex = bitIndex = 0;
//Number of vertices n
final int n = getNumberOfVertices();
//Add vertices to the graph
Map map = new HashMap<>();
for (int i = 0; i < n; i++) {
V vertex = vertexProvider.buildVertex(""+i, new HashMap<>());
map.put(i, vertex);
g.addVertex(vertex);
}
if(format == Format.GRAPH6)
this.readGraph6(g, map);
else
this.readSparse6(g, map);
}
private void readGraph6(Graph g, Map vertexIndexMap) throws ImportException {
//check whether there's enough data
int requiredBytes= (int) Math.ceil(vertexIndexMap.size()*(vertexIndexMap.size()-1)/12.0)+byteIndex;
if(bytes.length < requiredBytes)
throw new ImportException("Graph string seems to be corrupt. Not enough data to read graph6 graph");
//Read the lower triangle of the adjacency matrix of G
for (int i = 0; i < vertexIndexMap.size(); i++) {
for (int j = 0; j < i; j++) {
int bit = getBits(1);
if (bit == 1) {
V from= vertexIndexMap.get(i);
V to = vertexIndexMap.get(j);
String label = "e_" + i + "_" + j;
E e = edgeProvider.buildEdge(from, to, label, new HashMap<>());
g.addEdge(from, to, e);
if (g.getType().isWeighted())
g.setEdgeWeight(e, defaultWeight);
}
}
}
}
private void readSparse6(Graph g, Map vertexIndexMap) throws ImportException {
int n = vertexIndexMap.size();
//number of bits needed to represent n-1 in binary
int k = (int) Math.ceil(Math.log(n) / Math.log(2));
//Current vertex
int v = 0;
//The remaining bytes encode a sequence b[0] x[0] b[1] x[1] b[2] x[2] ... b[m] x[m]
//Read blocks. In decoding, an incomplete (b,x) pair at the end is discarded.
int dataBits=bytes.length*6-(byteIndex*6+bitIndex);
while(dataBits >= 1+k){ //while there's data remaining
int b = getBits(1); //Read x[i]
int x = getBits(k); //Read b[i]
if(b==1)
v++;
if (v >= n) //Ignore the last bit, this is just padding
break;
if (x > v)
v = x;
else {
V from= vertexIndexMap.get(x);
V to = vertexIndexMap.get(v);
String label = "e_" + x + "_" + v;
E e = edgeProvider.buildEdge(from, to, label, new HashMap<>());
g.addEdge(from, to, e);
if (g.getType().isWeighted())
g.setEdgeWeight(e, defaultWeight);
}
dataBits-=1+k;
}
}
/**
* Check whether the g6 or s6 encoding contains any obvious errors
* @throws ImportException in case any error occurs, such as I/O or parse error
*/
private void validateInput() throws ImportException {
for(byte b : bytes)
if (b < 63 || b > 126)
throw new ImportException("Graph string seems to be corrupt. Illegal character detected: " + b);
}
/**
* Read the number of vertices in the graph
* @return number of vertices in the graph
* @throws ImportException in case any error occurs, such as I/O or parse error
*/
private int getNumberOfVertices() throws ImportException {
//Determine whether the number of vertices is encoded in 1, 4 or 8 bytes.
int n;
if(bytes.length > 8 && bytes[0] == 126 && bytes[1]==126) {
byteIndex+=2; //Strip the first 2 garbage bytes
n= getBits(36);
if(n < 258048)
throw new ImportException("Graph string seems to be corrupt. Invalid number of vertices.");
}else if(bytes.length > 4 && bytes[0] == 126) {
byteIndex++; //Strip the first garbage byte
n= getBits(18);
if(n < 63 || n > 258047)
throw new ImportException("Graph string seems to be corrupt. Invalid number of vertices.");
}else {
n = getBits(6);
if(n < 0 || n > 62)
throw new ImportException("Graph string seems to be corrupt. Invalid number of vertices.");
}
return n;
}
/**
* Converts the next k bits of data to an integer
* @param k number of bits
* @return the next k bits of data represented by an integer
*/
private int getBits(int k) throws ImportException {
int value=0;
//Read minimum{bits we need, remaining bits in current byte}
if(bitIndex > 0 || k < 6){
int x=Math.min(k, 6-bitIndex);
int mask = (1 << x) - 1;
int y = (bytes[byteIndex]-63) >> (6 - bitIndex - x);
y &= mask;
value = (value << k) + y;
k -= x;
bitIndex = bitIndex + x;
if(bitIndex == 6){
byteIndex++;
bitIndex=0;
}
}
//Read blocks of 6 bits at a time
int blocks=k/6;
for (int j = 0; j < blocks; j++) {
value = (value << 6) + bytes[byteIndex]-63;
byteIndex++;
k -= 6;
}
//Read remaining bits
if(k>0){
int y=bytes[byteIndex]-63;
y=y >> (6-k);
value=(value << k) + y;
bitIndex=k;
}
return value;
}
private String getBitString(int i){
return String.format("%8s", Integer.toBinaryString(i & 0xFF)).replace(' ', '0');
}
}