org.jgrapht.io.Graph6Sparse6Exporter Maven / Gradle / Ivy
/*
* (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.*;
/**
* Exporter which exports 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.
*
* In particular, the length of a Graph6 string representation of a graph depends only on the number of vertices.
* However, this also means that graphs with few edges take as much space as graphs with many edges. On the other
* hand, Sparse6 is a variable length format which can use dramatically less space for sparse graphs but can have
* a much larger storage size for dense graphs.
*
* @author Joris Kinable
*
* @param graph vertex type
* @param graph edge type
*/
public class Graph6Sparse6Exporter
implements GraphExporter{
/**
* Format type: graph6 (g6) or sparse6(s6)
*/
public enum Format{GRAPH6, SPARSE6}
private Format format;
private ByteArrayOutputStream byteArrayOutputStream;
/**
* The default format used by the exporter.
*/
public static final Format DEFAULT_GRAPH6SPARSE6_FORMAT = Format.GRAPH6;
/**
* Constructs a new exporter with a given vertex ID provider.
*
*/
public Graph6Sparse6Exporter()
{
this(DEFAULT_GRAPH6SPARSE6_FORMAT);
}
/**
* Constructs a new exporter with a given vertex ID provider.
*
* @param format the format to use
*/
public Graph6Sparse6Exporter(Format format)
{
this.format = Objects.requireNonNull(format, "Format cannot be null");
}
@Override
public void exportGraph(Graph g, Writer writer) throws ExportException {
GraphTests.requireUndirected(g);
if(format == Format.GRAPH6 && !GraphTests.isSimple(g))
throw new ExportException("Graphs exported in graph6 format cannot contain loops or multiple edges.");
//Map all vertices to a unique integer
List vertices = new ArrayList<>(g.vertexSet());
byteArrayOutputStream = new ByteArrayOutputStream();
currentByte=0;
bitIndex=0;
try {
if (format == Format.SPARSE6)
writeSparse6(g, vertices);
else
writeGraph6(g, vertices);
}catch (IOException e){
e.printStackTrace();
}
String g6="";
try {
g6=byteArrayOutputStream.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
PrintWriter out = new PrintWriter(writer);
out.print(g6);
out.flush();
}
private void writeSparse6(Graph g, List vertices) throws IOException {
int[][] edges=new int[g.edgeSet().size()][2];
int index=0;
for(int j=0; j v+1){
writeBit(true);
writeIntInKBits(edges[m][1], k);
v = edges[m][1];
}else if(edges[m][1] == v + 1){
writeBit(true);
writeIntInKBits(edges[m][0], k);
v++;
m++;
}else{
writeBit(false);
writeIntInKBits(edges[m][0], k);
m++;
}
}
//Pad right hand side with '1's to fill the last byte. This may not be the 'correct' way of padding as
//described in the sparse6 format descr, but it's hard to make sense of the sparse6 description. This seems to work fine.
if(bitIndex != 0) {
int padding = 6 - bitIndex;
for (int i = 0; i < padding; i++)
writeBit(true);
writeByte(); //push the last byte
}
}
private void writeGraph6(Graph g, List vertices) throws IOException {
writeNumberOfVertices(vertices.size());
//Write the lower triangle of the adjacency matrix of G as a bit vector x of length n(n-1)/2,
//using the ordering (0,1),(0,2),(1,2),(0,3),(1,3),(2,3),...,(n-1,n).
for(int i=0; i= 0;
if(n <= 62)
byteArrayOutputStream.write(n+63);
else if(n <= 258047){
//write number in 4 bytes
writeIntInKBits(63,6);
writeIntInKBits(n, 18);
}else{
//write number in 8 bytes
writeIntInKBits(63,6);
writeIntInKBits(63,6);
writeIntInKBits(n, 36);
}
}
private byte currentByte;
private int bitIndex;
private void writeIntInKBits(int number, int k){
for(int i=k-1; i>=0; i--)
writeBit((number & (1 << i)) != 0);
}
private void writeBit(boolean bit){
if(bitIndex == 6)
writeByte();
if(bit)
currentByte |= 1 << (5-bitIndex);
bitIndex++;
}
private void writeByte(){
byteArrayOutputStream.write(currentByte+63);
currentByte=0;
bitIndex=0;
}
}