diff --git a/README.md b/README.md index 2109782844c..dff33a94808 100644 --- a/README.md +++ b/README.md @@ -29,13 +29,12 @@ If you want to import a bigger OSM (Germany) then run: * For Germany it takes approx 25 minutes for the import and roughly 1 minute for the ugly&slow UI to pop up. Probably you'll need to tune the memory settings - send me a mail if this fails! * At the moment the UI is a bit rough and simple so, don't click or drag too much ;) -QuadTree Usage ---------------- - -See the performacen comparison subproject and the articles: - -http://karussell.wordpress.com/2012/06/17/failed-experiment-memory-efficient-spatial-hashtable/ -http://karussell.wordpress.com/2012/05/29/tricks-to-speed-up-neighbor-searches-of-quadtrees-geo-spatial-java/ - -http://karussell.wordpress.com/2012/05/23/spatial-keys-memory-efficient-geohashes/ \ No newline at end of file +Further Links +--------------- + * [Spatial Key](http://karussell.wordpress.com/2012/05/23/spatial-keys-memory-efficient-geohashes/) + * [Speed up your Quad-tree](http://karussell.wordpress.com/2012/05/29/tricks-to-speed-up-neighbor-searches-of-quadtrees-geo-spatial-java/) + * [Spatial Hashtable](http://karussell.wordpress.com/2012/06/17/failed-experiment-memory-efficient-spatial-hashtable/) + * [Author@Twitter](https://twitter.com/timetabling) + * [Lumeo - Implementing a Graph API via Lucene](https://github.com/karussell/lumeo) + * [Cassovary - A production ready (?) in-memory & memory efficient Graph DB](https://github.com/twitter/cassovary) \ No newline at end of file diff --git a/core/src/main/java/de/jetsli/graph/coll/MyBitSet.java b/core/src/main/java/de/jetsli/graph/coll/MyBitSet.java index d0fdd253be8..4e147b097b3 100644 --- a/core/src/main/java/de/jetsli/graph/coll/MyBitSet.java +++ b/core/src/main/java/de/jetsli/graph/coll/MyBitSet.java @@ -17,7 +17,7 @@ /** * Wrapper interface for different implementations like OpenBitset, BitSet, ... - * + * * Supports only integer value indices. * * @author Peter Karich, info@jetsli.de @@ -29,6 +29,10 @@ public interface MyBitSet { void add(int index); int getCardinality(); - + void clear(); + + void ensureCapacity(int size); + + int next(int index); } diff --git a/core/src/main/java/de/jetsli/graph/coll/MyBitSetImpl.java b/core/src/main/java/de/jetsli/graph/coll/MyBitSetImpl.java index cdb9364d74a..2e7576224f7 100644 --- a/core/src/main/java/de/jetsli/graph/coll/MyBitSetImpl.java +++ b/core/src/main/java/de/jetsli/graph/coll/MyBitSetImpl.java @@ -43,4 +43,13 @@ public void add(int index) { public int getCardinality() { return super.cardinality(); } + + @Override + public void ensureCapacity(int size) { + } + + @Override + public int next(int index) { + return super.nextSetBit(index); + } } diff --git a/core/src/main/java/de/jetsli/graph/coll/MyOpenBitSet.java b/core/src/main/java/de/jetsli/graph/coll/MyOpenBitSet.java index b8d2e47307c..0128ac1fa45 100644 --- a/core/src/main/java/de/jetsli/graph/coll/MyOpenBitSet.java +++ b/core/src/main/java/de/jetsli/graph/coll/MyOpenBitSet.java @@ -24,10 +24,22 @@ */ public class MyOpenBitSet implements MyBitSet { - private final OpenBitSet bitSet; + private final ProtectedHack bitSet; public MyOpenBitSet(int no) { - bitSet = new OpenBitSet(no); + bitSet = new ProtectedHack(no); + } + + static class ProtectedHack extends OpenBitSet { + + public ProtectedHack(long numBits) { + super(numBits); + } + + @Override + public int expandingWordNum(long index) { + return super.expandingWordNum(index); + } } public MyOpenBitSet() { @@ -45,12 +57,14 @@ public MyOpenBitSet() { @Override public String toString() { try { StringBuilder sb = new StringBuilder(); + sb.append("{"); for (DocIdSetIterator iter = bitSet.iterator(); iter.nextDoc() != DocIdSetIterator.NO_MORE_DOCS;) { - if (sb.length() != 0) + if (sb.length() > 1) sb.append(", "); sb.append(iter.docID()); } + sb.append("}"); return sb.toString(); } catch (Exception ex) { throw new RuntimeException("error constructing bitset string representation", ex); @@ -66,4 +80,16 @@ public int getCardinality() { public void clear() { bitSet.clear(0, bitSet.length()); } + + @Override + public void ensureCapacity(int size) { + bitSet.expandingWordNum(size); + // this does not update the numBits variable and failse if assertationen are on! + // bitSet.ensureCapacity((size); + } + + @Override + public int next(int index) { + return bitSet.nextSetBit(index); + } } diff --git a/core/src/main/java/de/jetsli/graph/coll/MyTBitSet.java b/core/src/main/java/de/jetsli/graph/coll/MyTBitSet.java index b575e63d415..a2a05500bea 100644 --- a/core/src/main/java/de/jetsli/graph/coll/MyTBitSet.java +++ b/core/src/main/java/de/jetsli/graph/coll/MyTBitSet.java @@ -54,4 +54,13 @@ public int getCardinality() { public void clear() { tHash.clear(); } + + @Override + public void ensureCapacity(int size) { + } + + @Override + public int next(int index) { + throw new UnsupportedOperationException("Not supported yet."); + } } diff --git a/core/src/main/java/de/jetsli/graph/routing/rideshare/DijkstraMultipleAtOnce.java b/core/src/main/java/de/jetsli/graph/routing/rideshare/DijkstraMultipleAtOnce.java index b92ec6494aa..44961cb71e9 100644 --- a/core/src/main/java/de/jetsli/graph/routing/rideshare/DijkstraMultipleAtOnce.java +++ b/core/src/main/java/de/jetsli/graph/routing/rideshare/DijkstraMultipleAtOnce.java @@ -15,7 +15,7 @@ */ package de.jetsli.graph.routing.rideshare; -import de.jetsli.graph.storage.MemoryGraph; +import de.jetsli.graph.storage.Graph; /** * @@ -23,9 +23,9 @@ */ public class DijkstraMultipleAtOnce { - private MemoryGraph graph; + private Graph graph; - public DijkstraMultipleAtOnce(MemoryGraph graph) { + public DijkstraMultipleAtOnce(Graph graph) { this.graph = graph; } diff --git a/core/src/main/java/de/jetsli/graph/storage/MemoryGraph.java b/core/src/main/java/de/jetsli/graph/storage/MemoryGraph.java index a374e0d68e4..bc4639e61a6 100644 --- a/core/src/main/java/de/jetsli/graph/storage/MemoryGraph.java +++ b/core/src/main/java/de/jetsli/graph/storage/MemoryGraph.java @@ -64,6 +64,7 @@ private void ensureNodeIndex(int index) { if (size <= lons.length) return; + deletedNodes.ensureCapacity(size); refToEdges = growArray(refToEdges, size, FACTOR); lons = growArray(lons, size, FACTOR); lats = growArray(lats, size, FACTOR); diff --git a/core/src/main/java/de/jetsli/graph/storage/MemoryGraphSafe.java b/core/src/main/java/de/jetsli/graph/storage/MemoryGraphSafe.java index 2d03c263858..2d189ee9986 100644 --- a/core/src/main/java/de/jetsli/graph/storage/MemoryGraphSafe.java +++ b/core/src/main/java/de/jetsli/graph/storage/MemoryGraphSafe.java @@ -20,6 +20,7 @@ import de.jetsli.graph.util.EdgeIdIterator; import de.jetsli.graph.util.Helper; import gnu.trove.list.array.TIntArrayList; +import gnu.trove.map.hash.TIntIntHashMap; import java.io.File; import java.io.IOException; import java.util.Arrays; @@ -79,7 +80,7 @@ public MemoryGraphSafe(String storageDir, int cap, int capEdge) { initNodes(cap); initEdges(capEdge); } - } + } @Override public int getNodes() { // readLock.lock(); @@ -124,15 +125,15 @@ private void ensureEdgePointer(int pointer) { // Use ONLY within a writer lock area private void ensureNodeIndex(int index) { - if(index < size) + if (index < size) return; - + size = index + 1; - if(size <= lats.length) + if (size <= lats.length) return; - + int cap = Math.max(10, Math.round(size * FACTOR)); - // TODO deletedNodes = copy(deletedNodes, cap); + deletedNodes.ensureCapacity(cap); lats = Arrays.copyOf(lats, cap); lons = Arrays.copyOf(lons, cap); // priorities = Arrays.copyOf(priorities, cap); @@ -337,6 +338,7 @@ public Graph clone() { System.arraycopy(lons, 0, g.lons, 0, lons.length); System.arraycopy(refToEdges, 0, g.refToEdges, 0, refToEdges.length); System.arraycopy(edgesArea, 0, g.edgesArea, 0, edgesArea.length); + g.nextEdgePointer = nextEdgePointer; g.size = size; return g; } @@ -371,14 +373,106 @@ public void optimize() { int deleted = deletedNodes.getCardinality(); if (deleted == 0) return; - - logger.info("lengthy operation: optimizing graph"); + + + if (deleted < size / 5) + optimizeIfFewDeletes(deleted); + else + optimizeIfLotsOfDeletes(deleted); + } + + /** + * This methods moves the last nodes into the deleted nodes, which is much more memory friendly + * but probably slower than optimizeIfLotsOfDeletes for many deletes. + */ + void optimizeIfFewDeletes(int deleted) { + // Alternative to this method: use smaller segments for nodes and not one big fat java array? + // + // Prepare edge-update of nodes which are connected to deleted nodes and + // create old- to new-index map. + int index = getNodes(); + int counter = 0; + int newIndices[] = new int[deleted]; + int oldIndices[] = new int[deleted]; + TIntIntHashMap markedMap = new TIntIntHashMap(deleted, 1.5f, -1, -1); + for (int del = deletedNodes.next(0); del >= 0; del = deletedNodes.next(del + 1)) { + EdgeIdIterator delEdgesIter = getEdges(del); + while (delEdgesIter.next()) { + int currNode = delEdgesIter.nodeId(); + if (deletedNodes.contains(currNode)) + continue; + + // remove all edges to the deleted nodes + EdgeIdIterator nodesConnectedToDelIter = getEdges(currNode); + boolean firstNext = nodesConnectedToDelIter.next(); + if (firstNext) { + // this forces new edges to be created => TODO only update the edges. do not create new entries + refToEdges[currNode] = -1; + do { + if (!deletedNodes.contains(nodesConnectedToDelIter.nodeId())) + internalAdd(currNode, nodesConnectedToDelIter.nodeId(), + nodesConnectedToDelIter.distance(), nodesConnectedToDelIter.flags()); + } while (nodesConnectedToDelIter.next()); + } + } + + index--; + for (; index >= 0; index--) { + if (!deletedNodes.contains(index)) + break; + } + + newIndices[counter] = del; + oldIndices[counter] = index; + markedMap.put(index, del); + counter++; + } + + // now move marked nodes into deleted nodes + for (int i = 0; i < oldIndices.length; i++) { + int oldI = oldIndices[i]; + int newI = newIndices[i]; + + EdgeIdIterator toMoveEdgeIter = getEdges(oldI); + while (toMoveEdgeIter.next()) { + int movedNewIndex = markedMap.get(toMoveEdgeIter.nodeId()); + // if node not moved at all (<0) or node is not yet moved use the old index + if (movedNewIndex < 0 || movedNewIndex >= newI) { + movedNewIndex = toMoveEdgeIter.nodeId(); + } + + // update edges of r to use newI instead of oldI + EdgeIdIterator updateIter = getEdges(movedNewIndex); + boolean firstNext = updateIter.next(); + if (firstNext) { + // this forces new edges to be created => TODO only update the edges. do not create new entries + refToEdges[movedNewIndex] = -1; + do { + int connNode; + if (updateIter.nodeId() == oldI) + connNode = newI; + else + connNode = updateIter.nodeId(); + internalAdd(movedNewIndex, connNode, updateIter.distance(), updateIter.flags()); + } while (updateIter.next()); + } + } + refToEdges[newI] = refToEdges[oldI]; + lats[newI] = lats[oldI]; + lons[newI] = lons[oldI]; + } + + deletedNodes = null; + size -= deleted; + } + + /** + * This methods creates a new in-memory graph without the specified deleted nodes. + */ + void optimizeIfLotsOfDeletes(int deleted) { MemoryGraphSafe inMemGraph = new MemoryGraphSafe(null, getNodes() - deleted, getMaxEdges()); - /** - * This methods creates a new in-memory graph without the specified deleted nodes. see - * MMapGraph for a near duplicate - */ + // see MMapGraph for a near duplicate int locs = this.getNodes(); int newNodeId = 0; int[] old2NewMap = new int[locs]; diff --git a/core/src/main/java/de/jetsli/graph/util/Helper.java b/core/src/main/java/de/jetsli/graph/util/Helper.java index 60017e7f437..6b465da4aab 100644 --- a/core/src/main/java/de/jetsli/graph/util/Helper.java +++ b/core/src/main/java/de/jetsli/graph/util/Helper.java @@ -15,6 +15,7 @@ */ package de.jetsli.graph.util; +import de.jetsli.graph.coll.MyBitSet; import java.io.*; import java.nio.MappedByteBuffer; import java.util.*; diff --git a/core/src/main/java/de/jetsli/graph/util/TopologicalSorting.java b/core/src/main/java/de/jetsli/graph/util/TopologicalSorting.java index 6275cc75b10..2b50da632f7 100644 --- a/core/src/main/java/de/jetsli/graph/util/TopologicalSorting.java +++ b/core/src/main/java/de/jetsli/graph/util/TopologicalSorting.java @@ -17,7 +17,6 @@ import de.jetsli.graph.coll.MyBitSet; import de.jetsli.graph.coll.MyOpenBitSet; -import de.jetsli.graph.storage.MemoryGraph; import de.jetsli.graph.storage.Graph; import gnu.trove.iterator.TIntIterator; import gnu.trove.list.array.TIntArrayList; @@ -32,7 +31,7 @@ public class TopologicalSorting { /** * conditions: acyclicGraph and all reachable from 0 */ - public TIntArrayList sort(MemoryGraph g) { + public TIntArrayList sort(Graph g) { final TIntArrayList list = new TIntArrayList(); if (g.getNodes() == 0) return list; diff --git a/core/src/test/java/de/jetsli/graph/coll/AbstractMyBitSetTest.java b/core/src/test/java/de/jetsli/graph/coll/AbstractMyBitSetTest.java index b1c7022230e..1bd7f21ce10 100644 --- a/core/src/test/java/de/jetsli/graph/coll/AbstractMyBitSetTest.java +++ b/core/src/test/java/de/jetsli/graph/coll/AbstractMyBitSetTest.java @@ -31,9 +31,37 @@ public void testToString() { MyBitSet bs = createBitSet(100); bs.add(12); bs.add(1); - assertEquals("1, 12", bs.toString()); + assertEquals("{1, 12}", bs.toString()); } - + + @Test + public void testNext() { + MyBitSet bs = createBitSet(100); + bs.add(7); + bs.add(90); + assertEquals(7, bs.next(0)); + assertEquals(7, bs.next(7)); + assertEquals(90, bs.next(8)); + assertEquals(-1, bs.next(91)); + } + + @Test + public void testEnsureCapacity() { + MyBitSet bs = createBitSet(8); + bs.add(7); + try { + bs.add(8); + assertTrue(false); + } catch (Throwable ex) { + } + bs.ensureCapacity(16); + bs.add(8); + bs.add(9); + assertFalse(bs.contains(6)); + assertTrue(bs.contains(7)); + assertTrue(bs.contains(8)); + } + @Test public void testClear() { MyBitSet bs = createBitSet(100); diff --git a/core/src/test/java/de/jetsli/graph/coll/MyBitSetImplTest.java b/core/src/test/java/de/jetsli/graph/coll/MyBitSetImplTest.java new file mode 100644 index 00000000000..6003ffd9f4a --- /dev/null +++ b/core/src/test/java/de/jetsli/graph/coll/MyBitSetImplTest.java @@ -0,0 +1,33 @@ +/* + * Copyright 2012 Peter Karich info@jetsli.de + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.jetsli.graph.coll; + +/** + * + * @author Peter Karich + */ +public class MyBitSetImplTest extends AbstractMyBitSetTest { + + @Override + public MyBitSet createBitSet(int no) { + return new MyBitSetImpl(no); + } + + @Override + public void testEnsureCapacity() { + // nothing to test + } +} diff --git a/core/src/test/java/de/jetsli/graph/reader/PrinctonReaderTest.java b/core/src/test/java/de/jetsli/graph/reader/PrinctonReaderTest.java index 7d6fc273f46..f2ed9da6948 100644 --- a/core/src/test/java/de/jetsli/graph/reader/PrinctonReaderTest.java +++ b/core/src/test/java/de/jetsli/graph/reader/PrinctonReaderTest.java @@ -15,8 +15,9 @@ */ package de.jetsli.graph.reader; +import de.jetsli.graph.storage.Graph; import static de.jetsli.graph.util.MyIteratorable.*; -import de.jetsli.graph.storage.MemoryGraph; +import de.jetsli.graph.storage.MemoryGraphSafe; import java.io.IOException; import java.util.zip.GZIPInputStream; import org.junit.Test; @@ -30,7 +31,7 @@ public class PrinctonReaderTest { @Test public void testRead() { - MemoryGraph graph = new MemoryGraph(); + Graph graph = new MemoryGraphSafe(100); new PrinctonReader(graph).setStream(PrinctonReader.class.getResourceAsStream("tinyEWD.txt")).read(); assertEquals(8, graph.getNodes()); assertEquals(2, count(graph.getOutgoing(0))); @@ -39,7 +40,7 @@ public void testRead() { @Test public void testMediumRead() throws IOException { - MemoryGraph graph = new MemoryGraph(); + Graph graph = new MemoryGraphSafe(100); new PrinctonReader(graph).setStream(new GZIPInputStream(PrinctonReader.class.getResourceAsStream("mediumEWD.txt.gz"))).read(); assertEquals(250, graph.getNodes()); assertEquals(13, count(graph.getOutgoing(244))); diff --git a/core/src/test/java/de/jetsli/graph/routing/AbstractRoutingAlgorithmTester.java b/core/src/test/java/de/jetsli/graph/routing/AbstractRoutingAlgorithmTester.java index 85ec89f0b62..23ab429f0f8 100644 --- a/core/src/test/java/de/jetsli/graph/routing/AbstractRoutingAlgorithmTester.java +++ b/core/src/test/java/de/jetsli/graph/routing/AbstractRoutingAlgorithmTester.java @@ -15,16 +15,11 @@ */ package de.jetsli.graph.routing; -import de.jetsli.graph.reader.OSMReader; import de.jetsli.graph.reader.PrinctonReader; -import de.jetsli.graph.storage.MemoryGraph; import de.jetsli.graph.storage.Graph; -import de.jetsli.graph.storage.Location2IDIndex; -import de.jetsli.graph.storage.Location2IDQuadtree; -import de.jetsli.graph.util.CmdArgs; -import de.jetsli.graph.util.Helper; +import de.jetsli.graph.storage.MemoryGraph; +import de.jetsli.graph.storage.MemoryGraphSafe; import de.jetsli.graph.util.StopWatch; -import java.io.File; import java.io.IOException; import java.util.Random; import java.util.zip.GZIPInputStream; @@ -249,7 +244,7 @@ public void testDirectedGraphBug1() { private static Graph createMatrixAlikeGraph() { int WIDTH = 10; int HEIGHT = 15; - Graph tmp = new MemoryGraph(WIDTH * HEIGHT); + Graph tmp = new MemoryGraphSafe(WIDTH * HEIGHT); int[][] matrix = new int[WIDTH][HEIGHT]; int counter = 0; Random rand = new Random(12); @@ -291,6 +286,6 @@ private static Graph createMatrixAlikeGraph() { } Graph createGraph(int size) { - return new MemoryGraph(size); + return new MemoryGraphSafe(size); } } diff --git a/core/src/test/java/de/jetsli/graph/routing/ContractionHierarchiesTest.java b/core/src/test/java/de/jetsli/graph/routing/ContractionHierarchiesTest.java index 6e354af31a2..cb895c75d99 100644 --- a/core/src/test/java/de/jetsli/graph/routing/ContractionHierarchiesTest.java +++ b/core/src/test/java/de/jetsli/graph/routing/ContractionHierarchiesTest.java @@ -15,12 +15,9 @@ */ package de.jetsli.graph.routing; -import de.jetsli.graph.routing.ContractionHierarchies; import static de.jetsli.graph.util.MyIteratorable.*; -import de.jetsli.graph.storage.DistEntry; -import de.jetsli.graph.storage.MemoryGraph; import de.jetsli.graph.storage.Graph; -import gnu.trove.set.hash.TIntHashSet; +import de.jetsli.graph.storage.MemoryGraphSafe; import org.junit.Test; import static org.junit.Assert.*; @@ -39,7 +36,7 @@ public class ContractionHierarchiesTest { @Test public void testSuperSimpleContract() { - Graph g = new MemoryGraph(3); + Graph g = new MemoryGraphSafe(3); g.edge(0, 1, 3, false); g.edge(1, 2, 4, false); @@ -63,7 +60,7 @@ public void testSuperSimpleContract() { @Test public void testIntroduceShortcut0_2() { - Graph g = new MemoryGraph(5); + Graph g = new MemoryGraphSafe(5); g.edge(0, 3, 2, false); g.edge(3, 4, 3, false); g.edge(4, 2, 1, false); @@ -81,7 +78,7 @@ public void testIntroduceShortcut0_2() { //@Test public void DoNotIntroduceShortCut0_2() { - Graph g = new MemoryGraph(5); + Graph g = new MemoryGraphSafe(5); g.edge(0, 1, 3, false); g.edge(1, 2, 4, false); diff --git a/core/src/test/java/de/jetsli/graph/routing/DijkstraBidirectionRefTest.java b/core/src/test/java/de/jetsli/graph/routing/DijkstraBidirectionRefTest.java index f930de59a4c..81d4eab7ea7 100644 --- a/core/src/test/java/de/jetsli/graph/routing/DijkstraBidirectionRefTest.java +++ b/core/src/test/java/de/jetsli/graph/routing/DijkstraBidirectionRefTest.java @@ -15,8 +15,8 @@ */ package de.jetsli.graph.routing; -import de.jetsli.graph.storage.MemoryGraph; import de.jetsli.graph.storage.Graph; +import de.jetsli.graph.storage.MemoryGraphSafe; import org.junit.Test; import static org.junit.Assert.*; @@ -45,7 +45,7 @@ public void testAddSkipNodes() { @Test public void testCannotCalculateSP2() { - Graph g = new MemoryGraph(); + Graph g = new MemoryGraphSafe(10); g.edge(0, 1, 1, false); g.edge(1, 2, 1, false); diff --git a/core/src/test/java/de/jetsli/graph/routing/DijkstraBidirectionTest.java b/core/src/test/java/de/jetsli/graph/routing/DijkstraBidirectionTest.java index e210dea17a1..b551c95e433 100644 --- a/core/src/test/java/de/jetsli/graph/routing/DijkstraBidirectionTest.java +++ b/core/src/test/java/de/jetsli/graph/routing/DijkstraBidirectionTest.java @@ -15,8 +15,8 @@ */ package de.jetsli.graph.routing; -import de.jetsli.graph.storage.MemoryGraph; import de.jetsli.graph.storage.Graph; +import de.jetsli.graph.storage.MemoryGraphSafe; import org.junit.Test; import static org.junit.Assert.*; @@ -45,7 +45,7 @@ public void testAddSkipNodes() { @Test public void testCannotCalculateSP2() { - Graph g = new MemoryGraph(); + Graph g = new MemoryGraphSafe(10); g.edge(0, 1, 1, false); g.edge(1, 2, 1, false); diff --git a/core/src/test/java/de/jetsli/graph/routing/rideshare/DijkstraShortestOf2ToPubTest.java b/core/src/test/java/de/jetsli/graph/routing/rideshare/DijkstraShortestOf2ToPubTest.java index 1962de7da95..4d5e2fee08e 100644 --- a/core/src/test/java/de/jetsli/graph/routing/rideshare/DijkstraShortestOf2ToPubTest.java +++ b/core/src/test/java/de/jetsli/graph/routing/rideshare/DijkstraShortestOf2ToPubTest.java @@ -18,7 +18,6 @@ import de.jetsli.graph.routing.DijkstraBidirection; import de.jetsli.graph.routing.Path; import de.jetsli.graph.routing.RoutingAlgorithm; -import de.jetsli.graph.storage.MemoryGraph; import de.jetsli.graph.storage.Graph; import org.junit.Test; import static org.junit.Assert.*; @@ -29,7 +28,7 @@ */ public class DijkstraShortestOf2ToPubTest { - public RoutingAlgorithm createDijkstra(MemoryGraph g) { + public RoutingAlgorithm createDijkstra(Graph g) { return new DijkstraWhichToOne(g); } diff --git a/core/src/test/java/de/jetsli/graph/storage/AbstractGraphTester.java b/core/src/test/java/de/jetsli/graph/storage/AbstractGraphTester.java index 11a74b5e6f2..81c862ca32a 100644 --- a/core/src/test/java/de/jetsli/graph/storage/AbstractGraphTester.java +++ b/core/src/test/java/de/jetsli/graph/storage/AbstractGraphTester.java @@ -16,8 +16,6 @@ package de.jetsli.graph.storage; import de.jetsli.graph.util.EdgeIdIterator; -import de.jetsli.graph.util.MyIteratorable; -import java.util.Iterator; import org.junit.Test; import static org.junit.Assert.*; import static de.jetsli.graph.util.MyIteratorable.*; @@ -107,9 +105,12 @@ public void testClone() { Graph g = createGraph(11); g.edge(1, 2, 10, true); g.setNode(0, 12, 23); + g.edge(1, 3, 10, true); Graph clone = g.clone(); assertEquals(g.getNodes(), clone.getNodes()); assertEquals(count(g.getOutgoing(1)), count(clone.getOutgoing(1))); + clone.edge(1, 4, 10, true); + assertEquals(3, count(clone.getOutgoing(1))); } @Test @@ -235,6 +236,11 @@ public void testCheckFirstNode() { @Test public void testDeleteNode() { + testDeleteNodes(20); + testDeleteNodes(6); + } + + public void testDeleteNodes(int fillToSize) { Graph g = createGraph(11); g.setNode(0, 12, 23); g.setNode(1, 38.33f, 235.3f); @@ -243,6 +249,18 @@ public void testDeleteNode() { g.setNode(4, 2, 1); g.setNode(5, 2.5f, 1); + int deleted = 2; + for (int i = 6; i < fillToSize; i++) { + g.setNode(i, i * 1.5, i * 1.6); + if (i % 3 == 0) { + g.markNodeDeleted(i); + deleted ++; + } else { + g.edge(i, 0, 10 * i, true); + g.edge(i, fillToSize - 1, 10 * i, true); + } + } + g.edge(0, 1, 10, true); g.edge(0, 3, 20, false); g.edge(3, 5, 20, true); @@ -251,14 +269,37 @@ public void testDeleteNode() { g.markNodeDeleted(0); g.markNodeDeleted(2); // no deletion happend - assertEquals(6, g.getNodes()); + assertEquals(fillToSize, g.getNodes()); // now actually perform deletion g.optimize(); - assertEquals(4, g.getNodes()); - assertEquals(38.33f, g.getLatitude(0), 1e-4); - assertEquals(78, g.getLatitude(1), 1e-4); - assertTrue(MyIteratorable.contains(g.getEdges(0), 3)); - assertFalse(MyIteratorable.contains(g.getEdges(0), 1)); + + assertEquals(fillToSize - deleted, g.getNodes()); + int id1 = getIdOf(g, 38.33f); + assertEquals(235.3f, g.getLongitude(id1), 1e-4); + assertTrue(containsLatitude(g, g.getEdges(id1), 2.5)); + assertFalse(containsLatitude(g, g.getEdges(id1), 12)); + + int id3 = getIdOf(g, 78); + assertEquals(89, g.getLongitude(id3), 1e-4); + assertTrue(containsLatitude(g, g.getEdges(id3), 2.5)); + assertFalse(containsLatitude(g, g.getEdges(id3), 12)); + } + + public boolean containsLatitude(Graph g, EdgeIdIterator iter, double latitude) { + while (iter.next()) { + if (Math.abs(g.getLatitude(iter.nodeId()) - latitude) < 1e-4) + return true; + } + return false; + } + + public int getIdOf(Graph g, double latitude) { + int s = g.getNodes(); + for (int i = 0; i < s; i++) { + if (Math.abs(g.getLatitude(i) - latitude) < 1e-4) + return i; + } + return -1; } } diff --git a/core/src/test/java/de/jetsli/graph/storage/MemoryGraphSafeTest.java b/core/src/test/java/de/jetsli/graph/storage/MemoryGraphSafeTest.java index c3ff12083bf..09d1240fe18 100644 --- a/core/src/test/java/de/jetsli/graph/storage/MemoryGraphSafeTest.java +++ b/core/src/test/java/de/jetsli/graph/storage/MemoryGraphSafeTest.java @@ -66,7 +66,7 @@ public void testSave() throws IOException { assertEquals(3, graph.getNodes()); assertEquals(3, graph.getNodes()); checkGraph(graph); - + graph.edge(3, 4, 123, true); checkGraph(graph); } diff --git a/core/src/test/java/de/jetsli/graph/util/TopologicalSortingTest.java b/core/src/test/java/de/jetsli/graph/util/TopologicalSortingTest.java index 284820a12b5..58882008d73 100644 --- a/core/src/test/java/de/jetsli/graph/util/TopologicalSortingTest.java +++ b/core/src/test/java/de/jetsli/graph/util/TopologicalSortingTest.java @@ -15,7 +15,8 @@ */ package de.jetsli.graph.util; -import de.jetsli.graph.storage.MemoryGraph; +import de.jetsli.graph.storage.Graph; +import de.jetsli.graph.storage.MemoryGraphSafe; import gnu.trove.list.array.TIntArrayList; import org.junit.Test; import static org.junit.Assert.*; @@ -28,7 +29,7 @@ public class TopologicalSortingTest { @Test public void testSort() { - MemoryGraph g = new MemoryGraph(); + Graph g = new MemoryGraphSafe(20); g.edge(7, 11, 1, false); g.edge(7, 8, 1, false); g.edge(5, 11, 1, false); @@ -52,7 +53,7 @@ public void testSort() { @Test public void testSort2() { - MemoryGraph g = new MemoryGraph(); + Graph g = new MemoryGraphSafe(20); g.edge(1, 2, 1, false); g.edge(7, 2, 1, false); g.edge(2, 0, 1, false); @@ -79,7 +80,7 @@ public void testSort2() { @Test public void testSortWithCycle() { - MemoryGraph g = new MemoryGraph(); + Graph g = new MemoryGraphSafe(20); g.edge(0, 1, 1, false); g.edge(1, 2, 1, false); g.edge(2, 0, 1, false); @@ -92,7 +93,7 @@ public void testSortWithCycle() { } } - private void checkOrder(MemoryGraph g, final TIntArrayList res, final int i) { + private void checkOrder(Graph g, final TIntArrayList res, final int i) { final int prev = res.get(i - 1); final int curr = res.get(i); XFirstSearch search = new XFirstSearch() { diff --git a/core/src/test/java/de/jetsli/graph/util/XFirstSearchTest.java b/core/src/test/java/de/jetsli/graph/util/XFirstSearchTest.java index 67604223886..01e3cd9c03b 100644 --- a/core/src/test/java/de/jetsli/graph/util/XFirstSearchTest.java +++ b/core/src/test/java/de/jetsli/graph/util/XFirstSearchTest.java @@ -15,7 +15,8 @@ */ package de.jetsli.graph.util; -import de.jetsli.graph.storage.MemoryGraph; +import de.jetsli.graph.storage.Graph; +import de.jetsli.graph.storage.MemoryGraphSafe; import gnu.trove.set.hash.TIntHashSet; import org.junit.Before; import org.junit.Test; @@ -48,7 +49,7 @@ public boolean goFurther(int v) { } }; - MemoryGraph g = new MemoryGraph(); + Graph g = new MemoryGraphSafe(20); g.edge(0, 1, 85, true); g.edge(0, 2, 217, true); g.edge(0, 3, 173, true);