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

src.it.unimi.dsi.webgraph.ArrayListMutableGraph Maven / Gradle / Ivy

package it.unimi.dsi.webgraph;

/*		 
 * Copyright (C) 2006-2011 Sebastiano Vigna 
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 3 of the License, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 *  for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, see .
 *
 */

import java.util.Arrays;
import java.util.ConcurrentModificationException;

import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.objects.ObjectArrays;
import it.unimi.dsi.lang.MutableString;

/** A very simple mutable graph class based on {@link it.unimi.dsi.fastutil.ints.IntArrayList}s.
 * 
 * 

When creating examples for test cases or everyday usage, this class offers practical constructors. * For instance, a 3-cycle is easily built as *

 *     new ArrayListMutableGraph( 3, new int[][] { { 0, 1 }, { 1, 2 }, { 2, 0 } } )
 * 
* *

Moreover, methods like {@link #addNodes(int)} and {@link #addArc(int, int)} allow to change * the graph structure after construction, and several static factory methods provides ready-made * common graphs (see, e.g., {@link #newCompleteBinaryIntree(int)}). * *

A mutable graph is not an {@link it.unimi.dsi.webgraph.ImmutableGraph}. However, * it is possible to obtain an {@linkplain #immutableView() immutable view} of a mutable graph. * The view is valid until the exposed mutable graph is modified. A modification counter is used * to cause a fail-fast behaviour in case the immutable view is used after modifications. * *

Warning: obtaining a {@link it.unimi.dsi.webgraph.NodeIterator} and using it * while modifying the graph will lead to unpredictable results. */ public class ArrayListMutableGraph { /** Current number of nodes. */ protected int n; /** Current number of arcs. */ protected long m; /** Current list of successor lists. The backing array might be longer than {@link #n}. */ protected IntArrayList successors[]; private final static IntArrayList[] EMPTY_INTARRAYLIST_ARRAY = {}; /** Guarantees that a node index is valid. * * @param x a node index. */ protected void ensureNode( final int x ) { if ( x < 0 ) throw new IllegalArgumentException( "Illegal node index " + x ); if ( x >= n ) throw new IllegalArgumentException( "Node index " + x + " is larger than graph order (" + n + ")" ); } /** Creates a new empty mutable graph. */ public ArrayListMutableGraph() { successors = EMPTY_INTARRAYLIST_ARRAY; } /** Creates a new disconnected mutable graph with specified number of nodes. * @param numNodes the number of nodes in the graph. */ public ArrayListMutableGraph( final int numNodes ) { n = numNodes; successors = new IntArrayList[ n ]; for( int i = n; i-- != 0; ) successors[ i ] = new IntArrayList(); } /** Creates a new mutable graph using a given number of nodes and a given list of arcs. * * @param numNodes the number of nodes in the graph. * @param arc an array of arrays of length 2, specifying the arcs; no sanity checks are performed.. */ public ArrayListMutableGraph( final int numNodes, final int[][] arc ) { this( numNodes ); m = arc.length; // Sanitize for( int i = arc.length; i-- != 0; ) { if ( arc[ i ].length != 2 ) throw new IllegalArgumentException( "The arc of index " + i + " has length " + arc[ i ].length ); if ( arc[ i ][ 0 ] < 0 || arc[ i ][ 1 ] < 0 || arc[ i ][ 0 ] >= numNodes || arc[ i ][ 1 ] >= numNodes ) throw new IllegalArgumentException( "The arc of index " + i + " (" + arc[ i ][ 0 ] + ", " + arc[ i ][ 1 ] + ") is illegal" ); } for( int i = 0; i < arc.length; i++ ) successors[ arc[ i ][ 0 ] ].add( arc[ i ][ 1 ] ); } /** Creates a new mutable graph copying a given immutable graph. * *

This method will not invoke {@link ImmutableGraph#numNodes()}, but rather just create a {@link NodeIterator} and exhaust it. * * @param g an immutable graph. */ public ArrayListMutableGraph( final ImmutableGraph g ) { this(); int d, s = -1; long numArcs = 0; for( NodeIterator nodeIterator = g.nodeIterator(); nodeIterator.hasNext(); ) { s = nodeIterator.nextInt(); d = nodeIterator.outdegree(); numArcs += d; successors = ObjectArrays.grow( successors, s + 1 ); successors[ s ] = new IntArrayList( nodeIterator.successorArray(), 0, d ); } n = s + 1; m = numArcs; } /** Creates a new mutable graph using a given number of nodes and a given arc filter. * * @param numNodes the number of nodes in the graph. * @param arcFilter an arc filter which will specify which arcs go into the graph. */ public ArrayListMutableGraph( final int numNodes, final Transform.ArcFilter arcFilter ) { this( numNodes ); for( int i = n; i-- != 0; ) { for( int j = 0; j < n; j++ ) if ( arcFilter.accept( i, j ) ) { successors[ i ].add( j ); m++; } } } /** Returns a new mutable graph containing a directed cycle. * * @param numNodes the number of nodes in the cycle. */ public static ArrayListMutableGraph newDirectedCycle( final int numNodes ) { return new ArrayListMutableGraph( numNodes, new Transform.ArcFilter() { public boolean accept( final int i, final int j ) { return ( i + 1 ) % numNodes == j; } }); } /** Returns a new mutable graph containing a bidirectional cycle. * * @param numNodes the number of nodes in the cycle. */ public static ArrayListMutableGraph newBidirectionalCycle( final int numNodes ) { return new ArrayListMutableGraph( numNodes, new Transform.ArcFilter() { public boolean accept( final int i, final int j ) { return ( i + 1 ) % numNodes == j || ( j + 1 ) % numNodes == i; } }); } /** Returns a new mutable graph containing a complete graph. * * @param numNodes the number of nodes in the graph. * @param loops true if you want loops, too. */ public static ArrayListMutableGraph newCompleteGraph( final int numNodes, final boolean loops ) { return new ArrayListMutableGraph( numNodes, new Transform.ArcFilter() { public boolean accept( final int i, final int j ) { return i != j || loops; } }); } /** Returns a new mutable graph containing a complete binary in-tree of given height. * * Warning: starting from version 1.7, the spurious loop * at the root has been removed. * * @param height the height of the tree (0 for the root only). */ public static ArrayListMutableGraph newCompleteBinaryIntree( final int height ) { return new ArrayListMutableGraph( ( 1 << ( height + 1 ) ) - 1, new Transform.ArcFilter() { public boolean accept( final int i, final int j ) { return i != j && ( i - 1 ) / 2 == j; } }); } /** Returns a new mutable graph containing a complete binary out-tree of given height. * * Warning: starting from version 1.7, the spurious loop * at the root has been removed. * * @param height the height of the tree (0 for the root only). */ public static ArrayListMutableGraph newCompleteBinaryOuttree( final int height ) { return new ArrayListMutableGraph( ( 1 << ( height + 1 ) ) - 1, new Transform.ArcFilter() { public boolean accept( final int i, final int j ) { return i != j && ( j - 1 ) / 2 == i; } }); } private static class ImmutableView extends ImmutableGraph { /** Cached number of nodes. */ final private int n; /** Cached number of arcs. */ final private long m; /** Cached successors. */ final private IntArrayList[] successors; /** A reference to the mutable graph we expose. */ final private ArrayListMutableGraph g; public ImmutableView( final ArrayListMutableGraph g ) { this.g = g; this.n = g.n; this.m = g.m; this.successors = g.successors; } public ImmutableView copy() { return this; }; private void ensureUnmodified() { if ( g.modificationCount != g.lastModificationCount ) throw new ConcurrentModificationException(); } public int numNodes() { ensureUnmodified(); return n; } public int outdegree( final int x ) { ensureUnmodified(); return successors[ x ].size(); } public long numArcs() { ensureUnmodified(); return m; } public boolean randomAccess() { return true; } public int[] successorArray( final int x ) { ensureUnmodified(); return successors[ x ].toIntArray(); } public LazyIntIterator successors( final int x ) { ensureUnmodified(); return LazyIntIterators.lazy( successors[ x ].iterator() ); } } /** A cached copy of the immutable view, if it has ever been requested. */ protected ImmutableView immutableView; /** The current modification count. */ protected int modificationCount = 0; /** The modification count at the last call to {@link #immutableView()}. */ protected int lastModificationCount = -1; /** Returns an immutable view of this mutable graph. * *

The view can be used until this mutable graph is modified. Attempt to use * the view after modifying this mutable graph will cause a {@link ConcurrentModificationException}. * After modification, a new call to this method will return a new immutable view. * * @return an immutable view of this mutable graph. */ public ImmutableGraph immutableView() { if ( modificationCount != lastModificationCount ) { for( int i = n; i-- != 0; ) Arrays.sort( successors[ i ].elements(), 0, successors[ i ].size() ); immutableView = new ImmutableView( this ); } lastModificationCount = modificationCount; return immutableView; } public int numNodes() { return n; } public int outdegree( final int x ) { ensureNode( x ); return successors[ x ].size(); } public long numArcs() { return m; } public int[] successorArray( final int x ) { ensureNode( x ); return successors[ x ].toIntArray(); } public IntIterator successors( final int x ) { ensureNode( x ); return successors[ x ].iterator(); } /** Adds the given number of nodes, numbering them from {@link #numNodes()} onwards. The new nodes have no successors. * * @param numNewNodes the number of new nodes. */ public void addNodes( final int numNewNodes ) { if ( numNewNodes != 0 ) { modificationCount++; final int newN = n + numNewNodes; successors = ObjectArrays.ensureCapacity( successors, newN, n ); while( n < newN ) successors[ n++ ] = new IntArrayList(); } } /** Removes the given node. All arcs incident on the node are removed, too. * * @param x the node to be removed. */ public void removeNode( final int x ) { ensureNode( x ); modificationCount++; System.arraycopy( successors, x + 1, successors, x, --n - x ); int t; for( int i = n; i-- != 0; ) for( int j = successors[ i ].size(); j-- != 0; ) { t = successors[ i ].getInt( j ); if ( t == x ) successors[ i ].remove( j ); else if ( t > x ) successors[ i ].set( j, t - 1 ); } } /** Adds the given arc. * * @param x the start of the arc. * @param y the end of the arc. */ public void addArc( final int x, final int y ) { ensureNode( x ); ensureNode( y ); if ( successors[ x ].indexOf( y ) != -1 ) throw new IllegalArgumentException( "Node " + y + " is already a successor of node " + x ); modificationCount++; successors[ x ].add( y ); m++; } /** Removes the given arc. * * @param x the start of the arc. * @param y the end of the arc. */ public void removeArc( final int x, final int y ) { ensureNode( x ); ensureNode( y ); final int pos = successors[ x ].indexOf( y ); if ( pos == -1 ) throw new IllegalArgumentException( "Node " + y + " is not a successor of node " + x ); modificationCount++; successors[ x ].remove( pos ); m--; } /** Compare this mutable graph to another object. * * @return true iff the given object is a mutable graph the same size, and * the successor list of every node of this graph is equal to the successor list of the corresponding node of o. */ public boolean equals( final Object o ) { if ( ! (o instanceof ArrayListMutableGraph ) ) return false; final ArrayListMutableGraph g = (ArrayListMutableGraph ) o; int n = numNodes(); if ( n != g.numNodes() ) return false; int[] s, t; int d; while( n-- != 0 ) { if ( ( d = outdegree( n ) ) != g.outdegree( n ) ) return false; s = successorArray( n ); t = g.successorArray( n ); while( d-- != 0 ) if ( s[ d ] != t[ d ] ) return false; } return true; } /** Returns a hash code for this mutable graph. * * @return a hash code for this mutable graph. */ public int hashCode() { int n = numNodes(), h = -1; int[] s; int d; for( int i = 0; i < n; i++ ) { h = h * 31 + i; s = successorArray( i ); d = outdegree( i ); while( d-- != 0 ) h = h * 31 + s[ d ]; } return h; } public String toString() { MutableString ms = new MutableString(); IntIterator ii; ms.append( "Nodes: " + numNodes() + "\nArcs: " + numArcs() + "\n" ); for ( int i = 0; i < numNodes(); i++ ) { ms.append( "Successors of " + i + " (degree " + outdegree( i ) + "):" ); ii = successors( i ); while ( ii.hasNext() ) ms.append( " " + ii.nextInt() ); ms.append( "\n" ); } return ms.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy