From d8a1ac85397a92ea998deef14fa08520b521fa48 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Thu, 18 Jan 2018 15:04:52 +0100 Subject: [PATCH 01/69] fixed a lot of bugs --- .../CommunicationHelper.java | 5 ++- .../scaleoutdistributedledger/LocalStore.java | 1 + .../TransactionSender.java | 5 ++- .../Verification.java | 25 +++++++++-- .../model/Chain.java | 12 ++++- .../scaleoutdistributedledger/model/Node.java | 10 ++--- .../model/Proof.java | 9 +++- .../sockets/SocketClient.java | 4 ++ .../utils/ReversedIterator.java | 45 +++++++++++++++++++ 9 files changed, 102 insertions(+), 14 deletions(-) create mode 100644 src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/ReversedIterator.java diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java index bb4b3a4..b2f0a82 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java @@ -27,11 +27,14 @@ public static boolean receiveTransaction(Proof proof, LocalStore localStore) { if (!localStore.getVerification().isValid(proof.getTransaction(), proof, localStore)) { + System.out.println(proof.getTransaction()); Log.log(Level.WARNING, "Received an invalid transaction/proof: " + proof.getTransaction()); + + System.exit(0); return false; } - proof.applyUpdates(); + proof.applyUpdates(localStore); Log.log(Level.INFO, "Received and validated transaction: " + proof.getTransaction()); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java index 71fd96a..7c358aa 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java @@ -75,6 +75,7 @@ public LocalStore(OwnNode ownNode, Application application, Block genesisBlock, if (genesisBlock != null) { this.addUnspentTransaction(genesisBlock.getTransactions().get(ownNode.getId())); + this.transactionId = genesisBlock.getTransactions().size() - 1; } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index c2ff0eb..29932e1 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -1,5 +1,6 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; +import java.io.IOException; import java.util.ListIterator; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -35,7 +36,7 @@ public class TransactionSender { * The number of blocks (with the same or higher block number) that need to be committed before * we send a certain block. */ - public static final int REQUIRED_COMMITS = 2; + public static final int REQUIRED_COMMITS = 1; private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); private final LocalStore localStore; @@ -131,7 +132,7 @@ private void sendBlock(Block block) { * @return - if the sending succeeded * @throws InterruptedException - If the current thread was interrupted while sending. */ - private boolean sendTransaction(Transaction transaction) throws InterruptedException { + private boolean sendTransaction(Transaction transaction) throws InterruptedException, IOException { Node to = transaction.getReceiver(); Proof proof = Proof.createProof(transaction); if (socketClient.sendMessage(to, new ProofMessage(proof))) { diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Verification.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Verification.java index c85e873..13ed6b9 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Verification.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Verification.java @@ -51,20 +51,37 @@ private boolean validate(Transaction transaction, Proof proof, LocalStore localS // Genesis transaction is always valid, TODO: something? if (transaction.getSender() == null) return true; +// System.out.println("HERE1"); + //Verify the proof if (!proof.verify(localStore)) return false; +// System.out.println("HERE2"); //Equality check: Check if the counts match up long expectedSum = transaction.getAmount() + transaction.getRemainder(); long sum = 0L; +// System.out.println(transaction); for (Transaction txj : transaction.getSource()) { - if(txj.getSender() != null && txj.getSender() == transaction.getSender()) sum += txj.getRemainder(); - else sum += txj.getAmount(); - if (sum > expectedSum) return false; + if(txj.getSender() != null && txj.getSender() == transaction.getSender()) { + sum += txj.getRemainder(); + } else { + sum += txj.getAmount(); + } + if (sum > expectedSum) { + System.out.println(txj); + System.out.println("HERE3"); + System.out.println("Sum = " + sum); + System.out.println("Expected sum = " + expectedSum); + return false; + } } +// System.out.println("HERE4"); if (sum != expectedSum) return false; + + +// System.out.println("HERE5"); //Double spending check Chain chain = transaction.getSender().getChain(); for (Block block : chain.getBlocks()) { @@ -81,6 +98,7 @@ private boolean validate(Transaction transaction, Proof proof, LocalStore localS if (found) break; } +// System.out.println("HERE6"); //Validate sources for (Transaction txj : transaction.getSource()) { Boolean cached = validationCache.get(txj); @@ -88,6 +106,7 @@ private boolean validate(Transaction transaction, Proof proof, LocalStore localS //We didn't see this transaction before, so we need to validate it. if (!isValid(txj, proof, localStore)) return false; } else if (!cached) { +// System.out.println("HERE7"); //We already invalidated this transaction return false; } else { diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java index bb4be29..8ad620d 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java @@ -2,9 +2,12 @@ import lombok.Getter; import lombok.Setter; +import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; +import nl.tudelft.blockchain.scaleoutdistributedledger.utils.ReversedIterator; import java.util.ArrayList; import java.util.List; +import java.util.Optional; /** * Chain class. @@ -47,9 +50,10 @@ public Chain(Node owner, List blocks) { * Updates this chain with the given updates. * This method is used for updating a chain belonging to a different node. * @param updates - the new blocks to append + * @param localStore - the localStore * @throws UnsupportedOperationException - If this chain is owned by us. */ - public synchronized void update(List updates) { + public synchronized void update(List updates, LocalStore localStore) { if (owner instanceof OwnNode) throw new UnsupportedOperationException("You cannot use update to update your own chain"); if (updates.isEmpty()) return; @@ -66,6 +70,12 @@ public synchronized void update(List updates) { nextNr++; } } + for (Block block : ReversedIterator.reversed(this.blocks)) { + if (block.isOnMainChain(localStore)) { + this.lastCommittedBlock = block; + return; + } + } } /** diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java index 7047906..07e473e 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java @@ -89,11 +89,11 @@ public boolean verify(byte[] message, byte[] signature) throws Exception { * @param proof - the proof to update with */ public void updateMetaKnowledge(Proof proof) { - Map> updates = proof.getChainUpdates(); - for (Entry> entry : updates.entrySet()) { - int lastBlockNr = getLastBlockNumber(entry.getValue()); - metaKnowledge.merge(entry.getKey(), lastBlockNr, Math::max); - } +// Map> updates = proof.getChainUpdates(); +// for (Entry> entry : updates.entrySet()) { +// int lastBlockNr = getLastBlockNumber(entry.getValue()); +// metaKnowledge.merge(entry.getKey(), lastBlockNr, Math::max); +// } } /** diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index ffeb237..369321b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -119,6 +119,7 @@ private boolean verify(Transaction transaction, LocalStore localStore) { } for (Block block : chainView) { +// System.out.println(block); if (block.getTransactions().contains(transaction)) { if (seen) { Log.log(Level.WARNING, "Duplicate transaction found, proof not verified"); @@ -129,8 +130,11 @@ private boolean verify(Transaction transaction, LocalStore localStore) { if (block.isOnMainChain(localStore)) absmark = block.getNumber(); } + + OptionalInt blockNumber = transaction.getBlockNumber(); if (!blockNumber.isPresent() || absmark < blockNumber.getAsInt()) { + System.out.println(this.getTransaction()); Log.log(Level.WARNING, "No suitable committed block found, proof not verified"); return false; } @@ -145,13 +149,14 @@ private boolean verify(Transaction transaction, LocalStore localStore) { /** * Applies the updates in this proof. * This method also updates the meta knowledge of the sender of the transaction. + * @param localStore - the localStore */ - public void applyUpdates() { + public void applyUpdates(LocalStore localStore) { for (Entry> entry : chainUpdates.entrySet()) { Node node = entry.getKey(); List updates = entry.getValue(); - node.getChain().update(updates); + node.getChain().update(updates, localStore); } //Update the meta knowledge of the sender diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketClient.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketClient.java index 6246aeb..6fe50d8 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketClient.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketClient.java @@ -8,9 +8,12 @@ import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; import io.netty.handler.codec.serialization.ObjectEncoder; +import io.netty.handler.stream.ChunkedWriteHandler; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; +import java.io.InputStream; +import java.io.ObjectInputStream; import java.util.HashMap; import java.util.logging.Level; @@ -46,6 +49,7 @@ private void initSocketClient() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline p = socketChannel.pipeline(); + p.addLast("streamer", new ChunkedWriteHandler()); p.addLast(new ObjectEncoder(), new ObjectDecoder(ClassResolvers.cacheDisabled(null)), new SocketClientHandler()); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/ReversedIterator.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/ReversedIterator.java new file mode 100644 index 0000000..8121d00 --- /dev/null +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/ReversedIterator.java @@ -0,0 +1,45 @@ +package nl.tudelft.blockchain.scaleoutdistributedledger.utils; + +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +/** + * Iterates over an iterable list in reversed order. + * @param + */ +public class ReversedIterator implements Iterable { + private final List original; + + /** + * Constructor. + * @param original - the original iterable list. + */ + public ReversedIterator(List original) { + this.original = original; + } + + /** + * Implementation of the iterator. + * @return - the iterator. + */ + public Iterator iterator() { + final ListIterator i = original.listIterator(original.size()); + + return new Iterator() { + public boolean hasNext() { return i.hasPrevious(); } + public T next() { return i.previous(); } + public void remove() { i.remove(); } + }; + } + + /** + * Gets a reversed iterator for an iterable list. + * @param original - the original list + * @param - the type of the original list + * @return - the reversed iterable. + */ + public static ReversedIterator reversed(List original) { + return new ReversedIterator(original); + } +} \ No newline at end of file From e9e0610eef838c3b405511b3bf38af3e729e336c Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Thu, 18 Jan 2018 16:34:55 +0100 Subject: [PATCH 02/69] some more bugfixing --- .../scaleoutdistributedledger/SimulationMain.java | 4 ++-- .../scaleoutdistributedledger/model/Node.java | 10 +++++----- .../scaleoutdistributedledger/model/Proof.java | 6 +++--- .../sockets/SocketClient.java | 1 - 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index 5391a92..d54892a 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -32,9 +32,9 @@ public static void main(String[] args) throws Exception { //SETTINGS //number of local nodes to generate - int localNodesNumber = 4; + int localNodesNumber = 3; //number of total nodes in the system - int totalNodesNumber = 4; + int totalNodesNumber = 3; //number from which our nodes are (e.g if we run nodes (2, 3, 4), then this should be 2 int nodesFromNumber = 0; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java index 07e473e..7047906 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java @@ -89,11 +89,11 @@ public boolean verify(byte[] message, byte[] signature) throws Exception { * @param proof - the proof to update with */ public void updateMetaKnowledge(Proof proof) { -// Map> updates = proof.getChainUpdates(); -// for (Entry> entry : updates.entrySet()) { -// int lastBlockNr = getLastBlockNumber(entry.getValue()); -// metaKnowledge.merge(entry.getKey(), lastBlockNr, Math::max); -// } + Map> updates = proof.getChainUpdates(); + for (Entry> entry : updates.entrySet()) { + int lastBlockNr = getLastBlockNumber(entry.getValue()); + metaKnowledge.merge(entry.getKey(), lastBlockNr, Math::max); + } } /** diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 369321b..d5f6d4e 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -140,9 +140,9 @@ private boolean verify(Transaction transaction, LocalStore localStore) { } // Verify source transaction - for (Transaction sourceTransaction : transaction.getSource()) { - if (!verify(sourceTransaction, localStore)) return false; - } +// for (Transaction sourceTransaction : transaction.getSource()) { +// if (!verify(sourceTransaction, localStore)) return false; +// } return true; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketClient.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketClient.java index 6fe50d8..e559858 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketClient.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketClient.java @@ -49,7 +49,6 @@ private void initSocketClient() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline p = socketChannel.pipeline(); - p.addLast("streamer", new ChunkedWriteHandler()); p.addLast(new ObjectEncoder(), new ObjectDecoder(ClassResolvers.cacheDisabled(null)), new SocketClientHandler()); From 81009a594242909db0ec216ce4d697dcf3fa8cb2 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Thu, 18 Jan 2018 22:39:54 +0100 Subject: [PATCH 03/69] Fix shared state through genesis transactions --- .../model/Block.java | 22 +++++++++++-------- .../model/Transaction.java | 9 ++++++++ .../simulation/Simulation.java | 2 +- .../tendermint/TendermintHelper.java | 3 ++- .../model/SerializationTest.java | 2 +- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index 2a09fd7..e223df5 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -17,7 +17,7 @@ /** * Block class. */ -public class Block implements Cloneable { +public class Block { public static final int GENESIS_BLOCK_NUMBER = 0; @@ -242,16 +242,20 @@ private Sha256Hash calculateHash() { } /** - * Clones the block. - * @return - the clone + * Creates a copy of this genesis block. + * @return - a deep copy of this block and transactions + * @throws UnsupportedOperationException - If this block is not a genesis block. */ - @Override - public Block clone() { - try { - return (Block) super.clone(); - } catch (CloneNotSupportedException e) { - throw new IllegalStateException("Clone not supported"); + public Block genesisCopy() { + if (this.number != GENESIS_BLOCK_NUMBER) throw new UnsupportedOperationException("You can only copy genesis blocks"); + + Block block = new Block(this.number, this.owner, new ArrayList<>()); + for (Transaction transaction : transactions) { + block.addTransaction(transaction.genesisCopy()); } + block.onMainChain = true; + block.finalized = true; + return block; } /** diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index 5f278a7..b012e0f 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -162,6 +162,15 @@ private Sha256Hash calculateHash() { return new Sha256Hash(transactionInBytes); } + + /** + * @return - a copy of this transaction + * @throws UnsupportedOperationException - If this transaction has sources. + */ + public Transaction genesisCopy() { + if (!source.isEmpty()) throw new UnsupportedOperationException("Only genesis transactions can be copied"); + return new Transaction(number, sender, receiver, amount, remainder, new HashSet<>(0)); + } @Override public int hashCode() { diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java index 46bc53a..b2acf0b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java @@ -83,7 +83,7 @@ public void runNodesLocally(List nodeNumbers, Map nodes, int port = nodePorts.get(nodeNumber); try { TendermintHelper.runTendermintNode(nodePorts.get(nodeNumber), addressesForThisNode, nodeNumber); - app.init(port, genesisBlock.clone(), nodeToKeyPair.get(nodeNumber), ownNodes.get(nodeNumber)); + app.init(port, genesisBlock.genesisCopy(), nodeToKeyPair.get(nodeNumber), ownNodes.get(nodeNumber)); } catch (Exception ex) { Log.log(Level.SEVERE, "Unable to initialize local node " + nodeNumber + " on port " + port + "!", ex); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java index 6a281e2..014b20e 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java @@ -364,10 +364,11 @@ private static void enableLogging(Process ps, String logPrefix) { */ public static Block generateGenesisBlock(long amount, Map nodeList) { List initialTransactions = new ArrayList<>(); - for (Integer i : nodeList.keySet()) { + for (int i = 0; i < nodeList.size(); i++) { Transaction t = new Transaction(i, null, nodeList.get(i), amount, 0, new HashSet<>(0)); initialTransactions.add(t); } + return new Block(0, null, initialTransactions); } diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java index 912973a..e6e798d 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java @@ -52,7 +52,7 @@ public void setUp() { // Generate genesis block (10 nodes, 1000 coins) this.genesisBlock = TendermintHelper.generateGenesisBlock(1000, nodeList); - this.sender.setGenesisBlock(genesisBlock.clone()); + this.sender.setGenesisBlock(this.genesisBlock); //Create two following blocks this.block = this.sender.getChain().appendNewBlock(); From 1b4596e936ff9df85b30d1df5a7e105146888892 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Thu, 18 Jan 2018 22:41:06 +0100 Subject: [PATCH 04/69] Don't update always --- .../blockchain/scaleoutdistributedledger/Application.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java index 254e08f..a5bbb99 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java @@ -62,7 +62,6 @@ public void init(int nodePort, Block genesisBlock, Ed25519Key key, OwnNode ownNo // Setup local store localStore = new LocalStore(ownNode, this, genesisBlock, this.isProduction); - localStore.updateNodes(); localStore.initMainChain(); serverThread = new Thread(new SocketServer(nodePort, localStore)); From 32096386c6e9017f2e26735f54e9a91e5320aa25 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Thu, 18 Jan 2018 22:41:27 +0100 Subject: [PATCH 05/69] Still sleep after error --- .../simulation/CancellableInfiniteRunnable.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/CancellableInfiniteRunnable.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/CancellableInfiniteRunnable.java index 39b223c..f0d3d00 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/CancellableInfiniteRunnable.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/CancellableInfiniteRunnable.java @@ -67,14 +67,23 @@ public void run() { try { while (!isCancelled()) { + //Do action try { action.accept(t); - Thread.sleep(sleepFunction.applyAsLong(t)); } catch (InterruptedException ex) { continue; } catch (Exception ex) { Log.log(Level.SEVERE, "Uncaught exception in action", ex); } + + //Sleep + try { + Thread.sleep(sleepFunction.applyAsLong(t)); + } catch (InterruptedException ex) { + continue; + } catch (Exception ex) { + Log.log(Level.SEVERE, "Uncaught exception in sleepFunction", ex); + } } } finally { if (onStop != null) { From 00cd07aef1964b770513753f6ea3a3cdef9235a6 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Thu, 18 Jan 2018 22:42:03 +0100 Subject: [PATCH 06/69] Small fix --- .../blockchain/scaleoutdistributedledger/LocalStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java index d90953c..4a50b42 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java @@ -166,7 +166,7 @@ public void initMainChain() { private void normalizeGenesis() { for (Transaction transaction : ownNode.getChain().getGenesisBlock().getTransactions()) { Node receiver = getNode(transaction.getReceiver().getId()); - if (receiver != transaction.getReceiver()) { + if (receiver != null && receiver != transaction.getReceiver()) { transaction.setReceiver(receiver); } } From dc4f462361d65fd29310c846fc6e837d3a22b6b0 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Thu, 18 Jan 2018 22:42:14 +0100 Subject: [PATCH 07/69] Don't set genesis blocks of others --- .../blockchain/scaleoutdistributedledger/TrackerHelper.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java index 8ba651f..f1b06d4 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java @@ -96,9 +96,9 @@ public static void updateNodes(Map nodes, OwnNode ownNode) throws } else { Node node = new Node(i, publicKey, address, port); - if (ownNode != null) { - node.setGenesisBlock(ownNode.getChain().getGenesisBlock()); - } +// if (ownNode != null) { +// node.setGenesisBlock(ownNode.getChain().getGenesisBlock()); +// } nodes.put(i, node); } From c6dca4ef0f4639c489f91e63a67c285f0e29fc42 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Sat, 20 Jan 2018 11:59:14 +0100 Subject: [PATCH 08/69] Debug logging The debug log displays the class, method and line number where it was called. Use Log.debug("Message") to log a simple message. Use Log.debug("Message arg1={0}", something) to log a message with parameters. --- .../scaleoutdistributedledger/utils/Log.java | 50 +++++++++++++++++-- .../utils/LogFormatter.java | 9 ++++ 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/Log.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/Log.java index 8aadd88..a15ad97 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/Log.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/Log.java @@ -9,9 +9,12 @@ * Class for logging functions. */ public final class Log { - public static final Level LEVEL = Level.INFO; - public static final String FORMAT = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$-6s %3$s %5$s%6$s%n"; + public static final Level LEVEL = Level.INFO; + public static final String FORMAT = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$-6s %3$s %5$s%6$s%n"; + public static final String DEBUG_FORMAT = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$-6s %2$s %5$s%6$s%n"; + public static final Logger PARENT_LOGGER = Logger.getLogger(Log.class.getName()).getParent(); + public static final Logger DEBUG_LOGGER = Logger.getLogger("DEBUG"); private Log() { throw new UnsupportedOperationException(); @@ -28,6 +31,16 @@ private Log() { handler.setLevel(LEVEL); } } + + //Create debug console handler + ConsoleHandler debugHandler = new ConsoleHandler(); + debugHandler.setLevel(Level.ALL); + debugHandler.setFormatter(new LogFormatter(DEBUG_FORMAT)); + + //Setup debug logger + DEBUG_LOGGER.setUseParentHandlers(false); + DEBUG_LOGGER.addHandler(debugHandler); + DEBUG_LOGGER.setLevel(LEVEL); } /** @@ -57,18 +70,45 @@ public static void log(Level level, String str, Throwable throwable) { public static void log(Level level, String str) { Logger.getLogger(getCallerClassName()).log(level, str); } + + /** + * Logs the given debug message. + * @param msg - the message + * @param params - the parameters + */ + public static void debug(String msg, Object... params) { + StackTraceElement ste = getCallerStackTrace(3); + if (ste == null) { + DEBUG_LOGGER.logp(Level.INFO, null, null, msg, params); + } else { + String className = ste.getClassName(); + className = className.substring(className.lastIndexOf('.') + 1); + String sourceMethod = ste.getMethodName() + ":" + ste.getLineNumber(); + DEBUG_LOGGER.logp(Level.INFO, className, sourceMethod, msg, params); + } + } /** * Get the name of the last class that added to the stack. * @return class name */ private static String getCallerClassName() { + StackTraceElement ste = getCallerStackTrace(4); + if (ste == null) return null; + String className = ste.getClassName(); + return className.substring(className.lastIndexOf('.') + 1); + } + + /** + * Determines the stack trace element of the caller. + * @return - the stack trace element of the caller + */ + private static StackTraceElement getCallerStackTrace(int start) { StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); - for (int i = 3; i < stackTraceElements.length; i++) { + for (int i = start; i < stackTraceElements.length; i++) { StackTraceElement ste = stackTraceElements[i]; if (ste.getClassName().indexOf("java.lang.Thread") != 0) { - String name = ste.getClassName(); - return name.substring(name.lastIndexOf('.') + 1); + return ste; } } return null; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/LogFormatter.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/LogFormatter.java index 0451f43..5d83625 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/LogFormatter.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/LogFormatter.java @@ -14,6 +14,15 @@ public class LogFormatter extends Formatter { private final Date dat = new Date(); /** + * Creates a new log formatter with the given format. + *
+	 * %1 = date
+	 * %2 = source class
+	 * %3 = logger name
+	 * %4 = level
+	 * %5 = message
+	 * %6 = exception
+	 * 
* @param format - the format string */ public LogFormatter(String format) { From d80c578f18e134286aeca2cffa6d430124d09fe3 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Sat, 20 Jan 2018 12:04:21 +0100 Subject: [PATCH 09/69] Small improvements --- .../scaleoutdistributedledger/CommunicationHelper.java | 5 +++-- .../blockchain/scaleoutdistributedledger/LocalStore.java | 6 +++--- .../blockchain/scaleoutdistributedledger/model/Block.java | 2 +- .../blockchain/scaleoutdistributedledger/model/Node.java | 4 ++++ 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java index 647332a..e73ca1c 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java @@ -20,6 +20,8 @@ private CommunicationHelper() { * @return true if the transaction was accepted, false otherwise */ public static boolean receiveTransaction(Proof proof, LocalStore localStore) { + Log.log(Level.INFO, "Received transaction: " + proof.getTransaction()); + if (proof.getTransaction().getReceiver().getId() != localStore.getOwnNode().getId()) { Log.log(Level.WARNING, "Received a transaction that isn't for us: " + proof.getTransaction()); return false; @@ -32,10 +34,9 @@ public static boolean receiveTransaction(Proof proof, LocalStore localStore) { return false; } + Log.log(Level.INFO, "Transaction " + proof.getTransaction() + " is valid, applying updates..."); proof.applyUpdates(localStore); - Log.log(Level.INFO, "Received and validated transaction: " + proof.getTransaction()); - if (proof.getTransaction().getAmount() > 0) { localStore.addUnspentTransaction(proof.getTransaction()); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java index 4a50b42..4c400ee 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java @@ -66,7 +66,7 @@ public LocalStore(OwnNode ownNode, Application application, Block genesisBlock, genesisTransaction.setReceiver(ownNode); this.addUnspentTransaction(genesisTransaction); - this.transactionId = genesisBlock.getTransactions().size() - 1; + this.transactionId = genesisBlock.getTransactions().size(); } } @@ -149,8 +149,8 @@ public void removeUnspentTransactions(Collection toRemove) { /** * @return a new transaction id */ - public int getNewTransactionId() { - return ++transactionId; + public synchronized int getNewTransactionId() { + return transactionId++; } /** diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index e223df5..1f14782 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -212,7 +212,7 @@ public boolean equals(Object obj) { @Override public String toString() { - return "Block<" + number + ", " + owner + ">"; + return "Block"; } /** diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java index 7047906..066f9ad 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java @@ -91,7 +91,11 @@ public boolean verify(byte[] message, byte[] signature) throws Exception { public void updateMetaKnowledge(Proof proof) { Map> updates = proof.getChainUpdates(); for (Entry> entry : updates.entrySet()) { + //Don't include self + if (entry.getKey() == this) continue; + int lastBlockNr = getLastBlockNumber(entry.getValue()); + if (lastBlockNr == -1) continue; metaKnowledge.merge(entry.getKey(), lastBlockNr, Math::max); } } From 09f71d7a9593f62959de07264104ae582f20eb69 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Mon, 22 Jan 2018 12:35:34 +0100 Subject: [PATCH 10/69] some debugging --- .../SimulationMain.java | 4 ++-- .../model/Transaction.java | 8 +++++++- .../OnlyNodeZeroTransactionPattern.java | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/OnlyNodeZeroTransactionPattern.java diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index 7f48e70..f7d7b9a 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -25,9 +25,9 @@ private SimulationMain() {} //SETTINGS //number of local nodes to generate - public static final int LOCAL_NODES_NUMBER = 4; + public static final int LOCAL_NODES_NUMBER = 2; //number of total nodes in the system - public static final int TOTAL_NODES_NUMBER = 4; + public static final int TOTAL_NODES_NUMBER = 2; //number from which our nodes are (e.g if we run nodes (2, 3, 4), then this should be 2 public static final int NODES_FROM_NUMBER = 0; /** diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index 1876f97..7dc012a 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -113,7 +113,13 @@ public Transaction(TransactionMessage transactionMessage, Map blockMessageList = encodedChainUpdates.get(owner.getId()); // Decode chain, in REVERSE order - BlockMessage lastBlockMessage = blockMessageList.get(blockMessageList.size() - 1); + BlockMessage lastBlockMessage = null; + try { + lastBlockMessage = blockMessageList.get(blockMessageList.size() - 1); + } catch(NullPointerException e) { + e.printStackTrace(); + System.exit(1); + } // Recursively decode the blocks of a chain (in reverse order) Block lastBlockLocal = new Block(lastBlockMessage, encodedChainUpdates, decodedChainUpdates, localStore); if (decodedChainUpdates.containsKey(owner)) { diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/OnlyNodeZeroTransactionPattern.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/OnlyNodeZeroTransactionPattern.java new file mode 100644 index 0000000..bf408c6 --- /dev/null +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/OnlyNodeZeroTransactionPattern.java @@ -0,0 +1,18 @@ +package nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern; + +import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; + +public class OnlyNodeZeroTransactionPattern extends UniformRandomTransactionPattern { + + public OnlyNodeZeroTransactionPattern(int minAmount, int maxAmount, int minWaitTime, int maxWaitTime, int commitEvery) { + super(minAmount, maxAmount, minWaitTime, maxWaitTime, commitEvery); + } + + @Override + public long selectAmount(LocalStore localStore) { + //Only let node 0 make transactions + if (localStore.getOwnNode().getId() != 0) return -1; + + return super.selectAmount(localStore); + } +} From a030afa4c737c16d437b4ef06da8fd5567514f57 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Mon, 22 Jan 2018 12:52:35 +0100 Subject: [PATCH 11/69] removed debug statements --- .../scaleoutdistributedledger/CommunicationHelper.java | 1 + .../scaleoutdistributedledger/SimulationMain.java | 4 +++- .../scaleoutdistributedledger/model/Transaction.java | 8 +------- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java index 6cd0dde..101547b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java @@ -5,6 +5,7 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import nl.tudelft.blockchain.scaleoutdistributedledger.validation.ValidationException; +import org.apache.commons.lang3.ObjectUtils; /** * Helper class for communication. diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index f7d7b9a..946fa8c 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -7,6 +7,7 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.Simulation; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.tendermint.TendermintHelper; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern.ITransactionPattern; +import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern.OnlyNodeZeroTransactionPattern; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern.UniformRandomTransactionPattern; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Utils; @@ -82,7 +83,8 @@ public static void main(String[] args) throws Exception { // --- PHASE 3: start the actual simulation --- Simulation simulation = new Simulation(); - ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 1); + ITransactionPattern itp = new OnlyNodeZeroTransactionPattern(10, 20, 1000, 2000, 1); +// ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 1); simulation.setTransactionPattern(itp); simulation.runNodesLocally(nodeNumbersToRunLocally, nodes, ownNodes, genesisBlock, nodeToKeyPair); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index 3f93443..70237f0 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -113,13 +113,7 @@ public Transaction(TransactionMessage transactionMessage, Map blockMessageList = encodedChainUpdates.get(owner.getId()); // Decode chain, in REVERSE order - BlockMessage lastBlockMessage = null; - try { - lastBlockMessage = blockMessageList.get(blockMessageList.size() - 1); - } catch(NullPointerException e) { - e.printStackTrace(); - System.exit(1); - } + BlockMessage lastBlockMessage = blockMessageList.get(blockMessageList.size() - 1); // Recursively decode the blocks of a chain (in reverse order) Block lastBlockLocal = new Block(lastBlockMessage, encodedChainUpdates, decodedChainUpdates, localStore); if (decodedChainUpdates.containsKey(owner)) { From 3a516e3fae6c18a0d8825fc38e4d09b75f33de72 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 22 Jan 2018 14:17:03 +0100 Subject: [PATCH 12/69] Decode all chains of the proof --- .../model/Proof.java | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 4bb9896..d213ce3 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -55,25 +55,31 @@ public Proof(Transaction transaction, Map> chainUpdates) { * @throws IOException - error while getting node info from tracker */ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOException { - this.chainUpdates = new HashMap<>(); - // Start by decoding the chain of the sender - Node senderNode = localStore.getNode(proofMessage.getTransactionMessage().getSenderId()); - List senderChain = proofMessage.getChainUpdates().get(senderNode.getId()); - // Start from the last block - BlockMessage lastBlockMessage = senderChain.get(senderChain.size() - 1); - // Recursively decode the transaction and chainUpdates - Block lastBlock = new Block(lastBlockMessage, proofMessage.getChainUpdates(), this.chainUpdates, localStore); - List currentDecodedBlockList; - if (this.chainUpdates.containsKey(senderNode)) { - // Add to already created list of blocks - currentDecodedBlockList = this.chainUpdates.get(senderNode); - currentDecodedBlockList.add(lastBlock); - } else { - // Create new list of blocks - currentDecodedBlockList = new ArrayList<>(); - currentDecodedBlockList.add(lastBlock); - this.chainUpdates.put(senderNode, currentDecodedBlockList); + this.chainUpdates = new HashMap<>(); + for (Map.Entry> entry : proofMessage.getChainUpdates().entrySet()) { + Node node = localStore.getNode(entry.getKey()); + if (this.chainUpdates.containsKey(node)) { + // We have already decoded this chain in a previous iteration + continue; + } + List blockMessageList = entry.getValue(); + // Start from the last block + BlockMessage lastBlockMessage = blockMessageList.get(blockMessageList.size() - 1); + // Recursively decode the transaction and chainUpdates + Block lastBlock = new Block(lastBlockMessage, proofMessage.getChainUpdates(), this.chainUpdates, localStore); + List currentDecodedBlockList; + if (this.chainUpdates.containsKey(senderNode)) { + // Add to already created list of blocks + currentDecodedBlockList = this.chainUpdates.get(senderNode); + currentDecodedBlockList.add(lastBlock); + } else { + // Create new list of blocks + currentDecodedBlockList = new ArrayList<>(); + currentDecodedBlockList.add(lastBlock); + this.chainUpdates.put(senderNode, currentDecodedBlockList); + } } + // Set the transaction from the decoded chain // TODO [possible improvement]: is the transaction always in the last block ? Transaction foundTransaction = null; From 4cd4bd3a775e6a841cee6c2ac06d8ea8dcbe443a Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 22 Jan 2018 14:52:47 +0100 Subject: [PATCH 13/69] Fix decoding proof --- .../model/Proof.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 9258453..be63ee2 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -53,7 +53,8 @@ public Proof(Transaction transaction, Map> chainUpdates) { * @throws IOException - error while getting node info from tracker */ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOException { - this.chainUpdates = new HashMap<>(); + this.chainUpdates = new HashMap<>(); + for (Map.Entry> entry : proofMessage.getChainUpdates().entrySet()) { Node node = localStore.getNode(entry.getKey()); if (this.chainUpdates.containsKey(node)) { @@ -65,23 +66,26 @@ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOExceptio BlockMessage lastBlockMessage = blockMessageList.get(blockMessageList.size() - 1); // Recursively decode the transaction and chainUpdates Block lastBlock = new Block(lastBlockMessage, proofMessage.getChainUpdates(), this.chainUpdates, localStore); - List currentDecodedBlockList; - if (this.chainUpdates.containsKey(senderNode)) { + if (this.chainUpdates.containsKey(node)) { // Add to already created list of blocks - currentDecodedBlockList = this.chainUpdates.get(senderNode); - currentDecodedBlockList.add(lastBlock); + this.chainUpdates.get(node).add(lastBlock); } else { // Create new list of blocks - currentDecodedBlockList = new ArrayList<>(); - currentDecodedBlockList.add(lastBlock); - this.chainUpdates.put(senderNode, currentDecodedBlockList); + List blockList = new ArrayList<>(); + blockList.add(lastBlock); + this.chainUpdates.put(node, blockList); } } + // Get decoded chain of sender, if any + Node senderNode = localStore.getNode(proofMessage.getTransactionMessage().getSenderId()); + List currentDecodedBlockList = new ArrayList<>(); + if (this.chainUpdates.containsKey(senderNode)) { + currentDecodedBlockList = this.chainUpdates.get(senderNode); + } + // Set the transaction from the decoded chain - // TODO [possible improvement]: is the transaction always in the last block ? Transaction foundTransaction = null; - ChainView cv = new ChainView(senderNode.getChain(), currentDecodedBlockList); Block block = cv.getBlock(proofMessage.getTransactionMessage().getBlockNumber()); for (Transaction transactionAux : block.getTransactions()) { From bc76ff3c44be2a7438725742f8192f48988e3302 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Mon, 22 Jan 2018 16:16:42 +0100 Subject: [PATCH 14/69] Fix meta knowledge? --- .../TransactionSender.java | 2 +- .../model/Proof.java | 67 +++++++++++++++++-- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index e7a099e..47786eb 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -165,7 +165,7 @@ private void sendBlock(Block block) { */ private boolean sendTransaction(Transaction transaction) throws InterruptedException { Node to = transaction.getReceiver(); - Proof proof = Proof.createProof(transaction); + Proof proof = Proof.createProof(localStore, transaction); if (socketClient.sendMessage(to, new ProofMessage(proof))) { to.updateMetaKnowledge(proof); return true; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 19b5789..f540bad 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -11,7 +11,9 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -231,29 +233,62 @@ public void applyUpdates() { * @param transaction - the transaction * @return the proof for the given transaction */ - public static Proof createProof(Transaction transaction) { + public static Proof createProof(LocalStore localStore, Transaction transaction) { Node receiver = transaction.getReceiver(); Proof proof = new Proof(transaction); + //Step 0: determine what blocks need to be sent + int blockRequired = transaction.getBlockNumber().getAsInt(); + Chain senderChain = transaction.getSender().getChain(); + + Block fromBlock = senderChain.getBlocks().get(blockRequired); + Block toBlock = getNextCommittedBlock(localStore, blockRequired, senderChain); + //Step 1: determine the chains that need to be sent //TODO We might want to do some kind of caching? - Set chains = new HashSet<>(); - appendChains(transaction, receiver, chains); + Map chains = new HashMap<>(); + for (Block b = toBlock; b != fromBlock; b = b.getPreviousBlock()) { + for (Transaction t : toBlock.getTransactions()) { + appendChains2(t, receiver, chains); + } + } + + appendChains2(transaction, receiver, chains); //Step 2: add only those blocks that are not yet known Map metaKnowledge = receiver.getMetaKnowledge(); - for (Chain chain : chains) { + for (Entry entry : chains.entrySet()) { + Chain chain = entry.getKey(); Node owner = chain.getOwner(); if (owner == receiver) continue; int alreadyKnown = metaKnowledge.getOrDefault(owner, -1); - int requiredKnown = chain.getLastCommittedBlock().getNumber(); - - proof.addBlocksOfChain(chain, alreadyKnown + 1, requiredKnown + 1); + int requiredKnown = entry.getValue(); + if (alreadyKnown < requiredKnown) { + proof.addBlocksOfChain(chain, alreadyKnown + 1, requiredKnown + 1); + } } return proof; } + + /** + * @param localStore + * @param blockRequired + * @param senderChain + * @return + */ + private static Block getNextCommittedBlock(LocalStore localStore, int blockRequired, Chain senderChain) { + ListIterator it = senderChain.getBlocks().listIterator(blockRequired); + while (it.hasNext()) { + Block block = it.next(); + if (block.isOnMainChain(localStore)) { + return block; + } + } + + throw new IllegalStateException("There is no next committed block!"); + } /** * Recursively calls itself with all the sources of the given transaction. Transactions which @@ -272,6 +307,24 @@ public static void appendChains(Transaction transaction, Node receiver, Set chains) { + Node owner = transaction.getSender(); + if (owner == null || owner == receiver) return; + + int blockNumber = transaction.getBlockNumber().getAsInt(); + chains.compute(owner.getChain(), (k, v) -> v == null ? blockNumber : Math.max(v, blockNumber)); + for (Transaction source : transaction.getSource()) { + appendChains2(source, receiver, chains); + } + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); From cf1157e49a1a30011db96e52b8885b3eb3adf48a Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Mon, 22 Jan 2018 16:18:11 +0100 Subject: [PATCH 15/69] some small bugfixes --- .../SimulationMain.java | 8 ++--- .../model/Proof.java | 31 ++++++++++--------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index 946fa8c..40bc1f6 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -26,9 +26,9 @@ private SimulationMain() {} //SETTINGS //number of local nodes to generate - public static final int LOCAL_NODES_NUMBER = 2; + public static final int LOCAL_NODES_NUMBER = 4; //number of total nodes in the system - public static final int TOTAL_NODES_NUMBER = 2; + public static final int TOTAL_NODES_NUMBER = 4; //number from which our nodes are (e.g if we run nodes (2, 3, 4), then this should be 2 public static final int NODES_FROM_NUMBER = 0; /** @@ -83,8 +83,8 @@ public static void main(String[] args) throws Exception { // --- PHASE 3: start the actual simulation --- Simulation simulation = new Simulation(); - ITransactionPattern itp = new OnlyNodeZeroTransactionPattern(10, 20, 1000, 2000, 1); -// ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 1); +// ITransactionPattern itp = new OnlyNodeZeroTransactionPattern(10, 20, 1000, 2000, 1); + ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 1); simulation.setTransactionPattern(itp); simulation.runNodesLocally(nodeNumbersToRunLocally, nodes, ownNodes, genesisBlock, nodeToKeyPair); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 19b5789..2cc2c72 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -57,21 +57,24 @@ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOExceptio // Start by decoding the chain of the sender Node senderNode = localStore.getNode(proofMessage.getTransactionMessage().getSenderId()); List senderChain = proofMessage.getChainUpdates().get(senderNode.getId()); - // Start from the last block - BlockMessage lastBlockMessage = senderChain.get(senderChain.size() - 1); - // Recursively decode the transaction and chainUpdates - Block lastBlock = new Block(lastBlockMessage, proofMessage.getChainUpdates(), this.chainUpdates, localStore); - List currentDecodedBlockList; - if (this.chainUpdates.containsKey(senderNode)) { - // Add to already created list of blocks - currentDecodedBlockList = this.chainUpdates.get(senderNode); - currentDecodedBlockList.add(lastBlock); - } else { - // Create new list of blocks - currentDecodedBlockList = new ArrayList<>(); - currentDecodedBlockList.add(lastBlock); - this.chainUpdates.put(senderNode, currentDecodedBlockList); + + List currentDecodedBlockList = new ArrayList<>(); + if (senderChain != null){ + // Start from the last block + BlockMessage lastBlockMessage = senderChain.get(senderChain.size() - 1); + // Recursively decode the transaction and chainUpdates + Block lastBlock = new Block(lastBlockMessage, proofMessage.getChainUpdates(), this.chainUpdates, localStore); + if (this.chainUpdates.containsKey(senderNode)) { + // Add to already created list of blocks + currentDecodedBlockList = this.chainUpdates.get(senderNode); + currentDecodedBlockList.add(lastBlock); + } else { + // Create new list of blocks + currentDecodedBlockList.add(lastBlock); + this.chainUpdates.put(senderNode, currentDecodedBlockList); + } } + // Set the transaction from the decoded chain // TODO [possible improvement]: is the transaction always in the last block ? Transaction foundTransaction = null; From 785803849808f87c8ebe8e4edb605ef444d0241a Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Mon, 22 Jan 2018 16:42:21 +0100 Subject: [PATCH 16/69] setting seed --- .../scaleoutdistributedledger/SimulationMain.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index be2a808..0eaa59d 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -30,9 +30,9 @@ private SimulationMain() {} //SETTINGS //number of local nodes to generate - public static final int LOCAL_NODES_NUMBER = 2; + public static final int LOCAL_NODES_NUMBER = 3; //number of total nodes in the system - public static final int TOTAL_NODES_NUMBER = 2; + public static final int TOTAL_NODES_NUMBER = 3; //number from which our nodes are (e.g if we run nodes (2, 3, 4), then this should be 2 public static final int NODES_FROM_NUMBER = 0; //Whether this main is the master coordinator of the simulation @@ -81,7 +81,9 @@ public static void main(String[] args) throws Exception { // --- PHASE 3: start the actual simulation --- Simulation simulation = new Simulation(IS_MASTER); // ITransactionPattern itp = new OnlyNodeZeroTransactionPattern(10, 20, 1000, 2000, 1); - ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 2); +// ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 2); + UniformRandomTransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 2); + itp.setSeed(1); simulation.setTransactionPattern(itp); simulation.runNodesLocally(nodes, ownNodes, genesisBlock, nodeToKeyPair); From 665d4ec333adfffb62309935a3c89be66677c9a8 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Mon, 22 Jan 2018 17:25:53 +0100 Subject: [PATCH 17/69] Fix last committed block --- .../blockchain/scaleoutdistributedledger/model/Chain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java index 98198d1..9eb6f85 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java @@ -70,7 +70,7 @@ public synchronized void update(List updates, LocalStore localStore) { for (Block block : ReversedIterator.reversed(this.blocks)) { if (block.isOnMainChain(localStore)) { - this.lastCommittedBlock = block; + setLastCommittedBlock(block); return; } } From 17b3d65403e9a9ab0ac98bc084021cdbe5b2c13c Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Mon, 22 Jan 2018 17:29:43 +0100 Subject: [PATCH 18/69] debug statment --- .../SimulationMain.java | 2 +- .../model/Block.java | 32 +++++++++++++++---- .../model/ChainView.java | 8 ++++- .../model/Proof.java | 3 ++ 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index 0eaa59d..6cfeaf9 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -82,7 +82,7 @@ public static void main(String[] args) throws Exception { Simulation simulation = new Simulation(IS_MASTER); // ITransactionPattern itp = new OnlyNodeZeroTransactionPattern(10, 20, 1000, 2000, 1); // ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 2); - UniformRandomTransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 2); + UniformRandomTransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 1); itp.setSeed(1); simulation.setTransactionPattern(itp); simulation.runNodesLocally(nodes, ownNodes, genesisBlock, nodeToKeyPair); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index 2792287..ec71cdd 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -215,16 +215,36 @@ public int hashCode() { @Override public boolean equals(Object obj) { if (this == obj) return true; - if (!(obj instanceof Block)) return false; + if (!(obj instanceof Block)) { + System.out.println("HEREEQUALS1"); + System.out.println(this); + System.out.println(obj); + return false; + } Block other = (Block) obj; - if (this.number != other.number) return false; + if (this.number != other.number){ + System.out.println("HEREEQUALS2"); + return false; + } if (this.owner == null) { - if (other.owner != null) return false; - } else if (other.owner == null || this.owner.getId() != other.owner.getId()) return false; + if (other.owner != null) { + System.out.println("HEREEQUALS3"); + return false; + } + } else if (other.owner == null || this.owner.getId() != other.owner.getId()) { + System.out.println("HEREEQUALS4"); + return false; + } if (this.previousBlock == null) { - if (other.previousBlock != null) return false; - } else if (!this.previousBlock.equals(other.previousBlock)) return false; + if (other.previousBlock != null) { + System.out.println("HEREEQUALS5"); + return false; + } + } else if (!this.previousBlock.equals(other.previousBlock)) { + System.out.println("HEREEQUALS6"); + return false; + } return this.transactions.equals(other.transactions); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java index b70c5d6..2bc05e4 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java @@ -87,6 +87,7 @@ private boolean checkIntegrity() { int lastOwnNumber = blocks.get(blocks.size() - 1).getNumber(); if (firstUpdateNumber - lastOwnNumber > 1) { //We are missing blocks between what we know and what we were sent! Can never happen with an honest node. + System.out.println("HERE1"); this.valid = false; return false; } else if (lastOwnNumber >= firstUpdateNumber) { @@ -100,13 +101,18 @@ private boolean checkIntegrity() { //TODO we might need a special equality check if (!ownBlock.equals(updatedBlock)) { + System.out.println("HERE2"); + System.out.println(ownBlock); + System.out.println(updatedBlock); + System.out.println(ownBlock.getClass().getName()); + System.out.println(updatedBlock.getClass().getName()); this.valid = false; return false; } updates.remove(0); } - + return checkNoGaps(0, lastOwnNumber); } else { //The first updated block number follows directly after the last block we knew about. diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 2ab1e4f..9221912 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -204,6 +204,9 @@ private void verify(Transaction transaction, LocalStore localStore) throws Proof try { verify(sourceTransaction, localStore); } catch (ValidationException ex) { + ex.printStackTrace(); + System.out.println(this); + System.exit(1); throw new ProofValidationException("Source " + sourceTransaction + " is not valid", ex); } } From 0812ca24e0559910da4ebc8316441e68f3c9648a Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Mon, 22 Jan 2018 17:33:58 +0100 Subject: [PATCH 19/69] Fix block pointers --- .../model/Block.java | 4 +-- .../model/Chain.java | 25 +++++++++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index eef8fe2..6a4cf73 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -25,8 +25,8 @@ public class Block { @Getter private final int number; - @Getter - private final Block previousBlock; + @Getter @Setter + private Block previousBlock; @Getter @Setter private Node owner; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java index 9eb6f85..c3ab572 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java @@ -55,17 +55,26 @@ public synchronized void update(List updates, LocalStore localStore) { if (updates.isEmpty()) return; + int nextNr; + Block previousBlock; if (blocks.isEmpty()) { - blocks.addAll(updates); + //Should start at 0, there is no previous block + nextNr = 0; + previousBlock = null; } else { + //Should start with the first block after our last block. Block lastBlock = blocks.get(blocks.size() - 1); - int nextNr = lastBlock.getNumber() + 1; - for (Block block : updates) { - //Skip any overlap - if (block.getNumber() != nextNr) continue; - blocks.add(block); - nextNr++; - } + nextNr = lastBlock.getNumber() + 1; + previousBlock = lastBlock; + } + + for (Block block : updates) { + //Skip any overlap + if (block.getNumber() != nextNr) continue; + block.setPreviousBlock(previousBlock); + blocks.add(block); + nextNr++; + previousBlock = block; } for (Block block : ReversedIterator.reversed(this.blocks)) { From 37c5b0204ea67c88719c890630d7da57b7251d85 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Mon, 22 Jan 2018 17:35:39 +0100 Subject: [PATCH 20/69] node settings --- tracker-server/app.js | 3 +++ tracker-server/package.json | 49 +++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/tracker-server/app.js b/tracker-server/app.js index f24077e..f3c575c 100644 --- a/tracker-server/app.js +++ b/tracker-server/app.js @@ -15,6 +15,9 @@ app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'ejs'); +app.use(express.static(path.join(__dirname, 'public'))); const port = normalizePort(process.env.PORT || '3000'); app.set('port', port); diff --git a/tracker-server/package.json b/tracker-server/package.json index 2978867..0fca38a 100644 --- a/tracker-server/package.json +++ b/tracker-server/package.json @@ -1,26 +1,27 @@ { - "name": "tracker-server", - "version": "1.0.0", - "description": "Tracker server for scale-out blockchain project", - "main": "app.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build": "rimraf dist/ && babel ./ --out-dir dist/ --ignore ./node_modules,./.babelrc,./package.json,./npm-debug.log --copy-files", - "start": "npm run build && node dist/app.js" - }, - "author": "Bart de Jonge", - "license": "ISC", - "devDependencies": { - "babel-cli": "^6.26.0", - "babel-core": "^6.26.0", - "babel-preset-es2015": "^6.24.1", - "rimraf": "^2.6.2" - }, - "dependencies": { - "body-parser": "latest", - "cookie-parser": "^1.4.3", - "debug": "latest", - "express": "^4.16.2", - "morgan": "^1.9.0" - } + "name": "tracker-server", + "version": "1.0.0", + "description": "Tracker server for scale-out blockchain project", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "rimraf dist/ && babel ./ --out-dir dist/ --ignore ./node_modules,./.babelrc,./package.json,./npm-debug.log --copy-files", + "start": "npm run build && node dist/app.js" + }, + "author": "Bart de Jonge", + "license": "ISC", + "devDependencies": { + "babel-cli": "^6.26.0", + "babel-core": "^6.26.0", + "babel-preset-es2015": "^6.24.1", + "rimraf": "^2.6.2" + }, + "dependencies": { + "body-parser": "latest", + "cookie-parser": "^1.4.3", + "debug": "latest", + "ejs": "^2.5.7", + "express": "^4.16.2", + "morgan": "^1.9.0" + } } From cad76d890800efad6589114ee91d0bb2f84c6e66 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Mon, 22 Jan 2018 17:45:15 +0100 Subject: [PATCH 21/69] Check order --- .../blockchain/scaleoutdistributedledger/model/Chain.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java index c3ab572..bbbcf52 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java @@ -55,6 +55,12 @@ public synchronized void update(List updates, LocalStore localStore) { if (updates.isEmpty()) return; + ArrayList copy = new ArrayList<>(updates); + copy.sort((a, b) -> Integer.compare(a.getNumber(), b.getNumber())); + if (!copy.equals(updates)) { + throw new IllegalArgumentException("The blocks are not ordered correctly :("); + } + int nextNr; Block previousBlock; if (blocks.isEmpty()) { From 38ccc09886a4ddd0a815427f97f14b1227208795 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Mon, 22 Jan 2018 17:49:36 +0100 Subject: [PATCH 22/69] added odometer --- .../public/javascripts/odometer.min.js | 2 + .../public/stylesheets/odometer-theme-car.css | 132 ++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 tracker-server/public/javascripts/odometer.min.js create mode 100644 tracker-server/public/stylesheets/odometer-theme-car.css diff --git a/tracker-server/public/javascripts/odometer.min.js b/tracker-server/public/javascripts/odometer.min.js new file mode 100644 index 0000000..48da2e9 --- /dev/null +++ b/tracker-server/public/javascripts/odometer.min.js @@ -0,0 +1,2 @@ +/*! odometer 0.4.8 */ +(function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G=[].slice;q='',n=''+q+"",d='8'+n+"",g='',c="(,ddd).dd",h=/^\(?([^)]*)\)?(?:(.)(d+))?$/,i=30,f=2e3,a=20,j=2,e=.5,k=1e3/i,b=1e3/a,o="transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd",y=document.createElement("div").style,p=null!=y.transition||null!=y.webkitTransition||null!=y.mozTransition||null!=y.oTransition,w=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame,l=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver,s=function(a){var b;return b=document.createElement("div"),b.innerHTML=a,b.children[0]},v=function(a,b){return a.className=a.className.replace(new RegExp("(^| )"+b.split(" ").join("|")+"( |$)","gi")," ")},r=function(a,b){return v(a,b),a.className+=" "+b},z=function(a,b){var c;return null!=document.createEvent?(c=document.createEvent("HTMLEvents"),c.initEvent(b,!0,!0),a.dispatchEvent(c)):void 0},u=function(){var a,b;return null!=(a=null!=(b=window.performance)&&"function"==typeof b.now?b.now():void 0)?a:+new Date},x=function(a,b){return null==b&&(b=0),b?(a*=Math.pow(10,b),a+=.5,a=Math.floor(a),a/=Math.pow(10,b)):Math.round(a)},A=function(a){return 0>a?Math.ceil(a):Math.floor(a)},t=function(a){return a-x(a)},C=!1,(B=function(){var a,b,c,d,e;if(!C&&null!=window.jQuery){for(C=!0,d=["html","text"],e=[],b=0,c=d.length;c>b;b++)a=d[b],e.push(function(a){var b;return b=window.jQuery.fn[a],window.jQuery.fn[a]=function(a){var c;return null==a||null==(null!=(c=this[0])?c.odometer:void 0)?b.apply(this,arguments):this[0].odometer.update(a)}}(a));return e}})(),setTimeout(B,0),m=function(){function a(b){var c,d,e,g,h,i,l,m,n,o,p=this;if(this.options=b,this.el=this.options.el,null!=this.el.odometer)return this.el.odometer;this.el.odometer=this,m=a.options;for(d in m)g=m[d],null==this.options[d]&&(this.options[d]=g);null==(h=this.options).duration&&(h.duration=f),this.MAX_VALUES=this.options.duration/k/j|0,this.resetFormat(),this.value=this.cleanValue(null!=(n=this.options.value)?n:""),this.renderInside(),this.render();try{for(o=["innerHTML","innerText","textContent"],i=0,l=o.length;l>i;i++)e=o[i],null!=this.el[e]&&!function(a){return Object.defineProperty(p.el,a,{get:function(){var b;return"innerHTML"===a?p.inside.outerHTML:null!=(b=p.inside.innerText)?b:p.inside.textContent},set:function(a){return p.update(a)}})}(e)}catch(q){c=q,this.watchForMutations()}}return a.prototype.renderInside=function(){return this.inside=document.createElement("div"),this.inside.className="odometer-inside",this.el.innerHTML="",this.el.appendChild(this.inside)},a.prototype.watchForMutations=function(){var a,b=this;if(null!=l)try{return null==this.observer&&(this.observer=new l(function(a){var c;return c=b.el.innerText,b.renderInside(),b.render(b.value),b.update(c)})),this.watchMutations=!0,this.startWatchingMutations()}catch(c){a=c}},a.prototype.startWatchingMutations=function(){return this.watchMutations?this.observer.observe(this.el,{childList:!0}):void 0},a.prototype.stopWatchingMutations=function(){var a;return null!=(a=this.observer)?a.disconnect():void 0},a.prototype.cleanValue=function(a){var b;return"string"==typeof a&&(a=a.replace(null!=(b=this.format.radix)?b:".",""),a=a.replace(/[.,]/g,""),a=a.replace("","."),a=parseFloat(a,10)||0),x(a,this.format.precision)},a.prototype.bindTransitionEnd=function(){var a,b,c,d,e,f,g=this;if(!this.transitionEndBound){for(this.transitionEndBound=!0,b=!1,e=o.split(" "),f=[],c=0,d=e.length;d>c;c++)a=e[c],f.push(this.el.addEventListener(a,function(){return b?!0:(b=!0,setTimeout(function(){return g.render(),b=!1,z(g.el,"odometerdone")},0),!0)},!1));return f}},a.prototype.resetFormat=function(){var a,b,d,e,f,g,i,j;if(a=null!=(i=this.options.format)?i:c,a||(a="d"),d=h.exec(a),!d)throw new Error("Odometer: Unparsable digit format");return j=d.slice(1,4),g=j[0],f=j[1],b=j[2],e=(null!=b?b.length:void 0)||0,this.format={repeating:g,radix:f,precision:e}},a.prototype.render=function(a){var b,c,d,e,f,g,h;for(null==a&&(a=this.value),this.stopWatchingMutations(),this.resetFormat(),this.inside.innerHTML="",f=this.options.theme,b=this.el.className.split(" "),e=[],g=0,h=b.length;h>g;g++)c=b[g],c.length&&((d=/^odometer-theme-(.+)$/.exec(c))?f=d[1]:/^odometer(-|$)/.test(c)||e.push(c));return e.push("odometer"),p||e.push("odometer-no-transitions"),f?e.push("odometer-theme-"+f):e.push("odometer-auto-theme"),this.el.className=e.join(" "),this.ribbons={},this.formatDigits(a),this.startWatchingMutations()},a.prototype.formatDigits=function(a){var b,c,d,e,f,g,h,i,j,k;if(this.digits=[],this.options.formatFunction)for(d=this.options.formatFunction(a),j=d.split("").reverse(),f=0,h=j.length;h>f;f++)c=j[f],c.match(/0-9/)?(b=this.renderDigit(),b.querySelector(".odometer-value").innerHTML=c,this.digits.push(b),this.insertDigit(b)):this.addSpacer(c);else for(e=!this.format.precision||!t(a)||!1,k=a.toString().split("").reverse(),g=0,i=k.length;i>g;g++)b=k[g],"."===b&&(e=!0),this.addDigit(b,e)},a.prototype.update=function(a){var b,c=this;return a=this.cleanValue(a),(b=a-this.value)?(v(this.el,"odometer-animating-up odometer-animating-down odometer-animating"),b>0?r(this.el,"odometer-animating-up"):r(this.el,"odometer-animating-down"),this.stopWatchingMutations(),this.animate(a),this.startWatchingMutations(),setTimeout(function(){return c.el.offsetHeight,r(c.el,"odometer-animating")},0),this.value=a):void 0},a.prototype.renderDigit=function(){return s(d)},a.prototype.insertDigit=function(a,b){return null!=b?this.inside.insertBefore(a,b):this.inside.children.length?this.inside.insertBefore(a,this.inside.children[0]):this.inside.appendChild(a)},a.prototype.addSpacer=function(a,b,c){var d;return d=s(g),d.innerHTML=a,c&&r(d,c),this.insertDigit(d,b)},a.prototype.addDigit=function(a,b){var c,d,e,f;if(null==b&&(b=!0),"-"===a)return this.addSpacer(a,null,"odometer-negation-mark");if("."===a)return this.addSpacer(null!=(f=this.format.radix)?f:".",null,"odometer-radix-mark");if(b)for(e=!1;;){if(!this.format.repeating.length){if(e)throw new Error("Bad odometer format without digits");this.resetFormat(),e=!0}if(c=this.format.repeating[this.format.repeating.length-1],this.format.repeating=this.format.repeating.substring(0,this.format.repeating.length-1),"d"===c)break;this.addSpacer(c)}return d=this.renderDigit(),d.querySelector(".odometer-value").innerHTML=a,this.digits.push(d),this.insertDigit(d)},a.prototype.animate=function(a){return p&&"count"!==this.options.animation?this.animateSlide(a):this.animateCount(a)},a.prototype.animateCount=function(a){var c,d,e,f,g,h=this;if(d=+a-this.value)return f=e=u(),c=this.value,(g=function(){var i,j,k;return u()-f>h.options.duration?(h.value=a,h.render(),void z(h.el,"odometerdone")):(i=u()-e,i>b&&(e=u(),k=i/h.options.duration,j=d*k,c+=j,h.render(Math.round(c))),null!=w?w(g):setTimeout(g,b))})()},a.prototype.getDigitCount=function(){var a,b,c,d,e,f;for(d=1<=arguments.length?G.call(arguments,0):[],a=e=0,f=d.length;f>e;a=++e)c=d[a],d[a]=Math.abs(c);return b=Math.max.apply(Math,d),Math.ceil(Math.log(b+1)/Math.log(10))},a.prototype.getFractionalDigitCount=function(){var a,b,c,d,e,f,g;for(e=1<=arguments.length?G.call(arguments,0):[],b=/^\-?\d*\.(\d*?)0*$/,a=f=0,g=e.length;g>f;a=++f)d=e[a],e[a]=d.toString(),c=b.exec(e[a]),null==c?e[a]=0:e[a]=c[1].length;return Math.max.apply(Math,e)},a.prototype.resetDigits=function(){return this.digits=[],this.ribbons=[],this.inside.innerHTML="",this.resetFormat()},a.prototype.animateSlide=function(a){var b,c,d,f,g,h,i,j,k,l,m,n,o,p,q,s,t,u,v,w,x,y,z,B,C,D,E;if(s=this.value,j=this.getFractionalDigitCount(s,a),j&&(a*=Math.pow(10,j),s*=Math.pow(10,j)),d=a-s){for(this.bindTransitionEnd(),f=this.getDigitCount(s,a),g=[],b=0,m=v=0;f>=0?f>v:v>f;m=f>=0?++v:--v){if(t=A(s/Math.pow(10,f-m-1)),i=A(a/Math.pow(10,f-m-1)),h=i-t,Math.abs(h)>this.MAX_VALUES){for(l=[],n=h/(this.MAX_VALUES+this.MAX_VALUES*b*e),c=t;h>0&&i>c||0>h&&c>i;)l.push(Math.round(c)),c+=n;l[l.length-1]!==i&&l.push(i),b++}else l=function(){E=[];for(var a=t;i>=t?i>=a:a>=i;i>=t?a++:a--)E.push(a);return E}.apply(this);for(m=w=0,y=l.length;y>w;m=++w)k=l[m],l[m]=Math.abs(k%10);g.push(l)}for(this.resetDigits(),D=g.reverse(),m=x=0,z=D.length;z>x;m=++x)for(l=D[m],this.digits[m]||this.addDigit(" ",m>=j),null==(u=this.ribbons)[m]&&(u[m]=this.digits[m].querySelector(".odometer-ribbon-inner")),this.ribbons[m].innerHTML="",0>d&&(l=l.reverse()),o=C=0,B=l.length;B>C;o=++C)k=l[o],q=document.createElement("div"),q.className="odometer-value",q.innerHTML=k,this.ribbons[m].appendChild(q),o===l.length-1&&r(q,"odometer-last-value"),0===o&&r(q,"odometer-first-value");return 0>t&&this.addDigit("-"),p=this.inside.querySelector(".odometer-radix-mark"),null!=p&&p.parent.removeChild(p),j?this.addSpacer(this.format.radix,this.digits[j-1],"odometer-radix-mark"):void 0}},a}(),m.options=null!=(E=window.odometerOptions)?E:{},setTimeout(function(){var a,b,c,d,e;if(window.odometerOptions){d=window.odometerOptions,e=[];for(a in d)b=d[a],e.push(null!=(c=m.options)[a]?(c=m.options)[a]:c[a]=b);return e}},0),m.init=function(){var a,b,c,d,e,f;if(null!=document.querySelectorAll){for(b=document.querySelectorAll(m.options.selector||".odometer"),f=[],c=0,d=b.length;d>c;c++)a=b[c],f.push(a.odometer=new m({el:a,value:null!=(e=a.innerText)?e:a.textContent}));return f}},null!=(null!=(F=document.documentElement)?F.doScroll:void 0)&&null!=document.createEventObject?(D=document.onreadystatechange,document.onreadystatechange=function(){return"complete"===document.readyState&&m.options.auto!==!1&&m.init(),null!=D?D.apply(this,arguments):void 0}):document.addEventListener("DOMContentLoaded",function(){return m.options.auto!==!1?m.init():void 0},!1),"function"==typeof define&&define.amd?define([],function(){return m}):"undefined"!=typeof exports&&null!==exports?module.exports=m:window.Odometer=m}).call(this); diff --git a/tracker-server/public/stylesheets/odometer-theme-car.css b/tracker-server/public/stylesheets/odometer-theme-car.css new file mode 100644 index 0000000..5067d9d --- /dev/null +++ b/tracker-server/public/stylesheets/odometer-theme-car.css @@ -0,0 +1,132 @@ +@import url("//fonts.googleapis.com/css?family=Arimo"); +.odometer.odometer-auto-theme, .odometer.odometer-theme-car { + display: inline-block; + vertical-align: middle; + *vertical-align: auto; + *zoom: 1; + *display: inline; + position: relative; +} +.odometer.odometer-auto-theme .odometer-digit, .odometer.odometer-theme-car .odometer-digit { + display: inline-block; + vertical-align: middle; + *vertical-align: auto; + *zoom: 1; + *display: inline; + position: relative; +} +.odometer.odometer-auto-theme .odometer-digit .odometer-digit-spacer, .odometer.odometer-theme-car .odometer-digit .odometer-digit-spacer { + display: inline-block; + vertical-align: middle; + *vertical-align: auto; + *zoom: 1; + *display: inline; + visibility: hidden; +} +.odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, .odometer.odometer-theme-car .odometer-digit .odometer-digit-inner { + text-align: left; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: hidden; +} +.odometer.odometer-auto-theme .odometer-digit .odometer-ribbon, .odometer.odometer-theme-car .odometer-digit .odometer-ribbon { + display: block; +} +.odometer.odometer-auto-theme .odometer-digit .odometer-ribbon-inner, .odometer.odometer-theme-car .odometer-digit .odometer-ribbon-inner { + display: block; + -webkit-backface-visibility: hidden; +} +.odometer.odometer-auto-theme .odometer-digit .odometer-value, .odometer.odometer-theme-car .odometer-digit .odometer-value { + display: block; + -webkit-transform: translateZ(0); +} +.odometer.odometer-auto-theme .odometer-digit .odometer-value.odometer-last-value, .odometer.odometer-theme-car .odometer-digit .odometer-value.odometer-last-value { + position: absolute; +} +.odometer.odometer-auto-theme.odometer-animating-up .odometer-ribbon-inner, .odometer.odometer-theme-car.odometer-animating-up .odometer-ribbon-inner { + -webkit-transition: -webkit-transform 2s; + -moz-transition: -moz-transform 2s; + -ms-transition: -ms-transform 2s; + -o-transition: -o-transform 2s; + transition: transform 2s; +} +.odometer.odometer-auto-theme.odometer-animating-up.odometer-animating .odometer-ribbon-inner, .odometer.odometer-theme-car.odometer-animating-up.odometer-animating .odometer-ribbon-inner { + -webkit-transform: translateY(-100%); + -moz-transform: translateY(-100%); + -ms-transform: translateY(-100%); + -o-transform: translateY(-100%); + transform: translateY(-100%); +} +.odometer.odometer-auto-theme.odometer-animating-down .odometer-ribbon-inner, .odometer.odometer-theme-car.odometer-animating-down .odometer-ribbon-inner { + -webkit-transform: translateY(-100%); + -moz-transform: translateY(-100%); + -ms-transform: translateY(-100%); + -o-transform: translateY(-100%); + transform: translateY(-100%); +} +.odometer.odometer-auto-theme.odometer-animating-down.odometer-animating .odometer-ribbon-inner, .odometer.odometer-theme-car.odometer-animating-down.odometer-animating .odometer-ribbon-inner { + -webkit-transition: -webkit-transform 2s; + -moz-transition: -moz-transform 2s; + -ms-transition: -ms-transform 2s; + -o-transition: -o-transform 2s; + transition: transform 2s; + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -ms-transform: translateY(0); + -o-transform: translateY(0); + transform: translateY(0); +} + +.odometer.odometer-auto-theme, .odometer.odometer-theme-car { + -moz-border-radius: 0.34em; + -webkit-border-radius: 0.34em; + border-radius: 0.34em; + font-family: "Arimo", monospace; + padding: 0.15em; + background: #000; + color: #eee0d3; +} +.odometer.odometer-auto-theme .odometer-digit, .odometer.odometer-theme-car .odometer-digit { + -moz-box-shadow: inset 0 0 0.3em rgba(0, 0, 0, 0.8); + -webkit-box-shadow: inset 0 0 0.3em rgba(0, 0, 0, 0.8); + box-shadow: inset 0 0 0.3em rgba(0, 0, 0, 0.8); + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzMzMzMzMyIvPjxzdG9wIG9mZnNldD0iNDAlIiBzdG9wLWNvbG9yPSIjMzMzMzMzIi8+PHN0b3Agb2Zmc2V0PSI2MCUiIHN0b3AtY29sb3I9IiMxMDEwMTAiLz48c3RvcCBvZmZzZXQ9IjgwJSIgc3RvcC1jb2xvcj0iIzMzMzMzMyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzMzMzMzMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #333333), color-stop(40%, #333333), color-stop(60%, #101010), color-stop(80%, #333333), color-stop(100%, #333333)); + background-image: -moz-linear-gradient(top, #333333 0%, #333333 40%, #101010 60%, #333333 80%, #333333 100%); + background-image: -webkit-linear-gradient(top, #333333 0%, #333333 40%, #101010 60%, #333333 80%, #333333 100%); + background-image: linear-gradient(to bottom, #333333 0%, #333333 40%, #101010 60%, #333333 80%, #333333 100%); + padding: 0 0.15em; +} +.odometer.odometer-auto-theme .odometer-digit:first-child, .odometer.odometer-theme-car .odometer-digit:first-child { + -moz-border-radius: 0.2em 0 0 0.2em; + -webkit-border-radius: 0.2em; + border-radius: 0.2em 0 0 0.2em; +} +.odometer.odometer-auto-theme .odometer-digit:last-child, .odometer.odometer-theme-car .odometer-digit:last-child { + -moz-border-radius: 0 0.2em 0.2em 0; + -webkit-border-radius: 0; + border-radius: 0 0.2em 0.2em 0; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2VlZTBkMyIvPjxzdG9wIG9mZnNldD0iNDAlIiBzdG9wLWNvbG9yPSIjZWVlMGQzIi8+PHN0b3Agb2Zmc2V0PSI2MCUiIHN0b3AtY29sb3I9IiNiYmFhOWEiLz48c3RvcCBvZmZzZXQ9IjgwJSIgc3RvcC1jb2xvcj0iI2VlZTBkMyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2VlZTBkMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #eee0d3), color-stop(40%, #eee0d3), color-stop(60%, #bbaa9a), color-stop(80%, #eee0d3), color-stop(100%, #eee0d3)); + background-image: -moz-linear-gradient(top, #eee0d3 0%, #eee0d3 40%, #bbaa9a 60%, #eee0d3 80%, #eee0d3 100%); + background-image: -webkit-linear-gradient(top, #eee0d3 0%, #eee0d3 40%, #bbaa9a 60%, #eee0d3 80%, #eee0d3 100%); + background-image: linear-gradient(to bottom, #eee0d3 0%, #eee0d3 40%, #bbaa9a 60%, #eee0d3 80%, #eee0d3 100%); + background-color: #eee0d3; + color: #000; +} +.odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, .odometer.odometer-theme-car .odometer-digit .odometer-digit-inner { + left: 0.15em; +} +.odometer.odometer-auto-theme.odometer-animating-up .odometer-ribbon-inner, .odometer.odometer-auto-theme.odometer-animating-down.odometer-animating .odometer-ribbon-inner, .odometer.odometer-theme-car.odometer-animating-up .odometer-ribbon-inner, .odometer.odometer-theme-car.odometer-animating-down.odometer-animating .odometer-ribbon-inner { + -webkit-transition-timing-function: linear; + -moz-transition-timing-function: linear; + -ms-transition-timing-function: linear; + -o-transition-timing-function: linear; + transition-timing-function: linear; +} From f3b075558c8b15d2f9db6e9c6a0f559fb67db973 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Mon, 22 Jan 2018 18:20:45 +0100 Subject: [PATCH 23/69] Fix block pointers --- .../model/Proof.java | 26 +++++++++++++++++++ .../model/Transaction.java | 5 ++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index e959ce7..85efed0 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -109,6 +109,8 @@ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOExceptio //>>>>>>> 4cd4bd3a775e6a841cee6c2ac06d8ea8dcbe443a // Set the transaction from the decoded chain + fixPreviousBlockPointersAndOrder(); + Transaction foundTransaction = null; ChainView cv = new ChainView(senderNode.getChain(), currentDecodedBlockList); Block block = cv.getBlock(proofMessage.getTransactionMessage().getBlockNumber()); @@ -121,6 +123,30 @@ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOExceptio this.transaction = foundTransaction; } + private void fixPreviousBlockPointersAndOrder() { + for (Entry> entry : this.chainUpdates.entrySet()) { + Node node = entry.getKey(); + List updates = entry.getValue(); + + System.out.println("Presort = " + updates); + updates.sort((a, b) -> Integer.compare(a.getNumber(), b.getNumber())); + System.out.println("Postsort = " + updates); + + Block previousBlock = null; + for (int i = 0; i < updates.size(); i++) { + Block block = updates.get(i); + block.setPreviousBlock(previousBlock); + previousBlock = block; + } + + Block firstBlock = updates.get(0); + if (firstBlock.getNumber() != 0) { + previousBlock = node.getChain().getBlocks().get(firstBlock.getNumber() - 1); + firstBlock.setPreviousBlock(previousBlock); + } + } + } + /** * Add a block to the proof. * @param block - the block to be added diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index 7701bd4..e50c08a 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -235,13 +235,12 @@ public boolean equals(Object obj) { Transaction other = (Transaction) obj; if (number != other.number) return false; - if (receiver.getId() != other.receiver.getId()) return false; + if (!receiver.equals(other.receiver)) return false; if (sender == null) { if (other.sender != null) return false; - } else if (other.sender == null || sender.getId() != other.sender.getId()) return false; + } else if (!sender.equals(other.sender)) return false; if (amount != other.amount) return false; if (remainder != other.remainder) return false; - if (!hash.equals(other.hash)) return false; if (!source.equals(other.source)) return false; if (!blockNumber.equals(other.blockNumber)) return false; return true; From 4c7e866aaa32849a80e84546dcb42a9d6e62ee6b Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Mon, 22 Jan 2018 21:17:18 +0100 Subject: [PATCH 24/69] started new deserialization --- .../SimulationMain.java | 10 +- .../message/BlockMessage.java | 8 ++ .../message/TransactionMessage.java | 57 ++++----- .../model/Block.java | 57 ++------- .../model/Proof.java | 120 +++++++----------- .../model/Transaction.java | 76 +---------- 6 files changed, 95 insertions(+), 233 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index 6cfeaf9..404d935 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -30,9 +30,9 @@ private SimulationMain() {} //SETTINGS //number of local nodes to generate - public static final int LOCAL_NODES_NUMBER = 3; + public static final int LOCAL_NODES_NUMBER = 2; //number of total nodes in the system - public static final int TOTAL_NODES_NUMBER = 3; + public static final int TOTAL_NODES_NUMBER = 2; //number from which our nodes are (e.g if we run nodes (2, 3, 4), then this should be 2 public static final int NODES_FROM_NUMBER = 0; //Whether this main is the master coordinator of the simulation @@ -81,9 +81,9 @@ public static void main(String[] args) throws Exception { // --- PHASE 3: start the actual simulation --- Simulation simulation = new Simulation(IS_MASTER); // ITransactionPattern itp = new OnlyNodeZeroTransactionPattern(10, 20, 1000, 2000, 1); -// ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 2); - UniformRandomTransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 1); - itp.setSeed(1); + ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 2); +// UniformRandomTransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 1); +// itp.setSeed(1); simulation.setTransactionPattern(itp); simulation.runNodesLocally(nodes, ownNodes, genesisBlock, nodeToKeyPair); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java index 320d0d5..3a885db 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java @@ -67,4 +67,12 @@ public BlockMessage(Block block, Node proofReceiver) { public void handle(LocalStore localStore) { // Do nothing. } + + public Block toBlockWithoutSources(LocalStore localStore) { + List transactions = new ArrayList<>(); + for(TransactionMessage tm : this.transactions) { + transactions.add(tm.toTransactionWithoutSources(localStore)); + } + return new Block(this.number, localStore.getNode(this.ownerId), transactions); + } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java index e368b9a..24ad127 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java @@ -7,6 +7,7 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; import java.util.HashSet; import java.util.Map.Entry; import java.util.Objects; @@ -28,17 +29,10 @@ public class TransactionMessage extends Message { /** * Transactions known by the receiver. - * Entry: node id, transaction number + * Entry: node id, [block number, transaction number] */ @Getter - private final Set> knownSource; - - /** - * Transactions new to the receiver. - * Entry: node id, transaction number - */ - @Getter - private final Set> newSource; + private final Set> source; @Getter private final Sha256Hash hash; @@ -65,36 +59,36 @@ public TransactionMessage(Transaction transaction, Node proofReceiver) { this.receiverId = transaction.getReceiver().getId(); this.amount = transaction.getAmount(); this.remainder = transaction.getRemainder(); - this.knownSource = new HashSet<>(); - this.newSource = new HashSet<>(); + this.source = new HashSet<>(); // Optimization: categorize each transaction already known (or not) by the receiver for (Transaction sourceTransaction : transaction.getSource()) { Node sourceSender = sourceTransaction.getSender(); - if (sourceSender == null) { - // Receiver already knows about a genesis transaction - this.knownSource.add(new SimpleEntry<>(Transaction.GENESIS_SENDER, sourceTransaction.getNumber())); - } else { - if (proofReceiver.equals(sourceSender)) { - // Receiver is the sender of the source - // So receiver knows about himself - this.knownSource.add(new SimpleEntry<>(sourceSender.getId(), sourceTransaction.getNumber())); + if (sourceTransaction.getBlockNumber().isPresent()) { + if (sourceSender == null) { + // Genesis transaction + this.source.add(new SimpleEntry<>(sourceTransaction.getReceiver().getId(), + new int[]{sourceTransaction.getBlockNumber().getAsInt(), + sourceTransaction.getNumber()})); } else { - // Receiver is not the sender of the source - Integer lastBlockNumber = proofReceiver.getMetaKnowledge().get(sourceSender); - if (lastBlockNumber != null && sourceTransaction.getBlockNumber().getAsInt() <= lastBlockNumber) { - // Receiver knows about other node - this.knownSource.add(new SimpleEntry<>(sourceSender.getId(), sourceTransaction.getNumber())); - } else { - // Receiver does NOT know about other node - this.newSource.add(new SimpleEntry<>(sourceSender.getId(), sourceTransaction.getNumber())); - } + this.source.add(new SimpleEntry<>(sourceSender.getId(), + new int[]{sourceTransaction.getBlockNumber().getAsInt(), + sourceTransaction.getNumber()})); } + } else { + throw new IllegalStateException("Transaction without blocknumber found"); } } this.hash = transaction.getHash(); this.blockNumber = transaction.getBlockNumber().getAsInt(); } + public Transaction toTransactionWithoutSources(LocalStore localStore) { + Transaction tx = new Transaction(this.number, localStore.getNode(this.senderId), + localStore.getNode(this.receiverId), this.amount, this.remainder, new HashSet<>()); + tx.setMessage(this); + return tx; + } + @Override public void handle(LocalStore localStore) { // Do nothing @@ -111,8 +105,7 @@ public boolean equals(Object obj) { if (receiverId != other.receiverId) return false; if (amount != other.amount) return false; if (remainder != other.remainder) return false; - if (knownSource.equals(other.knownSource)) return false; - if (newSource.equals(other.newSource)) return false; + if (source.equals(other.source)) return false; if (hash.equals(other.hash)) return false; return blockNumber == other.blockNumber; } @@ -126,11 +119,9 @@ public int hashCode() { result = prime * result + this.receiverId; result = prime * result + (int) (this.amount ^ (this.amount >>> 32)); result = prime * result + (int) (this.remainder ^ (this.remainder >>> 32)); - result = prime * result + Objects.hashCode(this.knownSource); - result = prime * result + Objects.hashCode(this.newSource); + result = prime * result + Objects.hashCode(this.source); result = prime * result + Objects.hashCode(this.hash); result = prime * result + this.blockNumber; return result; } - } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index 3224c7a..4bd99e0 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -75,57 +75,18 @@ public Block(Block previousBlock, Node owner) { } /** - * Constructor to decode a block message. - * @param blockMessage - block message from network. - * @param encodedChainUpdates - received chain of updates - * @param decodedChainUpdates - current decoded chain of updates - * @param localStore - local store. - * @throws IOException - error while getting node from tracker. + * Gets the transaction with the correct number in this block. + * @param transactionNumber - the number of the transaction to get. + * @return - the transaction. */ - public Block(BlockMessage blockMessage, Map> encodedChainUpdates, - Map> decodedChainUpdates, LocalStore localStore) throws IOException { - this.number = blockMessage.getNumber(); - // It's a genesis block - if (blockMessage.getOwnerId() == Transaction.GENESIS_SENDER) { - this.owner = null; - } else { - this.owner = localStore.getNode(blockMessage.getOwnerId()); - } - - if (blockMessage.getPreviousBlockNumber() != -1) { - // Check if we have it in the local store - if (this.owner.getChain().getLastBlock().getNumber() < blockMessage.getPreviousBlockNumber()) { - // We don't have it (it should be in the received chain of updates) - int currentBlockIndex = encodedChainUpdates.get(this.owner.getId()).indexOf(blockMessage); - BlockMessage previousBlockMesssage = encodedChainUpdates.get(this.owner.getId()).get(currentBlockIndex - 1); - // Get decoded block list from the owner - Block previousBlockLocal = new Block(previousBlockMesssage, encodedChainUpdates, decodedChainUpdates, localStore); - if (decodedChainUpdates.containsKey(this.owner)) { - decodedChainUpdates.get(this.owner).add(previousBlockLocal); - } else { - List currentDecodedBlockList = new ArrayList<>(); - currentDecodedBlockList.add(previousBlockLocal); - decodedChainUpdates.put(this.owner, currentDecodedBlockList); - } - this.previousBlock = previousBlockLocal; - } else { - // We have it (we infer it's the lastBlock from the chain) - this.previousBlock = this.owner.getChain().getLastBlock(); - } - } else { - // It's a genesis block - this.previousBlock = null; - } - - // Convert TransactionMessage to Transaction - this.transactions = new ArrayList<>(); - for (TransactionMessage transactionMessage : blockMessage.getTransactions()) { - this.transactions.add(new Transaction(transactionMessage, encodedChainUpdates, decodedChainUpdates, localStore)); + public Transaction getTransaction(int transactionNumber) { + for (Transaction transaction : this.transactions) { + if (transaction.getNumber() == transactionNumber) + return transaction; } - //TODO Do we want to send the hash along? - this.hash = blockMessage.getHash(); + throw new IllegalStateException("Invalid transaction number"); } - + /** * Adds the given transaction to this block and sets its block number. * @param transaction - the transaction to add diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 85efed0..fbdad90 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -8,15 +8,8 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.validation.ValidationException; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Set; /** * Proof class. @@ -56,81 +49,36 @@ public Proof(Transaction transaction, Map> chainUpdates) { */ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOException { this.chainUpdates = new HashMap<>(); - - for (Map.Entry> entry : proofMessage.getChainUpdates().entrySet()) { - Node node = localStore.getNode(entry.getKey()); - if (this.chainUpdates.containsKey(node)) { - // We have already decoded this chain in a previous iteration - continue; - } - List blockMessageList = entry.getValue(); - // Start from the last block - BlockMessage lastBlockMessage = blockMessageList.get(blockMessageList.size() - 1); - // Recursively decode the transaction and chainUpdates - Block lastBlock = new Block(lastBlockMessage, proofMessage.getChainUpdates(), this.chainUpdates, localStore); - if (this.chainUpdates.containsKey(node)) { - // Add to already created list of blocks - this.chainUpdates.get(node).add(lastBlock); - } else { - // Create new list of blocks - List blockList = new ArrayList<>(); - blockList.add(lastBlock); - this.chainUpdates.put(node, blockList); - } - } - - // Get decoded chain of sender, if any - Node senderNode = localStore.getNode(proofMessage.getTransactionMessage().getSenderId()); -//<<<<<<< HEAD -// List senderChain = proofMessage.getChainUpdates().get(senderNode.getId()); -// -// List currentDecodedBlockList = new ArrayList<>(); -// if (senderChain != null){ -// // Start from the last block -// BlockMessage lastBlockMessage = senderChain.get(senderChain.size() - 1); -// // Recursively decode the transaction and chainUpdates -// Block lastBlock = new Block(lastBlockMessage, proofMessage.getChainUpdates(), this.chainUpdates, localStore); -// if (this.chainUpdates.containsKey(senderNode)) { -// // Add to already created list of blocks -// currentDecodedBlockList = this.chainUpdates.get(senderNode); -// currentDecodedBlockList.add(lastBlock); -// } else { -// // Create new list of blocks -// currentDecodedBlockList.add(lastBlock); -// this.chainUpdates.put(senderNode, currentDecodedBlockList); -// } -// } -//======= - List currentDecodedBlockList = new ArrayList<>(); - if (this.chainUpdates.containsKey(senderNode)) { - currentDecodedBlockList = this.chainUpdates.get(senderNode); + // Decode the transactions while skipping sources + for (Map.Entry> entry : proofMessage.getChainUpdates().entrySet()) { + List blocks = new ArrayList<>(); + entry.getValue().forEach(blockMessage -> blocks.add(blockMessage.toBlockWithoutSources(localStore))); + chainUpdates.put(localStore.getNode(entry.getKey()), blocks); } - -//>>>>>>> 4cd4bd3a775e6a841cee6c2ac06d8ea8dcbe443a - // Set the transaction from the decoded chain - fixPreviousBlockPointersAndOrder(); - - Transaction foundTransaction = null; - ChainView cv = new ChainView(senderNode.getChain(), currentDecodedBlockList); - Block block = cv.getBlock(proofMessage.getTransactionMessage().getBlockNumber()); - for (Transaction transactionAux : block.getTransactions()) { - if (transactionAux.getNumber() == proofMessage.getTransactionMessage().getNumber()) { - foundTransaction = transactionAux; - break; - } + // Fix the sources + try { + this.fixTransactionSources(); + } catch(Exception e) { + e.printStackTrace(); + System.exit(1); } - this.transaction = foundTransaction; + + // Fix backlinks + this.fixPreviousBlockPointersAndOrder(); + + Node senderNode = localStore.getNode(proofMessage.getTransactionMessage().getSenderId()); + ChainView senderChainView = new ChainView(senderNode.getChain(), this.chainUpdates.get(senderNode)); + this.transaction = senderChainView.getBlock(proofMessage.getTransactionMessage().getBlockNumber()) + .getTransaction(proofMessage.getTransactionMessage().getNumber()); } private void fixPreviousBlockPointersAndOrder() { for (Entry> entry : this.chainUpdates.entrySet()) { Node node = entry.getKey(); List updates = entry.getValue(); - - System.out.println("Presort = " + updates); - updates.sort((a, b) -> Integer.compare(a.getNumber(), b.getNumber())); - System.out.println("Postsort = " + updates); + + updates.sort(Comparator.comparingInt(Block::getNumber)); Block previousBlock = null; for (int i = 0; i < updates.size(); i++) { @@ -146,7 +94,29 @@ private void fixPreviousBlockPointersAndOrder() { } } } - + + private void fixTransactionSources() { + HashMap chainViews = new HashMap<>(); + // Initialize the chainviews only once + for (Node node : this.chainUpdates.keySet()) { + chainViews.put(node.getId(), new ChainView(node.getChain(), this.chainUpdates.get(node))); + } + + // For all transactions of all nodes do + for (Node node : this.chainUpdates.keySet()) { + synchronized (this.chainUpdates.get(node)) { + for (Block block : this.chainUpdates.get(node)) { + for (Transaction tx : block.getTransactions()) { + tx.getMessage().getSource().forEach(entry -> { + Block sourceBlock = chainViews.get(entry.getKey()).getBlock(entry.getValue()[0]); + tx.getSource().add(sourceBlock.getTransaction(entry.getValue()[1])); + }); + } + } + } + } + } + /** * Add a block to the proof. * @param block - the block to be added diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index e50c08a..3a07b0d 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -48,6 +48,10 @@ public class Transaction { private OptionalInt blockNumber; + // Only temporarily used while decoding + @Getter @Setter + private TransactionMessage message; + /** * Constructor. * @param number - the number of this transaction. @@ -67,78 +71,6 @@ public Transaction(int number, Node sender, Node receiver, long amount, long rem this.blockNumber = OptionalInt.empty(); } - /** - * Constructor to decode a transaction message. - * @param transactionMessage - the message received from a transaction. - * @param encodedChainUpdates - the received chain of updates - * @param decodedChainUpdates - current chain of updates, from the decoding process - * @param localStore - local store, to get each Node object - * @throws java.io.IOException - error while getting node - */ - public Transaction(TransactionMessage transactionMessage, Map> encodedChainUpdates, - Map> decodedChainUpdates, LocalStore localStore) throws IOException { - this.number = transactionMessage.getNumber(); - this.blockNumber = OptionalInt.of(transactionMessage.getBlockNumber()); - - // It's a genesis transaction - if (transactionMessage.getSenderId() == GENESIS_SENDER) { - this.sender = null; - } else { - this.sender = localStore.getNode(transactionMessage.getSenderId()); - } - this.receiver = localStore.getNode(transactionMessage.getReceiverId()); - this.amount = transactionMessage.getAmount(); - this.remainder = transactionMessage.getRemainder(); - // Decode transaction messages to normal transactions - this.source = new HashSet<>(); - // Use local store for known sources - for (Entry knownSourceEntry : transactionMessage.getKnownSource()) { - Integer nodeId = knownSourceEntry.getKey(); - Integer transactionId = knownSourceEntry.getValue(); - //TODO This might need to be done in a certain order - this.source.add(localStore.getTransactionFromNode(nodeId, transactionId)); - } - // Use chain of updates for new sources - for (Entry newSourceEntry : transactionMessage.getNewSource()) { - try { - // Try to find the transaction in the local store - this.source.add(localStore.getTransactionFromNode(newSourceEntry.getKey(), newSourceEntry.getValue())); - continue; - } catch (IllegalStateException ex) { - // Not in localStore - } - // Use the transaction from the current chain of updates - Node owner = localStore.getNode(newSourceEntry.getKey()); - if (!decodedChainUpdates.containsKey(owner)) { - // Get that new chain - List blockMessageList = encodedChainUpdates.get(owner.getId()); - // Decode chain, in REVERSE order - BlockMessage lastBlockMessage = blockMessageList.get(blockMessageList.size() - 1); - // Recursively decode the blocks of a chain (in reverse order) - Block lastBlockLocal = new Block(lastBlockMessage, encodedChainUpdates, decodedChainUpdates, localStore); - if (decodedChainUpdates.containsKey(owner)) { - // Add to already created list - decodedChainUpdates.get(owner).add(lastBlockLocal); - } else { - // Create a new list - List blockList = new ArrayList<>(); - blockList.add(lastBlockLocal); - decodedChainUpdates.put(owner, blockList); - } - } - // TODO [Performance]: Find a way to directly go to the correct block instead of iterating through all of them - for (Block blockAux : decodedChainUpdates.get(owner)) { - for (Transaction transactionAux : blockAux.getTransactions()) { - if (transactionAux.getNumber() == newSourceEntry.getValue()) { - this.source.add(transactionAux); - break; - } - } - } - } - this.hash = transactionMessage.getHash(); - } - /** * Returns the number of the block (if it is in a block). * TODO: maybe do this more efficiently (when adding the transaction to the local chain or something) From e8aef59b27a34fd4d039c0efdd956f95be06e83c Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Mon, 22 Jan 2018 21:23:41 +0100 Subject: [PATCH 25/69] solved concurrency --- .../blockchain/scaleoutdistributedledger/model/Proof.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index fbdad90..99e4fa2 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -100,6 +100,7 @@ private void fixTransactionSources() { // Initialize the chainviews only once for (Node node : this.chainUpdates.keySet()) { chainViews.put(node.getId(), new ChainView(node.getChain(), this.chainUpdates.get(node))); + chainViews.get(node.getId()).isValid(); } // For all transactions of all nodes do @@ -108,6 +109,10 @@ private void fixTransactionSources() { for (Block block : this.chainUpdates.get(node)) { for (Transaction tx : block.getTransactions()) { tx.getMessage().getSource().forEach(entry -> { +// System.out.println(chainViews.get(entry.getKey())); +// System.out.println(entry.getKey()); +// System.out.println(entry.getValue()[0]); +// System.out.println(entry.getValue()[1]); Block sourceBlock = chainViews.get(entry.getKey()).getBlock(entry.getValue()[0]); tx.getSource().add(sourceBlock.getTransaction(entry.getValue()[1])); }); From 548611806b0ef2521f69b97e80bfbc047cb6002b Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Mon, 22 Jan 2018 21:24:07 +0100 Subject: [PATCH 26/69] removed unnecessary synchronized statement --- .../scaleoutdistributedledger/model/Proof.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 99e4fa2..28cc605 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -105,18 +105,16 @@ private void fixTransactionSources() { // For all transactions of all nodes do for (Node node : this.chainUpdates.keySet()) { - synchronized (this.chainUpdates.get(node)) { - for (Block block : this.chainUpdates.get(node)) { - for (Transaction tx : block.getTransactions()) { - tx.getMessage().getSource().forEach(entry -> { + for (Block block : this.chainUpdates.get(node)) { + for (Transaction tx : block.getTransactions()) { + tx.getMessage().getSource().forEach(entry -> { // System.out.println(chainViews.get(entry.getKey())); // System.out.println(entry.getKey()); // System.out.println(entry.getValue()[0]); // System.out.println(entry.getValue()[1]); - Block sourceBlock = chainViews.get(entry.getKey()).getBlock(entry.getValue()[0]); - tx.getSource().add(sourceBlock.getTransaction(entry.getValue()[1])); - }); - } + Block sourceBlock = chainViews.get(entry.getKey()).getBlock(entry.getValue()[0]); + tx.getSource().add(sourceBlock.getTransaction(entry.getValue()[1])); + }); } } } From 7465b598f13e2e6e6ff7185af6b740645775fbba Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Mon, 22 Jan 2018 21:37:21 +0100 Subject: [PATCH 27/69] fixed an error --- .../model/Proof.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 28cc605..a340470 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -57,12 +57,7 @@ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOExceptio chainUpdates.put(localStore.getNode(entry.getKey()), blocks); } // Fix the sources - try { - this.fixTransactionSources(); - } catch(Exception e) { - e.printStackTrace(); - System.exit(1); - } + this.fixTransactionSources(localStore); // Fix backlinks this.fixPreviousBlockPointersAndOrder(); @@ -95,7 +90,7 @@ private void fixPreviousBlockPointersAndOrder() { } } - private void fixTransactionSources() { + private void fixTransactionSources(LocalStore localStore) { HashMap chainViews = new HashMap<>(); // Initialize the chainviews only once for (Node node : this.chainUpdates.keySet()) { @@ -108,11 +103,12 @@ private void fixTransactionSources() { for (Block block : this.chainUpdates.get(node)) { for (Transaction tx : block.getTransactions()) { tx.getMessage().getSource().forEach(entry -> { -// System.out.println(chainViews.get(entry.getKey())); -// System.out.println(entry.getKey()); -// System.out.println(entry.getValue()[0]); -// System.out.println(entry.getValue()[1]); - Block sourceBlock = chainViews.get(entry.getKey()).getBlock(entry.getValue()[0]); + Block sourceBlock; + if (!chainViews.containsKey(entry.getKey())) { + sourceBlock = localStore.getNode(entry.getKey()).getChain().getBlocks().get(entry.getValue()[0]); + } else { + sourceBlock = chainViews.get(entry.getKey()).getBlock(entry.getValue()[0]); + } tx.getSource().add(sourceBlock.getTransaction(entry.getValue()[1])); }); } From 2d9a4a51205453e687b2f683bcee5c65774c7abc Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Mon, 22 Jan 2018 22:04:34 +0100 Subject: [PATCH 28/69] fixed stuff --- .../SimulationMain.java | 4 +-- .../TransactionSender.java | 2 +- .../model/Block.java | 27 +++++-------------- .../model/ChainView.java | 5 ---- .../model/Proof.java | 5 ++-- 5 files changed, 13 insertions(+), 30 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index 404d935..d66cb1b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -30,9 +30,9 @@ private SimulationMain() {} //SETTINGS //number of local nodes to generate - public static final int LOCAL_NODES_NUMBER = 2; + public static final int LOCAL_NODES_NUMBER = 3; //number of total nodes in the system - public static final int TOTAL_NODES_NUMBER = 2; + public static final int TOTAL_NODES_NUMBER = 3; //number from which our nodes are (e.g if we run nodes (2, 3, 4), then this should be 2 public static final int NODES_FROM_NUMBER = 0; //Whether this main is the master coordinator of the simulation diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index c6437e1..9d6caea 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -36,7 +36,7 @@ public class TransactionSender { * The number of blocks (with the same or higher block number) that need to be committed before * we send a certain block. */ - public static final int REQUIRED_COMMITS = 1; + public static final int REQUIRED_COMMITS = 6; private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); private final LocalStore localStore; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index 4bd99e0..d7a1506 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -177,33 +177,20 @@ public int hashCode() { public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Block)) { - System.out.println("HEREEQUALS1"); - System.out.println(this); - System.out.println(obj); + System.out.println("Block not equal because one is null"); return false; } Block other = (Block) obj; - if (this.number != other.number){ - System.out.println("HEREEQUALS2"); - return false; - } + if (this.number != other.number) return false; if (this.owner == null) { - if (other.owner != null) { - System.out.println("HEREEQUALS3"); - return false; - } - } else if (other.owner == null || this.owner.getId() != other.owner.getId()) { - System.out.println("HEREEQUALS4"); - return false; - } + if (other.owner != null) return false; + } else if (other.owner == null || this.owner.getId() != other.owner.getId()) return false; + if (this.previousBlock == null) { - if (other.previousBlock != null) { - System.out.println("HEREEQUALS5"); - return false; - } + if (other.previousBlock != null) return false; } else if (!this.previousBlock.equals(other.previousBlock)) { - System.out.println("HEREEQUALS6"); + System.out.println("Blocks not equals because of previousBlockPointer"); return false; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java index 2bc05e4..2f0170c 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java @@ -101,11 +101,6 @@ private boolean checkIntegrity() { //TODO we might need a special equality check if (!ownBlock.equals(updatedBlock)) { - System.out.println("HERE2"); - System.out.println(ownBlock); - System.out.println(updatedBlock); - System.out.println(ownBlock.getClass().getName()); - System.out.println(updatedBlock.getClass().getName()); this.valid = false; return false; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index a340470..72e1ed7 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -56,12 +56,13 @@ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOExceptio entry.getValue().forEach(blockMessage -> blocks.add(blockMessage.toBlockWithoutSources(localStore))); chainUpdates.put(localStore.getNode(entry.getKey()), blocks); } - // Fix the sources - this.fixTransactionSources(localStore); // Fix backlinks this.fixPreviousBlockPointersAndOrder(); + // Fix the sources + this.fixTransactionSources(localStore); + Node senderNode = localStore.getNode(proofMessage.getTransactionMessage().getSenderId()); ChainView senderChainView = new ChainView(senderNode.getChain(), this.chainUpdates.get(senderNode)); this.transaction = senderChainView.getBlock(proofMessage.getTransactionMessage().getBlockNumber()) From 9fa061c71992b77bb0b98f2dc6baeed818bab812 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Mon, 22 Jan 2018 22:49:09 +0100 Subject: [PATCH 29/69] Pretty logging --- .../TransactionSender.java | 4 +++- .../message/BlockMessage.java | 11 +++++++++++ .../message/ProofMessage.java | 16 ++++++++++++++++ .../message/TransactionMessage.java | 18 ++++++++++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index 9d6caea..e848527 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -167,7 +167,9 @@ private void sendBlock(Block block) { private boolean sendTransaction(Transaction transaction) throws InterruptedException, IOException { Node to = transaction.getReceiver(); Proof proof = Proof.createProof(localStore, transaction); - if (socketClient.sendMessage(to, new ProofMessage(proof))) { + ProofMessage msg = new ProofMessage(proof); + Log.debug("{0}: {1}", localStore.getOwnNode().getId(), msg); + if (socketClient.sendMessage(to, msg)) { to.updateMetaKnowledge(proof); return true; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java index 3a885db..36f864e 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java @@ -75,4 +75,15 @@ public Block toBlockWithoutSources(LocalStore localStore) { } return new Block(this.number, localStore.getNode(this.ownerId), transactions); } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64); + sb.append("BlockMessage> entry : chainUpdates.entrySet()) { + sb.append("\n ").append(entry.getKey()).append(": ["); + for (BlockMessage bm : entry.getValue()) { + sb.append("\n ").append(bm); + } + sb.append("\n ]"); + } + sb.append("\n}"); + return sb.toString(); + } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java index 24ad127..a2df642 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java @@ -124,4 +124,22 @@ public int hashCode() { result = prime * result + this.blockNumber; return result; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64); + sb.append("TransactionMessage entry : source) { + sb.append("\n ").append(entry.getKey()) + .append(": block=").append(entry.getValue()[0]) + .append(": id=").append(entry.getValue()[1]); + } + sb.append("\n ]"); + return sb.toString(); + } } From 22bc1312e4654fbf228c10845ad857b54c404de0 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Mon, 22 Jan 2018 23:08:28 +0100 Subject: [PATCH 30/69] Small changes --- .../scaleoutdistributedledger/message/BlockMessage.java | 4 ++++ .../scaleoutdistributedledger/message/ProofMessage.java | 7 ++++++- .../message/TransactionMessage.java | 8 +++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java index 36f864e..32ae5e4 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java @@ -1,6 +1,7 @@ package nl.tudelft.blockchain.scaleoutdistributedledger.message; import lombok.Getter; + import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Sha256Hash; @@ -8,6 +9,7 @@ import java.util.ArrayList; import java.util.List; + import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; /** @@ -80,6 +82,8 @@ public Block toBlockWithoutSources(LocalStore localStore) { public String toString() { StringBuilder sb = new StringBuilder(64); sb.append("BlockMessage> entry : chainUpdates.entrySet()) { sb.append("\n ").append(entry.getKey()).append(": ["); for (BlockMessage bm : entry.getValue()) { diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java index a2df642..e89ef94 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java @@ -1,6 +1,7 @@ package nl.tudelft.blockchain.scaleoutdistributedledger.message; import lombok.Getter; + import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Sha256Hash; @@ -134,10 +135,15 @@ public String toString() { .append(", amount=").append(amount) .append(", remainder=").append(remainder) .append(", source=["); + + if (source.isEmpty()) { + return sb.append("]").toString(); + } + for (Entry entry : source) { sb.append("\n ").append(entry.getKey()) .append(": block=").append(entry.getValue()[0]) - .append(": id=").append(entry.getValue()[1]); + .append(", id=").append(entry.getValue()[1]); } sb.append("\n ]"); return sb.toString(); From e6d9d883ed1d8df24f16c5490d0ba26b8103a5d3 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Tue, 23 Jan 2018 00:13:56 +0100 Subject: [PATCH 31/69] Improve chainviews --- .../model/ChainView.java | 33 +++++++++++++++---- .../model/Proof.java | 18 ++++++---- .../validation/Verification.java | 2 -- .../model/SerializationTest.java | 2 +- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java index 2f0170c..b485555 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java @@ -15,6 +15,8 @@ public class ChainView implements Iterable { private Chain chain; private List updates; private Boolean valid; + private boolean trim; + private int startIndex; /** * @param chain @@ -26,6 +28,19 @@ public ChainView(Chain chain, List updates) { this.chain = chain; this.updates = updates; if (this.updates == null) this.updates = new ArrayList<>(); + this.trim = true; + } + + /** + * @param chain - the chain + * @param updates - the blocks of this chain that were sent with the proof + * @param trim - if this chainview should trim + */ + public ChainView(Chain chain, List updates, boolean trim) { + this.chain = chain; + this.updates = updates; + if (this.updates == null) this.updates = new ArrayList<>(); + this.trim = trim; } /** @@ -97,7 +112,7 @@ private boolean checkIntegrity() { int baseI = blocks.size() - overlap; for (int i = 0; i < overlap && !updates.isEmpty(); i++) { Block ownBlock = blocks.get(baseI + i); - Block updatedBlock = updates.get(0); + Block updatedBlock = updates.get(startIndex); //TODO we might need a special equality check if (!ownBlock.equals(updatedBlock)) { @@ -105,10 +120,14 @@ private boolean checkIntegrity() { return false; } - updates.remove(0); + if (trim) { + updates.remove(0); + } else { + startIndex++; + } } - return checkNoGaps(0, lastOwnNumber); + return checkNoGaps(startIndex, lastOwnNumber); } else { //The first updated block number follows directly after the last block we knew about. return checkNoGaps(0, lastOwnNumber); @@ -157,7 +176,7 @@ public Block getBlock(int number) { if (number < chain.getBlocks().size()) { return chain.getBlocks().get(number); } else if (isValid()) { - int index = number - chain.getBlocks().size(); + int index = number - chain.getBlocks().size() + startIndex; return updates.get(index); } else { throw new IllegalStateException( @@ -200,7 +219,7 @@ private class ChainViewIterator implements ListIterator { ChainViewIterator() { chainIterator = chain.getBlocks().listIterator(); - updatesIterator = updates.listIterator(); + updatesIterator = updates.subList(startIndex, updates.size()).listIterator(); currentIndex = -1; } @@ -208,10 +227,10 @@ private class ChainViewIterator implements ListIterator { int chainLength = chain.getBlocks().size(); if (number < chainLength) { chainIterator = chain.getBlocks().listIterator(number); - updatesIterator = updates.listIterator(); + updatesIterator = updates.subList(startIndex, updates.size()).listIterator(); } else { int index = number - chainLength; - updatesIterator = updates.listIterator(index); + updatesIterator = updates.subList(startIndex, updates.size()).listIterator(index); chainIterator = chain.getBlocks().listIterator(chainLength); updatesReached = true; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index c2d2575..1451f5b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -27,6 +27,8 @@ public class Proof { @Getter private final Map> chainUpdates; + + private final Map chainViews = new HashMap<>(); /** * Constructor. @@ -70,7 +72,7 @@ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOExceptio this.fixTransactionSources(localStore); Node senderNode = localStore.getNode(proofMessage.getTransactionMessage().getSenderId()); - ChainView senderChainView = new ChainView(senderNode.getChain(), this.chainUpdates.get(senderNode)); + ChainView senderChainView = getChainView(senderNode); this.transaction = senderChainView.getBlock(proofMessage.getTransactionMessage().getBlockNumber()) .getTransaction(proofMessage.getTransactionMessage().getNumber()); } @@ -101,8 +103,7 @@ private void fixTransactionSources(LocalStore localStore) { HashMap chainViews = new HashMap<>(); // Initialize the chainviews only once for (Node node : this.chainUpdates.keySet()) { - chainViews.put(node.getId(), new ChainView(node.getChain(), this.chainUpdates.get(node))); - chainViews.get(node.getId()).isValid(); + chainViews.put(node.getId(), getChainView(node)); } // For all transactions of all nodes do @@ -181,7 +182,6 @@ private void verify(Transaction transaction, LocalStore localStore) throws Proof int absmark = 0; boolean seen = false; - //TODO [PERFORMANCE]: We check the same chain views multiple times, even though we don't have to. ChainView chainView = getChainView(transaction.getSender()); if (!chainView.isValid()) { throw new ProofValidationException("ChainView of node " + transaction.getSender().getId() + " is invalid."); @@ -247,8 +247,14 @@ private void verifyGenesisTransaction(Transaction transaction, LocalStore localS * @param node - the node * @return - a chainview for the specified node */ - public ChainView getChainView(Node node) { - return new ChainView(node.getChain(), chainUpdates.get(node)); + public synchronized ChainView getChainView(Node node) { + ChainView chainView = chainViews.get(node); + if (chainView == null) { + chainView = new ChainView(node.getChain(), chainUpdates.get(node), false); + chainView.isValid(); + chainViews.put(node, chainView); + } + return chainView; } /** diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/validation/Verification.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/validation/Verification.java index f5c4344..60829f1 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/validation/Verification.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/validation/Verification.java @@ -96,8 +96,6 @@ private void checkMoney(Transaction transaction) throws ValidationException { * @throws ValidationException - If we detect double spending. */ private void checkDoubleSpending(Transaction transaction, Proof proof) throws ValidationException { - //TODO [PERFORMANCE]: We will revalidate the same chainview for every source that we check, even though is is not necessary. - //TODO [PERFORMANCE]: We might want some kind of caching or other mechanism to prevent this. ChainView chainView = proof.getChainView(transaction.getSender()); for (Block block : chainView) { boolean found = false; diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java index 6fe8199..9ac15a5 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java @@ -233,7 +233,7 @@ public void testDecoding_Valid() throws IOException { // Check Block and Transaction Block originalBlock = this.aliceNode.getChain().getBlocks().get(1); // Note: The decoding process got rid of the genesis block - Block decodedBlock = decodedProof.getChainUpdates().get(this.charlieLocalStore.getNodes().get(0)).get(0); + Block decodedBlock = decodedProof.getChainUpdates().get(this.charlieLocalStore.getNodes().get(0)).get(1); assertEquals(originalBlock, decodedBlock); } From 4cbc58bc8e1250ae91119a71dff7e0813b138431 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Tue, 23 Jan 2018 00:16:40 +0100 Subject: [PATCH 32/69] Synchronize meta knowledge --- .../TransactionCreator.java | 22 +++++---- .../model/Proof.java | 49 ++++++++++--------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java index 271086d..0552425 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java @@ -53,16 +53,18 @@ public TransactionCreator(LocalStore localStore, Node receiver, long amount) { * @return a bitset with the chains the receiver already knows about */ private BitSet calculateKnowledge() { - BitSet collected = receiver.getMetaKnowledge() - .keySet() - .stream() - .map(Node::getId) - .collect(() -> new BitSet(nodesCount), - (bs, i) -> bs.set(i), - (bs1, bs2) -> bs1.or(bs2) - ); - - return collected; + synchronized (receiver.getMetaKnowledge()) { + BitSet collected = receiver.getMetaKnowledge() + .keySet() + .stream() + .map(Node::getId) + .collect(() -> new BitSet(nodesCount), + (bs, i) -> bs.set(i), + (bs1, bs2) -> bs1.or(bs2) + ); + + return collected; + } } /** diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 1451f5b..31994e9 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -281,33 +281,36 @@ public void applyUpdates(LocalStore localStore) { */ public static Proof createProof(LocalStore localStore, Transaction transaction) { Node receiver = transaction.getReceiver(); - Proof proof = new Proof(transaction); - //Step 1: determine what blocks need to be sent - int blockRequired = transaction.getBlockNumber().getAsInt(); - Chain senderChain = transaction.getSender().getChain(); - - Block fromBlock = senderChain.getBlocks().get(blockRequired); - Block toBlock = getNextCommittedBlock(localStore, blockRequired, senderChain); - - //Step 2: determine the chains that need to be sent - Map chains = determineChains(transaction, fromBlock, toBlock); - - //Step 3: add only those blocks that are not yet known - Map metaKnowledge = receiver.getMetaKnowledge(); - for (Entry entry : chains.entrySet()) { - Chain chain = entry.getKey(); - Node owner = chain.getOwner(); - if (owner == receiver) continue; + synchronized (receiver.getMetaKnowledge()) { + Proof proof = new Proof(transaction); + + //Step 1: determine what blocks need to be sent + int blockRequired = transaction.getBlockNumber().getAsInt(); + Chain senderChain = transaction.getSender().getChain(); - int alreadyKnown = metaKnowledge.getOrDefault(owner, -1); - int requiredKnown = entry.getValue(); - if (alreadyKnown < requiredKnown) { - proof.addBlocksOfChain(chain, alreadyKnown + 1, requiredKnown + 1); + Block fromBlock = senderChain.getBlocks().get(blockRequired); + Block toBlock = getNextCommittedBlock(localStore, blockRequired, senderChain); + + //Step 2: determine the chains that need to be sent + Map chains = determineChains(transaction, fromBlock, toBlock); + + //Step 3: add only those blocks that are not yet known + Map metaKnowledge = receiver.getMetaKnowledge(); + for (Entry entry : chains.entrySet()) { + Chain chain = entry.getKey(); + Node owner = chain.getOwner(); + if (owner == receiver) continue; + + int alreadyKnown = metaKnowledge.getOrDefault(owner, -1); + int requiredKnown = entry.getValue(); + if (alreadyKnown < requiredKnown) { + proof.addBlocksOfChain(chain, alreadyKnown + 1, requiredKnown + 1); + } } + + return proof; } - - return proof; } /** From d40193117eb9aea44920068ff8669a87b00410b5 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Tue, 23 Jan 2018 00:17:03 +0100 Subject: [PATCH 33/69] End properly by committing empty blocks --- .../scaleoutdistributedledger/Application.java | 2 +- .../transactionpattern/ITransactionPattern.java | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java index da769eb..8cdcc99 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java @@ -132,7 +132,7 @@ public MainChain getMainChain() { */ public void finishTransactionSending() { int nodeID = localStore.getOwnNode().getId(); - transactionSender.stop(); + //transactionSender.stop(); try { transactionSender.waitUntilDone(); TrackerHelper.setRunning(nodeID, false); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java index 9611ae1..3f499d5 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java @@ -5,6 +5,7 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; import nl.tudelft.blockchain.scaleoutdistributedledger.TransactionCreator; +import nl.tudelft.blockchain.scaleoutdistributedledger.TransactionSender; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Chain; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; @@ -109,6 +110,20 @@ public default void commitBlocks(LocalStore localStore, boolean force) throws In } } } + + /** + * Commits extra empty blocks. + * @param localStore - the local store + */ + public default void commitExtraEmpty(LocalStore localStore) { + Chain ownChain = localStore.getOwnNode().getChain(); + for (int i = 0; i < TransactionSender.REQUIRED_COMMITS; i++) { + synchronized (ownChain) { + Block block = ownChain.appendNewBlock(); + block.commit(localStore); + } + } + } /** * @param localStore - the local store @@ -123,6 +138,7 @@ public default void commitBlocks(LocalStore localStore, boolean force) throws In public default void onStop(LocalStore localStore) { try { commitBlocks(localStore, true); + commitExtraEmpty(localStore); } catch (InterruptedException ex) { Log.log(Level.SEVERE, "Interrupted while committing blocks!"); } catch (Exception ex) { From 442d7b75023cda3fcdbebc69055d998f9d698322 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Tue, 23 Jan 2018 00:40:30 +0100 Subject: [PATCH 34/69] Log less --- .../blockchain/scaleoutdistributedledger/TransactionSender.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index e848527..65f6971 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -168,7 +168,7 @@ private boolean sendTransaction(Transaction transaction) throws InterruptedExcep Node to = transaction.getReceiver(); Proof proof = Proof.createProof(localStore, transaction); ProofMessage msg = new ProofMessage(proof); - Log.debug("{0}: {1}", localStore.getOwnNode().getId(), msg); +// Log.debug("{0}: {1}", localStore.getOwnNode().getId(), msg); if (socketClient.sendMessage(to, msg)) { to.updateMetaKnowledge(proof); return true; From 4c6804c04ca1b237ebe25b5127ffdcb515f367ad Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Tue, 23 Jan 2018 01:41:31 +0100 Subject: [PATCH 35/69] Fix small mistake in chainview --- .../blockchain/scaleoutdistributedledger/model/ChainView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java index b485555..dcd0fdc 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java @@ -110,7 +110,7 @@ private boolean checkIntegrity() { //At the same time, we will remove the overlapping elements int overlap = lastOwnNumber + 1 - firstUpdateNumber; int baseI = blocks.size() - overlap; - for (int i = 0; i < overlap && !updates.isEmpty(); i++) { + for (int i = 0; i < overlap && !updates.isEmpty() && startIndex < updates.size(); i++) { Block ownBlock = blocks.get(baseI + i); Block updatedBlock = updates.get(startIndex); From 14307757e59c7ae4a2a3f4da8dfe944e3e4f0dd5 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Tue, 23 Jan 2018 02:05:25 +0100 Subject: [PATCH 36/69] some demo stuff --- tracker-server/app.js | 5 +- tracker-server/package.json | 3 +- tracker-server/public/javascripts/d3.min.js | 2 + tracker-server/public/javascripts/demo.js | 55 +++++++++++++++++++ .../public/javascripts/jsnetworkx.js | 8 +++ tracker-server/public/stylesheets/demo.css | 6 ++ tracker-server/routes/index.js | 4 ++ tracker-server/views/demo.ejs | 33 +++++++++++ 8 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 tracker-server/public/javascripts/d3.min.js create mode 100644 tracker-server/public/javascripts/demo.js create mode 100644 tracker-server/public/javascripts/jsnetworkx.js create mode 100644 tracker-server/public/stylesheets/demo.css create mode 100644 tracker-server/views/demo.ejs diff --git a/tracker-server/app.js b/tracker-server/app.js index f3c575c..4db8c6f 100644 --- a/tracker-server/app.js +++ b/tracker-server/app.js @@ -6,6 +6,7 @@ import express from 'express'; import http from 'http'; import index from './routes/index'; import NodeList from './model/NodeList'; +import path from "path"; const app = express(); const debug = Debug('test:app'); @@ -15,9 +16,9 @@ app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); -app.set('views', path.join(__dirname, 'views')); +app.set('views', path.join(__dirname, '../views')); app.set('view engine', 'ejs'); -app.use(express.static(path.join(__dirname, 'public'))); +app.use(express.static(path.join(__dirname, '../public'))); const port = normalizePort(process.env.PORT || '3000'); app.set('port', port); diff --git a/tracker-server/package.json b/tracker-server/package.json index 0fca38a..48c383e 100644 --- a/tracker-server/package.json +++ b/tracker-server/package.json @@ -5,7 +5,7 @@ "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "build": "rimraf dist/ && babel ./ --out-dir dist/ --ignore ./node_modules,./.babelrc,./package.json,./npm-debug.log --copy-files", + "build": "rimraf dist/ && babel ./ --out-dir dist/ --ignore ./node_modules,./.babelrc,./package.json,./npm-debug.log,./public,./views--copy-files", "start": "npm run build && node dist/app.js" }, "author": "Bart de Jonge", @@ -22,6 +22,7 @@ "debug": "latest", "ejs": "^2.5.7", "express": "^4.16.2", + "jsnetworkx": "^0.3.4", "morgan": "^1.9.0" } } diff --git a/tracker-server/public/javascripts/d3.min.js b/tracker-server/public/javascripts/d3.min.js new file mode 100644 index 0000000..97e9581 --- /dev/null +++ b/tracker-server/public/javascripts/d3.min.js @@ -0,0 +1,2 @@ +// https://d3js.org Version 4.12.2. Copyright 2017 Mike Bostock. +(function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(t.d3=t.d3||{})})(this,function(t){"use strict";function n(t,n){return tn?1:t>=n?0:NaN}function e(t){return 1===t.length&&(t=function(t){return function(e,r){return n(t(e),r)}}(t)),{left:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r>>1;t(n[o],e)<0?r=o+1:i=o}return r},right:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r>>1;t(n[o],e)>0?i=o:r=o+1}return r}}}function r(t,n){return[t,n]}function i(t){return null===t?NaN:+t}function o(t,n){var e,r,o=t.length,u=0,a=-1,c=0,s=0;if(null==n)for(;++a1)return s/(u-1)}function u(t,n){var e=o(t,n);return e?Math.sqrt(e):e}function a(t,n){var e,r,i,o=t.length,u=-1;if(null==n){for(;++u=e)for(r=i=e;++ue&&(r=e),i=e)for(r=i=e;++ue&&(r=e),i0)return[t];if((r=n0)for(t=Math.ceil(t/u),n=Math.floor(n/u),o=new Array(i=Math.ceil(n-t+1));++a=0?(o>=Ys?10:o>=Bs?5:o>=Hs?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=Ys?10:o>=Bs?5:o>=Hs?2:1)}function p(t,n,e){var r=Math.abs(n-t)/Math.max(0,e),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),o=r/i;return o>=Ys?i*=10:o>=Bs?i*=5:o>=Hs&&(i*=2),n=1)return+e(t[r-1],r-1,t);var r,o=(r-1)*n,u=Math.floor(o),a=+e(t[u],u,t);return a+(+e(t[u+1],u+1,t)-a)*(o-u)}}function g(t){for(var n,e,r,i=t.length,o=-1,u=0;++o=0;)for(n=(r=t[i]).length;--n>=0;)e[--u]=r[n];return e}function _(t,n){var e,r,i=t.length,o=-1;if(null==n){for(;++o=e)for(r=e;++oe&&(r=e)}else for(;++o=e)for(r=e;++oe&&(r=e);return r}function y(t){if(!(i=t.length))return[];for(var n=-1,e=_(t,m),r=new Array(e);++n=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),Js.hasOwnProperty(n)?{space:Js[n],local:t}:t}function A(t){var n=E(t);return(n.local?function(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}:function(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===Qs&&n.documentElement.namespaceURI===Qs?n.createElement(t):n.createElementNS(e,t)}})(n)}function C(){return new z}function z(){this._="@"+(++Ks).toString(36)}function P(t,n,e){return t=R(t,n,e),function(n){var e=n.relatedTarget;e&&(e===this||8&e.compareDocumentPosition(this))||t.call(this,n)}}function R(n,e,r){return function(i){var o=t.event;t.event=i;try{n.call(this,this.__data__,e,r)}finally{t.event=o}}}function L(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;rn?1:t>=n?0:NaN}function Z(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function G(t,n){return t.style.getPropertyValue(n)||Z(t).getComputedStyle(t,null).getPropertyValue(n)}function Q(t){return t.trim().split(/^|\s+/)}function J(t){return t.classList||new K(t)}function K(t){this._node=t,this._names=Q(t.getAttribute("class")||"")}function tt(t,n){for(var e=J(t),r=-1,i=n.length;++r>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1)):(n=hf.exec(t))?St(parseInt(n[1],16)):(n=pf.exec(t))?new zt(n[1],n[2],n[3],1):(n=df.exec(t))?new zt(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=vf.exec(t))?Et(n[1],n[2],n[3],n[4]):(n=gf.exec(t))?Et(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=_f.exec(t))?Pt(n[1],n[2]/100,n[3]/100,1):(n=yf.exec(t))?Pt(n[1],n[2]/100,n[3]/100,n[4]):mf.hasOwnProperty(t)?St(mf[t]):"transparent"===t?new zt(NaN,NaN,NaN,0):null}function St(t){return new zt(t>>16&255,t>>8&255,255&t,1)}function Et(t,n,e,r){return r<=0&&(t=n=e=NaN),new zt(t,n,e,r)}function At(t){return t instanceof Nt||(t=kt(t)),t?(t=t.rgb(),new zt(t.r,t.g,t.b,t.opacity)):new zt}function Ct(t,n,e,r){return 1===arguments.length?At(t):new zt(t,n,e,null==r?1:r)}function zt(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function Pt(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Lt(t,n,e,r)}function Rt(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Lt)return new Lt(t.h,t.s,t.l,t.opacity);if(t instanceof Nt||(t=kt(t)),!t)return new Lt;if(t instanceof Lt)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),u=NaN,a=o-i,c=(o+i)/2;return a?(u=n===o?(e-r)/a+6*(e0&&c<1?0:u,new Lt(u,a,c,t.opacity)}(t):new Lt(t,n,e,null==r?1:r)}function Lt(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function qt(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}function Dt(t){if(t instanceof Ot)return new Ot(t.l,t.a,t.b,t.opacity);if(t instanceof jt){var n=t.h*xf;return new Ot(t.l,Math.cos(n)*t.c,Math.sin(n)*t.c,t.opacity)}t instanceof zt||(t=At(t));var e=Bt(t.r),r=Bt(t.g),i=Bt(t.b),o=Ft((.4124564*e+.3575761*r+.1804375*i)/wf),u=Ft((.2126729*e+.7151522*r+.072175*i)/Mf);return new Ot(116*u-16,500*(o-u),200*(u-Ft((.0193339*e+.119192*r+.9503041*i)/Tf)),t.opacity)}function Ut(t,n,e,r){return 1===arguments.length?Dt(t):new Ot(t,n,e,null==r?1:r)}function Ot(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function Ft(t){return t>Ef?Math.pow(t,1/3):t/Sf+Nf}function It(t){return t>kf?t*t*t:Sf*(t-Nf)}function Yt(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Bt(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Ht(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof jt)return new jt(t.h,t.c,t.l,t.opacity);t instanceof Ot||(t=Dt(t));var n=Math.atan2(t.b,t.a)*bf;return new jt(n<0?n+360:n,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}(t):new jt(t,n,e,null==r?1:r)}function jt(t,n,e,r){this.h=+t,this.c=+n,this.l=+e,this.opacity=+r}function Xt(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Vt)return new Vt(t.h,t.s,t.l,t.opacity);t instanceof zt||(t=At(t));var n=t.r/255,e=t.g/255,r=t.b/255,i=(Lf*r+Pf*n-Rf*e)/(Lf+Pf-Rf),o=r-i,u=(zf*(e-i)-Af*o)/Cf,a=Math.sqrt(u*u+o*o)/(zf*i*(1-i)),c=a?Math.atan2(u,o)*bf-120:NaN;return new Vt(c<0?c+360:c,a,i,t.opacity)}(t):new Vt(t,n,e,null==r?1:r)}function Vt(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function $t(t,n,e,r,i){var o=t*t,u=o*t;return((1-3*t+3*o-u)*n+(4-6*o+3*u)*e+(1+3*t+3*o-3*u)*r+u*i)/6}function Wt(t){var n=t.length-1;return function(e){var r=e<=0?e=0:e>=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],u=r>0?t[r-1]:2*i-o,a=r180||e<-180?e-360*Math.round(e/360):e):Gt(isNaN(t)?n:t)}function Kt(t){return 1==(t=+t)?tn:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):Gt(isNaN(n)?e:n)}}function tn(t,n){var e=n-t;return e?Qt(t,e):Gt(isNaN(t)?n:t)}function nn(t){return function(n){var e,r,i=n.length,o=new Array(i),u=new Array(i),a=new Array(i);for(e=0;eo&&(i=n.slice(o,i),a[u]?a[u]+=i:a[++u]=i),(e=e[0])===(r=r[0])?a[u]?a[u]+=r:a[++u]=r:(a[++u]=null,c.push({i:u,x:on(e,r)})),o=Xf.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:on(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,u.rotate,a,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:on(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,u.skewX,a,c),function(t,n,e,r,o,u){if(t!==e||n!==r){var a=o.push(i(o)+"scale(",null,",",null,")");u.push({i:a-4,x:on(t,e)},{i:a-2,x:on(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,u.scaleX,u.scaleY,a,c),o=u=null,function(t){for(var n,e=-1,r=c.length;++e=0&&n._call.call(null,t),n=n._next;--ul}function wn(){ll=(fl=pl.now())+hl,ul=al=0;try{bn()}finally{ul=0,function(){var t,n,e=Ff,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:Ff=n);If=t,Tn(r)}(),ll=0}}function Mn(){var t=pl.now(),n=t-fl;n>sl&&(hl-=n,fl=t)}function Tn(t){if(!ul){al&&(al=clearTimeout(al));t-ll>24?(t<1/0&&(al=setTimeout(wn,t-pl.now()-hl)),cl&&(cl=clearInterval(cl))):(cl||(fl=pl.now(),cl=setInterval(Mn,sl)),ul=1,dl(wn))}}function Nn(t,n,e){var r=new mn;return n=null==n?0:+n,r.restart(function(e){r.stop(),t(e+n)},n,e),r}function kn(t,n,e,r,i,o){var u=t.__transition;if(u){if(e in u)return}else t.__transition={};(function(t,n,e){function r(c){var s,f,l,h;if(e.state!==yl)return o();for(s in a)if((h=a[s]).name===e.name){if(h.state===xl)return Nn(r);h.state===bl?(h.state=Ml,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete a[s]):+s_l)throw new Error("too late; already scheduled");return e}function En(t,n){var e=An(t,n);if(e.state>ml)throw new Error("too late; already started");return e}function An(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function Cn(t,n){var e,r,i,o=t.__transition,u=!0;if(o){n=null==n?null:n+"";for(i in o)(e=o[i]).name===n?(r=e.state>ml&&e.stateMath.abs(t[1]-D[1])?b=!0:x=!0),D=t,m=!0,Xn(),o()}function o(){var t;switch(_=D[0]-q[0],y=D[1]-q[1],T){case Jl:case Ql:N&&(_=Math.max(C-a,Math.min(P-p,_)),s=a+_,d=p+_),k&&(y=Math.max(z-l,Math.min(R-v,y)),h=l+y,g=v+y);break;case Kl:N<0?(_=Math.max(C-a,Math.min(P-a,_)),s=a+_,d=p):N>0&&(_=Math.max(C-p,Math.min(P-p,_)),s=a,d=p+_),k<0?(y=Math.max(z-l,Math.min(R-l,y)),h=l+y,g=v):k>0&&(y=Math.max(z-v,Math.min(R-v,y)),h=l,g=v+y);break;case th:N&&(s=Math.max(C,Math.min(P,a-_*N)),d=Math.max(C,Math.min(P,p+_*N))),k&&(h=Math.max(z,Math.min(R,l-y*k)),g=Math.max(z,Math.min(R,v+y*k)))}d0&&(a=s-_),k<0?v=g-y:k>0&&(l=h-y),T=Jl,I.attr("cursor",ih.selection),o());break;default:return}Xn()},!0).on("keyup.brush",function(){switch(t.event.keyCode){case 16:L&&(x=b=L=!1,o());break;case 18:T===th&&(N<0?p=d:N>0&&(a=s),k<0?v=g:k>0&&(l=h),T=Kl,o());break;case 32:T===Jl&&(t.event.altKey?(N&&(p=d-_*N,a=s+_*N),k&&(v=g-y*k,l=h+y*k),T=th):(N<0?p=d:N>0&&(a=s),k<0?v=g:k>0&&(l=h),T=Kl),I.attr("cursor",ih[M]),o());break;default:return}Xn()},!0).on("mousemove.brush",e,!0).on("mouseup.brush",u,!0);vt(t.event.view)}jn(),Cn(w),r.call(w),U.start()}}function a(){var t=this.__brush||{selection:null};return t.extent=s.apply(this,arguments),t.dim=n,t}var c,s=Wn,f=$n,l=N(e,"start","brush","end"),h=6;return e.move=function(t,e){t.selection?t.on("start.brush",function(){i(this,arguments).beforestart().start()}).on("interrupt.brush end.brush",function(){i(this,arguments).end()}).tween("brush",function(){function t(t){u.selection=1===t&&Gn(s)?null:f(t),r.call(o),a.brush()}var o=this,u=o.__brush,a=i(o,arguments),c=u.selection,s=n.input("function"==typeof e?e.apply(this,arguments):e,u.extent),f=cn(c,s);return c&&s?t:t(1)}):t.each(function(){var t=arguments,o=this.__brush,u=n.input("function"==typeof e?e.apply(this,t):e,o.extent),a=i(this,t).beforestart();Cn(this),o.selection=null==u||Gn(u)?null:u,r.call(this),a.start().brush().end()})},o.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(){return this.starting&&(this.starting=!1,this.emit("start")),this},brush:function(){return this.emit("brush"),this},end:function(){return 0==--this.active&&(delete this.state.emitter,this.emit("end")),this},emit:function(t){D(new function(t,n,e){this.target=t,this.type=n,this.selection=e}(e,t,n.output(this.state.selection)),l.apply,l,[t,this.that,this.args])}},e.extent=function(t){return arguments.length?(s="function"==typeof t?t:Hn([[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]]),e):s},e.filter=function(t){return arguments.length?(f="function"==typeof t?t:Hn(!!t),e):f},e.handleSize=function(t){return arguments.length?(h=+t,e):h},e.on=function(){var t=l.on.apply(l,arguments);return t===l?e:t},e}function Jn(t){return function(){return t}}function Kn(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function te(){return new Kn}function ne(t){return t.source}function ee(t){return t.target}function re(t){return t.radius}function ie(t){return t.startAngle}function oe(t){return t.endAngle}function ue(){}function ae(t,n){var e=new ue;if(t instanceof ue)t.each(function(t,n){e.set(n,t)});else if(Array.isArray(t)){var r,i=-1,o=t.length;if(null==n)for(;++i=u?s=!0:(e=t.charCodeAt(a++))===Mh?f=!0:e===Th&&(f=!0,t.charCodeAt(a)===Mh&&++a),t.slice(r+1,n-1).replace(/""/g,'"')}for(;a=(o=(v+_)/2))?v=o:_=o,(f=e>=(u=(g+y)/2))?g=u:y=u,i=p,!(p=p[l=f<<1|s]))return i[l]=d,t;if(a=+t._x.call(null,p.data),c=+t._y.call(null,p.data),n===a&&e===c)return d.next=p,i?i[l]=d:t._root=d,t;do{i=i?i[l]=new Array(4):t._root=new Array(4),(s=n>=(o=(v+_)/2))?v=o:_=o,(f=e>=(u=(g+y)/2))?g=u:y=u}while((l=f<<1|s)==(h=(c>=u)<<1|a>=o));return i[h]=p,i[l]=d,t}function me(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function xe(t){return t[0]}function be(t){return t[1]}function we(t,n,e){var r=new Me(null==n?xe:n,null==e?be:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function Me(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function Te(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}function Ne(t){return t.x+t.vx}function ke(t){return t.y+t.vy}function Se(t){return t.index}function Ee(t,n){var e=t.get(n);if(!e)throw new Error("missing: "+n);return e}function Ae(t){return t.x}function Ce(t){return t.y}function ze(t,n){if((e=(t=n?t.toExponential(n-1):t.toExponential()).indexOf("e"))<0)return null;var e,r=t.slice(0,e);return[r.length>1?r[0]+r.slice(2):r,+t.slice(e+1)]}function Pe(t){return(t=ze(Math.abs(t)))?t[1]:NaN}function Re(t,n){var e=ze(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}function Le(t){return new qe(t)}function qe(t){if(!(n=Ih.exec(t)))throw new Error("invalid format: "+t);var n,e=n[1]||" ",r=n[2]||">",i=n[3]||"-",o=n[4]||"",u=!!n[5],a=n[6]&&+n[6],c=!!n[7],s=n[8]&&+n[8].slice(1),f=n[9]||"";"n"===f?(c=!0,f="g"):Fh[f]||(f=""),(u||"0"===e&&"="===r)&&(u=!0,e="0",r="="),this.fill=e,this.align=r,this.sign=i,this.symbol=o,this.zero=u,this.width=a,this.comma=c,this.precision=s,this.type=f}function De(t){return t}function Ue(t){function n(t){function n(t){var n,r,u,f=g,x=_;if("c"===v)x=y(t)+x,t="";else{var b=(t=+t)<0;if(t=y(Math.abs(t),d),b&&0==+t&&(b=!1),f=(b?"("===s?s:"-":"-"===s||"("===s?"":s)+f,x=x+("s"===v?Bh[8+Dh/3]:"")+(b&&"("===s?")":""),m)for(n=-1,r=t.length;++n(u=t.charCodeAt(n))||u>57){x=(46===u?i+t.slice(n+1):t.slice(n))+x,t=t.slice(0,n);break}}p&&!l&&(t=e(t,1/0));var w=f.length+t.length+x.length,M=w>1)+f+t+x+M.slice(w);break;default:t=M+f+t+x}return o(t)}var a=(t=Le(t)).fill,c=t.align,s=t.sign,f=t.symbol,l=t.zero,h=t.width,p=t.comma,d=t.precision,v=t.type,g="$"===f?r[0]:"#"===f&&/[boxX]/.test(v)?"0"+v.toLowerCase():"",_="$"===f?r[1]:/[%p]/.test(v)?u:"",y=Fh[v],m=!v||/[defgprs%]/.test(v);return d=null==d?v?6:12:/[gprs]/.test(v)?Math.max(1,Math.min(21,d)):Math.max(0,Math.min(20,d)),n.toString=function(){return t+""},n}var e=t.grouping&&t.thousands?function(t,n){return function(e,r){for(var i=e.length,o=[],u=0,a=t[0],c=0;i>0&&a>0&&(c+a+1>r&&(a=Math.max(1,r-c)),o.push(e.substring(i-=a,i+a)),!((c+=a+1)>r));)a=t[u=(u+1)%t.length];return o.reverse().join(n)}}(t.grouping,t.thousands):De,r=t.currency,i=t.decimal,o=t.numerals?function(t){return function(n){return n.replace(/[0-9]/g,function(n){return t[+n]})}}(t.numerals):De,u=t.percent||"%";return{format:n,formatPrefix:function(t,e){var r=n((t=Le(t),t.type="f",t)),i=3*Math.max(-8,Math.min(8,Math.floor(Pe(e)/3))),o=Math.pow(10,-i),u=Bh[8+i/3];return function(t){return r(o*t)+u}}}}function Oe(n){return Yh=Ue(n),t.format=Yh.format,t.formatPrefix=Yh.formatPrefix,Yh}function Fe(t){return Math.max(0,-Pe(Math.abs(t)))}function Ie(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(Pe(n)/3)))-Pe(Math.abs(t)))}function Ye(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,Pe(n)-Pe(t))+1}function Be(){return new He}function He(){this.reset()}function je(t,n,e){var r=t.s=n+e,i=r-n,o=r-i;t.t=n-o+(e-i)}function Xe(t){return t>1?0:t<-1?Mp:Math.acos(t)}function Ve(t){return t>1?Tp:t<-1?-Tp:Math.asin(t)}function $e(t){return(t=Up(t/2))*t}function We(){}function Ze(t,n){t&&Bp.hasOwnProperty(t.type)&&Bp[t.type](t,n)}function Ge(t,n,e){var r,i=-1,o=t.length-e;for(n.lineStart();++i=0?1:-1,i=r*e,o=Pp(n),u=Up(n),a=$h*u,c=Vh*o+a*Pp(i),s=a*r*Up(i);Hp.add(zp(s,c)),Xh=t,Vh=o,$h=u}function rr(t){return[zp(t[1],t[0]),Ve(t[2])]}function ir(t){var n=t[0],e=t[1],r=Pp(e);return[r*Pp(n),r*Up(n),Up(e)]}function or(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function ur(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function ar(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function cr(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function sr(t){var n=Fp(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}function fr(t,n){ep.push(rp=[Wh=t,Gh=t]),nQh&&(Qh=n)}function lr(t,n){var e=ir([t*Ep,n*Ep]);if(np){var r=ur(np,e),i=ur([r[1],-r[0],0],r);sr(i),i=rr(i);var o,u=t-Jh,a=u>0?1:-1,c=i[0]*Sp*a,s=Ap(u)>180;s^(a*JhQh&&(Qh=o):(c=(c+360)%360-180,s^(a*JhQh&&(Qh=n))),s?t_r(Wh,Gh)&&(Gh=t):_r(t,Gh)>_r(Wh,Gh)&&(Wh=t):Gh>=Wh?(tGh&&(Gh=t)):t>Jh?_r(Wh,t)>_r(Wh,Gh)&&(Gh=t):_r(t,Gh)>_r(Wh,Gh)&&(Wh=t)}else ep.push(rp=[Wh=t,Gh=t]);nQh&&(Qh=n),np=e,Jh=t}function hr(){$p.point=lr}function pr(){rp[0]=Wh,rp[1]=Gh,$p.point=fr,np=null}function dr(t,n){if(np){var e=t-Jh;Vp.add(Ap(e)>180?e+(e>0?360:-360):e)}else Kh=t,tp=n;Xp.point(t,n),lr(t,n)}function vr(){Xp.lineStart()}function gr(){dr(Kh,tp),Xp.lineEnd(),Ap(Vp)>bp&&(Wh=-(Gh=180)),rp[0]=Wh,rp[1]=Gh,np=null}function _r(t,n){return(n-=t)<0?n+360:n}function yr(t,n){return t[0]-n[0]}function mr(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nMp?t-kp:t<-Mp?t+kp:t,n]}function Rr(t,n,e){return(t%=kp)?n||e?zr(qr(t),Dr(n,e)):qr(t):n||e?Dr(n,e):Pr}function Lr(t){return function(n,e){return n+=t,[n>Mp?n-kp:n<-Mp?n+kp:n,e]}}function qr(t){var n=Lr(t);return n.invert=Lr(-t),n}function Dr(t,n){function e(t,n){var e=Pp(n),a=Pp(t)*e,c=Up(t)*e,s=Up(n),f=s*r+a*i;return[zp(c*o-f*u,a*r-s*i),Ve(f*o+c*u)]}var r=Pp(t),i=Up(t),o=Pp(n),u=Up(n);return e.invert=function(t,n){var e=Pp(n),a=Pp(t)*e,c=Up(t)*e,s=Up(n),f=s*o-c*u;return[zp(c*o+s*u,a*r+f*i),Ve(f*r-a*i)]},e}function Ur(t){function n(n){return n=t(n[0]*Ep,n[1]*Ep),n[0]*=Sp,n[1]*=Sp,n}return t=Rr(t[0]*Ep,t[1]*Ep,t.length>2?t[2]*Ep:0),n.invert=function(n){return n=t.invert(n[0]*Ep,n[1]*Ep),n[0]*=Sp,n[1]*=Sp,n},n}function Or(t,n,e,r,i,o){if(e){var u=Pp(n),a=Up(n),c=r*e;null==i?(i=n+r*kp,o=n-c/2):(i=Fr(u,i),o=Fr(u,o),(r>0?io)&&(i+=r*kp));for(var s,f=i;r>0?f>o:f1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function Yr(t,n){return Ap(t[0]-n[0])=0;--o)i.point((f=s[o])[0],f[1]);else r(h.x,h.p.x,-1,i);h=h.p}s=(h=h.o).z,p=!p}while(!h.v);i.lineEnd()}}}function jr(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r=0?1:-1,T=M*w,N=T>Mp,k=d*x;if(ud.add(zp(k*M*Up(T),v*b+k*Pp(T))),o+=N?w+M*kp:w,N^h>=e^y>=e){var S=ur(ir(l),ir(_));sr(S);var E=ur(i,S);sr(E);var A=(N^w>=0?-1:1)*Ve(E[2]);(r>A||r===A&&(S[0]||S[1]))&&(u+=N^w>=0?1:-1)}}return(o<-bp||o0){for(m||(i.polygonStart(),m=!0),i.lineStart(),t=0;t1&&2&o&&u.push(u.pop().concat(u.shift())),p.push(u.filter($r))}var h,p,d,v=n(i),_=Ir(),y=n(_),m=!1,x={point:o,lineStart:a,lineEnd:c,polygonStart:function(){x.point=s,x.lineStart=f,x.lineEnd=l,p=[],h=[]},polygonEnd:function(){x.point=o,x.lineStart=a,x.lineEnd=c,p=g(p);var t=Xr(h,r);p.length?(m||(i.polygonStart(),m=!0),Hr(p,Wr,t,e,i)):t&&(m||(i.polygonStart(),m=!0),i.lineStart(),e(null,null,1,i),i.lineEnd()),m&&(i.polygonEnd(),m=!1),p=h=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}};return x}}function $r(t){return t.length>1}function Wr(t,n){return((t=t.x)[0]<0?t[1]-Tp-bp:Tp-t[1])-((n=n.x)[0]<0?n[1]-Tp-bp:Tp-n[1])}function Zr(t){function n(t,n){return Pp(t)*Pp(n)>i}function e(t,n,e){var r=[1,0,0],o=ur(ir(t),ir(n)),u=or(o,o),a=o[0],c=u-a*a;if(!c)return!e&&t;var s=i*u/c,f=-i*a/c,l=ur(r,o),h=cr(r,s);ar(h,cr(o,f));var p=l,d=or(h,p),v=or(p,p),g=d*d-v*(or(h,h)-1);if(!(g<0)){var _=Fp(g),y=cr(p,(-d-_)/v);if(ar(y,h),y=rr(y),!e)return y;var m,x=t[0],b=n[0],w=t[1],M=n[1];b0^y[1]<(Ap(y[0]-x)Mp^(x<=y[0]&&y[0]<=b)){var k=cr(p,(-d+_)/v);return ar(k,h),[y,rr(k)]}}}function r(n,e){var r=u?t:Mp-t,i=0;return n<-r?i|=1:n>r&&(i|=2),e<-r?i|=4:e>r&&(i|=8),i}var i=Pp(t),o=6*Ep,u=i>0,a=Ap(i)>bp;return Vr(n,function(t){var i,o,c,s,f;return{lineStart:function(){s=c=!1,f=1},point:function(l,h){var p,d=[l,h],v=n(l,h),g=u?v?0:r(l,h):v?r(l+(l<0?Mp:-Mp),h):0;if(!i&&(s=c=v)&&t.lineStart(),v!==c&&(!(p=e(i,d))||Yr(i,p)||Yr(d,p))&&(d[0]+=bp,d[1]+=bp,v=n(d[0],d[1])),v!==c)f=0,v?(t.lineStart(),p=e(d,i),t.point(p[0],p[1])):(p=e(i,d),t.point(p[0],p[1]),t.lineEnd()),i=p;else if(a&&i&&u^v){var _;g&o||!(_=e(d,i,!0))||(f=0,u?(t.lineStart(),t.point(_[0][0],_[0][1]),t.point(_[1][0],_[1][1]),t.lineEnd()):(t.point(_[1][0],_[1][1]),t.lineEnd(),t.lineStart(),t.point(_[0][0],_[0][1])))}!v||i&&Yr(i,d)||t.point(d[0],d[1]),i=d,c=v,o=g},lineEnd:function(){c&&t.lineEnd(),i=null},clean:function(){return f|(s&&c)<<1}}},function(n,e,r,i){Or(i,t,o,r,n,e)},u?[0,-t]:[-Mp,t-Mp])}function Gr(t,n,e,r){function i(i,o){return t<=i&&i<=e&&n<=o&&o<=r}function o(i,o,a,s){var f=0,l=0;if(null==i||(f=u(i,a))!==(l=u(o,a))||c(i,o)<0^a>0)do{s.point(0===f||3===f?t:e,f>1?r:n)}while((f=(f+a+4)%4)!==l);else s.point(o[0],o[1])}function u(r,i){return Ap(r[0]-t)0?0:3:Ap(r[0]-e)0?2:1:Ap(r[1]-n)0?1:0:i>0?3:2}function a(t,n){return c(t.x,n.x)}function c(t,n){var e=u(t,1),r=u(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(u){function c(t,n){i(t,n)&&w.point(t,n)}function s(o,u){var a=i(o,u);if(l&&h.push([o,u]),x)p=o,d=u,v=a,x=!1,a&&(w.lineStart(),w.point(o,u));else if(a&&m)w.point(o,u);else{var c=[_=Math.max(sd,Math.min(cd,_)),y=Math.max(sd,Math.min(cd,y))],s=[o=Math.max(sd,Math.min(cd,o)),u=Math.max(sd,Math.min(cd,u))];!function(t,n,e,r,i,o){var u,a=t[0],c=t[1],s=0,f=1,l=n[0]-a,h=n[1]-c;if(u=e-a,l||!(u>0)){if(u/=l,l<0){if(u0){if(u>f)return;u>s&&(s=u)}if(u=i-a,l||!(u<0)){if(u/=l,l<0){if(u>f)return;u>s&&(s=u)}else if(l>0){if(u0)){if(u/=h,h<0){if(u0){if(u>f)return;u>s&&(s=u)}if(u=o-c,h||!(u<0)){if(u/=h,h<0){if(u>f)return;u>s&&(s=u)}else if(h>0){if(u0&&(t[0]=a+s*l,t[1]=c+s*h),f<1&&(n[0]=a+f*l,n[1]=c+f*h),!0}}}}}(c,s,t,n,e,r)?a&&(w.lineStart(),w.point(o,u),b=!1):(m||(w.lineStart(),w.point(c[0],c[1])),w.point(s[0],s[1]),a||w.lineEnd(),b=!1)}_=o,y=u,m=a}var f,l,h,p,d,v,_,y,m,x,b,w=u,M=Ir(),T={point:c,lineStart:function(){T.point=s,l&&l.push(h=[]),x=!0,m=!1,_=y=NaN},lineEnd:function(){f&&(s(p,d),v&&m&&M.rejoin(),f.push(M.result())),T.point=c,m&&w.lineEnd()},polygonStart:function(){w=M,f=[],l=[],b=!0},polygonEnd:function(){var n=function(){for(var n=0,e=0,i=l.length;er&&(h-o)*(r-u)>(p-u)*(t-o)&&++n:p<=r&&(h-o)*(r-u)<(p-u)*(t-o)&&--n;return n}(),e=b&&n,i=(f=g(f)).length;(e||i)&&(u.polygonStart(),e&&(u.lineStart(),o(null,null,1,u),u.lineEnd()),i&&Hr(f,a,n,o,u),u.polygonEnd()),w=u,f=l=h=null}};return T}}function Qr(){ld.point=ld.lineEnd=We}function Jr(t,n){Zp=t*=Ep,Gp=Up(n*=Ep),Qp=Pp(n),ld.point=Kr}function Kr(t,n){t*=Ep;var e=Up(n*=Ep),r=Pp(n),i=Ap(t-Zp),o=Pp(i),u=r*Up(i),a=Qp*e-Gp*r*o,c=Gp*e+Qp*r*o;fd.add(zp(Fp(u*u+a*a),c)),Zp=t,Gp=e,Qp=r}function ti(t){return fd.reset(),Je(t,ld),+fd}function ni(t,n){return hd[0]=t,hd[1]=n,ti(pd)}function ei(t,n){return!(!t||!vd.hasOwnProperty(t.type))&&vd[t.type](t,n)}function ri(t,n){return 0===ni(t,n)}function ii(t,n){var e=ni(t[0],t[1]);return ni(t[0],n)+ni(n,t[1])<=e+bp}function oi(t,n){return!!Xr(t.map(ui),ai(n))}function ui(t){return(t=t.map(ai)).pop(),t}function ai(t){return[t[0]*Ep,t[1]*Ep]}function ci(t,n,e){var r=f(t,n-bp,e).concat(n);return function(t){return r.map(function(n){return[t,n]})}}function si(t,n,e){var r=f(t,n-bp,e).concat(n);return function(t){return r.map(function(n){return[n,t]})}}function fi(){function t(){return{type:"MultiLineString",coordinates:n()}}function n(){return f(Rp(o/_)*_,i,_).map(p).concat(f(Rp(s/y)*y,c,y).map(d)).concat(f(Rp(r/v)*v,e,v).filter(function(t){return Ap(t%_)>bp}).map(l)).concat(f(Rp(a/g)*g,u,g).filter(function(t){return Ap(t%y)>bp}).map(h))}var e,r,i,o,u,a,c,s,l,h,p,d,v=10,g=v,_=90,y=360,m=2.5;return t.lines=function(){return n().map(function(t){return{type:"LineString",coordinates:t}})},t.outline=function(){return{type:"Polygon",coordinates:[p(o).concat(d(c).slice(1),p(i).reverse().slice(1),d(s).reverse().slice(1))]}},t.extent=function(n){return arguments.length?t.extentMajor(n).extentMinor(n):t.extentMinor()},t.extentMajor=function(n){return arguments.length?(o=+n[0][0],i=+n[1][0],s=+n[0][1],c=+n[1][1],o>i&&(n=o,o=i,i=n),s>c&&(n=s,s=c,c=n),t.precision(m)):[[o,s],[i,c]]},t.extentMinor=function(n){return arguments.length?(r=+n[0][0],e=+n[1][0],a=+n[0][1],u=+n[1][1],r>e&&(n=r,r=e,e=n),a>u&&(n=a,a=u,u=n),t.precision(m)):[[r,a],[e,u]]},t.step=function(n){return arguments.length?t.stepMajor(n).stepMinor(n):t.stepMinor()},t.stepMajor=function(n){return arguments.length?(_=+n[0],y=+n[1],t):[_,y]},t.stepMinor=function(n){return arguments.length?(v=+n[0],g=+n[1],t):[v,g]},t.precision=function(n){return arguments.length?(m=+n,l=ci(a,u,90),h=si(r,e,m),p=ci(s,c,90),d=si(o,i,m),t):m},t.extentMajor([[-180,-90+bp],[180,90-bp]]).extentMinor([[-180,-80-bp],[180,80+bp]])}function li(t){return t}function hi(){yd.point=pi}function pi(t,n){yd.point=di,Jp=td=t,Kp=nd=n}function di(t,n){_d.add(nd*t-td*n),td=t,nd=n}function vi(){di(Jp,Kp)}function gi(t,n){Td+=t,Nd+=n,++kd}function _i(){Rd.point=yi}function yi(t,n){Rd.point=mi,gi(id=t,od=n)}function mi(t,n){var e=t-id,r=n-od,i=Fp(e*e+r*r);Sd+=i*(id+t)/2,Ed+=i*(od+n)/2,Ad+=i,gi(id=t,od=n)}function xi(){Rd.point=gi}function bi(){Rd.point=Mi}function wi(){Ti(ed,rd)}function Mi(t,n){Rd.point=Ti,gi(ed=id=t,rd=od=n)}function Ti(t,n){var e=t-id,r=n-od,i=Fp(e*e+r*r);Sd+=i*(id+t)/2,Ed+=i*(od+n)/2,Ad+=i,Cd+=(i=od*t-id*n)*(id+t),zd+=i*(od+n),Pd+=3*i,gi(id=t,od=n)}function Ni(t){this._context=t}function ki(t,n){Id.point=Si,qd=Ud=t,Dd=Od=n}function Si(t,n){Ud-=t,Od-=n,Fd.add(Fp(Ud*Ud+Od*Od)),Ud=t,Od=n}function Ei(){this._string=[]}function Ai(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function Ci(t){return function(n){var e=new zi;for(var r in t)e[r]=t[r];return e.stream=n,e}}function zi(){}function Pi(t,n,e){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),Je(e,t.stream(Md)),n(Md.result()),null!=r&&t.clipExtent(r),t}function Ri(t,n,e){return Pi(t,function(e){var r=n[1][0]-n[0][0],i=n[1][1]-n[0][1],o=Math.min(r/(e[1][0]-e[0][0]),i/(e[1][1]-e[0][1])),u=+n[0][0]+(r-o*(e[1][0]+e[0][0]))/2,a=+n[0][1]+(i-o*(e[1][1]+e[0][1]))/2;t.scale(150*o).translate([u,a])},e)}function Li(t,n,e){return Ri(t,[[0,0],n],e)}function qi(t,n,e){return Pi(t,function(e){var r=+n,i=r/(e[1][0]-e[0][0]),o=(r-i*(e[1][0]+e[0][0]))/2,u=-i*e[0][1];t.scale(150*i).translate([o,u])},e)}function Di(t,n,e){return Pi(t,function(e){var r=+n,i=r/(e[1][1]-e[0][1]),o=-i*e[0][0],u=(r-i*(e[1][1]+e[0][1]))/2;t.scale(150*i).translate([o,u])},e)}function Ui(t,n){return+n?function(t,n){function e(r,i,o,u,a,c,s,f,l,h,p,d,v,g){var _=s-r,y=f-i,m=_*_+y*y;if(m>4*n&&v--){var x=u+h,b=a+p,w=c+d,M=Fp(x*x+b*b+w*w),T=Ve(w/=M),N=Ap(Ap(w)-1)n||Ap((_*A+y*C)/m-.5)>.3||u*h+a*p+c*d2?t[2]%360*Ep:0,r()):[x*Sp,b*Sp,w*Sp]},n.precision=function(t){return arguments.length?(E=Ui(e,S=t*t),i()):Fp(S)},n.fitExtent=function(t,e){return Ri(n,t,e)},n.fitSize=function(t,e){return Li(n,t,e)},n.fitWidth=function(t,e){return qi(n,t,e)},n.fitHeight=function(t,e){return Di(n,t,e)},function(){return o=t.apply(this,arguments),n.invert=o.invert&&function(t){return(t=s.invert((t[0]-u)/v,(a-t[1])/v))&&[t[0]*Sp,t[1]*Sp]},r()}}function Ii(t){var n=0,e=Mp/3,r=Fi(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*Ep,e=t[1]*Ep):[n*Sp,e*Sp]},i}function Yi(t,n){function e(t,n){var e=Fp(o-2*i*Up(n))/i;return[e*Up(t*=i),u-e*Pp(t)]}var r=Up(t),i=(r+Up(n))/2;if(Ap(i)0?n<-Tp+bp&&(n=-Tp+bp):n>Tp-bp&&(n=Tp-bp);var e=o/Dp(Wi(n),i);return[e*Up(i*t),o-e*Pp(i*t)]}var r=Pp(t),i=t===n?Up(t):qp(r/Pp(n))/qp(Wi(n)/Wi(t)),o=r*Dp(Wi(t),i)/i;return i?(e.invert=function(t,n){var e=o-n,r=Op(i)*Fp(t*t+e*e);return[zp(t,Ap(e))/i*Op(e),2*Cp(Dp(o/r,1/i))-Tp]},e):Vi}function Gi(t,n){return[t,n]}function Qi(t,n){function e(t,n){var e=o-n,r=i*t;return[e*Up(r),o-e*Pp(r)]}var r=Pp(t),i=t===n?Up(t):(r-Pp(n))/(n-t),o=r/i+t;return Ap(i)=0;)n+=e[r].value;else n=1;t.value=n}function co(t,n){var e,r,i,o,u,a=new ho(t),c=+t.value&&(a.value=t.value),s=[a];for(null==n&&(n=so);e=s.pop();)if(c&&(e.value=+e.data.value),(i=n(e.data))&&(u=i.length))for(e.children=new Array(u),o=u-1;o>=0;--o)s.push(r=e.children[o]=new ho(i[o])),r.parent=e,r.depth=e.depth+1;return a.eachBefore(lo)}function so(t){return t.children}function fo(t){t.data=t.data.data}function lo(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function ho(t){this.data=t,this.depth=this.height=0,this.parent=null}function po(t){for(var n,e,r=0,i=(t=function(t){for(var n,e,r=t.length;r;)e=Math.random()*r--|0,n=t[r],t[r]=t[e],t[e]=n;return t}(Vd.call(t))).length,o=[];r0&&e*e>r*r+i*i}function _o(t,n){for(var e=0;ee*e+r*r}function wo(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function Mo(t){this._=t,this.next=null,this.previous=null}function To(t){if(!(i=t.length))return 0;var n,e,r,i,o,u,a,c,s,f,l;if(n=t[0],n.x=0,n.y=0,!(i>1))return n.r;if(e=t[1],n.x=-e.r,e.x=n.r,e.y=0,!(i>2))return n.r+e.r;xo(e,n,r=t[2]),n=new Mo(n),e=new Mo(e),r=new Mo(r),n.next=r.previous=e,e.next=n.previous=r,r.next=e.previous=n;t:for(a=3;ah&&(h=a),g=f*f*v,(p=Math.max(h/g,g/l))>d){f-=a;break}d=p}_.push(u={value:f,dice:c1&&jo(t[e[r-2]],t[e[r-1]],t[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function $o(t){this._size=t,this._call=this._error=null,this._tasks=[],this._data=[],this._waiting=this._active=this._ended=this._start=0}function Wo(t){if(!t._start)try{(function(t){for(;t._start=t._waiting&&t._active=0;)if((e=t._tasks[r])&&(t._tasks[r]=null,e.abort))try{e.abort()}catch(n){}t._active=NaN,Go(t)}function Go(t){if(!t._active&&t._call){var n=t._data;t._data=void 0,t._call(t._error,n)}}function Qo(t){if(null==t)t=1/0;else if(!((t=+t)>=1))throw new Error("invalid concurrency");return new $o(t)}function Jo(){return Math.random()}function Ko(t,n){function e(t){var n,e=s.status;if(!e&&function(t){var n=t.responseType;return n&&"text"!==n?t.response:t.responseText}(s)||e>=200&&e<300||304===e){if(o)try{n=o.call(r,s)}catch(t){return void a.call("error",r,t)}else n=s;a.call("load",r,n)}else a.call("error",r,t)}var r,i,o,u,a=N("beforesend","progress","load","error"),c=ae(),s=new XMLHttpRequest,f=null,l=null,h=0;if("undefined"==typeof XDomainRequest||"withCredentials"in s||!/^(http(s)?:)?\/\//.test(t)||(s=new XDomainRequest),"onload"in s?s.onload=s.onerror=s.ontimeout=e:s.onreadystatechange=function(t){s.readyState>3&&e(t)},s.onprogress=function(t){a.call("progress",r,t)},r={header:function(t,n){return t=(t+"").toLowerCase(),arguments.length<2?c.get(t):(null==n?c.remove(t):c.set(t,n+""),r)},mimeType:function(t){return arguments.length?(i=null==t?null:t+"",r):i},responseType:function(t){return arguments.length?(u=t,r):u},timeout:function(t){return arguments.length?(h=+t,r):h},user:function(t){return arguments.length<1?f:(f=null==t?null:t+"",r)},password:function(t){return arguments.length<1?l:(l=null==t?null:t+"",r)},response:function(t){return o=t,r},get:function(t,n){return r.send("GET",t,n)},post:function(t,n){return r.send("POST",t,n)},send:function(n,e,o){return s.open(n,t,!0,f,l),null==i||c.has("accept")||c.set("accept",i+",*/*"),s.setRequestHeader&&c.each(function(t,n){s.setRequestHeader(n,t)}),null!=i&&s.overrideMimeType&&s.overrideMimeType(i),null!=u&&(s.responseType=u),h>0&&(s.timeout=h),null==o&&"function"==typeof e&&(o=e,e=null),null!=o&&1===o.length&&(o=function(t){return function(n,e){t(null==n?e:null)}}(o)),null!=o&&r.on("error",o).on("load",function(t){o(null,t)}),a.call("beforesend",r,s),s.send(null==e?null:e),r},abort:function(){return s.abort(),r},on:function(){var t=a.on.apply(a,arguments);return t===a?r:t}},null!=n){if("function"!=typeof n)throw new Error("invalid callback: "+n);return r.get(n)}return r}function tu(t,n){return function(e,r){var i=Ko(e).mimeType(t).response(n);if(null!=r){if("function"!=typeof r)throw new Error("invalid callback: "+r);return i.get(r)}return i}}function nu(t,n){return function(e,r,i){arguments.length<3&&(i=r,r=null);var o=Ko(e).mimeType(t);return o.row=function(t){return arguments.length?o.response(function(t,n){return function(e){return t(e.responseText,n)}}(n,r=t)):r},o.row(r),i?o.get(i):o}}function eu(t){function n(n){var o=n+"",u=e.get(o);if(!u){if(i!==gv)return i;e.set(o,u=r.push(n))}return t[(u-1)%t.length]}var e=ae(),r=[],i=gv;return t=null==t?[]:vv.call(t),n.domain=function(t){if(!arguments.length)return r.slice();r=[],e=ae();for(var i,o,u=-1,a=t.length;++u2?su:cu,o=u=null,r}function r(n){return(o||(o=i(a,c,f?function(t){return function(n,e){var r=t(n=+n,e=+e);return function(t){return t<=n?0:t>=e?1:r(t)}}}(t):t,s)))(+n)}var i,o,u,a=_v,c=_v,s=cn,f=!1;return r.invert=function(t){return(u||(u=i(c,a,au,f?function(t){return function(n,e){var r=t(n=+n,e=+e);return function(t){return t<=0?n:t>=1?e:r(t)}}}(n):n)))(+t)},r.domain=function(t){return arguments.length?(a=dv.call(t,uu),e()):a.slice()},r.range=function(t){return arguments.length?(c=vv.call(t),e()):c.slice()},r.rangeRound=function(t){return c=vv.call(t),s=sn,e()},r.clamp=function(t){return arguments.length?(f=!!t,e()):f},r.interpolate=function(t){return arguments.length?(s=t,e()):s},e()}function hu(n){var e=n.domain;return n.ticks=function(t){var n=e();return l(n[0],n[n.length-1],null==t?10:t)},n.tickFormat=function(n,r){return function(n,e,r){var i,o=n[0],u=n[n.length-1],a=p(o,u,null==e?10:e);switch((r=Le(null==r?",f":r)).type){case"s":var c=Math.max(Math.abs(o),Math.abs(u));return null!=r.precision||isNaN(i=Ie(a,c))||(r.precision=i),t.formatPrefix(r,c);case"":case"e":case"g":case"p":case"r":null!=r.precision||isNaN(i=Ye(a,Math.max(Math.abs(o),Math.abs(u))))||(r.precision=i-("e"===r.type));break;case"f":case"%":null!=r.precision||isNaN(i=Fe(a))||(r.precision=i-2*("%"===r.type))}return t.format(r)}(e(),n,r)},n.nice=function(t){null==t&&(t=10);var r,i=e(),o=0,u=i.length-1,a=i[o],c=i[u];return c0?r=h(a=Math.floor(a/r)*r,c=Math.ceil(c/r)*r,t):r<0&&(r=h(a=Math.ceil(a*r)/r,c=Math.floor(c*r)/r,t)),r>0?(i[o]=Math.floor(a/r)*r,i[u]=Math.ceil(c/r)*r,e(i)):r<0&&(i[o]=Math.ceil(a*r)/r,i[u]=Math.floor(c*r)/r,e(i)),n},n}function pu(){var t=lu(au,on);return t.copy=function(){return fu(t,pu())},hu(t)}function du(){function t(t){return+t}var n=[0,1];return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=dv.call(e,uu),t):n.slice()},t.copy=function(){return du().domain(n)},hu(t)}function vu(t,n){var e,r=0,i=(t=t.slice()).length-1,o=t[r],u=t[i];return u0){for(;pc)break;g.push(h)}}else for(;p=1;--f)if(!((h=s*f)c)break;g.push(h)}}else g=l(p,d,Math.min(d-p,v)).map(u);return n?g.reverse():g},e.tickFormat=function(n,r){if(null==r&&(r=10===i?".0e":","),"function"!=typeof r&&(r=t.format(r)),n===1/0)return r;null==n&&(n=10);var a=Math.max(1,i*n/e.ticks().length);return function(t){var n=t/u(Math.round(o(t)));return n*i0?o[n-1]:r[0],n=i?[o[i-1],r]:[o[n-1],o[n]]},t.copy=function(){return ku().domain([e,r]).range(u)},hu(t)}function Su(){function t(t){if(t<=t)return e[Ds(n,t,0,r)]}var n=[.5],e=[0,1],r=1;return t.domain=function(i){return arguments.length?(n=vv.call(i),r=Math.min(n.length,e.length-1),t):n.slice()},t.range=function(i){return arguments.length?(e=vv.call(i),r=Math.min(n.length,e.length-1),t):e.slice()},t.invertExtent=function(t){var r=e.indexOf(t);return[n[r-1],n[r]]},t.copy=function(){return Su().domain(n).range(e)},t}function Eu(t,n,e,r){function i(n){return t(n=new Date(+n)),n}return i.floor=i,i.ceil=function(e){return t(e=new Date(e-1)),n(e,1),t(e),e},i.round=function(t){var n=i(t),e=i.ceil(t);return t-n0))return a;do{a.push(u=new Date(+e)),n(e,o),t(e)}while(u=n)for(;t(n),!e(n);)n.setTime(n-1)},function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););})},e&&(i.count=function(n,r){return yv.setTime(+n),mv.setTime(+r),t(yv),t(mv),Math.floor(e(yv,mv))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(n){return r(n)%t==0}:function(n){return i.count(0,n)%t==0}):i:null}),i}function Au(t){return Eu(function(n){n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+7*n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*wv)/Mv})}function Cu(t){return Eu(function(n){n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+7*n)},function(t,n){return(n-t)/Mv})}function zu(t){if(0<=t.y&&t.y<100){var n=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return n.setFullYear(t.y),n}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function Pu(t){if(0<=t.y&&t.y<100){var n=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return n.setUTCFullYear(t.y),n}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function Ru(t){return{y:t,m:0,d:1,H:0,M:0,S:0,L:0}}function Lu(t){function n(t,n){return function(e){var r,i,o,u=[],a=-1,c=0,s=t.length;for(e instanceof Date||(e=new Date(+e));++a53)return null;"w"in u||(u.w=1),"Z"in u?(i=(o=(i=Pu(Ru(u.y))).getUTCDay())>4||0===o?rg.ceil(i):rg(i),i=tg.offset(i,7*(u.V-1)),u.y=i.getUTCFullYear(),u.m=i.getUTCMonth(),u.d=i.getUTCDate()+(u.w+6)%7):(i=(o=(i=n(Ru(u.y))).getDay())>4||0===o?Rv.ceil(i):Rv(i),i=Cv.offset(i,7*(u.V-1)),u.y=i.getFullYear(),u.m=i.getMonth(),u.d=i.getDate()+(u.w+6)%7)}else("W"in u||"U"in u)&&("w"in u||(u.w="u"in u?u.u%7:"W"in u?1:0),o="Z"in u?Pu(Ru(u.y)).getUTCDay():n(Ru(u.y)).getDay(),u.m=0,u.d="W"in u?(u.w+6)%7+7*u.W-(o+5)%7:u.w+7*u.U-(o+6)%7);return"Z"in u?(u.H+=u.Z/100|0,u.M+=u.Z%100,Pu(u)):n(u)}}function r(t,n,e,r){for(var i,o,u=0,a=n.length,c=e.length;u=c)return-1;if(37===(i=n.charCodeAt(u++))){if(i=n.charAt(u++),!(o=T[i in bg?n.charAt(u++):i])||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}var i=t.dateTime,o=t.date,u=t.time,a=t.periods,c=t.days,s=t.shortDays,f=t.months,l=t.shortMonths,h=Uu(a),p=Ou(a),d=Uu(c),v=Ou(c),g=Uu(s),_=Ou(s),y=Uu(f),m=Ou(f),x=Uu(l),b=Ou(l),w={a:function(t){return s[t.getDay()]},A:function(t){return c[t.getDay()]},b:function(t){return l[t.getMonth()]},B:function(t){return f[t.getMonth()]},c:null,d:ia,e:ia,f:sa,H:oa,I:ua,j:aa,L:ca,m:fa,M:la,p:function(t){return a[+(t.getHours()>=12)]},Q:Fa,s:Ia,S:ha,u:pa,U:da,V:va,w:ga,W:_a,x:null,X:null,y:ya,Y:ma,Z:xa,"%":Oa},M={a:function(t){return s[t.getUTCDay()]},A:function(t){return c[t.getUTCDay()]},b:function(t){return l[t.getUTCMonth()]},B:function(t){return f[t.getUTCMonth()]},c:null,d:ba,e:ba,f:ka,H:wa,I:Ma,j:Ta,L:Na,m:Sa,M:Ea,p:function(t){return a[+(t.getUTCHours()>=12)]},Q:Fa,s:Ia,S:Aa,u:Ca,U:za,V:Pa,w:Ra,W:La,x:null,X:null,y:qa,Y:Da,Z:Ua,"%":Oa},T={a:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.w=_[r[0].toLowerCase()],e+r[0].length):-1},A:function(t,n,e){var r=d.exec(n.slice(e));return r?(t.w=v[r[0].toLowerCase()],e+r[0].length):-1},b:function(t,n,e){var r=x.exec(n.slice(e));return r?(t.m=b[r[0].toLowerCase()],e+r[0].length):-1},B:function(t,n,e){var r=y.exec(n.slice(e));return r?(t.m=m[r[0].toLowerCase()],e+r[0].length):-1},c:function(t,n,e){return r(t,i,n,e)},d:Wu,e:Wu,f:ta,H:Gu,I:Gu,j:Zu,L:Ku,m:$u,M:Qu,p:function(t,n,e){var r=h.exec(n.slice(e));return r?(t.p=p[r[0].toLowerCase()],e+r[0].length):-1},Q:ea,s:ra,S:Ju,u:Iu,U:Yu,V:Bu,w:Fu,W:Hu,x:function(t,n,e){return r(t,o,n,e)},X:function(t,n,e){return r(t,u,n,e)},y:Xu,Y:ju,Z:Vu,"%":na};return w.x=n(o,w),w.X=n(u,w),w.c=n(i,w),M.x=n(o,M),M.X=n(u,M),M.c=n(i,M),{format:function(t){var e=n(t+="",w);return e.toString=function(){return t},e},parse:function(t){var n=e(t+="",zu);return n.toString=function(){return t},n},utcFormat:function(t){var e=n(t+="",M);return e.toString=function(){return t},e},utcParse:function(t){var n=e(t,Pu);return n.toString=function(){return t},n}}}function qu(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o68?1900:2e3),e+r[0].length):-1}function Vu(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function $u(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function Wu(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function Zu(t,n,e){var r=wg.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function Gu(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function Qu(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function Ju(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function Ku(t,n,e){var r=wg.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function ta(t,n,e){var r=wg.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function na(t,n,e){var r=Mg.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function ea(t,n,e){var r=wg.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function ra(t,n,e){var r=wg.exec(n.slice(e));return r?(t.Q=1e3*+r[0],e+r[0].length):-1}function ia(t,n){return qu(t.getDate(),n,2)}function oa(t,n){return qu(t.getHours(),n,2)}function ua(t,n){return qu(t.getHours()%12||12,n,2)}function aa(t,n){return qu(1+Cv.count(Wv(t),t),n,3)}function ca(t,n){return qu(t.getMilliseconds(),n,3)}function sa(t,n){return ca(t,n)+"000"}function fa(t,n){return qu(t.getMonth()+1,n,2)}function la(t,n){return qu(t.getMinutes(),n,2)}function ha(t,n){return qu(t.getSeconds(),n,2)}function pa(t){var n=t.getDay();return 0===n?7:n}function da(t,n){return qu(Pv.count(Wv(t),t),n,2)}function va(t,n){var e=t.getDay();return t=e>=4||0===e?Dv(t):Dv.ceil(t),qu(Dv.count(Wv(t),t)+(4===Wv(t).getDay()),n,2)}function ga(t){return t.getDay()}function _a(t,n){return qu(Rv.count(Wv(t),t),n,2)}function ya(t,n){return qu(t.getFullYear()%100,n,2)}function ma(t,n){return qu(t.getFullYear()%1e4,n,4)}function xa(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+qu(n/60|0,"0",2)+qu(n%60,"0",2)}function ba(t,n){return qu(t.getUTCDate(),n,2)}function wa(t,n){return qu(t.getUTCHours(),n,2)}function Ma(t,n){return qu(t.getUTCHours()%12||12,n,2)}function Ta(t,n){return qu(1+tg.count(yg(t),t),n,3)}function Na(t,n){return qu(t.getUTCMilliseconds(),n,3)}function ka(t,n){return Na(t,n)+"000"}function Sa(t,n){return qu(t.getUTCMonth()+1,n,2)}function Ea(t,n){return qu(t.getUTCMinutes(),n,2)}function Aa(t,n){return qu(t.getUTCSeconds(),n,2)}function Ca(t){var n=t.getUTCDay();return 0===n?7:n}function za(t,n){return qu(eg.count(yg(t),t),n,2)}function Pa(t,n){var e=t.getUTCDay();return t=e>=4||0===e?ug(t):ug.ceil(t),qu(ug.count(yg(t),t)+(4===yg(t).getUTCDay()),n,2)}function Ra(t){return t.getUTCDay()}function La(t,n){return qu(rg.count(yg(t),t),n,2)}function qa(t,n){return qu(t.getUTCFullYear()%100,n,2)}function Da(t,n){return qu(t.getUTCFullYear()%1e4,n,4)}function Ua(){return"+0000"}function Oa(){return"%"}function Fa(t){return+t}function Ia(t){return Math.floor(+t/1e3)}function Ya(n){return mg=Lu(n),t.timeFormat=mg.format,t.timeParse=mg.parse,t.utcFormat=mg.utcFormat,t.utcParse=mg.utcParse,mg}function Ba(t){return new Date(t)}function Ha(t){return t instanceof Date?+t:+new Date(+t)}function ja(t,n,r,i,o,u,a,c,s){function f(e){return(a(e)=1?e_:t<=-1?-e_:Math.asin(t)}function Ga(t){return t.innerRadius}function Qa(t){return t.outerRadius}function Ja(t){return t.startAngle}function Ka(t){return t.endAngle}function tc(t){return t&&t.padAngle}function nc(t,n,e,r,i,o,u){var a=t-e,c=n-r,s=(u?o:-o)/Kg(a*a+c*c),f=s*c,l=-s*a,h=t+f,p=n+l,d=e+f,v=r+l,g=(h+d)/2,_=(p+v)/2,y=d-h,m=v-p,x=y*y+m*m,b=i-o,w=h*v-d*p,M=(m<0?-1:1)*Kg(Gg(0,b*b*x-w*w)),T=(w*m-y*M)/x,N=(-w*y-m*M)/x,k=(w*m+y*M)/x,S=(-w*y+m*M)/x,E=T-g,A=N-_,C=k-g,z=S-_;return E*E+A*A>C*C+z*z&&(T=k,N=S),{cx:T,cy:N,x01:-f,y01:-l,x11:T*(i/b-1),y11:N*(i/b-1)}}function ec(t){this._context=t}function rc(t){return new ec(t)}function ic(t){return t[0]}function oc(t){return t[1]}function uc(){function t(t){var a,c,s,f=t.length,l=!1;for(null==i&&(u=o(s=te())),a=0;a<=f;++a)!(a=f;--l)s.point(g[l],_[l]);s.lineEnd(),s.areaEnd()}v&&(g[n]=+e(h,n,t),_[n]=+i(h,n,t),s.point(r?+r(h,n,t):g[n],o?+o(h,n,t):_[n]))}if(p)return s=null,p+""||null}function n(){return uc().defined(u).curve(c).context(a)}var e=ic,r=null,i=Wa(0),o=oc,u=Wa(!0),a=null,c=rc,s=null;return t.x=function(n){return arguments.length?(e="function"==typeof n?n:Wa(+n),r=null,t):e},t.x0=function(n){return arguments.length?(e="function"==typeof n?n:Wa(+n),t):e},t.x1=function(n){return arguments.length?(r=null==n?null:"function"==typeof n?n:Wa(+n),t):r},t.y=function(n){return arguments.length?(i="function"==typeof n?n:Wa(+n),o=null,t):i},t.y0=function(n){return arguments.length?(i="function"==typeof n?n:Wa(+n),t):i},t.y1=function(n){return arguments.length?(o=null==n?null:"function"==typeof n?n:Wa(+n),t):o},t.lineX0=t.lineY0=function(){return n().x(e).y(i)},t.lineY1=function(){return n().x(e).y(o)},t.lineX1=function(){return n().x(r).y(i)},t.defined=function(n){return arguments.length?(u="function"==typeof n?n:Wa(!!n),t):u},t.curve=function(n){return arguments.length?(c=n,null!=a&&(s=c(a)),t):c},t.context=function(n){return arguments.length?(null==n?a=s=null:s=c(a=n),t):a},t}function cc(t,n){return nt?1:n>=t?0:NaN}function sc(t){return t}function fc(t){this._curve=t}function lc(t){function n(n){return new fc(t(n))}return n._curve=t,n}function hc(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(lc(t)):n()._curve},t}function pc(){return hc(uc().curve(i_))}function dc(){var t=ac().curve(i_),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return hc(e())},delete t.lineX0,t.lineEndAngle=function(){return hc(r())},delete t.lineX1,t.lineInnerRadius=function(){return hc(i())},delete t.lineY0,t.lineOuterRadius=function(){return hc(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(lc(t)):n()._curve},t}function vc(t,n){return[(n=+n)*Math.cos(t-=Math.PI/2),n*Math.sin(t)]}function gc(t){return t.source}function _c(t){return t.target}function yc(t){function n(){var n,a=o_.call(arguments),c=e.apply(this,a),s=r.apply(this,a);if(u||(u=n=te()),t(u,+i.apply(this,(a[0]=c,a)),+o.apply(this,a),+i.apply(this,(a[0]=s,a)),+o.apply(this,a)),n)return u=null,n+""||null}var e=gc,r=_c,i=ic,o=oc,u=null;return n.source=function(t){return arguments.length?(e=t,n):e},n.target=function(t){return arguments.length?(r=t,n):r},n.x=function(t){return arguments.length?(i="function"==typeof t?t:Wa(+t),n):i},n.y=function(t){return arguments.length?(o="function"==typeof t?t:Wa(+t),n):o},n.context=function(t){return arguments.length?(u=null==t?null:t,n):u},n}function mc(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n=(n+r)/2,e,n,i,r,i)}function xc(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n,e=(e+i)/2,r,e,r,i)}function bc(t,n,e,r,i){var o=vc(n,e),u=vc(n,e=(e+i)/2),a=vc(r,e),c=vc(r,i);t.moveTo(o[0],o[1]),t.bezierCurveTo(u[0],u[1],a[0],a[1],c[0],c[1])}function wc(){}function Mc(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function Tc(t){this._context=t}function Nc(t){this._context=t}function kc(t){this._context=t}function Sc(t,n){this._basis=new Tc(t),this._beta=n}function Ec(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function Ac(t,n){this._context=t,this._k=(1-n)/6}function Cc(t,n){this._context=t,this._k=(1-n)/6}function zc(t,n){this._context=t,this._k=(1-n)/6}function Pc(t,n,e){var r=t._x1,i=t._y1,o=t._x2,u=t._y2;if(t._l01_a>t_){var a=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*a-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*a-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>t_){var s=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,f=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*s+t._x1*t._l23_2a-n*t._l12_2a)/f,u=(u*s+t._y1*t._l23_2a-e*t._l12_2a)/f}t._context.bezierCurveTo(r,i,o,u,t._x2,t._y2)}function Rc(t,n){this._context=t,this._alpha=n}function Lc(t,n){this._context=t,this._alpha=n}function qc(t,n){this._context=t,this._alpha=n}function Dc(t){this._context=t}function Uc(t){return t<0?-1:1}function Oc(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),u=(e-t._y1)/(i||r<0&&-0),a=(o*i+u*r)/(r+i);return(Uc(o)+Uc(u))*Math.min(Math.abs(o),Math.abs(u),.5*Math.abs(a))||0}function Fc(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function Ic(t,n,e){var r=t._x0,i=t._y0,o=t._x1,u=t._y1,a=(o-r)/3;t._context.bezierCurveTo(r+a,i+a*n,o-a,u-a*e,o,u)}function Yc(t){this._context=t}function Bc(t){this._context=new Hc(t)}function Hc(t){this._context=t}function jc(t){this._context=t}function Xc(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),u=new Array(r);for(i[0]=0,o[0]=2,u[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(u[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n1)for(var e,r,i,o=1,u=t[n[0]],a=u.length;o=0;)e[n]=n;return e}function Zc(t,n){return t[n]}function Gc(t){var n=t.map(Qc);return Wc(t).sort(function(t,e){return n[t]-n[e]})}function Qc(t){for(var n,e=0,r=-1,i=t.length;++r0)){if(o/=h,h<0){if(o0){if(o>l)return;o>f&&(f=o)}if(o=r-c,h||!(o<0)){if(o/=h,h<0){if(o>l)return;o>f&&(f=o)}else if(h>0){if(o0)){if(o/=p,p<0){if(o0){if(o>l)return;o>f&&(f=o)}if(o=i-s,p||!(o<0)){if(o/=p,p<0){if(o>l)return;o>f&&(f=o)}else if(p>0){if(o0||l<1)||(f>0&&(t[0]=[c+f*h,s+f*p]),l<1&&(t[1]=[c+l*h,s+l*p]),!0)}}}}}function fs(t,n,e,r,i){var o=t[1];if(o)return!0;var u,a,c=t[0],s=t.left,f=t.right,l=s[0],h=s[1],p=f[0],d=f[1],v=(l+p)/2,g=(h+d)/2;if(d===h){if(v=r)return;if(l>p){if(c){if(c[1]>=i)return}else c=[v,e];o=[v,i]}else{if(c){if(c[1]1)if(l>p){if(c){if(c[1]>=i)return}else c=[(e-a)/u,e];o=[(i-a)/u,i]}else{if(c){if(c[1]=r)return}else c=[n,u*n+a];o=[r,u*r+a]}else{if(c){if(c[0]=-O_)){var p=c*c+s*s,d=f*f+l*l,v=(l*p-s*d)/h,g=(c*d-f*p)/h,_=q_.pop()||new function(){es(this),this.x=this.y=this.arc=this.site=this.cy=null};_.arc=t,_.site=i,_.x=v+u,_.y=(_.cy=g+a)+Math.sqrt(v*v+g*g),t.circle=_;for(var y=null,m=R_._;m;)if(_.yU_)a=a.L;else{if(!((i=o-function(t,n){var e=t.N;if(e)return xs(e,n);var r=t.site;return r[1]===n?r[0]:1/0}(a,u))>U_)){r>-U_?(n=a.P,e=a):i>-U_?(n=a,e=a.N):n=e=a;break}if(!a.R){n=a;break}a=a.R}(function(t){P_[t.index]={site:t,halfedges:[]}})(t);var c=gs(t);if(z_.insert(n,c),n||e){if(n===e)return vs(n),e=gs(n.site),z_.insert(c,e),c.edge=e.edge=us(n.site,c.site),ds(n),void ds(e);if(e){vs(n),vs(e);var s=n.site,f=s[0],l=s[1],h=t[0]-f,p=t[1]-l,d=e.site,v=d[0]-f,g=d[1]-l,_=2*(h*g-p*v),y=h*h+p*p,m=v*v+g*g,x=[(g*y-p*m)/_+f,(h*m-v*y)/_+l];cs(e.edge,s,d,x),c.edge=us(s,t,null,x),e.edge=us(t,d,null,x),ds(n),ds(e)}else c.edge=us(n.site,c.site)}}function xs(t,n){var e=t.site,r=e[0],i=e[1],o=i-n;if(!o)return r;var u=t.P;if(!u)return-1/0;var a=(e=u.site)[0],c=e[1],s=c-n;if(!s)return a;var f=a-r,l=1/o-1/s,h=f/s;return l?(-h+Math.sqrt(h*h-2*l*(f*f/(-2*s)-c+s/2+i-o/2)))/l+r:(r+a)/2}function bs(t,n,e){return(t[0]-e[0])*(n[1]-t[1])-(t[0]-n[0])*(e[1]-t[1])}function ws(t,n){return n[1]-t[1]||n[0]-t[0]}function Ms(t,n){var e,r,i,o=t.sort(ws).pop();for(L_=[],P_=new Array(t.length),z_=new ns,R_=new ns;;)if(i=C_,o&&(!i||o[1]U_||Math.abs(i[0][1]-i[1][1])>U_)||delete L_[o]})(u,a,c,s),function(t,n,e,r){var i,o,u,a,c,s,f,l,h,p,d,v,g=P_.length,_=!0;for(i=0;iU_||Math.abs(v-h)>U_)&&(c.splice(a,0,L_.push(as(u,p,Math.abs(d-t)U_?[t,Math.abs(l-t)U_?[Math.abs(h-r)U_?[e,Math.abs(l-e)U_?[Math.abs(h-n)r?(r+i)/2:Math.min(0,r)||Math.max(0,i),u>o?(o+u)/2:Math.min(0,o)||Math.max(0,u))}var qs=e(n),Ds=qs.right,Us=qs.left,Os=Array.prototype,Fs=Os.slice,Is=Os.map,Ys=Math.sqrt(50),Bs=Math.sqrt(10),Hs=Math.sqrt(2),js=Array.prototype.slice,Xs=1,Vs=2,$s=3,Ws=4,Zs=1e-6,Gs={value:function(){}};k.prototype=N.prototype={constructor:k,on:function(t,n){var e,r=this._,i=function(t,n){return t.trim().split(/^|\s+/).map(function(t){var e="",r=t.indexOf(".");if(r>=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}})}(t+"",r),o=-1,u=i.length;{if(!(arguments.length<2)){if(null!=n&&"function"!=typeof n)throw new Error("invalid callback: "+n);for(;++o0)for(var e,r,i=new Array(e),o=0;o=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var af=[null];st.prototype=ft.prototype={constructor:st,select:function(t){"function"!=typeof t&&(t=Y(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i=x&&(x=m+1);!(y=g[x])&&++x=0;)(r=i[o])&&(u&&u!==r.nextSibling&&u.parentNode.insertBefore(r,u),u=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=W);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?function(t){return function(){this.style.removeProperty(t)}}:"function"==typeof n?function(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}:function(t,n,e){return function(){this.style.setProperty(t,n,e)}})(t,n,null==e?"":e)):G(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?function(t){return function(){delete this[t]}}:"function"==typeof n?function(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}:function(t,n){return function(){this[t]=n}})(t,n)):this.node()[t]},classed:function(t,n){var e=Q(t+"");if(arguments.length<2){for(var r=J(this.node()),i=-1,o=e.length;++i=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}})}(t+""),u=o.length;if(!(arguments.length<2)){for(a=n?q:L,null==e&&(e=!1),r=0;r=240?t-240:t+120,i,r),qt(t,i,r),qt(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1}}));var xf=Math.PI/180,bf=180/Math.PI,wf=.95047,Mf=1,Tf=1.08883,Nf=4/29,kf=6/29,Sf=3*kf*kf,Ef=kf*kf*kf;Mt(Ot,Ut,Tt(Nt,{brighter:function(t){return new Ot(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new Ot(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,n=isNaN(this.a)?t:t+this.a/500,e=isNaN(this.b)?t:t-this.b/200;return t=Mf*It(t),n=wf*It(n),e=Tf*It(e),new zt(Yt(3.2404542*n-1.5371385*t-.4985314*e),Yt(-.969266*n+1.8760108*t+.041556*e),Yt(.0556434*n-.2040259*t+1.0572252*e),this.opacity)}})),Mt(jt,Ht,Tt(Nt,{brighter:function(t){return new jt(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new jt(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return Dt(this).rgb()}}));var Af=-.29227,Cf=-.90649,zf=1.97294,Pf=zf*Cf,Rf=1.78277*zf,Lf=1.78277*Af- -.14861*Cf;Mt(Vt,Xt,Tt(Nt,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new Vt(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new Vt(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*xf,n=+this.l,e=isNaN(this.s)?0:this.s*n*(1-n),r=Math.cos(t),i=Math.sin(t);return new zt(255*(n+e*(-.14861*r+1.78277*i)),255*(n+e*(Af*r+Cf*i)),255*(n+e*(zf*r)),this.opacity)}}));var qf,Df,Uf,Of,Ff,If,Yf=function t(n){function e(t,n){var e=r((t=Ct(t)).r,(n=Ct(n)).r),i=r(t.g,n.g),o=r(t.b,n.b),u=tn(t.opacity,n.opacity);return function(n){return t.r=e(n),t.g=i(n),t.b=o(n),t.opacity=u(n),t+""}}var r=Kt(n);return e.gamma=t,e}(1),Bf=nn(Wt),Hf=nn(Zt),jf=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Xf=new RegExp(jf.source,"g"),Vf=180/Math.PI,$f={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1},Wf=ln(function(t){return"none"===t?$f:(qf||(qf=document.createElement("DIV"),Df=document.documentElement,Uf=document.defaultView),qf.style.transform=t,t=Uf.getComputedStyle(Df.appendChild(qf),null).getPropertyValue("transform"),Df.removeChild(qf),t=t.slice(7,-1).split(","),fn(+t[0],+t[1],+t[2],+t[3],+t[4],+t[5]))},"px, ","px)","deg)"),Zf=ln(function(t){return null==t?$f:(Of||(Of=document.createElementNS("http://www.w3.org/2000/svg","g")),Of.setAttribute("transform",t),(t=Of.transform.baseVal.consolidate())?(t=t.matrix,fn(t.a,t.b,t.c,t.d,t.e,t.f)):$f)},", ",")",")"),Gf=Math.SQRT2,Qf=2,Jf=4,Kf=1e-12,tl=dn(Jt),nl=dn(tn),el=vn(Jt),rl=vn(tn),il=gn(Jt),ol=gn(tn),ul=0,al=0,cl=0,sl=1e3,fl=0,ll=0,hl=0,pl="object"==typeof performance&&performance.now?performance:Date,dl="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};mn.prototype=xn.prototype={constructor:mn,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?_n():+e)+(null==n?0:+n),this._next||If===this||(If?If._next=this:Ff=this,If=this),this._call=t,this._time=e,Tn()},stop:function(){this._call&&(this._call=null,this._time=1/0,Tn())}};var vl=N("start","end","interrupt"),gl=[],_l=0,yl=1,ml=2,xl=3,bl=4,wl=5,Ml=6,Tl=ft.prototype.constructor,Nl=0,kl=ft.prototype;Rn.prototype=Ln.prototype={constructor:Rn,select:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=Y(t));for(var r=this._groups,i=r.length,o=new Array(i),u=0;u=0&&(t=t.slice(0,n)),!t||"start"===t})}(n)?Sn:En;return function(){var u=o(this,t),a=u.on;a!==r&&(i=(r=a).copy()).on(n,e),u.on=i}}(e,t,n))},attr:function(t,n){var e=E(t),r="transform"===e?Zf:Pn;return this.attrTween(t,"function"==typeof n?(e.local?function(t,n,e){var r,i,o;return function(){var u,a=e(this);if(null!=a)return(u=this.getAttributeNS(t.space,t.local))===a?null:u===r&&a===i?o:o=n(r=u,i=a);this.removeAttributeNS(t.space,t.local)}}:function(t,n,e){var r,i,o;return function(){var u,a=e(this);if(null!=a)return(u=this.getAttribute(t))===a?null:u===r&&a===i?o:o=n(r=u,i=a);this.removeAttribute(t)}})(e,r,zn(this,"attr."+t,n)):null==n?(e.local?function(t){return function(){this.removeAttributeNS(t.space,t.local)}}:function(t){return function(){this.removeAttribute(t)}})(e):(e.local?function(t,n,e){var r,i;return function(){var o=this.getAttributeNS(t.space,t.local);return o===e?null:o===r?i:i=n(r=o,e)}}:function(t,n,e){var r,i;return function(){var o=this.getAttribute(t);return o===e?null:o===r?i:i=n(r=o,e)}})(e,r,n+""))},attrTween:function(t,n){var e="attr."+t;if(arguments.length<2)return(e=this.tween(e))&&e._value;if(null==n)return this.tween(e,null);if("function"!=typeof n)throw new Error;var r=E(t);return this.tween(e,(r.local?function(t,n){function e(){var e=this,r=n.apply(e,arguments);return r&&function(n){e.setAttributeNS(t.space,t.local,r(n))}}return e._value=n,e}:function(t,n){function e(){var e=this,r=n.apply(e,arguments);return r&&function(n){e.setAttribute(t,r(n))}}return e._value=n,e})(r,n))},style:function(t,n,e){var r="transform"==(t+="")?Wf:Pn;return null==n?this.styleTween(t,function(t,n){var e,r,i;return function(){var o=G(this,t),u=(this.style.removeProperty(t),G(this,t));return o===u?null:o===e&&u===r?i:i=n(e=o,r=u)}}(t,r)).on("end.style."+t,function(t){return function(){this.style.removeProperty(t)}}(t)):this.styleTween(t,"function"==typeof n?function(t,n,e){var r,i,o;return function(){var u=G(this,t),a=e(this);return null==a&&(this.style.removeProperty(t),a=G(this,t)),u===a?null:u===r&&a===i?o:o=n(r=u,i=a)}}(t,r,zn(this,"style."+t,n)):function(t,n,e){var r,i;return function(){var o=G(this,t);return o===e?null:o===r?i:i=n(r=o,e)}}(t,r,n+""),e)},styleTween:function(t,n,e){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==n)return this.tween(r,null);if("function"!=typeof n)throw new Error;return this.tween(r,function(t,n,e){function r(){var r=this,i=n.apply(r,arguments);return i&&function(n){r.style.setProperty(t,i(n),e)}}return r._value=n,r}(t,n,null==e?"":e))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var n=t(this);this.textContent=null==n?"":n}}(zn(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},remove:function(){return this.on("end.remove",function(t){return function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}}(this._id))},tween:function(t,n){var e=this._id;if(t+="",arguments.length<2){for(var r,i=An(this.node(),e).tween,o=0,u=i.length;o1e-6)if(Math.abs(f*a-c*s)>1e-6&&i){var h=e-o,p=r-u,d=a*a+c*c,v=h*h+p*p,g=Math.sqrt(d),_=Math.sqrt(l),y=i*Math.tan((gh-Math.acos((d+l-v)/(2*g*_)))/2),m=y/_,x=y/g;Math.abs(m-1)>1e-6&&(this._+="L"+(t+m*s)+","+(n+m*f)),this._+="A"+i+","+i+",0,0,"+ +(f*h>s*p)+","+(this._x1=t+x*a)+","+(this._y1=n+x*c)}else this._+="L"+(this._x1=t)+","+(this._y1=n);else;},arc:function(t,n,e,r,i,o){t=+t,n=+n;var u=(e=+e)*Math.cos(r),a=e*Math.sin(r),c=t+u,s=n+a,f=1^o,l=o?r-i:i-r;if(e<0)throw new Error("negative radius: "+e);null===this._x1?this._+="M"+c+","+s:(Math.abs(this._x1-c)>1e-6||Math.abs(this._y1-s)>1e-6)&&(this._+="L"+c+","+s),e&&(l<0&&(l=l%_h+_h),l>yh?this._+="A"+e+","+e+",0,1,"+f+","+(t-u)+","+(n-a)+"A"+e+","+e+",0,1,"+f+","+(this._x1=c)+","+(this._y1=s):l>1e-6&&(this._+="A"+e+","+e+",0,"+ +(l>=gh)+","+f+","+(this._x1=t+e*Math.cos(i))+","+(this._y1=n+e*Math.sin(i))))},rect:function(t,n,e,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+n)+"h"+ +e+"v"+ +r+"h"+-e+"Z"},toString:function(){return this._}};ue.prototype=ae.prototype={constructor:ue,has:function(t){return"$"+t in this},get:function(t){return this["$"+t]},set:function(t,n){return this["$"+t]=n,this},remove:function(t){var n="$"+t;return n in this&&delete this[n]},clear:function(){for(var t in this)"$"===t[0]&&delete this[t]},keys:function(){var t=[];for(var n in this)"$"===n[0]&&t.push(n.slice(1));return t},values:function(){var t=[];for(var n in this)"$"===n[0]&&t.push(this[n]);return t},entries:function(){var t=[];for(var n in this)"$"===n[0]&&t.push({key:n.slice(1),value:this[n]});return t},size:function(){var t=0;for(var n in this)"$"===n[0]&&++t;return t},empty:function(){for(var t in this)if("$"===t[0])return!1;return!0},each:function(t){for(var n in this)"$"===n[0]&&t(this[n],n.slice(1),this)}};var mh=ae.prototype;he.prototype=pe.prototype={constructor:he,has:mh.has,add:function(t){return t+="",this["$"+t]=t,this},remove:mh.remove,clear:mh.clear,values:mh.keys,size:mh.size,empty:mh.empty,each:mh.each};var xh={},bh={},wh=34,Mh=10,Th=13,Nh=ve(","),kh=Nh.parse,Sh=Nh.parseRows,Eh=Nh.format,Ah=Nh.formatRows,Ch=ve("\t"),zh=Ch.parse,Ph=Ch.parseRows,Rh=Ch.format,Lh=Ch.formatRows,qh=we.prototype=Me.prototype;qh.copy=function(){var t,n,e=new Me(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=Te(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=Te(n));return e},qh.add=function(t){var n=+this._x.call(null,t),e=+this._y.call(null,t);return ye(this.cover(n,e),n,e,t)},qh.addAll=function(t){var n,e,r,i,o=t.length,u=new Array(o),a=new Array(o),c=1/0,s=1/0,f=-1/0,l=-1/0;for(e=0;ef&&(f=r),il&&(l=i));for(ft||t>i||r>n||n>o))return this;var u,a,c=i-e,s=this._root;switch(a=(n<(r+o)/2)<<1|t<(e+i)/2){case 0:do{u=new Array(4),u[a]=s,s=u}while(c*=2,i=e+c,o=r+c,t>i||n>o);break;case 1:do{u=new Array(4),u[a]=s,s=u}while(c*=2,e=i-c,o=r+c,e>t||n>o);break;case 2:do{u=new Array(4),u[a]=s,s=u}while(c*=2,i=e+c,r=o-c,t>i||r>n);break;case 3:do{u=new Array(4),u[a]=s,s=u}while(c*=2,e=i-c,r=o-c,e>t||r>n)}this._root&&this._root.length&&(this._root=s)}return this._x0=e,this._y0=r,this._x1=i,this._y1=o,this},qh.data=function(){var t=[];return this.visit(function(n){if(!n.length)do{t.push(n.data)}while(n=n.next)}),t},qh.extent=function(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]},qh.find=function(t,n,e){var r,i,o,u,a,c,s,f=this._x0,l=this._y0,h=this._x1,p=this._y1,d=[],v=this._root;for(v&&d.push(new me(v,f,l,h,p)),null==e?e=1/0:(f=t-e,l=n-e,h=t+e,p=n+e,e*=e);c=d.pop();)if(!(!(v=c.node)||(i=c.x0)>h||(o=c.y0)>p||(u=c.x1)=_)<<1|t>=g)&&(c=d[d.length-1],d[d.length-1]=d[d.length-1-s],d[d.length-1-s]=c)}else{var y=t-+this._x.call(null,v.data),m=n-+this._y.call(null,v.data),x=y*y+m*m;if(x=(a=(d+g)/2))?d=a:g=a,(f=u>=(c=(v+_)/2))?v=c:_=c,n=p,!(p=p[l=f<<1|s]))return this;if(!p.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;p.data!==t;)if(r=p,!(p=p.next))return this;return(i=p.next)&&delete p.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(p=n[0]||n[1]||n[2]||n[3])&&p===(n[3]||n[2]||n[1]||n[0])&&!p.length&&(e?e[h]=p:this._root=p),this):(this._root=i,this)},qh.removeAll=function(t){for(var n=0,e=t.length;n0&&(o=0)}return o>0?t.slice(0,o)+t.slice(e+1):t},"%":function(t,n){return(100*t).toFixed(n)},b:function(t){return Math.round(t).toString(2)},c:function(t){return t+""},d:function(t){return Math.round(t).toString(10)},e:function(t,n){return t.toExponential(n)},f:function(t,n){return t.toFixed(n)},g:function(t,n){return t.toPrecision(n)},o:function(t){return Math.round(t).toString(8)},p:function(t,n){return Re(100*t,n)},r:Re,s:function(t,n){var e=ze(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(Dh=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,u=r.length;return o===u?r:o>u?r+new Array(o-u+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+ze(t,Math.max(0,n+o-1))[0]},X:function(t){return Math.round(t).toString(16).toUpperCase()},x:function(t){return Math.round(t).toString(16)}},Ih=/^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$/i;Le.prototype=qe.prototype,qe.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(null==this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(null==this.precision?"":"."+Math.max(0,0|this.precision))+this.type};var Yh,Bh=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];Oe({decimal:".",thousands:",",grouping:[3],currency:["$",""]}),He.prototype={constructor:He,reset:function(){this.s=this.t=0},add:function(t){je(xp,t,this.t),je(this,xp.s,this.s),this.s?this.t+=xp.t:this.s=xp.t},valueOf:function(){return this.s}};var Hh,jh,Xh,Vh,$h,Wh,Zh,Gh,Qh,Jh,Kh,tp,np,ep,rp,ip,op,up,ap,cp,sp,fp,lp,hp,pp,dp,vp,gp,_p,yp,mp,xp=new He,bp=1e-6,wp=1e-12,Mp=Math.PI,Tp=Mp/2,Np=Mp/4,kp=2*Mp,Sp=180/Mp,Ep=Mp/180,Ap=Math.abs,Cp=Math.atan,zp=Math.atan2,Pp=Math.cos,Rp=Math.ceil,Lp=Math.exp,qp=Math.log,Dp=Math.pow,Up=Math.sin,Op=Math.sign||function(t){return t>0?1:t<0?-1:0},Fp=Math.sqrt,Ip=Math.tan,Yp={Feature:function(t,n){Ze(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++rbp?Qh=90:Vp<-bp&&(Zh=-90),rp[0]=Wh,rp[1]=Gh}},Wp={sphere:We,point:xr,lineStart:wr,lineEnd:Nr,polygonStart:function(){Wp.lineStart=kr,Wp.lineEnd=Sr},polygonEnd:function(){Wp.lineStart=wr,Wp.lineEnd=Nr}};Pr.invert=Pr;var Zp,Gp,Qp,Jp,Kp,td,nd,ed,rd,id,od,ud=Be(),ad=Vr(function(){return!0},function(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,u){var a=o>0?Mp:-Mp,c=Ap(o-e);Ap(c-Mp)0?Tp:-Tp),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(a,r),t.point(o,r),n=0):i!==a&&c>=Mp&&(Ap(e-i)bp?Cp((Up(n)*(o=Pp(r))*Up(e)-Up(r)*(i=Pp(n))*Up(t))/(i*o*u)):(n+r)/2}(e,r,o,u),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(a,r),n=0),t.point(e=o,r=u),i=a},lineEnd:function(){t.lineEnd(),e=r=NaN},clean:function(){return 2-n}}},function(t,n,e,r){var i;if(null==t)i=e*Tp,r.point(-Mp,i),r.point(0,i),r.point(Mp,i),r.point(Mp,0),r.point(Mp,-i),r.point(0,-i),r.point(-Mp,-i),r.point(-Mp,0),r.point(-Mp,i);else if(Ap(t[0]-n[0])>bp){var o=t[0]bd&&(bd=t),nwd&&(wd=n)},lineStart:We,lineEnd:We,polygonStart:We,polygonEnd:We,result:function(){var t=[[md,xd],[bd,wd]];return bd=wd=-(xd=md=1/0),t}},Td=0,Nd=0,kd=0,Sd=0,Ed=0,Ad=0,Cd=0,zd=0,Pd=0,Rd={point:gi,lineStart:_i,lineEnd:xi,polygonStart:function(){Rd.lineStart=bi,Rd.lineEnd=wi},polygonEnd:function(){Rd.point=gi,Rd.lineStart=_i,Rd.lineEnd=xi},result:function(){var t=Pd?[Cd/Pd,zd/Pd]:Ad?[Sd/Ad,Ed/Ad]:kd?[Td/kd,Nd/kd]:[NaN,NaN];return Td=Nd=kd=Sd=Ed=Ad=Cd=zd=Pd=0,t}};Ni.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._context.moveTo(t,n),this._point=1;break;case 1:this._context.lineTo(t,n);break;default:this._context.moveTo(t+this._radius,n),this._context.arc(t,n,this._radius,0,kp)}},result:We};var Ld,qd,Dd,Ud,Od,Fd=Be(),Id={point:We,lineStart:function(){Id.point=ki},lineEnd:function(){Ld&&Si(qd,Dd),Id.point=We},polygonStart:function(){Ld=!0},polygonEnd:function(){Ld=null},result:function(){var t=+Fd;return Fd.reset(),t}};Ei.prototype={_radius:4.5,_circle:Ai(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._string.push("M",t,",",n),this._point=1;break;case 1:this._string.push("L",t,",",n);break;default:null==this._circle&&(this._circle=Ai(this._radius)),this._string.push("M",t,",",n,this._circle)}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}return null}},zi.prototype={constructor:zi,point:function(t,n){this.stream.point(t,n)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var Yd=16,Bd=Pp(30*Ep),Hd=Ci({point:function(t,n){this.stream.point(t*Ep,n*Ep)}}),jd=ji(function(t){return Fp(2/(1+t))});jd.invert=Xi(function(t){return 2*Ve(t/2)});var Xd=ji(function(t){return(t=Xe(t))&&t/Up(t)});Xd.invert=Xi(function(t){return t}),Vi.invert=function(t,n){return[t,2*Cp(Lp(n))-Tp]},Gi.invert=Gi,Ji.invert=Xi(Cp),to.invert=function(t,n){var e,r=n,i=25;do{var o=r*r,u=o*o;r-=e=(r*(1.007226+o*(.015085+u*(.028874*o-.044475-.005916*u)))-n)/(1.007226+o*(.045255+u*(.259866*o-.311325-.005916*11*u)))}while(Ap(e)>bp&&--i>0);return[t/(.8707+(o=r*r)*(o*(o*o*o*(.003971-.001529*o)-.013791)-.131979)),r]},no.invert=Xi(Ve),eo.invert=Xi(function(t){return 2*Cp(t)}),ro.invert=function(t,n){return[-n,2*Cp(Lp(t))-Tp]},ho.prototype=co.prototype={constructor:ho,count:function(){return this.eachAfter(ao)},each:function(t){var n,e,r,i,o=this,u=[o];do{for(n=u.reverse(),u=[];o=n.pop();)if(t(o),e=o.children)for(r=0,i=e.length;r=0;--e)i.push(n[e]);return this},sum:function(t){return this.eachAfter(function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e})},sort:function(t){return this.eachBefore(function(n){n.children&&n.children.sort(t)})},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;for(t=e.pop(),n=r.pop();t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){var t=[];return this.each(function(n){t.push(n)}),t},leaves:function(){var t=[];return this.eachBefore(function(n){n.children||t.push(n)}),t},links:function(){var t=this,n=[];return t.each(function(e){e!==t&&n.push({source:e.parent,target:e})}),n},copy:function(){return co(this).eachBefore(fo)}};var Vd=Array.prototype.slice,$d="$",Wd={depth:-1},Zd={};Yo.prototype=Object.create(ho.prototype);var Gd=(1+Math.sqrt(5))/2,Qd=function t(n){function e(t,e,r,i,o){Ho(n,t,e,r,i,o)}return e.ratio=function(n){return t((n=+n)>1?n:1)},e}(Gd),Jd=function t(n){function e(t,e,r,i,o){if((u=t._squarify)&&u.ratio===n)for(var u,a,c,s,f,l=-1,h=u.length,p=t.value;++l1?n:1)},e}(Gd),Kd=[].slice,tv={};$o.prototype=Qo.prototype={constructor:$o,defer:function(t){if("function"!=typeof t)throw new Error("invalid callback");if(this._call)throw new Error("defer after await");if(null!=this._error)return this;var n=Kd.call(arguments,1);return n.push(t),++this._waiting,this._tasks.push(n),Wo(this),this},abort:function(){return null==this._error&&Zo(this,new Error("abort")),this},await:function(t){if("function"!=typeof t)throw new Error("invalid callback");if(this._call)throw new Error("multiple await");return this._call=function(n,e){t.apply(null,[n].concat(e))},Go(this),this},awaitAll:function(t){if("function"!=typeof t)throw new Error("invalid callback");if(this._call)throw new Error("multiple await");return this._call=t,Go(this),this}};var nv=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,1===arguments.length?(e=t,t=0):e-=t,function(){return n()*e+t}}return e.source=t,e}(Jo),ev=function t(n){function e(t,e){var r,i;return t=null==t?0:+t,e=null==e?1:+e,function(){var o;if(null!=r)o=r,r=null;else do{r=2*n()-1,o=2*n()-1,i=r*r+o*o}while(!i||i>1);return t+e*o*Math.sqrt(-2*Math.log(i)/i)}}return e.source=t,e}(Jo),rv=function t(n){function e(){var t=ev.source(n).apply(this,arguments);return function(){return Math.exp(t())}}return e.source=t,e}(Jo),iv=function t(n){function e(t){return function(){for(var e=0,r=0;r0?t>1?Eu(function(n){n.setTime(Math.floor(n/t)*t)},function(n,e){n.setTime(+n+e*t)},function(n,e){return(e-n)/t}):xv:null};var bv=xv.range,wv=6e4,Mv=6048e5,Tv=Eu(function(t){t.setTime(1e3*Math.floor(t/1e3))},function(t,n){t.setTime(+t+1e3*n)},function(t,n){return(n-t)/1e3},function(t){return t.getUTCSeconds()}),Nv=Tv.range,kv=Eu(function(t){t.setTime(Math.floor(t/wv)*wv)},function(t,n){t.setTime(+t+n*wv)},function(t,n){return(n-t)/wv},function(t){return t.getMinutes()}),Sv=kv.range,Ev=Eu(function(t){var n=t.getTimezoneOffset()*wv%36e5;n<0&&(n+=36e5),t.setTime(36e5*Math.floor((+t-n)/36e5)+n)},function(t,n){t.setTime(+t+36e5*n)},function(t,n){return(n-t)/36e5},function(t){return t.getHours()}),Av=Ev.range,Cv=Eu(function(t){t.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*wv)/864e5},function(t){return t.getDate()-1}),zv=Cv.range,Pv=Au(0),Rv=Au(1),Lv=Au(2),qv=Au(3),Dv=Au(4),Uv=Au(5),Ov=Au(6),Fv=Pv.range,Iv=Rv.range,Yv=Lv.range,Bv=qv.range,Hv=Dv.range,jv=Uv.range,Xv=Ov.range,Vv=Eu(function(t){t.setDate(1),t.setHours(0,0,0,0)},function(t,n){t.setMonth(t.getMonth()+n)},function(t,n){return n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())},function(t){return t.getMonth()}),$v=Vv.range,Wv=Eu(function(t){t.setMonth(0,1),t.setHours(0,0,0,0)},function(t,n){t.setFullYear(t.getFullYear()+n)},function(t,n){return n.getFullYear()-t.getFullYear()},function(t){return t.getFullYear()});Wv.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Eu(function(n){n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)},function(n,e){n.setFullYear(n.getFullYear()+e*t)}):null};var Zv=Wv.range,Gv=Eu(function(t){t.setUTCSeconds(0,0)},function(t,n){t.setTime(+t+n*wv)},function(t,n){return(n-t)/wv},function(t){return t.getUTCMinutes()}),Qv=Gv.range,Jv=Eu(function(t){t.setUTCMinutes(0,0,0)},function(t,n){t.setTime(+t+36e5*n)},function(t,n){return(n-t)/36e5},function(t){return t.getUTCHours()}),Kv=Jv.range,tg=Eu(function(t){t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+n)},function(t,n){return(n-t)/864e5},function(t){return t.getUTCDate()-1}),ng=tg.range,eg=Cu(0),rg=Cu(1),ig=Cu(2),og=Cu(3),ug=Cu(4),ag=Cu(5),cg=Cu(6),sg=eg.range,fg=rg.range,lg=ig.range,hg=og.range,pg=ug.range,dg=ag.range,vg=cg.range,gg=Eu(function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCMonth(t.getUTCMonth()+n)},function(t,n){return n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())},function(t){return t.getUTCMonth()}),_g=gg.range,yg=Eu(function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCFullYear(t.getUTCFullYear()+n)},function(t,n){return n.getUTCFullYear()-t.getUTCFullYear()},function(t){return t.getUTCFullYear()});yg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Eu(function(n){n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)},function(n,e){n.setUTCFullYear(n.getUTCFullYear()+e*t)}):null};var mg,xg=yg.range,bg={"-":"",_:" ",0:"0"},wg=/^\s*\d+/,Mg=/^%/,Tg=/[\\^$*+?|[\]().{}]/g;Ya({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var Ng="%Y-%m-%dT%H:%M:%S.%LZ",kg=Date.prototype.toISOString?function(t){return t.toISOString()}:t.utcFormat(Ng),Sg=+new Date("2000-01-01T00:00:00.000Z")?function(t){var n=new Date(t);return isNaN(n)?null:n}:t.utcParse(Ng),Eg=1e3,Ag=60*Eg,Cg=60*Ag,zg=24*Cg,Pg=7*zg,Rg=30*zg,Lg=365*zg,qg=Xa("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"),Dg=Xa("393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6"),Ug=Xa("3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9"),Og=Xa("1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5"),Fg=ol(Xt(300,.5,0),Xt(-240,.5,1)),Ig=ol(Xt(-100,.75,.35),Xt(80,1.5,.8)),Yg=ol(Xt(260,.75,.35),Xt(80,1.5,.8)),Bg=Xt(),Hg=Va(Xa("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),jg=Va(Xa("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),Xg=Va(Xa("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),Vg=Va(Xa("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921")),$g=Math.abs,Wg=Math.atan2,Zg=Math.cos,Gg=Math.max,Qg=Math.min,Jg=Math.sin,Kg=Math.sqrt,t_=1e-12,n_=Math.PI,e_=n_/2,r_=2*n_;ec.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}};var i_=lc(rc);fc.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};var o_=Array.prototype.slice,u_={draw:function(t,n){var e=Math.sqrt(n/n_);t.moveTo(e,0),t.arc(0,0,e,0,r_)}},a_={draw:function(t,n){var e=Math.sqrt(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}},c_=Math.sqrt(1/3),s_=2*c_,f_={draw:function(t,n){var e=Math.sqrt(n/s_),r=e*c_;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},l_=Math.sin(n_/10)/Math.sin(7*n_/10),h_=Math.sin(r_/10)*l_,p_=-Math.cos(r_/10)*l_,d_={draw:function(t,n){var e=Math.sqrt(.8908130915292852*n),r=h_*e,i=p_*e;t.moveTo(0,-e),t.lineTo(r,i);for(var o=1;o<5;++o){var u=r_*o/5,a=Math.cos(u),c=Math.sin(u);t.lineTo(c*e,-a*e),t.lineTo(a*r-c*i,c*r+a*i)}t.closePath()}},v_={draw:function(t,n){var e=Math.sqrt(n),r=-e/2;t.rect(r,r,e,e)}},g_=Math.sqrt(3),__={draw:function(t,n){var e=-Math.sqrt(n/(3*g_));t.moveTo(0,2*e),t.lineTo(-g_*e,-e),t.lineTo(g_*e,-e),t.closePath()}},y_=Math.sqrt(3)/2,m_=1/Math.sqrt(12),x_=3*(m_/2+1),b_={draw:function(t,n){var e=Math.sqrt(n/x_),r=e/2,i=e*m_,o=r,u=e*m_+e,a=-o,c=u;t.moveTo(r,i),t.lineTo(o,u),t.lineTo(a,c),t.lineTo(-.5*r-y_*i,y_*r+-.5*i),t.lineTo(-.5*o-y_*u,y_*o+-.5*u),t.lineTo(-.5*a-y_*c,y_*a+-.5*c),t.lineTo(-.5*r+y_*i,-.5*i-y_*r),t.lineTo(-.5*o+y_*u,-.5*u-y_*o),t.lineTo(-.5*a+y_*c,-.5*c-y_*a),t.closePath()}},w_=[u_,a_,f_,v_,d_,__,b_];Tc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Mc(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Mc(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Nc.prototype={areaStart:wc,areaEnd:wc,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:Mc(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},kc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:Mc(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Sc.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],u=t[e]-i,a=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*u),this._beta*n[c]+(1-this._beta)*(o+r*a));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var M_=function t(n){function e(t){return 1===n?new Tc(t):new Sc(t,n)}return e.beta=function(n){return t(+n)},e}(.85);Ac.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Ec(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:Ec(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var T_=function t(n){function e(t){return new Ac(t,n)}return e.tension=function(n){return t(+n)},e}(0);Cc.prototype={areaStart:wc,areaEnd:wc,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Ec(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var N_=function t(n){function e(t){return new Cc(t,n)}return e.tension=function(n){return t(+n)},e}(0);zc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Ec(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var k_=function t(n){function e(t){return new zc(t,n)}return e.tension=function(n){return t(+n)},e}(0);Rc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3;default:Pc(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var S_=function t(n){function e(t){return n?new Rc(t,n):new Ac(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);Lc.prototype={areaStart:wc,areaEnd:wc,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Pc(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var E_=function t(n){function e(t){return n?new Lc(t,n):new Cc(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);qc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Pc(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var A_=function t(n){function e(t){return n?new qc(t,n):new zc(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);Dc.prototype={areaStart:wc,areaEnd:wc,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,n){t=+t,n=+n,this._point?this._context.lineTo(t,n):(this._point=1,this._context.moveTo(t,n))}},Yc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:Ic(this,this._t0,Fc(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){var e=NaN;if(t=+t,n=+n,t!==this._x1||n!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,Ic(this,Fc(this,e=Oc(this,t,n)),e);break;default:Ic(this,this._t0,e=Oc(this,t,n))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n,this._t0=e}}},(Bc.prototype=Object.create(Yc.prototype)).point=function(t,n){Yc.prototype.point.call(this,n,t)},Hc.prototype={moveTo:function(t,n){this._context.moveTo(n,t)},closePath:function(){this._context.closePath()},lineTo:function(t,n){this._context.lineTo(n,t)},bezierCurveTo:function(t,n,e,r,i,o){this._context.bezierCurveTo(n,t,r,e,o,i)}},jc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,n=this._y,e=t.length;if(e)if(this._line?this._context.lineTo(t[0],n[0]):this._context.moveTo(t[0],n[0]),2===e)this._context.lineTo(t[1],n[1]);else for(var r=Xc(t),i=Xc(n),o=0,u=1;u=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}},ns.prototype={constructor:ns,insert:function(t,n){var e,r,i;if(t){if(n.P=t,n.N=t.N,t.N&&(t.N.P=n),t.N=n,t.R){for(t=t.R;t.L;)t=t.L;t.L=n}else t.R=n;e=t}else this._?(t=os(this._),n.P=null,n.N=t,t.P=t.L=n,e=t):(n.P=n.N=null,this._=n,e=null);for(n.L=n.R=null,n.U=e,n.C=!0,t=n;e&&e.C;)e===(r=e.U).L?(i=r.R)&&i.C?(e.C=i.C=!1,r.C=!0,t=r):(t===e.R&&(rs(this,e),e=(t=e).U),e.C=!1,r.C=!0,is(this,r)):(i=r.L)&&i.C?(e.C=i.C=!1,r.C=!0,t=r):(t===e.L&&(is(this,e),e=(t=e).U),e.C=!1,r.C=!0,rs(this,r)),e=t.U;this._.C=!1},remove:function(t){t.N&&(t.N.P=t.P),t.P&&(t.P.N=t.N),t.N=t.P=null;var n,e,r,i=t.U,o=t.L,u=t.R;if(e=o?u?os(u):o:u,i?i.L===t?i.L=e:i.R=e:this._=e,o&&u?(r=e.C,e.C=t.C,e.L=o,o.U=e,e!==u?(i=e.U,e.U=t.U,t=e.R,i.L=t,e.R=u,u.U=e):(e.U=i,i=e,t=e.R)):(r=t.C,t=e),t&&(t.U=i),!r)if(t&&t.C)t.C=!1;else{do{if(t===this._)break;if(t===i.L){if((n=i.R).C&&(n.C=!1,i.C=!0,rs(this,i),n=i.R),n.L&&n.L.C||n.R&&n.R.C){n.R&&n.R.C||(n.L.C=!1,n.C=!0,is(this,n),n=i.R),n.C=i.C,i.C=n.R.C=!1,rs(this,i),t=this._;break}}else if((n=i.L).C&&(n.C=!1,i.C=!0,is(this,i),n=i.L),n.L&&n.L.C||n.R&&n.R.C){n.L&&n.L.C||(n.R.C=!1,n.C=!0,rs(this,n),n=i.L),n.C=i.C,i.C=n.L.C=!1,is(this,i),t=this._;break}n.C=!0,t=i,i=i.U}while(!t.C);t&&(t.C=!1)}}};var C_,z_,P_,R_,L_,q_=[],D_=[],U_=1e-6,O_=1e-12;Ms.prototype={constructor:Ms,polygons:function(){var t=this.edges;return this.cells.map(function(n){var e=n.halfedges.map(function(e){return hs(n,t[e])});return e.data=n.site.data,e})},triangles:function(){var t=[],n=this.edges;return this.cells.forEach(function(e,r){if(o=(i=e.halfedges).length)for(var i,o,u,a=e.site,c=-1,s=n[i[o-1]],f=s.left===a?s.right:s.left;++c=a)return null;var c=t-i.site[0],s=n-i.site[1],f=c*c+s*s;do{i=o.cells[r=u],u=null,i.halfedges.forEach(function(e){var r=o.edges[e],a=r.left;if(a!==i.site&&a||(a=r.right)){var c=t-a[0],s=n-a[1],l=c*c+s*s;lt?1:n>=t?0:NaN},t.deviation=u,t.extent=a,t.histogram=function(){function t(t){var i,o,u=t.length,a=new Array(u);for(i=0;il;)h.pop(),--d;var v,g=new Array(d+1);for(i=0;i<=d;++i)(v=g[i]=[]).x0=i>0?h[i-1]:s,v.x1=i=e)for(r=e;++or&&(r=e)}else for(;++o=e)for(r=e;++or&&(r=e);return r},t.mean=function(t,n){var e,r=t.length,o=r,u=-1,a=0;if(null==n)for(;++u=o.length)return null!=e&&n.sort(e),null!=r?r(n):n;for(var c,s,f,l=-1,h=n.length,p=o[i++],d=ae(),v=u();++lo.length)return t;var i,a=u[e-1];return null!=r&&e>=o.length?i=t.entries():(i=[],t.each(function(t,r){i.push({key:r,values:n(t,e)})})),null!=a?i.sort(function(t,n){return a(t.key,n.key)}):i}var e,r,i,o=[],u=[];return i={object:function(n){return t(n,0,ce,se)},map:function(n){return t(n,0,fe,le)},entries:function(e){return n(t(e,0,fe,le),0)},key:function(t){return o.push(t),i},sortKeys:function(t){return u[o.length-1]=t,i},sortValues:function(t){return e=t,i},rollup:function(t){return r=t,i}}},t.set=pe,t.map=ae,t.keys=function(t){var n=[];for(var e in t)n.push(e);return n},t.values=function(t){var n=[];for(var e in t)n.push(t[e]);return n},t.entries=function(t){var n=[];for(var e in t)n.push({key:e,value:t[e]});return n},t.color=kt,t.rgb=Ct,t.hsl=Rt,t.lab=Ut,t.hcl=Ht,t.cubehelix=Xt,t.dispatch=N,t.drag=function(){function n(t){t.on("mousedown.drag",e).filter(g).on("touchstart.drag",o).on("touchmove.drag",u).on("touchend.drag touchcancel.drag",a).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function e(){if(!h&&p.apply(this,arguments)){var n=c("mouse",d.apply(this,arguments),F,this,arguments);n&&(lt(t.event.view).on("mousemove.drag",r,!0).on("mouseup.drag",i,!0),vt(t.event.view),pt(),l=!1,s=t.event.clientX,f=t.event.clientY,n("start"))}}function r(){if(dt(),!l){var n=t.event.clientX-s,e=t.event.clientY-f;l=n*n+e*e>x}_.mouse("drag")}function i(){lt(t.event.view).on("mousemove.drag mouseup.drag",null),gt(t.event.view,l),dt(),_.mouse("end")}function o(){if(p.apply(this,arguments)){var n,e,r=t.event.changedTouches,i=d.apply(this,arguments),o=r.length;for(n=0;nc+p||is+p||or.index){var d=c-a.x-a.vx,v=s-a.y-a.vy,g=d*d+v*v;gt.r&&(t.r=t[n].r)}function r(){if(i){var n,e,r=i.length;for(o=new Array(r),n=0;n=f)){(t.data!==o||t.next)&&(0===i&&(i=_e(),p+=i*i),0===c&&(c=_e(),p+=c*c),p1?(null==n?l.remove(t):l.set(t,i(n)),o):l.get(t)},find:function(n,e,r){var i,o,u,a,c,s=0,f=t.length;for(null==r?r=1/0:r*=r,s=0;s1?(p.on(t,n),o):p.on(t)}}},t.forceX=function(t){function n(t){for(var n,e=0,u=r.length;e_r(r[0],r[1])&&(r[1]=i[1]),_r(i[0],r[1])>_r(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(u=-1/0,n=0,r=o[e=o.length-1];n<=e;r=i,++n)i=o[n],(a=_r(r[1],i[0]))>u&&(u=a,Wh=i[0],Gh=r[1])}return ep=rp=null,Wh===1/0||Zh===1/0?[[NaN,NaN],[NaN,NaN]]:[[Wh,Zh],[Gh,Qh]]},t.geoCentroid=function(t){ip=op=up=ap=cp=sp=fp=lp=hp=pp=dp=0,Je(t,Wp);var n=hp,e=pp,r=dp,i=n*n+e*e+r*r;return i=.12&&i<.234&&r>=-.425&&r<-.214?s:i>=.166&&i<.234&&r>=-.214&&r<-.115?f:c).invert(t)},t.stream=function(t){return e&&r===t?e:e=function(t){var n=t.length;return{point:function(e,r){for(var i=-1;++i2?t[2]+90:90]):(t=e(),[t[0],t[1],t[2]-90])},e([0,0,90]).scale(159.155)},t.geoTransverseMercatorRaw=ro,t.geoRotation=Ur,t.geoStream=Je,t.geoTransform=function(t){return{stream:Ci(t)}},t.cluster=function(){function t(t){var o,u=0;t.eachAfter(function(t){var e=t.children;e?(t.x=function(t){return t.reduce(oo,0)/t.length}(e),t.y=function(t){return 1+t.reduce(uo,0)}(e)):(t.x=o?u+=n(t,o):0,t.y=0,o=t)});var a=function(t){for(var n;n=t.children;)t=n[0];return t}(t),c=function(t){for(var n;n=t.children;)t=n[n.length-1];return t}(t),s=a.x-n(a,c)/2,f=c.x+n(c,a)/2;return t.eachAfter(i?function(n){n.x=(n.x-t.x)*e,n.y=(t.y-n.y)*r}:function(n){n.x=(n.x-s)/(f-s)*e,n.y=(1-(t.y?n.y/t.y:1))*r})}var n=io,e=1,r=1,i=!1;return t.separation=function(e){return arguments.length?(n=e,t):n},t.size=function(n){return arguments.length?(i=!1,e=+n[0],r=+n[1],t):i?null:[e,r]},t.nodeSize=function(n){return arguments.length?(i=!0,e=+n[0],r=+n[1],t):i?[e,r]:null},t},t.hierarchy=co,t.pack=function(){function t(t){return t.x=e/2,t.y=r/2,n?t.eachBefore(Ao(n)).eachAfter(Co(i,.5)).eachBefore(zo(1)):t.eachBefore(Ao(Eo)).eachAfter(Co(ko,1)).eachAfter(Co(i,t.r/Math.min(e,r))).eachBefore(zo(Math.min(e,r)/(2*t.r))),t}var n=null,e=1,r=1,i=ko;return t.radius=function(e){return arguments.length?(n=function(t){return null==t?null:No(t)}(e),t):n},t.size=function(n){return arguments.length?(e=+n[0],r=+n[1],t):[e,r]},t.padding=function(n){return arguments.length?(i="function"==typeof n?n:So(+n),t):i},t},t.packSiblings=function(t){return To(t),t},t.packEnclose=po,t.partition=function(){function t(t){var o=t.height+1;return t.x0=t.y0=r,t.x1=n,t.y1=e/o,t.eachBefore(function(t,n){return function(e){e.children&&Ro(e,e.x0,t*(e.depth+1)/n,e.x1,t*(e.depth+2)/n);var i=e.x0,o=e.y0,u=e.x1-r,a=e.y1-r;u0)throw new Error("cycle");return o}var n=Lo,e=qo;return t.id=function(e){return arguments.length?(n=No(e),t):n},t.parentId=function(n){return arguments.length?(e=No(n),t):e},t},t.tree=function(){function t(t){var c=function(t){for(var n,e,r,i,o,u=new Yo(t,0),a=[u];n=a.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)a.push(e=n.children[i]=new Yo(r[i],i)),e.parent=n;return(u.parent=new Yo(null,0)).children=[u],u}(t);if(c.eachAfter(n),c.parent.m=-c.z,c.eachBefore(e),a)t.eachBefore(r);else{var s=t,f=t,l=t;t.eachBefore(function(t){t.xf.x&&(f=t),t.depth>l.depth&&(l=t)});var h=s===f?1:i(s,f)/2,p=h-s.x,d=o/(f.x+h+p),v=u/(l.depth||1);t.eachBefore(function(t){t.x=(t.x+p)*d,t.y=t.depth*v})}return t}function n(t){var n=t.children,e=t.parent.children,r=t.i?e[t.i-1]:null;if(n){(function(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)(n=i[o]).z+=e,n.m+=e,e+=n.s+(r+=n.c)})(t);var o=(n[0].z+n[n.length-1].z)/2;r?(t.z=r.z+i(t._,r._),t.m=t.z-o):t.z=o}else r&&(t.z=r.z+i(t._,r._));t.parent.A=function(t,n,e){if(n){for(var r,o=t,u=t,a=n,c=o.parent.children[0],s=o.m,f=u.m,l=a.m,h=c.m;a=Oo(a),o=Uo(o),a&&o;)c=Uo(c),(u=Oo(u)).a=t,(r=a.z+l-o.z-s+i(a._,o._))>0&&(Fo(Io(a,t,e),t,r),s+=r,f+=r),l+=a.m,s+=o.m,h+=c.m,f+=u.m;a&&!Oo(u)&&(u.t=a,u.m+=l-f),o&&!Uo(c)&&(c.t=o,c.m+=s-h,e=t)}return e}(t,r,t.parent.A||e[0])}function e(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function r(t){t.x*=o,t.y=t.depth*u}var i=Do,o=1,u=1,a=null;return t.separation=function(n){return arguments.length?(i=n,t):i},t.size=function(n){return arguments.length?(a=!1,o=+n[0],u=+n[1],t):a?null:[o,u]},t.nodeSize=function(n){return arguments.length?(a=!0,o=+n[0],u=+n[1],t):a?[o,u]:null},t},t.treemap=function(){function t(t){return t.x0=t.y0=0,t.x1=i,t.y1=o,t.eachBefore(n),u=[0],r&&t.eachBefore(Po),t}function n(t){var n=u[t.depth],r=t.x0+n,i=t.y0+n,o=t.x1-n,h=t.y1-n;o=n-1){var s=c[t];return s.x0=r,s.y0=i,s.x1=u,void(s.y1=a)}for(var l=f[t],h=e/2+l,p=t+1,d=n-1;p>>1;f[v]a-i){var y=(r*_+u*g)/e;o(t,p,g,r,i,y,a),o(p,n,_,y,i,u,a)}else{var m=(i*_+a*g)/e;o(t,p,g,r,i,u,m),o(p,n,_,r,m,u,a)}}var u,a,c=t.children,s=c.length,f=new Array(s+1);for(f[0]=a=u=0;u=0;--n)s.push(t[r[o[n]][2]]);for(n=+a;na!=s>a&&u<(c-e)*(a-r)/(s-r)+e&&(f=!f),c=e,s=r;return f},t.polygonLength=function(t){for(var n,e,r=-1,i=t.length,o=t[i-1],u=o[0],a=o[1],c=0;++r1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return Bg.h=360*t-100,Bg.s=1.5-1.5*n,Bg.l=.8-.9*n,Bg+""},t.interpolateWarm=Ig,t.interpolateCool=Yg,t.interpolateViridis=Hg,t.interpolateMagma=jg,t.interpolateInferno=Xg,t.interpolatePlasma=Vg,t.scaleSequential=$a,t.creator=A,t.local=C,t.matcher=rf,t.mouse=F,t.namespace=E,t.namespaces=Js,t.clientPoint=O,t.select=lt,t.selectAll=function(t){return"string"==typeof t?new st([document.querySelectorAll(t)],[document.documentElement]):new st([null==t?[]:t],af)},t.selection=ft,t.selector=Y,t.selectorAll=H,t.style=G,t.touch=ht,t.touches=function(t,n){null==n&&(n=U().touches);for(var e=0,r=n?n.length:0,i=new Array(r);eh;if(c||(c=t=te()),lt_)if(d>r_-t_)c.moveTo(l*Zg(h),l*Jg(h)),c.arc(0,0,l,h,p,!v),f>t_&&(c.moveTo(f*Zg(p),f*Jg(p)),c.arc(0,0,f,p,h,v));else{var g,_,y=h,m=p,x=h,b=p,w=d,M=d,T=a.apply(this,arguments)/2,N=T>t_&&(i?+i.apply(this,arguments):Kg(f*f+l*l)),k=Qg($g(l-f)/2,+r.apply(this,arguments)),S=k,E=k;if(N>t_){var A=Za(N/f*Jg(T)),C=Za(N/l*Jg(T));(w-=2*A)>t_?(A*=v?1:-1,x+=A,b-=A):(w=0,x=b=(h+p)/2),(M-=2*C)>t_?(C*=v?1:-1,y+=C,m-=C):(M=0,y=m=(h+p)/2)}var z=l*Zg(y),P=l*Jg(y),R=f*Zg(b),L=f*Jg(b);if(k>t_){var q=l*Zg(m),D=l*Jg(m),U=f*Zg(x),O=f*Jg(x);if(dt_?function(t,n,e,r,i,o,u,a){var c=e-t,s=r-n,f=u-i,l=a-o,h=(f*(n-o)-l*(t-i))/(l*c-f*s);return[t+h*c,n+h*s]}(z,P,U,O,q,D,R,L):[R,L],I=z-F[0],Y=P-F[1],B=q-F[0],H=D-F[1],j=1/Jg(function(t){return t>1?0:t<-1?n_:Math.acos(t)}((I*B+Y*H)/(Kg(I*I+Y*Y)*Kg(B*B+H*H)))/2),X=Kg(F[0]*F[0]+F[1]*F[1]);S=Qg(k,(f-X)/(j-1)),E=Qg(k,(l-X)/(j+1))}}M>t_?E>t_?(g=nc(U,O,z,P,l,E,v),_=nc(q,D,R,L,l,E,v),c.moveTo(g.cx+g.x01,g.cy+g.y01),Et_&&w>t_?S>t_?(g=nc(R,L,q,D,f,-S,v),_=nc(z,P,U,O,f,-S,v),c.lineTo(g.cx+g.x01,g.cy+g.y01),S0&&(p+=l);for(null!=e?d.sort(function(t,n){return e(v[t],v[n])}):null!=r&&d.sort(function(n,e){return r(t[n],t[e])}),a=0,s=p?(_-h*m)/p:0;a0?l*s:0)+m,v[c]={data:t[c],index:a,value:l,startAngle:g,endAngle:f,padAngle:y};return v}var n=sc,e=cc,r=null,i=Wa(0),o=Wa(r_),u=Wa(0);return t.value=function(e){return arguments.length?(n="function"==typeof e?e:Wa(+e),t):n},t.sortValues=function(n){return arguments.length?(e=n,r=null,t):e},t.sort=function(n){return arguments.length?(r=n,e=null,t):r},t.startAngle=function(n){return arguments.length?(i="function"==typeof n?n:Wa(+n),t):i},t.endAngle=function(n){return arguments.length?(o="function"==typeof n?n:Wa(+n),t):o},t.padAngle=function(n){return arguments.length?(u="function"==typeof n?n:Wa(+n),t):u},t},t.areaRadial=dc,t.radialArea=dc,t.lineRadial=pc,t.radialLine=pc,t.pointRadial=vc,t.linkHorizontal=function(){return yc(mc)},t.linkVertical=function(){return yc(xc)},t.linkRadial=function(){var t=yc(bc);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t},t.symbol=function(){function t(){var t;if(r||(r=t=te()),n.apply(this,arguments).draw(r,+e.apply(this,arguments)),t)return r=null,t+""||null}var n=Wa(u_),e=Wa(64),r=null;return t.type=function(e){return arguments.length?(n="function"==typeof e?e:Wa(e),t):n},t.size=function(n){return arguments.length?(e="function"==typeof n?n:Wa(+n),t):e},t.context=function(n){return arguments.length?(r=null==n?null:n,t):r},t},t.symbols=w_,t.symbolCircle=u_,t.symbolCross=a_,t.symbolDiamond=f_,t.symbolSquare=v_,t.symbolStar=d_,t.symbolTriangle=__,t.symbolWye=b_,t.curveBasisClosed=function(t){return new Nc(t)},t.curveBasisOpen=function(t){return new kc(t)},t.curveBasis=function(t){return new Tc(t)},t.curveBundle=M_,t.curveCardinalClosed=N_,t.curveCardinalOpen=k_,t.curveCardinal=T_,t.curveCatmullRomClosed=E_,t.curveCatmullRomOpen=A_,t.curveCatmullRom=S_,t.curveLinearClosed=function(t){return new Dc(t)},t.curveLinear=rc,t.curveMonotoneX=function(t){return new Yc(t)},t.curveMonotoneY=function(t){return new Bc(t)},t.curveNatural=function(t){return new jc(t)},t.curveStep=function(t){return new Vc(t,.5)},t.curveStepAfter=function(t){return new Vc(t,1)},t.curveStepBefore=function(t){return new Vc(t,0)},t.stack=function(){function t(t){var o,u,a=n.apply(this,arguments),c=t.length,s=a.length,f=new Array(s);for(o=0;o0){for(var e,r,i,o=0,u=t[0].length;o1)for(var e,r,i,o,u,a,c=0,s=t[n[0]].length;c=0?(r[0]=o,r[1]=o+=i):i<0?(r[1]=u,r[0]=u+=i):r[0]=o},t.stackOffsetNone=$c,t.stackOffsetSilhouette=function(t,n){if((e=t.length)>0){for(var e,r=0,i=t[n[0]],o=i.length;r0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,u=1;uyl&&e.name===n)return new Rn([[t]],Gl,n,+r)}return null},t.interrupt=Cn,t.voronoi=function(){function t(t){return new Ms(t.map(function(r,i){var o=[Math.round(n(r,i,t)/U_)*U_,Math.round(e(r,i,t)/U_)*U_];return o.index=i,o.data=r,o}),r)}var n=Kc,e=ts,r=null;return t.polygons=function(n){return t(n).polygons()},t.links=function(n){return t(n).links()},t.triangles=function(n){return t(n).triangles()},t.x=function(e){return arguments.length?(n="function"==typeof e?e:Jc(+e),t):n},t.y=function(n){return arguments.length?(e="function"==typeof n?n:Jc(+n),t):e},t.extent=function(n){return arguments.length?(r=null==n?null:[[+n[0][0],+n[0][1]],[+n[1][0],+n[1][1]]],t):r&&[[r[0][0],r[0][1]],[r[1][0],r[1][1]]]},t.size=function(n){return arguments.length?(r=null==n?null:[[0,0],[+n[0],+n[1]]],t):r&&[r[1][0]-r[0][0],r[1][1]-r[0][1]]},t},t.zoom=function(){function n(t){t.property("__zoom",zs).on("wheel.zoom",c).on("mousedown.zoom",s).on("dblclick.zoom",f).filter(x).on("touchstart.zoom",l).on("touchmove.zoom",h).on("touchend.zoom touchcancel.zoom",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function e(t,n){return(n=Math.max(b[0],Math.min(b[1],n)))===t.k?t:new Ns(n,t.x,t.y)}function r(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new Ns(t.k,r,i)}function i(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function o(t,n,e){t.on("start.zoom",function(){u(this,arguments).start()}).on("interrupt.zoom end.zoom",function(){u(this,arguments).end()}).tween("zoom",function(){var t=arguments,r=u(this,t),o=_.apply(this,t),a=e||i(o),c=Math.max(o[1][0]-o[0][0],o[1][1]-o[0][1]),s=this.__zoom,f="function"==typeof n?n.apply(this,t):n,l=T(s.invert(a).concat(c/s.k),f.invert(a).concat(c/f.k));return function(t){if(1===t)t=f;else{var n=l(t),e=c/n[2];t=new Ns(e,a[0]-n[0]*e,a[1]-n[1]*e)}r.zoom(null,t)}})}function u(t,n){for(var e,r=0,i=k.length;rC}n.zoom("mouse",y(r(n.that.__zoom,n.mouse[0]=F(n.that),n.mouse[1]),n.extent,w))},!0).on("mouseup.zoom",function(){e.on("mousemove.zoom mouseup.zoom",null),gt(t.event.view,n.moved),Es(),n.end()},!0),i=F(this),o=t.event.clientX,a=t.event.clientY;vt(t.event.view),Ss(),n.mouse=[i,this.__zoom.invert(i)],Cn(this),n.start()}}function f(){if(g.apply(this,arguments)){var i=this.__zoom,u=F(this),a=i.invert(u),c=i.k*(t.event.shiftKey?.5:2),s=y(r(e(i,c),u,a),_.apply(this,arguments),w);Es(),M>0?lt(this).transition().duration(M).call(o,s,u):lt(this).call(n.transform,s)}}function l(){if(g.apply(this,arguments)){var n,e,r,i,o=u(this,arguments),a=t.event.changedTouches,c=a.length;for(Ss(),e=0;ea;a++)n[a]=arguments[a];for(var i=0,o=n.length;o>i;i++){var s=!0,l=!1,c=void 0;try{for(var f,d=u(n[i]);!(s=(f=d.next()).done);s=!0){var h=f.value;t["delete"](h)}}catch(p){l=!0,c=p}finally{try{!s&&d["return"]&&d["return"]()}finally{if(l)throw c}}}return t}},{key:"intersection",value:function(){var t=new e,r=!0,n=!1,a=void 0;try{for(var i,o=u(this);!(r=(i=o.next()).done);r=!0){for(var s=i.value,l=arguments.length,c=Array(l),f=0;l>f;f++)c[f]=arguments[f];c.every(function(e){return e.has(s)})&&t.add(s)}}catch(d){n=!0,a=d}finally{try{!r&&o["return"]&&o["return"]()}finally{if(n)throw a}}return t}},{key:"pop",value:function(){try{var e=this.values().next().value;return this["delete"](e),e}catch(t){}}},{key:l,value:function(){return this.values()}},{key:"size",get:function(){return this._map.size}}]),e}();r["default"]=v},{"./Map":3,"./toIterator":39,"babel-runtime/core-js/get-iterator":89,"babel-runtime/core-js/symbol/iterator":100,"babel-runtime/helpers/class-call-check":101,"babel-runtime/helpers/create-class":102,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/regenerator":166}],6:[function(e,t,r){(function(e){"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=e.Worker,t.exports=r["default"]}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],7:[function(e,t,r){"use strict";function n(e){for(var t in e)delete e[t]}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n,t.exports=r["default"]},{}],8:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0});var a=e("lodash/lang/clone"),i=n(a);r["default"]=i["default"],t.exports=r["default"]},{"babel-runtime/helpers/interop-require-default":107,"lodash/lang/clone":223}],9:[function(e,t,r){"use strict";function n(e,t,r){var n=function(){};n.prototype=e.constructor.prototype;var i,o,u={};for(i in e)e.hasOwnProperty(i)&&(u[i]=e[i]);u=a(u,t,r),o=new n;for(i in u)o[i]=u[i];return o}function a(e,t,r){return s["default"](e,!0,function(e){if(d["default"](e)||p["default"](e)||c["default"](e)){var a=n(e,t,r);return t.push(e),r.push(a),a}},null,null,t,r)}function i(e){return a(e,[],[])}var o=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=i;var u=e("lodash/internal/baseClone"),s=o(u),l=e("./isGraph"),c=o(l),f=e("./isMap"),d=o(f),h=e("./isSet"),p=o(h);t.exports=r["default"]},{"./isGraph":23,"./isMap":26,"./isSet":28,"babel-runtime/helpers/interop-require-default":107,"lodash/internal/baseClone":179}],10:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0});var a=e("lodash/object/merge"),i=n(a);r["default"]=i["default"],t.exports=r["default"]},{"babel-runtime/helpers/interop-require-default":107,"lodash/object/merge":238}],11:[function(e,t,r){"use strict";function n(e,t){return new a(function(r,n){try{var a=s["default"].methodLookupFunction(e).apply(null,t);c["default"](a)&&(a=i(a)),r(a)}catch(o){n(o)}})}var a=e("babel-runtime/core-js/promise")["default"],i=e("babel-runtime/core-js/array/from")["default"],o=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var u=e("../WorkerSettings"),s=o(u),l=e("./isIterator"),c=o(l);t.exports=r["default"]},{"../WorkerSettings":1,"./isIterator":25,"babel-runtime/core-js/array/from":88,"babel-runtime/core-js/promise":98,"babel-runtime/helpers/interop-require-default":107}],12:[function(e,t,r){"use strict";function n(e,t){return o(e,t)}var a=e("babel-runtime/core-js/promise")["default"],i=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var o,u=e("./Worker"),s=i(u),l=e("../WorkerSettings"),c=i(l),f=e("./delegateSync"),d=i(f),h=e("./message");o="function"==typeof s["default"]?function(e,t){var r=h.serializeAll(t),n=r.serializable,i=r.serializedValues;return n?new a(function(t,r){var n=new s["default"](c["default"].workerPath);n.addEventListener("message",function(e){return t(h.deserialize(e.data))},!1),n.addEventListener("error",r,!1),n.postMessage({method:e,args:i})}):(console.info("At least one argument can't be serialized and sent to the worker. "+("We will run "+e+" in the same thread instead.")),d["default"](e,t))}:function(e,t){return console.info('Workers are not supported in this environment, so "'+e+'" will run in the same thread instead. This might block the environment.'),d["default"](e,t)},t.exports=r["default"]},{"../WorkerSettings":1,"./Worker":6,"./delegateSync":11,"./message":32,"babel-runtime/core-js/promise":98,"babel-runtime/helpers/interop-require-default":107}],13:[function(e,t,r){"use strict";function n(e,t){for(var r=new Array(e),n=0;e>n;n++)r[n]=t;return r}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n,t.exports=r["default"]},{}],14:[function(e,t,r){"use strict";function n(e,t,r){if(Array.isArray(e)){var n=0,i=e.length;if(r)for(;i>n;n++)t.call(r,e[n],n);else for(;i>n;n++)t(e[n],n)}else if(u["default"](e)&&(e=a(e)),l["default"](e)){var o,s;if(void 0!==r){var c=!0,f=!1,d=void 0;try{for(var h,p=a(e);!(c=(h=p.next()).done);c=!0)o=h.value,s+=1,t.call(r,o,s)}catch(v){f=!0,d=v}finally{try{!c&&p["return"]&&p["return"]()}finally{if(f)throw d}}}else{var b=!0,g=!1,y=void 0;try{for(var m,w=a(e);!(b=(m=w.next()).done);b=!0)o=m.value,s+=1,t(o,s)}catch(v){g=!0,y=v}finally{try{!b&&w["return"]&&w["return"]()}finally{if(g)throw y}}}}else if(e&&"object"==typeof e)if(r)for(var x in e)t.call(r,e[x],x);else for(var x in e)t(e[x],x)}var a=e("babel-runtime/core-js/get-iterator")["default"],i=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var o=e("./isIterable"),u=i(o),s=e("./isIterator"),l=i(s);t.exports=r["default"]},{"./isIterable":24,"./isIterator":25,"babel-runtime/core-js/get-iterator":89,"babel-runtime/helpers/interop-require-default":107}],15:[function(e,t,r){"use strict";function n(e,t){for(;0!==t;){var r=e;e=t,t=r%t}return e}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n,t.exports=r["default"]},{}],16:[function(e,t,r){"use strict";function n(e){return e.slice().reverse()}function a(e,t){var r,a,u,l,f,d,h;return i.wrap(function(i){for(;;)switch(i.prev=i.next){case 0:if(r=o(e),a=r.length,!(t>a)){i.next=4;break}return i.abrupt("return");case 4:return u=c["default"](t),l=n(u),i.next=8,u.map(function(e){return r[e]});case 8:f=void 0,d=0;case 11:if(!(dh;h++)u[h]=u[h-1]+1;return i.next=24,u.map(function(e){return r[e]});case 24:i.next=8;break;case 26:case"end":return i.stop()}},s[0],this)}var i=e("babel-runtime/regenerator")["default"],o=e("babel-runtime/core-js/array/from")["default"],u=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=a;var s=[a].map(i.mark),l=e("./range"),c=u(l);t.exports=r["default"]},{"./range":35,"babel-runtime/core-js/array/from":88,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/regenerator":166}],17:[function(e,t,r){"use strict";function n(e,t){var r,n,o,s,c,f,d,h,p;return a.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:if(r=i(e),n=r.length,t=null==t?n:t,!(t>n)){a.next=5;break}return a.abrupt("return");case 5:return o=l["default"](n),s=l["default"](n,n-t,-1),c=l["default"](t-1,-1,-1),a.next=10,o.slice(0,t).map(function(e){return r[e]});case 10:f=0;case 12:if(!(fr,o=e;case 18:if(!(n&&o>t||!n&&t>o)){a.next=24;break}return a.next=21,o;case 21:o+=r,a.next=18;break;case 24:case"end":return a.stop()}},i[0],this)}var a=e("babel-runtime/regenerator")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var i=[n].map(a.mark);t.exports=r["default"]},{"babel-runtime/regenerator":166}],19:[function(e,t,r){"use strict";function n(e,t){return null==e?t:e}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n,t.exports=r["default"]},{}],20:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-default")["default"],a=e("babel-runtime/helpers/interop-require-wildcard")["default"],i=e("babel-runtime/helpers/defaults")["default"];Object.defineProperty(r,"__esModule",{value:!0});var o=e("./Arrays"),u=n(o),s=e("./Map"),l=n(s),c=e("./PriorityQueue"),f=n(c),d=e("./Set"),h=n(d),p=e("./clone"),v=n(p),b=e("./clear"),g=n(b),y=e("./deepcopy"),m=n(y),w=e("./deepmerge"),x=n(w),k=e("./gcd"),j=n(k),E=e("./genCombinations"),_=n(E),S=e("./genPermutations"),O=n(S),M=e("./genRange"),I=n(M),P=e("./getDefault"),N=n(P),A=e("./fillArray"),$=n(A),q=e("./forEach"),D=n(q),L=e("./isArrayLike"),F=n(L),G=e("./isBoolean"),C=n(G),z=e("./isGraph"),T=n(z),J=e("./isIterable"),R=n(J),X=e("./isIterator"),B=n(X),U=e("./isMap"),V=n(U),W=e("./isPlainObject"),H=n(W),K=e("./mapIterator"),Y=n(K),Q=e("./mapSequence"),Z=n(Q),ee=e("./max"),te=n(ee),re=e("./next"),ne=n(re),ae=e("./nodesAreEqual"),ie=n(ae),oe=e("./range"),ue=n(oe),se=e("./someIterator"),le=n(se),ce=e("./toIterator"),fe=n(ce),de=e("./tuple"),he=a(de),pe=e("./size"),ve=n(pe),be=e("./sprintf"),ge=n(be),ye=e("./zipIterator"),me=n(ye),we=e("./zipSequence"),xe=n(we);r.Arrays=u["default"],r.Map=l["default"],r.PriorityQueue=f["default"],r.Set=h["default"],r.clone=v["default"],r.clear=g["default"],r.deepcopy=m["default"],r.deepmerge=x["default"],r.gcd=j["default"],r.genCombinations=_["default"],r.genPermutations=O["default"],r.genRange=I["default"],r.getDefault=N["default"],r.fillArray=$["default"],r.forEach=D["default"],r.isArrayLike=F["default"],r.isBoolean=C["default"],r.isGraph=T["default"],r.isIterable=R["default"],r.isIterator=B["default"],r.isMap=V["default"],r.isPlainObject=H["default"],r.mapIterator=Y["default"],r.mapSequence=Z["default"],r.max=te["default"],r.next=ne["default"],r.nodesAreEqual=ie["default"],r.range=ue["default"],r.someIterator=le["default"],r.toIterator=fe["default"],r.tuple=he,r.size=ve["default"],r.sprintf=ge["default"],r.zipIterator=me["default"],r.zipSequence=xe["default"],i(r,a(de))},{"./Arrays":2,"./Map":3,"./PriorityQueue":4,"./Set":5,"./clear":7,"./clone":8,"./deepcopy":9,"./deepmerge":10,"./fillArray":13,"./forEach":14,"./gcd":15,"./genCombinations":16,"./genPermutations":17,"./genRange":18,"./getDefault":19,"./isArrayLike":21,"./isBoolean":22,"./isGraph":23,"./isIterable":24,"./isIterator":25,"./isMap":26,"./isPlainObject":27,"./mapIterator":29,"./mapSequence":30,"./max":31,"./next":33,"./nodesAreEqual":34,"./range":35,"./size":36,"./someIterator":37,"./sprintf":38,"./toIterator":39,"./tuple":40,"./zipIterator":41,"./zipSequence":42,"babel-runtime/helpers/defaults":103,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/interop-require-wildcard":108}],21:[function(e,t,r){"use strict";function n(e){return e&&"object"==typeof e&&"number"==typeof e.length&&"function"!=typeof e}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n,t.exports=r["default"]},{}],22:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0});var a=e("lodash/lang/isBoolean"),i=n(a);r["default"]=i["default"],t.exports=r["default"]},{"babel-runtime/helpers/interop-require-default":107,"lodash/lang/isBoolean":226}],23:[function(e,t,r){"use strict";function n(e){return e&&"function"==typeof e.addNode}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n,t.exports=r["default"]},{}],24:[function(e,t,r){"use strict";function n(e){return"function"==typeof e[a]}var a=e("babel-runtime/core-js/symbol/iterator")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n,t.exports=r["default"]},{"babel-runtime/core-js/symbol/iterator":100}],25:[function(e,t,r){"use strict";function n(e){return e&&"function"==typeof e.next}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n,t.exports=r["default"]},{}],26:[function(e,t,r){"use strict";function n(e){return e instanceof o["default"]}var a=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var i=e("./Map"),o=a(i);t.exports=r["default"]},{"./Map":3,"babel-runtime/helpers/interop-require-default":107}],27:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0});var a=e("lodash/lang/isPlainObject"),i=n(a);r["default"]=i["default"],t.exports=r["default"]},{"babel-runtime/helpers/interop-require-default":107,"lodash/lang/isPlainObject":230}],28:[function(e,t,r){"use strict";function n(e){return e instanceof o["default"]}var a=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var i=e("./Set"),o=a(i);t.exports=r["default"]},{"./Set":5,"babel-runtime/helpers/interop-require-default":107}],29:[function(e,t,r){"use strict";function n(e,t,r){var n,u,s,l,c,f;return a.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:n=!0,u=!1,s=void 0,a.prev=3,l=i(e);case 5:if(n=(c=l.next()).done){a.next=12;break}return f=c.value,a.next=9,t.call(r,f);case 9:n=!0,a.next=5;break;case 12:a.next=18;break;case 14:a.prev=14,a.t0=a["catch"](3),u=!0,s=a.t0;case 18:a.prev=18,a.prev=19,!n&&l["return"]&&l["return"]();case 21:if(a.prev=21,!u){a.next=24;break}throw s;case 24:return a.finish(21);case 25:return a.finish(18);case 26:case"end":return a.stop()}},o[0],this,[[3,14,18,26],[19,,21,25]])}var a=e("babel-runtime/regenerator")["default"],i=e("babel-runtime/core-js/get-iterator")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var o=[n].map(a.mark);t.exports=r["default"]},{"babel-runtime/core-js/get-iterator":89,"babel-runtime/regenerator":166}],30:[function(e,t,r){"use strict";function n(e,t,r){if(f["default"](e))return y.call(e,t,r);if(h["default"](e)&&(e=a(e)),v["default"](e))return g["default"](e,t,r);if(u["default"](e))return l["default"](e,t,r);throw new TypeError("Can't map value of type %s",typeof e)}var a=e("babel-runtime/core-js/get-iterator")["default"],i=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var o=e("lodash/lang/isPlainObject"),u=i(o),s=e("lodash/object/mapValues"),l=i(s),c=e("./isArrayLike"),f=i(c),d=e("./isIterable"),h=i(d),p=e("./isIterator"),v=i(p),b=e("./mapIterator"),g=i(b),y=Array.prototype.map;t.exports=r["default"]},{"./isArrayLike":21,"./isIterable":24,"./isIterator":25,"./mapIterator":29,"babel-runtime/core-js/get-iterator":89,"babel-runtime/helpers/interop-require-default":107,"lodash/lang/isPlainObject":230,"lodash/object/mapValues":237}],31:[function(e,t,r){"use strict";function n(e,t){var r,n=-(1/0);return o["default"](e,function(e){var a=t?t(e):e;a>n&&(n=a,r=e)}),r}var a=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var i=e("./forEach"),o=a(i);t.exports=r["default"]},{"./forEach":14,"babel-runtime/helpers/interop-require-default":107}],32:[function(e,t,r){"use strict";function n(e){var t;return t={},h(t,I,"Set"),h(t,"data",v(e.values())),t}function a(e){return new S["default"](e.data)}function i(e){var t;return t={},h(t,I,"Map"),h(t,"data",function(){var t=[],r=!0,n=!1,a=void 0;try{for(var i,o=b(e);!(r=(i=o.next()).done);r=!0){var u=p(i.value,2),s=u[0],l=u[1];t.push([s,c(l)])}}catch(f){n=!0,a=f}finally{try{!r&&o["return"]&&o["return"]()}finally{if(n)throw a}}return t}()),t}function o(e){return new E["default"](e.data.map(function(e){return e[1]=f(e[1]),e}))}function u(e){var t;return t={},h(t,I,e.constructor.__name__),h(t,"data",e.graph),h(t,"nodes",v(e.node)),h(t,"edges",e.edges(null,!0)),t}function s(e){var t=new M[e[I]](e.edges,e.data);return t.addNodesFrom(e.nodes),t}function l(e){var t=typeof e;return null==e||"string"===t||"number"===t||"boolean"===t||k["default"](e)||Array.isArray(e)||e instanceof E["default"]||e instanceof S["default"]||"Graph"===e.constructor.__name__||"DiGraph"===e.constructor.__name__||w["default"](e)}function c(e){var t=typeof e;return e&&"string"!==t&&"number"!==t&&"boolean"!==t?e instanceof S["default"]?n(e):e instanceof E["default"]?i(e):"Graph"===e.constructor.__name__||"DiGraph"===e.constructor.__name__?u(e):w["default"](e)?v(e):e:e}function f(e){var t=typeof e;if(!e||"string"===t||"number"===t||"boolean"===t)return e;if(e[I])switch(e[I]){case"Map":return o(e);case"Set":return a(e);case"Graph":case"DiGraph":return s(e)}return e}function d(){var e=arguments.length<=0||void 0===arguments[0]?[]:arguments[0],t=new Array(e.length),r=e.every(function(e,r){var n=l(e);return n&&(t[r]=c(e)),n});return{serializable:r,serializedValues:t}}var h=e("babel-runtime/helpers/define-property")["default"],p=e("babel-runtime/helpers/sliced-to-array")["default"],v=e("babel-runtime/core-js/array/from")["default"],b=e("babel-runtime/core-js/get-iterator")["default"],g=e("babel-runtime/helpers/interop-require-default")["default"],y=e("babel-runtime/helpers/interop-require-wildcard")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.isSupported=l,r.serialize=c,r.deserialize=f,r.serializeAll=d;var m=e("./isIterable"),w=g(m),x=e("./isPlainObject"),k=g(x),j=e("./Map"),E=g(j),_=e("./Set"),S=g(_),O=e("../classes"),M=y(O),I="__type-jsnx__"},{"../classes":65,"./Map":3,"./Set":5,"./isIterable":24,"./isPlainObject":27,"babel-runtime/core-js/array/from":88,"babel-runtime/core-js/get-iterator":89,"babel-runtime/helpers/define-property":104,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/interop-require-wildcard":108,"babel-runtime/helpers/sliced-to-array":109}],33:[function(e,t,r){"use strict";function n(e){var t=e.next();if(t.done)throw new Error("Iterator is already exhausted");return t.value}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n,t.exports=r["default"]},{}],34:[function(e,t,r){"use strict";function n(e,t){return e===t||"object"==typeof e&&e.toString()===t.toString()}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n,t.exports=r["default"]},{}],35:[function(e,t,r){"use strict";function n(e,t,r){return a(u["default"](e,t,r))}var a=e("babel-runtime/core-js/array/from")["default"],i=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var o=e("./genRange"),u=i(o);t.exports=r["default"]},{"./genRange":18,"babel-runtime/core-js/array/from":88,"babel-runtime/helpers/interop-require-default":107}],36:[function(e,t,r){"use strict";function n(e){if(s["default"](e))return e.numberOfNodes();if("string"==typeof e||o["default"](e))return e.length;if(c["default"](e))return d["default"](e);throw new TypeError("Expected a graph object, array, string or object, but got %s instead",typeof e)}var a=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var i=e("./isArrayLike"),o=a(i),u=e("./isGraph"),s=a(u),l=e("lodash/lang/isPlainObject"),c=a(l),f=e("lodash/collection/size"),d=a(f);t.exports=r["default"]},{"./isArrayLike":21,"./isGraph":23,"babel-runtime/helpers/interop-require-default":107,"lodash/collection/size":172,"lodash/lang/isPlainObject":230}],37:[function(e,t,r){"use strict";function n(e,t){var r=!0,n=!1,i=void 0;try{for(var o,u=a(e);!(r=(o=u.next()).done);r=!0){var s=o.value;if(t(s))return!0}}catch(l){n=!0,i=l}finally{try{!r&&u["return"]&&u["return"]()}finally{if(n)throw i}}return!1}var a=e("babel-runtime/core-js/get-iterator")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n,t.exports=r["default"]},{"babel-runtime/core-js/get-iterator":89}],38:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0});var a,i=e("tiny-sprintf"),o=n(i);o["default"].j=function(e){if(e===a)return a+"";try{return JSON.stringify(e)}catch(t){return e+""}},r["default"]=o["default"],t.exports=r["default"]},{"babel-runtime/helpers/interop-require-default":107,"tiny-sprintf":243}],39:[function(e,t,r){"use strict";function n(e){if(c["default"](e))return e;if(d["default"](e))return a(e);if(Array.isArray(e)||s["default"](e))return i.mark(function t(e){var r,n;return i.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:r=0,n=e.length;case 1:if(!(n>r)){t.next=7;break}return t.next=4,e[r];case 4:r++,t.next=1;break;case 7:case"end":return t.stop()}},t,this)})(e);throw new TypeError("Unable to convert "+e+" to an iterator")}var a=e("babel-runtime/core-js/get-iterator")["default"],i=e("babel-runtime/regenerator")["default"],o=e("babel-runtime/helpers/interop-require-default")["default"]; + Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var u=e("./isArrayLike"),s=o(u),l=e("./isIterator"),c=o(l),f=e("./isIterable"),d=o(f);t.exports=r["default"]},{"./isArrayLike":21,"./isIterable":24,"./isIterator":25,"babel-runtime/core-js/get-iterator":89,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/regenerator":166}],40:[function(e,t,r){"use strict";function n(e,t){return c[0]=e,c[1]=t,c}function a(e,t,r){return f[0]=e,f[1]=t,f[2]=r,f}function i(e,t,r,n){return d[0]=e,d[1]=t,d[2]=r,d[3]=n,d}function o(e,t,r){return r.length=2,r[0]=e,r[1]=t,r}function u(e,t,r,n){return n.length=3,n[0]=e,n[1]=t,n[2]=r,n}function s(e,t,r,n,a){return a.length=4,a[0]=e,a[1]=t,a[2]=r,a[3]=n,a}function l(e){var t=new Array(e);switch(e){case 2:return function(e,r){return t[0]=e,t[1]=r,t};case 3:return function(e,r,n){return t[0]=e,t[1]=r,t[2]=n,t};default:throw new Error("Typle size not supported.")}}Object.defineProperty(r,"__esModule",{value:!0}),r.tuple2=n,r.tuple3=a,r.tuple4=i,r.tuple2c=o,r.tuple3c=u,r.tuple4c=s,r.createTupleFactory=l;var c=new Array(2),f=new Array(3),d=new Array(4)},{}],41:[function(e,t,r){"use strict";function n(){var e,t,r,n,o,u,s=arguments;return a.wrap(function(a){for(;;)switch(a.prev=a.next){case 0:e=s,t=e.length;case 2:r=!1,n=new Array(t),o=0;case 6:if(!(t>o)){a.next=15;break}if(u=e[o].next(),!u.done){a.next=11;break}return r=!0,a.abrupt("break",15);case 11:n[o]=u.value;case 12:o++,a.next=6;break;case 15:if(!r){a.next=17;break}return a.abrupt("break",21);case 17:return a.next=19,n;case 19:a.next=2;break;case 21:case"end":return a.stop()}},i[0],this)}var a=e("babel-runtime/regenerator")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n;var i=[n].map(a.mark);t.exports=r["default"]},{"babel-runtime/regenerator":166}],42:[function(e,t,r){"use strict";function n(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];var n,a,i=t.length,o=1/0,u=new Array(i);for(n=0;i>n;n++){var s=t[n],l=s.length;if(o>l&&(o=l,0===o))return[];u[n]=s[0]}for(a=new Array(o),a[0]=u,n=1;o>n;n++){u=new Array(i);for(var c=0;i>c;c++)u[c]=t[c][n];a[n]=u}return a}function a(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];var a=t[0];if(u["default"](a))return n.apply(null,t);if(l["default"](a))return f["default"].apply(null,t);throw new TypeError("Expected an iterator, array-like object or object, but got %s instead",a)}var i=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=a;var o=e("./isArrayLike"),u=i(o),s=e("./isIterator"),l=i(s),c=e("./zipIterator"),f=i(c);t.exports=r["default"]},{"./isArrayLike":21,"./isIterator":25,"./zipIterator":41,"babel-runtime/helpers/interop-require-default":107}],43:[function(e,t,r){"use strict";function n(e){var t=this,r=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],n=r.k,a=r.normalized,i=r.weight,o=r.endpoints;a=null==a?!0:a,o=null==o?!1:o;var f=new w.Map(v.mark(function g(){var r,n,a,i,o,u;return v.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:r=!0,n=!1,a=void 0,t.prev=3,i=b(e);case 5:if(r=(o=i.next()).done){t.next=12;break}return u=o.value,t.next=9,w.tuple2(u,0);case 9:r=!0,t.next=5;break;case 12:t.next=18;break;case 14:t.prev=14,t.t0=t["catch"](3),n=!0,a=t.t0;case 18:t.prev=18,t.prev=19,!r&&i["return"]&&i["return"]();case 21:if(t.prev=21,!n){t.next=24;break}throw a;case 24:return t.finish(21);case 25:return t.finish(18);case 26:case"end":return t.stop()}},g,t,[[3,14,18,26],[19,,21,25]])})()),h=e.nodes();return null!=n&&(h=w.Arrays.sample(h,n)),h.forEach(function(t){var r=null==i?u(e,t):s(e,t,i),n=p(r,3),a=n[0],d=n[1],h=n[2];f=o?c(f,a,d,h,t):l(f,a,d,h,t)}),d(f,e.order(),a,e.isDirected(),n)}function a(e,t){return m["default"]("betweennessCentrality",[e,t])}function i(e){var t=this,r=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],n=r.normalized,a=r.weight;n=null==n?!0:n;var i=new w.Map(v.mark(function G(){var r,n,a,i,o,u;return v.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:r=!0,n=!1,a=void 0,t.prev=3,i=b(e);case 5:if(r=(o=i.next()).done){t.next=12;break}return u=o.value,t.next=9,w.tuple2(u,0);case 9:r=!0,t.next=5;break;case 12:t.next=18;break;case 14:t.prev=14,t.t0=t["catch"](3),n=!0,a=t.t0;case 18:t.prev=18,t.prev=19,!r&&i["return"]&&i["return"]();case 21:if(t.prev=21,!n){t.next=24;break}throw a;case 24:return t.finish(21);case 25:return t.finish(18);case 26:case"end":return t.stop()}},G,t,[[3,14,18,26],[19,,21,25]])})()),o=!0,l=!1,c=void 0;try{for(var d,g=b(e.edgesIter());!(o=(d=g.next()).done);o=!0){var y=d.value;i.set(y,0)}}catch(m){l=!0,c=m}finally{try{!o&&g["return"]&&g["return"]()}finally{if(l)throw c}}var x=!0,k=!1,j=void 0;try{for(var E,_=b(e);!(x=(E=_.next()).done);x=!0){var S=E.value,O=null==a?u(e,S):s(e,S,a),M=p(O,3),I=M[0],P=M[1],N=M[2];i=f(i,I,P,N,S)}}catch(m){k=!0,j=m}finally{try{!x&&_["return"]&&_["return"]()}finally{if(k)throw j}}var A=!0,$=!1,q=void 0;try{for(var D,L=b(e);!(A=(D=L.next()).done);A=!0){var F=D.value;i["delete"](F)}}catch(m){$=!0,q=m}finally{try{!A&&L["return"]&&L["return"]()}finally{if($)throw q}}return h(i,e.order(),n,e.isDirected())}function o(e,t){return m["default"]("edgeBetweennessCentrality",[e,t])}function u(e,t){var r=this,n=[],a=new w.Map(v.mark(function f(){var t,n,a,i,o,u;return v.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:t=!0,n=!1,a=void 0,r.prev=3,i=b(e);case 5:if(t=(o=i.next()).done){r.next=12;break}return u=o.value,r.next=9,w.tuple2(u,[]);case 9:t=!0,r.next=5;break;case 12:r.next=18;break;case 14:r.prev=14,r.t0=r["catch"](3),n=!0,a=r.t0;case 18:r.prev=18,r.prev=19,!t&&i["return"]&&i["return"]();case 21:if(r.prev=21,!n){r.next=24;break}throw a;case 24:return r.finish(21);case 25:return r.finish(18);case 26:case"end":return r.stop()}},f,r,[[3,14,18,26],[19,,21,25]])})()),i=new w.Map(v.mark(function d(){var t,n,a,i,o,u;return v.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:t=!0,n=!1,a=void 0,r.prev=3,i=b(e);case 5:if(t=(o=i.next()).done){r.next=12;break}return u=o.value,r.next=9,w.tuple2(u,0);case 9:t=!0,r.next=5;break;case 12:r.next=18;break;case 14:r.prev=14,r.t0=r["catch"](3),n=!0,a=r.t0;case 18:r.prev=18,r.prev=19,!t&&i["return"]&&i["return"]();case 21:if(r.prev=21,!n){r.next=24;break}throw a;case 24:return r.finish(21);case 25:return r.finish(18);case 26:case"end":return r.stop()}},d,r,[[3,14,18,26],[19,,21,25]])})()),o=new w.Map;i.set(t,1),o.set(t,0);for(var u=[t];u.length>0;){var s=u.shift();n.push(s);var l=o.get(s),c=i.get(s);e.neighbors(s).forEach(function(e){o.has(e)||(u.push(e),o.set(e,l+1)),o.get(e)===l+1&&(i.set(e,i.get(e)+c),a.get(e).push(s))})}return[n,a,i]}function s(e,t){var r=this,n=arguments.length<=2||void 0===arguments[2]?"weight":arguments[2],a=[],i=new w.Map(v.mark(function P(){var t,n,a,i,o,u;return v.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:t=!0,n=!1,a=void 0,r.prev=3,i=b(e);case 5:if(t=(o=i.next()).done){r.next=12;break}return u=o.value,r.next=9,w.tuple2(u,[]);case 9:t=!0,r.next=5;break;case 12:r.next=18;break;case 14:r.prev=14,r.t0=r["catch"](3),n=!0,a=r.t0;case 18:r.prev=18,r.prev=19,!t&&i["return"]&&i["return"]();case 21:if(r.prev=21,!n){r.next=24;break}throw a;case 24:return r.finish(21);case 25:return r.finish(18);case 26:case"end":return r.stop()}},P,r,[[3,14,18,26],[19,,21,25]])})()),o=new w.Map(v.mark(function N(){var t,n,a,i,o,u;return v.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:t=!0,n=!1,a=void 0,r.prev=3,i=b(e);case 5:if(t=(o=i.next()).done){r.next=12;break}return u=o.value,r.next=9,w.tuple2(u,0);case 9:t=!0,r.next=5;break;case 12:r.next=18;break;case 14:r.prev=14,r.t0=r["catch"](3),n=!0,a=r.t0;case 18:r.prev=18,r.prev=19,!t&&i["return"]&&i["return"]();case 21:if(r.prev=21,!n){r.next=24;break}throw a;case 24:return r.finish(21);case 25:return r.finish(18);case 26:case"end":return r.stop()}},N,r,[[3,14,18,26],[19,,21,25]])})()),u=new w.Map;o.set(t,1);var s=new w.Map([w.tuple2(t,0)]),l=new w.PriorityQueue;for(l.enqueue(0,[t,t]);l.size>0;){var c=l.dequeue(),f=p(c,2),d=f[0],h=p(f[1],2),g=h[0],y=h[1];if(!u.has(y)){o.set(y,o.get(y)+o.get(g)),a.push(y),u.set(y,d);var m=!0,x=!1,k=void 0;try{for(var j,E=b(e.get(y));!(m=(j=E.next()).done);m=!0){var _=p(j.value,2),S=_[0],O=_[1],M=d+w.getDefault(O[n],1);u.has(S)||s.has(S)&&!(M0;){var u=t.pop(),s=(1+o.get(u))/n.get(u);r.get(u).forEach(function(e){o.set(e,o.get(e)+n.get(e)*s)}),(u!==a||"object"==typeof u&&u.toString()!==a.toString())&&e.set(u,e.get(u)+o.get(u))}return e}function c(e,t,r,n,a){var i=this;e.set(a,e.get(a)+t.length-1);for(var o=new w.Map(v.mark(function l(){var e,r,n,a,o,u;return v.wrap(function(i){for(;;)switch(i.prev=i.next){case 0:e=!0,r=!1,n=void 0,i.prev=3,a=b(t);case 5:if(e=(o=a.next()).done){i.next=12;break}return u=o.value,i.next=9,w.tuple2(u,0);case 9:e=!0,i.next=5;break;case 12:i.next=18;break;case 14:i.prev=14,i.t0=i["catch"](3),r=!0,n=i.t0;case 18:i.prev=18,i.prev=19,!e&&a["return"]&&a["return"]();case 21:if(i.prev=21,!r){i.next=24;break}throw n;case 24:return i.finish(21);case 25:return i.finish(18);case 26:case"end":return i.stop()}},l,i,[[3,14,18,26],[19,,21,25]])})());t.length>0;){var u=t.pop(),s=(1+o.get(u))/n.get(u);r.get(u).forEach(function(e){o.set(e,o.get(e)+n.get(e)*s)}),(u!==a||"object"==typeof u&&u.toString()!==a.toString())&&e.set(u,e.get(u)+o.get(u)+1)}return e}function f(e,t,r,n,a){for(var i=this,o=new w.Map(v.mark(function l(){var e,r,n,a,o,u;return v.wrap(function(i){for(;;)switch(i.prev=i.next){case 0:e=!0,r=!1,n=void 0,i.prev=3,a=b(t);case 5:if(e=(o=a.next()).done){i.next=12;break}return u=o.value,i.next=9,w.tuple2(u,0);case 9:e=!0,i.next=5;break;case 12:i.next=18;break;case 14:i.prev=14,i.t0=i["catch"](3),r=!0,n=i.t0;case 18:i.prev=18,i.prev=19,!e&&a["return"]&&a["return"]();case 21:if(i.prev=21,!r){i.next=24;break}throw n;case 24:return i.finish(21);case 25:return i.finish(18);case 26:case"end":return i.stop()}},l,i,[[3,14,18,26],[19,,21,25]])})());t.length>0;){var u=t.pop(),s=(1+o.get(u))/n.get(u);r.get(u).forEach(function(t){var r=n.get(t)*s,a=[t,u];e.has(a)||(a=[u,t]),e.set(a,e.get(a)+r),o.set(t,o.get(t)+r)}),(u!==a||"object"==typeof u&&u.toString()!==a.toString())&&e.set(u,e.get(u)+o.get(u))}return e}function d(e,t,r,n,a){void 0===n&&(n=!1);var i;return i=r?2>=t?null:1/((t-1)*(t-2)):n?null:.5,null!=i&&(null!=a&&(i=i*t/a),e.forEach(function(t,r){return e.set(r,t*i)})),e}function h(e,t,r,n){var a;return a=r?1>=t?null:1/(t*(t-1)):n?null:.5,null!=a&&e.forEach(function(t,r){return e.set(r,t*a)}),e}var p=e("babel-runtime/helpers/sliced-to-array")["default"],v=e("babel-runtime/regenerator")["default"],b=e("babel-runtime/core-js/get-iterator")["default"],g=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.betweennessCentrality=n,r.genBetweennessCentrality=a,r.edgeBetweennessCentrality=i,r.genEdgeBetweennessCentrality=o;var y=e("../../_internals/delegate"),m=g(y),w=e("../../_internals")},{"../../_internals":20,"../../_internals/delegate":12,"babel-runtime/core-js/get-iterator":89,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/sliced-to-array":109,"babel-runtime/regenerator":166}],44:[function(e,t,r){"use strict";function n(e){var t=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],r=t.maxIter,n=void 0===r?100:r,a=t.tolerance,u=void 0===a?1e-6:a,s=t.nstart,l=t.weight,d=Math.sqrt,h=Math.pow,p=Math.abs;if(e.isMultigraph())throw new c.JSNetworkXException("Not defined for multigraphs.");if(0===e.order())throw new c.JSNetworkXException("Empty graph.");var v=void 0,b=new f.Map;if(s){v=s;var g=!0,y=!1,m=void 0;try{for(var w,x=o(v.keys());!(g=(w=x.next()).done);g=!0){var k=w.value;b.set(k,0)}}catch(j){y=!0,m=j}finally{try{!g&&x["return"]&&x["return"]()}finally{if(y)throw m}}}else{var E=1/e.order();v=new f.Map;var _=!0,S=!1,O=void 0;try{for(var M,I=o(e);!(_=(M=I.next()).done);_=!0){var k=M.value;v.set(k,E),b.set(k,0)}}catch(j){S=!0,O=j}finally{try{!_&&I["return"]&&I["return"]()}finally{if(S)throw O}}}var P=0,N=!0,A=!1,$=void 0;try{for(var q,D=o(v.values());!(N=(q=D.next()).done);N=!0){var L=q.value;P+=L}}catch(j){A=!0,$=j}finally{try{!N&&D["return"]&&D["return"]()}finally{if(A)throw $}}P=1/P;var F=!0,G=!1,C=void 0;try{for(var z,T=o(v);!(F=(z=T.next()).done);F=!0){var J=i(z.value,2),R=J[0],L=J[1];v.set(R,L*P)}}catch(j){G=!0,C=j}finally{try{!F&&T["return"]&&T["return"]()}finally{if(G)throw C}}u=e.order()*u;for(var X=0;n>X;X++){var B=v;v=new f.Map(b);var U=!0,V=!1,W=void 0;try{for(var H,K=o(v);!(U=(H=K.next()).done);U=!0){var Y=i(H.value,2),k=Y[0],L=Y[1],Q=!0,Z=!1,ee=void 0;try{for(var te,re=o(e.get(k));!(Q=(te=re.next()).done);Q=!0){var ne=i(te.value,2),ae=ne[0],ie=ne[1];v.set(ae,v.get(ae)+B.get(k)*f.getDefault(l&&ie[l],1))}}catch(j){Z=!0,ee=j}finally{try{!Q&&re["return"]&&re["return"]()}finally{if(Z)throw ee}}}}catch(j){V=!0,W=j}finally{try{!U&&K["return"]&&K["return"]()}finally{if(V)throw W}}var oe=0,ue=!0,se=!1,le=void 0;try{for(var ce,fe=o(v.values());!(ue=(ce=fe.next()).done);ue=!0){var L=ce.value;oe+=h(L,2)}}catch(j){se=!0,le=j}finally{try{!ue&&fe["return"]&&fe["return"]()}finally{if(se)throw le}}oe=d(oe),oe=0===oe?1:1/oe;var de=0,he=!0,pe=!1,ve=void 0;try{for(var be,ge=o(v);!(he=(be=ge.next()).done);he=!0){var ye=i(be.value,2),k=ye[0],L=ye[1];L*=oe,v.set(k,L),de+=p(L-B.get(k))}}catch(j){pe=!0,ve=j}finally{try{!he&&ge["return"]&&ge["return"]()}finally{if(pe)throw ve}}if(u>de)return v}throw new c.JSNetworkXError("eigenvectorCentrality(): power iteration failed to converge in "+(n+" iterations."))}function a(e,t){return l["default"]("eigenvectorCentrality",[e,t])}var i=e("babel-runtime/helpers/sliced-to-array")["default"],o=e("babel-runtime/core-js/get-iterator")["default"],u=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.eigenvectorCentrality=n,r.genEigenvectorCentrality=a;var s=e("../../_internals/delegate"),l=u(s),c=e("../../exceptions"),f=e("../../_internals")},{"../../_internals":20,"../../_internals/delegate":12,"../../exceptions":78,"babel-runtime/core-js/get-iterator":89,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/sliced-to-array":109}],45:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-wildcard")["default"],a=e("babel-runtime/helpers/defaults")["default"];Object.defineProperty(r,"__esModule",{value:!0});var i=e("./betweenness"),o=n(i),u=e("./eigenvector"),s=n(u);r.betweenness=o,r.eigenvector=s,a(r,n(i)),a(r,n(u))},{"./betweenness":43,"./eigenvector":44,"babel-runtime/helpers/defaults":103,"babel-runtime/helpers/interop-require-wildcard":108}],46:[function(e,t,r){"use strict";function n(e){var t,r,n,a,i,o,u,s,l,c,f,d,v;return p.wrap(function(p){for(;;)switch(p.prev=p.next){case 0:if(0!==e.numberOfNodes()){p.next=2;break}return p.abrupt("return",[]);case 2:t=new x.Map(x.mapIterator(e,function(t){var r=new x.Set(e.neighborsIter(t));return r["delete"](t),x.tuple2(t,r)})),r=new x.Set(e),n=new x.Set(e),a=[null],i=x.max(r,function(e){return n.intersection(t.get(e)).size}),o=n.difference(t.get(i)),u=[];case 9:if(!(o.size>0)){p.next=25;break}if(s=o.pop(),n["delete"](s),a[a.length-1]=s,l=t.get(s),c=r.intersection(l),0!==c.size){p.next=21;break}return p.next=19,a.slice();case 19:p.next=23;break;case 21:f=n.intersection(l),f.size>0&&(u.push([r,n,o]),a.push(null),r=c,n=f,i=x.max(r,function(e){return n.intersection(t.get(e)).size}),o=n.difference(t.get(i)));case 23:p.next=33;break;case 25:if(0!==a.length&&0!==u.length){p.next=27;break}return p.abrupt("break",35);case 27:a.pop(),d=u.pop(),v=h(d,3),r=v[0],n=v[1],o=v[2];case 33:p.next=9;break;case 35:case"end":return p.stop()}},y[0],this)}function a(e){return w["default"]("findCliques",[e])}function i(e){var t,r,n,a;return p.wrap(function(i){for(;;)switch(i.prev=i.next){case 0:if(a=function o(e,a){var i,u,s,l,c,f,d,h,b,g;return p.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:i=x.max(e,function(e){return a.intersection(r.get(e)).size}),u=!0,s=!1,l=void 0,t.prev=4,c=v(a.difference(r.get(i)));case 6:if(u=(f=c.next()).done){t.next=24;break}if(d=f.value,a["delete"](d),n.push(d),h=r.get(d),b=e.intersection(h),0!==b.size){t.next=17;break}return t.next=15,n.slice();case 15:t.next=20;break;case 17:if(g=a.intersection(h),!(g.size>0)){t.next=20;break}return t.delegateYield(o(b,g),"t0",20);case 20:n.pop();case 21:u=!0,t.next=6;break;case 24:t.next=30;break;case 26:t.prev=26,t.t1=t["catch"](4),s=!0,l=t.t1;case 30:t.prev=30,t.prev=31,!u&&c["return"]&&c["return"]();case 33:if(t.prev=33,!s){t.next=36;break}throw l;case 36:return t.finish(33);case 37:return t.finish(30);case 38:case"end":return t.stop()}},t[0],this,[[4,26,30,38],[31,,33,37]])},t=[a].map(p.mark),0!==e.size){i.next=5;break}return i.next=5,[];case 5:return r=new x.Map(x.mapIterator(e,function(t){var r=new x.Set(e.neighborsIter(t));return r["delete"](t),x.tuple2(t,r)})),n=[],i.delegateYield(a(new x.Set(e),new x.Set(e)),"t0",8);case 8:case"end":return i.stop()}},y[1],this)}function o(e){return w["default"]("findCliquesRecursive",[e])}function u(e,t){return null==t&&(t=n(e)),x.max(t,function(e){return e.length}).length}function s(e,t){return w["default"]("graphCliqueNumber",[e,t])}function l(e,t){return null==t&&(t=n(e)),b(t).length}function c(e,t){return w["default"]("graphNumberOfCliques",[e,t])}function f(e,t,r){r=b(r||n(e)),null==t&&(t=e.nodes());var a;if(Array.isArray(t))r=r.map(function(e){return new x.Set(e)}),a=new x.Map,t.forEach(function(e){a.set(e,r.filter(function(t){return t.has(e)}).length)});else{var i=t;a=r.filter(function(e){return new x.Set(e).has(i)}).length}return a}function d(e,t,r){return w["default"]("numberOfCliques",[e,t,r])}var h=e("babel-runtime/helpers/sliced-to-array")["default"],p=e("babel-runtime/regenerator")["default"],v=e("babel-runtime/core-js/get-iterator")["default"],b=e("babel-runtime/core-js/array/from")["default"],g=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.findCliques=n,r.genFindCliques=a,r.findCliquesRecursive=i,r.genFindCliquesRecursive=o,r.graphCliqueNumber=u,r.genGraphCliqueNumber=s,r.graphNumberOfCliques=l,r.genGraphNumberOfCliques=c,r.numberOfCliques=f,r.genNumberOfCliques=d;var y=[n,i].map(p.mark),m=e("../_internals/delegate"),w=g(m),x=e("../_internals")},{"../_internals":20,"../_internals/delegate":12,"babel-runtime/core-js/array/from":88,"babel-runtime/core-js/get-iterator":89,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/sliced-to-array":109,"babel-runtime/regenerator":166}],47:[function(e,t,r){"use strict";function n(e,t){if(e.isDirected())throw new E["default"]("triangles() is not defined for directed graphs.");return null!=t&&e.hasNode(t)?Math.floor(_.next(i(e,t))[2]/2):new _.Map(_.mapIterator(i(e,t),function(e){var t=v(e,3),r=t[0],n=(t[1],t[2]);return _.tuple2(r,Math.floor(n/2),r)}))}function a(e,t){return k["default"]("triangles",[e,t])}function i(e,t){var r,n,a,i,o,u,s,l,c,f,d,h,p,y,m,x,k,j;return b.wrap(function(b){for(;;)switch(b.prev=b.next){case 0:if(!e.isMultigraph()){b.next=2;break}throw new E["default"]("Not defined for multigraphs.");case 2:r=_.mapIterator(null==t?e:e.nbunchIter(t),function(t){return _.tuple2(t,e.get(t))}),n=!0,a=!1,i=void 0,b.prev=6,o=g(r);case 8:if(n=(u=o.next()).done){b.next=39;break}for(s=v(u.value,2),l=s[0],c=s[1],f=new _.Set(c.keys()),f["delete"](l),d=0,h=!0,p=!1,y=void 0,b.prev=18,m=g(f);!(h=(x=m.next()).done);h=!0)k=x.value,j=new _.Set(e.get(k).keys()),j["delete"](k),d+=f.intersection(j).size;b.next=26;break;case 22:b.prev=22,b.t0=b["catch"](18),p=!0,y=b.t0;case 26:b.prev=26,b.prev=27,!h&&m["return"]&&m["return"]();case 29:if(b.prev=29,!p){b.next=32;break}throw y;case 32:return b.finish(29);case 33:return b.finish(26);case 34:return b.next=36,_.tuple3(l,f.size,d);case 36:n=!0,b.next=8;break;case 39:b.next=45;break;case 41:b.prev=41,b.t1=b["catch"](6),a=!0,i=b.t1;case 45:b.prev=45,b.prev=46,!n&&o["return"]&&o["return"]();case 48:if(b.prev=48,!a){b.next=51;break}throw i;case 51:return b.finish(48);case 52:return b.finish(45);case 53:case"end":return b.stop()}},w[0],this,[[6,41,45,53],[18,22,26,34],[27,,29,33],[46,,48,52]])}function o(e,t){var r,n,a,i,o,u,s,l,c,f,d,h,p,y,m,x,k,j,S,O,M,I,P,N,A,$,q,D,L,F=arguments.length<=2||void 0===arguments[2]?"weight":arguments[2];return b.wrap(function(b){for(;;)switch(b.prev=b.next){case 0:if(!e.isMultigraph()){b.next=2;break}throw new E["default"]("Not defined for multigraphs.");case 2:r=null==F||0===e.edges().length?1:_.max(_.mapIterator(e.edgesIter(!0),function(e){var t=v(e,3),r=(t[0],t[1],t[2]);return _.getDefault(r[F],1)})),n=_.mapIterator(null==t?e:e.nbunchIter(t),function(t){return _.tuple2(t,e.get(t))}),a=!0,i=!1,o=void 0,b.prev=7,u=g(n);case 9:if(a=(s=u.next()).done){b.next=67;break}l=v(s.value,2),c=l[0],f=l[1],d=new _.Set(f.keys()).difference([c]),h=0,p=new _.Set,y=!0,m=!1,x=void 0,b.prev=19,k=g(d);case 21:if(y=(j=k.next()).done){b.next=48;break}for(S=j.value,O=_.getDefault(f.get(S)[F],1)/r,p.add(S),M=new _.Set(e.get(S).keys()).difference(p),I=!0,P=!1,N=void 0,b.prev=29,A=g(d.intersection(M));!(I=($=A.next()).done);I=!0)q=$.value,D=_.getDefault(e.get(S).get(q)[F],1)/r,L=_.getDefault(f.get(q)[F],1)/r,h+=Math.pow(O*D*L,1/3);b.next=37;break;case 33:b.prev=33,b.t0=b["catch"](29),P=!0,N=b.t0;case 37:b.prev=37,b.prev=38,!I&&A["return"]&&A["return"]();case 40:if(b.prev=40,!P){b.next=43;break}throw N;case 43:return b.finish(40);case 44:return b.finish(37);case 45:y=!0,b.next=21;break;case 48:b.next=54;break;case 50:b.prev=50,b.t1=b["catch"](19),m=!0,x=b.t1;case 54:b.prev=54,b.prev=55,!y&&k["return"]&&k["return"]();case 57:if(b.prev=57,!m){b.next=60;break}throw x;case 60:return b.finish(57);case 61:return b.finish(54);case 62:return b.next=64,_.tuple3(c,d.size,2*h);case 64:a=!0,b.next=9;break;case 67:b.next=73;break;case 69:b.prev=69,b.t2=b["catch"](7),i=!0,o=b.t2;case 73:b.prev=73,b.prev=74,!a&&u["return"]&&u["return"]();case 76:if(b.prev=76,!i){b.next=79;break}throw o;case 79:return b.finish(76);case 80:return b.finish(73);case 81:case"end":return b.stop()}},w[1],this,[[7,69,73,81],[19,50,54,62],[29,33,37,45],[38,,40,44],[55,,57,61],[74,,76,80]])}function u(e,t,r){var n=arguments.length<=3||void 0===arguments[3]?!0:arguments[3],a=y(l(e,t,r).values());return n||(a=a.filter(function(e){return e>0})),a.reduce(function(e,t){return e+t},0)/a.length}function s(e,t,r,n){return k["default"]("averageClustering",[e,t,r,n])}function l(e,t,r){if(e.isDirected())throw new E["default"]("Clustering algorithms are not defined for directed graphs.");var n=null==r?i(e,t):o(e,t,r),a=new _.Map(_.mapIterator(n,function(e){var t=v(e,3),r=t[0],n=t[1],a=t[2];return _.tuple2(r,0===a?0:a/(n*(n-1)))}));return e.hasNode(t)?_.next(a.values()):a}function c(e,t,r){return k["default"]("clustering",[e,t,r])}function f(e){var t=0,r=0,n=!0,a=!1,o=void 0;try{for(var u,s=g(i(e));!(n=(u=s.next()).done);n=!0){var l=v(u.value,3),c=(l[0],l[1]),f=l[2];r+=c*(c-1),t+=f}}catch(d){a=!0,o=d}finally{try{!n&&s["return"]&&s["return"]()}finally{if(a)throw o}}return 0===t?0:t/r}function d(e){return k["default"]("transitivity",[e])}function h(e,t){var r=null==t?e:e.nbunchIter(t),n=new _.Map,a=!0,i=!1,o=void 0;try{for(var u,s=g(r);!(a=(u=s.next()).done);a=!0){var l=u.value;n.set(l,0);var c=0,f=!0,d=!1,h=void 0;try{for(var p,b=g(_.genCombinations(e.get(l).keys(),2));!(f=(p=b.next()).done);f=!0){var y=v(p.value,2),m=y[0],w=y[1],x=new _.Set(e.get(m).keys()).intersection(new _.Set(e.get(w).keys()));x["delete"](l),x=x.size,n.set(l,n.get(l)+x);var k=x+1;e.get(m).has(w)&&(k+=1),c+=(e.get(m).size-k)*(e.get(w).size-k)+x}}catch(j){d=!0,h=j}finally{try{!f&&b["return"]&&b["return"]()}finally{if(d)throw h}}c>0&&n.set(l,n.get(l)/c)}}catch(j){i=!0,o=j}finally{try{!a&&s["return"]&&s["return"]()}finally{if(i)throw o}}return e.hasNode(t)?_.next(n.values()):n}function p(e,t){return k["default"]("squareClustering",[e,t])}var v=e("babel-runtime/helpers/sliced-to-array")["default"],b=e("babel-runtime/regenerator")["default"],g=e("babel-runtime/core-js/get-iterator")["default"],y=e("babel-runtime/core-js/array/from")["default"],m=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.triangles=n,r.genTriangles=a,r.averageClustering=u,r.genAverageClustering=s,r.clustering=l,r.genClustering=c,r.transitivity=f,r.genTransitivity=d,r.squareClustering=h,r.genSquareClustering=p;var w=[i,o].map(b.mark),x=e("../_internals/delegate"),k=m(x),j=e("../exceptions/JSNetworkXError"),E=m(j),_=e("../_internals")},{"../_internals":20,"../_internals/delegate":12,"../exceptions/JSNetworkXError":73,"babel-runtime/core-js/array/from":88,"babel-runtime/core-js/get-iterator":89,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/sliced-to-array":109,"babel-runtime/regenerator":166}],48:[function(e,t,r){"use strict";function n(e){try{return i(e),!0}catch(t){if(t instanceof g["default"])return!1;throw t}}function a(e){return h["default"]("isDirectedAcyclicGraph",[e])}function i(e,t){if(!e.isDirected())throw new v["default"]("Topological sort not defined on undirected graphs.");var r=new y.Set,n=[],a=new y.Set;return null==t&&(t=e.nodesIter()),y.forEach(t,function(t){if(!a.has(t))for(var i=[t];i.length>0;){var o=i[i.length-1];if(a.has(o))i.pop();else{r.add(o);var u=[];e.get(o).forEach(function(e,t){if(!a.has(t)){if(r.has(t))throw new g["default"]("Graph contains a cycle.");u.push(t)}}),u.length>0?i.push.apply(i,u):(a.add(o),n.unshift(o))}}}),n}function o(e,t){return h["default"]("topologicalSort",[e,t])}function u(e,t){function r(e,t,n,a){return t.add(a),e.get(a).forEach(function(a,i){if(t.has(i)){if(t.has(i)&&-1===n.indexOf(i))throw new g["default"]("Graph contains a cycle.")}else if(!r(e,t,n,i))return!1}),n.unshift(a),!0}if(!e.isDirected())throw new v["default"]("Topological sort not defined on undirected graphs.");var n=new y.Set,a=[];return null==t&&(t=e.nodesIter()),y.forEach(t,function(t){if(-1===a.indexOf(t)&&!r(e,n,a,t))throw new g["default"]("Graph contains a cycle.")}),a}function s(e,t){return h["default"]("topologicalSortRecursive",[e,t])}function l(e){for(var t,r=!0;r;){var n=e;if(a=i=o=u=s=l=c=f=void 0,r=!1,!n.isDirected())throw new v["default"]("is_aperiodic not defined for undirected graphs.");var a=n.nodesIter().next();if(a.done)return!0;var i=new y.Map;i.set(a.value,0);for(var o=[a.value],u=0,s=1;o.length>0;){for(var l=[],c=0;cu;u++){var s=e[u];if(0>s||s>=t)throw new g.JSNetworkXUnfeasible;s>0&&(n=Math.max(n,s),a=Math.min(a,s),i+=s,o+=1,r[s]+=1)}if(i%2===1||i>o*(o-1))throw new g.JSNetworkXUnfeasible;return[n,a,i,o,r]}function s(e){var t,r,n,a,i;try{var o=u(e),s=d(o,5);r=s[0],n=s[1],t=s[2],a=s[3],i=s[4]}catch(l){if(l instanceof g.JSNetworkXUnfeasible)return!1;throw l}if(0===a||4*n*a>=Math.pow(r+n+1,2))return!0;for(var c=m["default"](r+1,0);a>0;){for(;0===i[r];)r-=1;if(r>a-1)return!1;i[r]-=1,a-=1;for(var f=0,h=r,p=0;r>p;p++){for(;0===i[h];)h-=1;i[h]-=1,a-=1,h>1&&(c[f]=h-1,f+=1)}for(p=0;f>p;p++){var v=c[p];i[v]+=1,a+=1}}return!0}function l(e){return b["default"]("isValidDegreeSequenceHavelHakimi",[e])}function c(e){var t,r,n,a,i;try{var o=u(e),s=d(o,5);t=s[0],r=s[1],n=s[2],a=s[3],i=s[4]}catch(l){if(l instanceof g.JSNetworkXUnfeasible)return!1;throw l}if(0===a||4*r*a>=Math.pow(t+r+1,2))return!0;for(var c=0,f=0,h=0,p=0,v=t;v>=r;v-=1){if(c+1>v)return!0;if(i[v]>0){var b=i[v];c+b>v&&(b=v-c),f+=b*v;for(var y=0;b>y;y++)h+=i[c+y],p+=(c+y)*i[c+y];if(c+=b,f>c*(a-1)-c*h+p)return!1}}return!0}function f(e){return b["default"]("isValidDegreeSequenceErdosGallai",[e])}var d=e("babel-runtime/helpers/sliced-to-array")["default"],h=e("babel-runtime/core-js/array/from")["default"],p=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.isGraphical=n,r.genIsGraphical=a,r.isValidDegreeSequence=i,r.genIsValidDegreeSequence=o,r.isValidDegreeSequenceHavelHakimi=s,r.genIsValidDegreeSequenceHavelHakimi=l,r.isValidDegreeSequenceErdosGallai=c,r.genIsValidDegreeSequenceErdosGallai=f;var v=e("../_internals/delegate"),b=p(v),g=e("../exceptions"),y=e("../_internals/fillArray"),m=p(y)},{"../_internals/delegate":12,"../_internals/fillArray":13,"../exceptions":78,"babel-runtime/core-js/array/from":88,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/sliced-to-array":109}],50:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-wildcard")["default"],a=e("babel-runtime/helpers/defaults")["default"];Object.defineProperty(r,"__esModule",{value:!0});var i=e("./centrality"),o=n(i),u=e("./clique"),s=n(u),l=e("./cluster"),c=n(l),f=e("./dag"),d=n(f),h=e("./graphical"),p=n(h),v=e("./isomorphism"),b=n(v),g=e("./operators"),y=n(g),m=e("./shortestPaths"),w=n(m);r.centrality=o,r.clique=s,r.cluster=c,r.dag=d,r.graphical=p,r.isomorphism=b,r.operators=y,r.shortestPaths=w,a(r,n(i)),a(r,n(u)),a(r,n(l)),a(r,n(f)),a(r,n(h)),a(r,n(v)),a(r,n(g)),a(r,n(m))},{"./centrality":45,"./clique":46,"./cluster":47,"./dag":48,"./graphical":49,"./isomorphism":51,"./operators":54,"./shortestPaths":56,"babel-runtime/helpers/defaults":103,"babel-runtime/helpers/interop-require-wildcard":108}],51:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-wildcard")["default"],a=e("babel-runtime/helpers/defaults")["default"];Object.defineProperty(r,"__esModule",{value:!0});var i=e("./isomorph"),o=n(i);r.isomorph=o,a(r,n(i))},{"./isomorph":52,"babel-runtime/helpers/defaults":103,"babel-runtime/helpers/interop-require-wildcard":108}],52:[function(e,t,r){"use strict";function n(e,t){if(e.order()!==t.order())return!1; + var r=e.degree(),n=p.triangles(e),a=h.numberOfCliques(e),i=[];r.forEach(function(e,t){i.push([r.get(t),n.get(t),a.get(t)])}),i.sort(function(e,t){return e[0]-t[0]||e[1]-t[1]||e[2]-t[2]});var o=t.degree(),u=p.triangles(t),s=h.numberOfCliques(t),l=[];return o.forEach(function(e,t){l.push([o.get(t),u.get(t),s.get(t)])}),l.sort(function(e,t){return e[0]-t[0]||e[1]-t[1]||e[2]-t[2]}),i.every(function(e,t){var r=l[t];return e[0]===r[0]&&e[1]===r[1]&&e[2]===r[2]})}function a(e,t){return d["default"]("couldBeIsomorphic",[e,t])}function i(e,t){if(e.order()!==t.order())return!1;var r=e.degree(),n=p.triangles(e),a=[];r.forEach(function(e,t){a.push([r.get(t),n.get(t)])}),a.sort(function(e,t){return e[0]-t[0]||e[1]-t[1]});var i=t.degree(),o=p.triangles(t),u=[];return i.forEach(function(e,t){u.push([i.get(t),o.get(t)])}),u.sort(function(e,t){return e[0]-t[0]||e[1]-t[1]}),a.every(function(e,t){var r=u[t];return e[0]===r[0]&&e[1]===r[1]})}function o(e,t){return d["default"]("fastCouldBeIsomorphic",[e,t])}function u(e,t){if(e.order()!==t.order())return!1;var r=l(e.degree().values());r.sort(function(e,t){return e-t});var n=l(t.degree().values());return n.sort(function(e,t){return e-t}),r.every(function(e,t){return e===n[t]})}function s(e,t){return d["default"]("fasterCouldBeIsomorphic",[e,t])}var l=e("babel-runtime/core-js/array/from")["default"],c=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.couldBeIsomorphic=n,r.genCouldBeIsomorphic=a,r.fastCouldBeIsomorphic=i,r.genFastCouldBeIsomorphic=o,r.fasterCouldBeIsomorphic=u,r.genFasterCouldBeIsomorphic=s;var f=e("../../_internals/delegate"),d=c(f),h=e("../clique"),p=e("../cluster")},{"../../_internals/delegate":12,"../clique":46,"../cluster":47,"babel-runtime/core-js/array/from":88,"babel-runtime/helpers/interop-require-default":107}],53:[function(e,t,r){"use strict";function n(e,t){var r=new S["default"](t),n=new S["default"](e);if(r.size!==n.size||O.someIterator(n.values(),function(e){return!r.has(e)}))throw new k["default"]("Node sets of graphs are not equal.")}function a(e,t){function r(e,t){return t?j.relabelNodes(e,function(e){return t+e.toString()}):e}var n=arguments.length<=2||void 0===arguments[2]?{}:arguments[2],a=n.rename,i=void 0===a?[null,null]:a;if(e.isMultigraph()!==t.isMultigraph())throw new k["default"]("G and H must both be graphs or multigraphs");var o=new e.constructor;if(o.name="union("+e.name+", "+t.name+")",e=r(e,i[0]),t=r(t,i[1]),new S["default"](e).intersection(new S["default"](t)).size>0)throw new k["default"]("The node sets of G and H are not disjoint. Use appropriate {rename: [Gprefix, Hprefix]} or use disjointUnion({G, H})");return o.addNodesFrom(e.nodesIter(!0)),o.addNodesFrom(t.nodesIter(!0)),o.addEdgesFrom(e.isMultigraph()?e.edgesIter(!0,!0):e.edgesIter(!0)),o.addEdgesFrom(t.isMultigraph()?t.edgesIter(!0,!0):t.edgesIter(!0)),b(o.graph,e.graph,t.graph),o}function i(e,t,r){return w["default"]("union",[e,t,r])}function o(e,t){var r=j.convertNodeLabelsToIntegers(e),n=j.convertNodeLabelsToIntegers(t,r.order()),i=a(r,n);return i.name="disjointUnion("+e.name+", "+t.name+")",b(i.graph,e.graph,t.graph),i}function u(e,t){return w["default"]("disjointUnion",[e,t])}function s(e,t){if(e.isMultigraph()!==t.isMultigraph())throw new k["default"]("G and H must both be graphs or multigraphs");var r=E.createEmptyCopy(e);r.name="Intersection of ("+e.name+" and "+t.name+")",n(e,t);var a=e.numberOfEdges()0;){var o=i;i=new k.Map;var u=!0,s=!1,l=void 0;try{for(var c,f=g(o.keys());!(u=(c=f.next()).done);u=!0){var d=c.value;n.has(d)||(n.set(d,a),e.get(d).forEach(function(e,t){return i.set(t,1)}))}}catch(h){s=!0,l=h}finally{try{!u&&f["return"]&&f["return"]()}finally{if(s)throw l}}if(null!=r&&a>=r)break;a+=1}return n}function a(e,t,r){return w["default"]("singleSourceShortestPathLength",[e,t,r])}function i(e,t){var r=new k.Map,a=!0,i=!1,o=void 0;try{for(var u,s=g(e);!(a=(u=s.next()).done);a=!0){var l=u.value;r.set(l,n(e,l,t))}}catch(c){i=!0,o=c}finally{try{!a&&s["return"]&&s["return"]()}finally{if(i)throw o}}return r}function o(e,t){return w["default"]("allPairsShortestPathLength",[e,t])}function u(e,t,r){for(var n=l(e,t,r),a=b(n,3),i=a[0],o=a[1],u=a[2],s=[];null!=u;)s.push(u),u=i.get(u);for(u=o.get(s[0]),s.reverse();null!=u;)s.push(u),u=o.get(u);return s}function s(e,t,r){return w["default"]("bidirectionalShortestPath",[e,t,r])}function l(e,t,r){if(k.nodesAreEqual(t,r))return[new k.Map([[t,null]]),new k.Map([[r,null]]),t];var n,a;e.isDirected()?(n=e.predecessorsIter.bind(e),a=e.successorsIter.bind(e)):(n=e.neighborsIter.bind(e),a=e.neighborsIter.bind(e));for(var i,o=new k.Map([[t,null]]),u=new k.Map([[r,null]]),s=[t],l=[r];s.length>0&&l.length>0;)if(s.length<=l.length){i=s,s=[];var c=!0,f=!1,d=void 0;try{for(var h,p=g(i);!(c=(h=p.next()).done);c=!0){var v=h.value,b=!0,y=!1,m=void 0;try{for(var w,j=g(a(v));!(b=(w=j.next()).done);b=!0){var E=w.value;if(o.has(E)||(s.push(E),o.set(E,v)),u.has(E))return[o,u,E]}}catch(_){y=!0,m=_}finally{try{!b&&j["return"]&&j["return"]()}finally{if(y)throw m}}}}catch(_){f=!0,d=_}finally{try{!c&&p["return"]&&p["return"]()}finally{if(f)throw d}}}else{i=l,l=[];var S=!0,O=!1,M=void 0;try{for(var I,P=g(i);!(S=(I=P.next()).done);S=!0){var v=I.value,N=!0,A=!1,$=void 0;try{for(var q,D=g(n(v));!(N=(q=D.next()).done);N=!0){var E=q.value;if(u.has(E)||(l.push(E),u.set(E,v)),o.has(E))return[o,u,E]}}catch(_){A=!0,$=_}finally{try{!N&&D["return"]&&D["return"]()}finally{if(A)throw $}}}}catch(_){O=!0,M=_}finally{try{!S&&P["return"]&&P["return"]()}finally{if(O)throw M}}}throw new x.JSNetworkXNoPath(k.sprintf("No path between `%j` and `%j`.",t,r))}function c(e,t,r){var n=0,a=new k.Map([[t,1]]),i=new k.Map([[t,[t]]]);if(0===r)return i;for(;a.size>0;){var o=a;a=new k.Map;var u=!0,s=!1,l=void 0;try{for(var c,f=g(o.keys());!(u=(c=f.next()).done);u=!0){var d=c.value,h=!0,p=!1,v=void 0;try{for(var b,y=g(e.get(d).keys());!(h=(b=y.next()).done);h=!0){var m=b.value;i.has(m)||(i.set(m,i.get(d).concat([m])),a.set(m,1))}}catch(w){p=!0,v=w}finally{try{!h&&y["return"]&&y["return"]()}finally{if(p)throw v}}}}catch(w){s=!0,l=w}finally{try{!u&&f["return"]&&f["return"]()}finally{if(s)throw l}}if(n+=1,null!=r&&n>=r)break}return i}function f(e,t,r){return w["default"]("singleSourceShortestPath",[e,t,r])}function d(e,t){var r=new k.Map,n=!0,a=!1,i=void 0;try{for(var o,u=g(e);!(n=(o=u.next()).done);n=!0){var s=o.value;r.set(s,c(e,s,t))}}catch(l){a=!0,i=l}finally{try{!n&&u["return"]&&u["return"]()}finally{if(a)throw i}}return r}function h(e,t){return w["default"]("allPairsShortestPath",[e,t])}function p(e,t){for(var r=arguments.length<=2||void 0===arguments[2]?{}:arguments[2],n=r.target,a=r.cutoff,i=r.returnSeen,o=0,u=[t],s=new k.Map([[t,o]]),l=new k.Map([[t,[]]]);u.length>0;){o+=1;var c=u;if(u=[],c.forEach(function(t){e.get(t).forEach(function(e,r){s.has(r)?s.get(r)===o&&l.get(r).push(t):(l.set(r,[t]),s.set(r,o),u.push(r))})}),null!=a&&o>=a)break}return null!=n?i?l.has(n)?[l.get(n),s.get(n)]:[[],-1]:k.getDefault(l.get(n),[]):i?[l,s]:l}function v(e,t,r){return w["default"]("predecessor",[e,t,r])}var b=e("babel-runtime/helpers/sliced-to-array")["default"],g=e("babel-runtime/core-js/get-iterator")["default"],y=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.singleSourceShortestPathLength=n,r.genSingleSourceShortestPathLength=a,r.allPairsShortestPathLength=i,r.genAllPairsShortestPathLength=o,r.bidirectionalShortestPath=u,r.genBidirectionalShortestPath=s,r.singleSourceShortestPath=c,r.genSingleSourceShortestPath=f,r.allPairsShortestPath=d,r.genAllPairsShortestPath=h,r.predecessor=p,r.genPredecessor=v;var m=e("../../_internals/delegate"),w=y(m),x=e("../../exceptions"),k=e("../../_internals")},{"../../_internals":20,"../../_internals/delegate":12,"../../exceptions":78,"babel-runtime/core-js/get-iterator":89,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/sliced-to-array":109}],58:[function(e,t,r){"use strict";function n(e,t){var r=t.source,n=t.target,a=t.weight,i=void 0===a?"weight":a,o=d(e,{source:r,target:n,weight:i}),u=m(o,2),s=(u[0],u[1]),l=s.get(n);if(!l)throw new S["default"](E.sprintf("Node %j is not reachable from %j",r,n));return l}function a(e,t){return j["default"]("dijkstraPath",[e,t])}function i(e,t){var r=t.source,n=t.target,a=t.weight,i=void 0===a?"weight":a,o=c(e,{source:r,weight:i}),u=o.get(n);if(null==u)throw new S["default"](E.sprintf("Node %j is not reachable from %j",r,n));return u}function o(e,t){return j["default"]("dijkstraPathLength",[e,t])}function u(e,t){var r=1/0;for(var n in e){var a=E.getDefault(e[n][t],1);r>a&&(r=a)}return r}function s(e,t){var r=t.source,n=t.cutoff,a=t.weight,i=void 0===a?"weight":a,o=d(e,{source:r,cutoff:n,weight:i}),u=m(o,2),s=(u[0],u[1]);return s}function l(e,t){return j["default"]("singleSourceDijkstraPath",[e,t])}function c(e,t){var r=t.source,n=t.cutoff,a=t.weight,i=void 0===a?"weight":a,o=new E.Map,s=new E.Map([[r,0]]),l=new E.PriorityQueue,c=0;for(l.enqueue(0,[c++,r]);l.size>0;){var f=l.dequeue(),d=m(f,2),h=d[0],p=m(d[1],2),v=(p[0],p[1]);if(!o.has(v)){o.set(v,h);var b=void 0;b=e.isMultigraph()?E.mapIterator(e.get(v),function(e){var t=m(e,2),r=t[0],n=t[1];return[r,y({},i,u(n,i))]}):e.get(v);var g=!0,x=!1,k=void 0;try{for(var j,_=w(b);!(g=(j=_.next()).done);g=!0){var S=m(j.value,2),O=S[0],M=S[1],I=h+E.getDefault(M[i],1);if(!(null!=n&&I>n))if(o.has(O)){if(I0;){var h=f.dequeue(),p=m(h,2),v=p[0],b=m(p[1],2),g=(b[0],b[1]);if(!s.has(g)){if(s.set(g,v),E.nodesAreEqual(g,n))break;var x=void 0;x=e.isMultigraph()?E.mapIterator(e.get(g),function(e){var t=m(e,2),r=t[0],n=t[1];return[r,y({},o,u(n,o))]}):e.get(g);var k=!0,j=!1,_=void 0;try{for(var S,O=w(x);!(k=(S=O.next()).done);k=!0){var M=m(S.value,2),I=M[0],P=M[1],N=v+E.getDefault(P[o],1);if(!(null!=a&&N>a))if(s.has(I)){if(N0)if(e.isDirected()){var i=0,o=0,u=!0,s=!1,l=void 0;try{for(var c,f=E(e.inDegree().values());!(u=(c=f.next()).done);u=!0){var d=c.value;i+=d}}catch(h){s=!0,l=h}finally{try{!u&&f["return"]&&f["return"]()}finally{if(s)throw l}}var p=!0,v=!1,b=void 0;try{for(var g,y=E(e.outDegree().values());!(p=(g=y.next()).done);p=!0){var m=g.value;o+=m}}catch(h){v=!0,b=h}finally{try{!p&&y["return"]&&y["return"]()}finally{if(v)throw b}}r+=I.sprintf("Average in degree: %s\nAverage out degree: %s",(i/a).toFixed(4),(o/a).toFixed(4))}else{var w=0,x=!0,k=!1,j=void 0;try{for(var _,S=E(e.degree().values());!(x=(_=S.next()).done);x=!0){var O=_.value;w+=O}}catch(h){k=!0,j=h}finally{try{!x&&S["return"]&&S["return"]()}finally{if(k)throw j}}r+=I.sprintf("Average degree: %s",(w/a).toFixed(4))}}else{if(!e.hasNode(t))throw new M["default"](I.sprintf("Node %j not in graph.",t));r=I.sprintf("Node %j has the following properties:\nDegree: %s\nNeighbors: %s",t,e.degree(t),e.neighbors(t).map(function(e){return JSON.stringify(e)}).join(" "))}return r}function m(e,t,r){if(I.isMap(r))r.forEach(function(r,n){return e.node.get(n)[t]=r});else{if(!I.isPlainObject(r))throw new TypeError("Attributes must be a Map or a plain object");for(var n in r)n=isNaN(n)?n:+n,e.node.get(n)[t]=r[n]}}function w(e,t){var r=new _;return e.node.forEach(function(e,n){n.hasOwnProperty(t)&&r.set(e,n[t])}),r}function x(e,t,r){r.forEach(function(r,n){e.get(r[0]).get(r[1])[t]=n})}function k(e,t){var r=new _;return e.edges(null,!0).forEach(function(e){if(e[2].hasOwnProperty(t)){var n=e[2][t];e.length=2,r.set(e,n)}}),r}var j=e("babel-runtime/core-js/array/from")["default"],E=e("babel-runtime/core-js/get-iterator")["default"],_=e("babel-runtime/core-js/map")["default"],S=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.nodes=n,r.nodesIter=a,r.edges=i,r.edgesIter=o,r.degree=u,r.neighbors=s,r.numberOfNodes=l,r.numberOfEdges=c,r.density=f,r.degreeHistogram=d,r.isDirected=h,r.freeze=p,r.isFrozen=v,r.subgraph=b,r.createEmptyCopy=g,r.info=y,r.setNodeAttributes=m,r.getNodeAttributes=w,r.setEdgeAttributes=x,r.getEdgeAttributes=k;var O=e("../exceptions/JSNetworkXError"),M=S(O),I=e("../_internals")},{"../_internals":20,"../exceptions/JSNetworkXError":73,"babel-runtime/core-js/array/from":88,"babel-runtime/core-js/get-iterator":89,"babel-runtime/core-js/map":91,"babel-runtime/helpers/interop-require-default":107}],65:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-default")["default"],a=e("babel-runtime/helpers/interop-require-wildcard")["default"],i=e("babel-runtime/helpers/defaults")["default"];Object.defineProperty(r,"__esModule",{value:!0});var o=e("./Graph"),u=n(o),s=e("./DiGraph"),l=n(s),c=e("./MultiGraph"),f=n(c),d=e("./MultiDiGraph"),h=n(d),p=e("./functions"),v=a(p);r.Graph=u["default"],r.DiGraph=l["default"],r.MultiGraph=f["default"],r.MultiDiGraph=h["default"],r.functions=v,i(r,a(p))},{"./DiGraph":60,"./Graph":61,"./MultiDiGraph":62,"./MultiGraph":63,"./functions":64,"babel-runtime/helpers/defaults":103,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/interop-require-wildcard":108}],66:[function(e,t,r){"use strict";function n(e,t){var r=new h.Map;if(null!=t)s(t).forEach(function(n){return r.set(n,e.neighbors(n).filter(function(e){return t.indexOf(e)>-1}))});else{var n=!0,a=!1,i=void 0;try{for(var o,u=l(e);!(n=(o=u.next()).done);n=!0){var c=o.value;r.set(c,e.neighbors(c))}}catch(f){a=!0,i=f}finally{try{!n&&u["return"]&&u["return"]()}finally{if(a)throw i}}}return r}function a(e,t){var r=d["default"](t);if(r.addNodesFrom(e.keys()),r.isMultigraph()&&!r.isDirected()){var n=new h.Set;e.forEach(function(e,t){e.forEach(function(e){n.has(e)||r.addEdge(t,e)}),n.add(t)})}else e.forEach(function(e,t){e.forEach(function(e){return r.addEdge(t,e)})});return r}function i(e,t,r){var n=new h.Map;if(null!=t)t=s(t),t.forEach(function(a){var i=n.set(a,new h.Map);e.get(a).forEach(function(e,n){t.indexOf(e)>-1&&i.set(e,null==r?n:r)})});else{var a=!0,i=!1,o=void 0;try{for(var c,f=function(){var e=u(c.value,2),t=e[0],a=e[1],i=n.set(a,new h.Map);t.forEach(function(e,t){i.set(t,null==r?e:r)})},d=l(e.adjacencyIter());!(a=(c=d.next()).done);a=!0)f()}catch(p){i=!0,o=p}finally{try{!a&&d["return"]&&d["return"]()}finally{if(i)throw o}}}return n}function o(e,t,r){var n=d["default"](t),a=new h.Set;if(n.addNodesFrom(e.keys()),r)if(n.isDirected())e.forEach(function(e,t){if(h.isArrayLike(e))throw new TypeError("Value is not a map.");e.forEach(function(e,r){for(var a in e){var i=e[a];n.isMultigraph()?n.addEdge(t,r,a,i):n.addEdge(t,r,i)}})});else{var i=n.isMultigraph();e.forEach(function(e,t){if(h.isArrayLike(e))throw new TypeError("Not a map");e.forEach(function(e,r){if(!a.has(h.tuple2(t,r))){for(var o in e){var u=e[o];i?n.addEdge(t,r,o,u):n.addEdge(t,r,u)}a.add(h.tuple2(r,t))}})})}else n.isMultigraph()&&!n.isDirected()?e.forEach(function(e,t){if(h.isArrayLike(e))throw new TypeError("Value is not a map");e.forEach(function(e,r){a.has(h.tuple2(t,r))||(n.addEdge(t,r,e),a.add(h.tuple2(r,t)))})}):e.forEach(function(e,t){if(h.isArrayLike(e))throw new TypeError("Value is not a map");e.forEach(function(e,r){n.addEdge(t,r,e)})});return n}var u=e("babel-runtime/helpers/sliced-to-array")["default"],s=e("babel-runtime/core-js/array/from")["default"],l=e("babel-runtime/core-js/get-iterator")["default"],c=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.toMapOfLists=n,r.fromMapOfLists=a,r.toMapOfMaps=i,r.fromMapOfMaps=o;var f=e("./prepCreateUsing"),d=c(f),h=e("../_internals")},{"../_internals":20,"./prepCreateUsing":68,"babel-runtime/core-js/array/from":88,"babel-runtime/core-js/get-iterator":89,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/sliced-to-array":109}],67:[function(e,t,r){"use strict";function n(e){function t(e,t,a,i){var o=r[e.type];if(o){for(var u=0,s=o.length;s>u&&!e.isPropgationStopped();u+=3)o[u+2]&&o[u].call(o[u+1]||t,e);if(!e.isDefaultPrevented()&&(i?n[a].apply(t,i):n[a].call(t),!e.isPropgationStopped()))for(u=0,s=o.length;s>u&&!e.isPropgationStopped();u+=3)o[u+2]||o[u].call(o[u+1]||t,e)}}if("function"==typeof e.on)return e;var r={addNodes:[],removeNodes:[],addEdges:[],removeEdges:[],clear:[]},n=e.constructor.prototype;return e.on=function(e,t,n,a){if(!r[e])throw new Error('Event "'+e+'" is not supported.');r[e].push(t,n,!!a)},e.off=function(e,t,n){var a,i,o;if(1===arguments.length)r[e].length=0;else if(2===arguments.length)for(a=r[e],i=a.length-2,"function"!=typeof t&&(i+=1),o=i;o>0;o-=2)a[o]===t&&a.splice(o,3);else for(a=r[e],i=a.length-2,o=i;o>0;o-=2)a[o]===t&&a[o+1]===n&&a.splice(o,2)},e.addNode=function(r){var n=e.hasNode(r)?[]:[r],a=new c("addNodes",this);a.nodes=[r],a.newNodes=n,t(a,this,"addNode",arguments)},e.addNodesFrom=function(r){var n=[],a=[],i=!0,o=!1,u=void 0;try{for(var f,d=s(r);!(i=(f=d.next()).done);i=!0){var h=f.value,p=Array.isArray(h)?h[0]:h;n.push(Array.isArray(h)?h.slice():h),e.hasNode(p)||a.push(p)}}catch(v){o=!0,u=v}finally{try{!i&&d["return"]&&d["return"]()}finally{if(o)throw u}}var b=new c("addNodes",this);b.nodes=n.filter(function(e){return Array.isArray(e)?e[0]:e}),b.newNodes=a;var g=l(arguments);g[0]=n,t(b,this,"addNodesFrom",g)},e.addEdge=function(e,r){var n=[[e,r]],a=this.hasEdge(e,r)?[]:n,i=new c("addEdges",this);i.edges=n,i.newEdges=a,t(i,this,"addEdge",arguments)},e.addEdgesFrom=function(e){var r=[],n=[],a=!0,i=!1,o=void 0;try{for(var u,f=s(e);!(a=(u=f.next()).done);a=!0){var d=u.value;r.push(d.slice()),this.hasEdge(d[0],d[1])||n.push(d.slice(0,2))}}catch(h){i=!0,o=h}finally{try{!a&&f["return"]&&f["return"]()}finally{if(i)throw o}}var p=new c("addEdges",this);p.edges=r,p.newEdges=n;var v=l(arguments);v[0]=r,t(p,this,"addEdgesFrom",v)},e.removeNode=function(e){var r=new c("removeNodes",this);r.nodes=[e],t(r,this,"removeNode",arguments)},e.removeNodesFrom=function(e){var r=[],n=!0,a=!1,i=void 0;try{for(var o,u=s(e);!(n=(o=u.next()).done);n=!0){var f=o.value;r.push(Array.isArray(f)?f.slice():f)}}catch(d){a=!0,i=d}finally{try{!n&&u["return"]&&u["return"]()}finally{if(a)throw i}}var h=new c("removeNodes",this);h.nodes=r;var p=l(arguments);p[0]=r,t(h,this,"removeNodesFrom",p)},e.removeEdge=function(e,r){var n=new c("removeEdges",this);n.edges=[[e,r]],t(n,this,"removeEdge",arguments)},e.removeEdgesFrom=function(e){var r=[],n=!0,a=!1,i=void 0;try{for(var o,u=s(e);!(n=(o=u.next()).done);n=!0){var f=o.value;r.push(f.slice())}}catch(d){a=!0,i=d}finally{try{!n&&u["return"]&&u["return"]()}finally{if(a)throw i}}var h=new c("removeEdges");h.edges=r;var p=l(arguments);p[0]=r,t(h,this,"removeEdgesFrom",p)},e.clear=function(){t(new c("clear",this),this,"clear")},e}function a(e){var t=e.constructor.prototype;return"function"!=typeof e.on?e:(e.addNode=t.addNode,e.addNodesFrome=t.addNodesFrom,e.addEdge=t.addEdge,e.addEdgesFrome=t.addEdgesFrom,e.removeNode=t.removeNode,e.removeEdge=t.removeEdge,e.removeNodesFrom=t.removeNodesFrom,e.removeEdgesFrom=t.removeEdgesFrom,e.clear=t.clear,delete e.on,delete e.off,e)}function i(e){return"function"==typeof e.on&&"function"==typeof e.off}var o=e("babel-runtime/helpers/create-class")["default"],u=e("babel-runtime/helpers/class-call-check")["default"],s=e("babel-runtime/core-js/get-iterator")["default"],l=e("babel-runtime/core-js/array/from")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.observe=n,r.unobserve=a,r.isObservable=i;var c=function(){function e(t,r){u(this,e),this.type=t,this.target=r,this._defaultAction=!0,this._propagate=!0}return o(e,[{key:"stopPropagation",value:function(){this._propagate=!1}},{key:"isPropgationStopped",value:function(){return!this._propagate}},{key:"preventDefault",value:function(){this._defaultAction=!1}},{key:"isDefaultPrevented",value:function(){return!this._defaultAction}}]),e}()},{"babel-runtime/core-js/array/from":88,"babel-runtime/core-js/get-iterator":89,"babel-runtime/helpers/class-call-check":101,"babel-runtime/helpers/create-class":102}],68:[function(e,t,r){"use strict";function n(t){var r,n=e("../classes/Graph");if(null==t)r=new n;else{r=t;try{r.clear()}catch(a){throw new TypeError("Input graph is not a jsnx graph type")}}return r}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=n,t.exports=r["default"]},{"../classes/Graph":61}],69:[function(e,t,r){"use strict";function n(e,t){var r=arguments.length<=2||void 0===arguments[2]?!1:arguments[2],n=null;if(S.call(e,"adj"))try{return n=w.fromMapOfMaps(e.adj,t,e.isMultigraph()),S.call(e,"graph")&&"object"==typeof e.graph&&(n.graph=_.clone(e.graph)),S.call(e,"node")&&_.isMap(e.node)&&(n.node=new _.Map,e.node.forEach(function(e,t){return n.node.set(t,_.clone(e))})),n}catch(a){throw a}if(_.isMap(e))try{return w.fromMapOfMaps(e,t,r)}catch(i){try{return w.fromMapOfLists(e,t)}catch(a){throw new Error("Map data structure cannot be converted to a graph.")}}if(_.isPlainObject(e))try{return l(e,t,r)}catch(i){try{return u(e,t)}catch(a){throw new Error("Object data structure cannot be converted to a graph.")}}if(_.isArrayLike(e))try{return f(e,t)}catch(i){throw new Error("Input is not a valid edge list")}return n}function a(e){return e.toUndirected()}function i(e){return e.toDirected()}function o(e,t){var r=function(e){return t.indexOf(e)>-1},n=h(null);null==t?(t=e,r=function(e){return t.hasNode(e)}):t=p(t);var a=!0,i=!1,o=void 0;try{for(var u,s=v(t);!(a=(u=s.next()).done);a=!0){var l=u.value;n[l]=e.neighbors(l).filter(r)}}catch(c){i=!0,o=c}finally{try{!a&&s["return"]&&s["return"]()}finally{if(i)throw o}}return n}function u(e,t){var r=k["default"](t);r.addNodesFrom(b.mark(function u(){var t;return b.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:r.t0=b.keys(e);case 1:if((r.t1=r.t0()).done){r.next=7;break}return t=r.t1.value,r.next=5,isNaN(t)?t:+t;case 5:r.next=1;break;case 7:case"end":return r.stop()}},u,this)})());var n,a;if(r.isMultigraph()&&!r.isDirected()){var i=new _.Set;for(n in e)a=e[n],n=isNaN(n)?n:+n,_.forEach(a,function(e){i.has(e)||r.addEdge(n,e)}),i.add(n)}else{var o=[];for(n in e)a=e[n],n=isNaN(n)?n:+n,_.forEach(a,function(e){o.push([n,e])});r.addEdgesFrom(o)}return r}function s(e,t,r){var n={};if(null!=t)t=p(t),null!=r?t.forEach(function(a){n[a]={},e.get(a).forEach(function(e,i){t.indexOf(i)>-1&&(n[a][i]=r)})}):t.forEach(function(r){n[r]={},e.get(r).forEach(function(e,a){t.indexOf(a)>-1&&(n[r][a]=e)})});else if(null!=r){var a=!0,i=!1,o=void 0;try{for(var u,s=v(e.adjacencyIter());!(a=(u=s.next()).done);a=!0){var l=d(u.value,2),c=l[0],f=l[1];n[f]=E["default"](c,function(){return r})}}catch(h){i=!0,o=h}finally{try{!a&&s["return"]&&s["return"]()}finally{if(i)throw o}}}else{var b=!0,g=!1,y=void 0;try{for(var m,w=v(e.adjacencyIter());!(b=(m=w.next()).done);b=!0){var x=d(m.value,2),c=x[0],f=x[1];n[f]=_.clone(c)}}catch(h){g=!0,y=h}finally{try{!b&&w["return"]&&w["return"]()}finally{if(g)throw y}}}return n}function l(e,t){var r=arguments.length<=2||void 0===arguments[2]?!1:arguments[2],n=k["default"](t),a=new _.Set;if(n.addNodesFrom(b.mark(function f(){var t;return b.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:r.t0=b.keys(e);case 1:if((r.t1=r.t0()).done){r.next=7;break}return t=r.t1.value,r.next=5,isNaN(t)?t:+t;case 5:r.next=1;break;case 7:case"end":return r.stop()}},f,this)})()),r)if(n.isDirected())for(var i in e){var o=e[i];if(_.isArrayLike(o))throw new TypeError("Inner object seems to be an array");i=isNaN(i)?i:+i;for(var u in o){var s=o[u];u=isNaN(u)?u:+u;for(var l in s)n.isMultigraph()?n.addEdge(i,u,l,s[l]):n.addEdge(i,u,s[l])}}else for(var i in e){var o=e[i];if(_.isArrayLike(o))throw new TypeError("Inner object seems to be an array");i=isNaN(i)?i:+i;for(var u in o){var s=o[u];if(u=isNaN(u)?u:+u,!a.has([i,u])){for(var l in s)n.isMultigraph()?n.addEdge(i,u,l,s[l]):n.addEdge(i,u,s[l]);a.add([u,i])}}}else if(n.isMultigraph()&&!n.isDirected())for(var i in e){var o=e[i];if(_.isArrayLike(o))throw new TypeError("Inner object seems to be an array");i=isNaN(i)?i:+i;for(var u in o){var c=o[u];u=isNaN(u)?u:+u,a.has([i,u])||(n.addEdge(i,u,c),a.add([u,i]))}}else for(var i in e){var o=e[i];if(_.isArrayLike(o))throw new TypeError("Inner object seems to be an array");i=isNaN(i)?i:+i;for(var u in o){var c=o[u];u=isNaN(u)?u:+u,n.addEdge(i,u,c)}}return n}function c(e,t){return null!=t?e.edges(t,!0):e.edges(null,!0)}function f(e,t){var r=k["default"](t);return r.addEdgesFrom(e),r}var d=e("babel-runtime/helpers/sliced-to-array")["default"],h=e("babel-runtime/core-js/object/create")["default"],p=e("babel-runtime/core-js/array/from")["default"],v=e("babel-runtime/core-js/get-iterator")["default"],b=e("babel-runtime/regenerator")["default"],g=e("babel-runtime/helpers/interop-require-wildcard")["default"],y=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.toNetworkxGraph=n,r.convertToUndirected=a,r.convertToDirected=i,r.toDictOfLists=o,r.fromDictOfLists=u,r.toDictOfDicts=s,r.fromDictOfDicts=l,r.toEdgelist=c,r.fromEdgelist=f;var m=e("./contrib/convert"),w=g(m),x=e("./contrib/prepCreateUsing"),k=y(x),j=e("lodash/object/mapValues"),E=y(j),_=e("./_internals"),S=Object.prototype.hasOwnProperty},{"./_internals":20,"./contrib/convert":66,"./contrib/prepCreateUsing":68,"babel-runtime/core-js/array/from":88,"babel-runtime/core-js/get-iterator":89,"babel-runtime/core-js/object/create":93,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/interop-require-wildcard":108,"babel-runtime/helpers/sliced-to-array":109,"babel-runtime/regenerator":166,"lodash/object/mapValues":237}],70:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-default")["default"],a=e("babel-runtime/helpers/defaults")["default"],i=e("babel-runtime/helpers/interop-require-wildcard")["default"];Object.defineProperty(r,"__esModule",{value:!0});var o=e("./svg"),u=n(o);r.svg=u["default"],a(r,i(o))},{"./svg":71,"babel-runtime/helpers/defaults":103,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/interop-require-wildcard":108}],71:[function(e,t,r){(function(t){"use strict";function n(e,t,r,n){return 180*Math.atan2(n-t,r-e)/Math.PI}function a(e){if(Array.isArray(e))return e;var t=[],r=0,n=!0,a=!1,i=void 0;try{for(var o,u=m(e);!(n=(o=u.next()).done);n=!0){var s=o.value;t[r++]=Array.isArray(s)?w(s):s}}catch(l){a=!0,i=l}finally{try{!n&&u["return"]&&u["return"]()}finally{if(a)throw i}}return t}function i(e,t,r){if("boolean"==typeof t&&(r=t,t=null),t=t||_||{},_=t,t.d3&&(M=t.d3),t=k.deepmerge({},I,t),!M)throw new Error("D3 requried for draw()");if(null==t.element&&null==E)throw new Error("Output element required for draw()");E=t.element||E,M.select(E).select("svg.jsnx").remove();var a,i,c,f=M.select(E),d=[],h=[],y=f.append("svg").classed("jsnx",!0).attr("pointer-events","all"),w=y.append("g"),x=w.append("g").classed("edges",!0).selectAll("g.edge"),S=w.append("g").classed("nodes",!0).selectAll("g.node"),O=M.layout.force(),P=t.width||parseInt(f.style("width"),10),N=t.height||parseInt(f.style("height"),10),A=t.layoutAttr,$=t.nodelist||null,q=e.isDirected(),D=t.weighted,L={nodeSelection:S,edgeSelection:x};if(t.withLabels){var F=t.labels;switch(typeof F){case"object":a=function(e){return k.getDefault(F[e.node],"")};break;case"function":a=F;break;case"string":a=function(e){return e.data[F]};break;default:a=function(e){return e.node}}}if(t.labels=a,D){var G=t.weights;switch(typeof weigths){case"object":c=function(e){return k.getDefault(G[e.node],1)};break;case"function":c=G;break;case"string":c=function(e){return k.getDefault(e.data[G],1)};break;default:c=function(e){return 1}}}if(t.withEdgeLabels){var C=t.edgeLabels;if(D&&null==C)i=c;else switch(typeof C){case"object":i=function(e){return k.getDefault(F[e.node],"")};break;case"function":i=C;break;case"string":i=function(e){return e.data[C]};break;default:i=function(e){return e.edge}}t.edgeLabels=i}if(D&&t.weightedStroke){var z=1,T=!0,J=!1,R=void 0;try{for(var X,B=m(e.edgesIter(null,!0));!(T=(X=B.next()).done);T=!0){var U=X.value,V=(U.u,U.v,U.data),W=c({data:V});W>z&&(z=W)}}catch(H){J=!0,R=H}finally{try{!T&&B["return"]&&B["return"]()}finally{if(J)throw R}}var K=M.scale.linear().range([2,t.edgeStyle["stroke-width"]]).domain([0,z]);t.edgeStyle["stroke-width"]=function(e){return K(c.call(this,e))}}y.select("svg.jsnx").remove(),y.attr("width",P+"px").attr("height",N+"px").style("opacity",1e-6).transition().duration(1e3).style("opacity",1);var Y={size:!0,nodes:!0,links:!0,start:!0};for(var Q in A)Y[Q]!==!0&&O[Q](A[Q]);O.nodes(d).links(h).size([P,N]);var Z=1,ee=1;t.panZoom.enabled&&!function(){var e=t.panZoom.scale,r=!1,n=1,a=Z;y.call(M.behavior.zoom().on("zoom",function(){if(M.event.sourceEvent){var t=M.event.sourceEvent.shiftKey,i=e&&t||!(e||t);i&&!r?(n=M.event.scale,a=Z,r=!0):!i&&r&&(r=!1),Z=i?a*(M.event.scale/n):Z,ee=i?ee:Z/M.event.scale;var o=M.event.translate;w.attr("transform","translate("+o[0]+","+o[1]+")scale("+M.event.scale+")"),ue()}}))}();var te=j,re=t.edgeOffset,ne=t.nodeAttr.r,ae=t.nodeStyle["stroke-width"];"circle"===t.nodeShape?("function"!=typeof ne&&(ne=function(){return t.nodeAttr.r}),"function"!=typeof ae&&(ae=function(){return t.nodeStyle["stroke-width"]}),re=function(e){return[ne(e.source)+ae(e.source),ne(e.target)+ae(e.target)]}):Array.isArray(re)?re=function(){return t.edgeOffset}:"number"==typeof re&&(re=function(){return[t.edgeOffset,t.edgeOffset]});var ie=t.edgeStyle["stroke-width"];"function"!=typeof ie&&(ie=function(){return t.edgeStyle["stroke-width"]});var oe=t.edgeLabelOffset;te=q?function(){L.edgeSelection.each(function(e){if(e.source!==e.target){var t=M.select(this),r=e.source.x,a=e.source.y,i=e.target.x,o=e.target.y,u=n(r,a,i,o),s=Math.sqrt(Math.pow(i-r,2)+Math.pow(o-a,2)),l=re(e);l=[l[0]*ee,l[1]*ee],t.attr("transform",["translate(",r,",",a,")","rotate(",u,")"].join(""));var c=ie(e)*ee,f=s-l[1]-2*c,d=c/2;t.select(".line").attr("d",["M",l[0],0,"L",l[0],-d,"L",f,-d,"L",f,-c,"L",s-l[1],0,"z"].join(" "));var h=1/ee;t.select("text").attr("x",oe.x*h+l[0]+(s*h-l[0]-l[1])/2).attr("y",-ie(e)/2+-oe.y*h).attr("transform","scale("+ee+")")}})}:function(){L.edgeSelection.each(function(e){if(e.source!==e.target){var r=M.select(this),a=e.source.x,i=e.source.y,o=e.target.x,u=e.target.y,s=n(a,i,o,u),l=Math.sqrt(Math.pow(o-a,2)+Math.pow(u-i,2)),c=l/2,f=re(e);f=[f[0]*ee,f[1]*ee];var d=1/ee,h=ie(e)*ee,p=s>90&&279>s;r.attr("transform",["translate(",a,",",i,")","rotate(",s,")"].join("")),r.select(".line").attr("d",["M",f[0],h/4,"L",f[0],-h/4,"L",l-f[1],-h/4,"L",l-f[1],h/4,"z"].join(" ")),t.withEdgeLabels&&r.select("text").attr("x",(p?1:-1)*oe.x*d+f[0]+(l*d-f[0]-f[1])/2).attr("y",-ie(e)/4+-oe.y*d).attr("transform","scale("+ee+")"+(p?"rotate(180,"+c*(1/ee)+",0)":""))}})};var ue=function(){L.nodeSelection.attr("transform",function(e){return["translate(",e.x,",",e.y,")","scale(",ee,")"].join("")}),te()};O.on("tick",ue);var se=e.nodesIter(),le=e.edgesIter();return $&&(r=!1,se=e.nbunch_iter($),le=e.edges_iter($)),L.nodeSelection=o(e,se,O,S,t),L.edgeSelection=u(e,le,O,x,i),s(L.nodeSelection,t),l(L.edgeSelection,t,null,q),r?p(e,O,t,L):v(e)?b(e):g(e),O.start(),O}function o(e,t,r,n,a){var i=r.nodes(),o=!0,u=!1,s=void 0;try{for(var l,f=m(t);!(o=(l=f.next()).done);o=!0){var d=l.value,h=e.node.get(d),p={node:d,data:h,G:e};i.push(p),h[O]=p}}catch(v){u=!0,s=v}finally{try{!o&&f["return"]&&f["return"]()}finally{if(u)throw s}}n=n.data(i,c);var b=r.drag().on("dragstart",function(e){M.event.sourceEvent.stopPropagation(),a.stickyDrag&&(e.fixed=!0,M.select(this).classed("fixed",!0))}),g=n.enter().append("g").classed("node",!0).call(b);return g.append(a.nodeShape).classed("node-shape",!0),a.labels&&g.append("text").text(a.labels),n}function u(e,t,r,n,a){var i=r.links(),o=!0,u=!1,s=void 0;try{for(var l,c=m(t);!(o=(l=c.next()).done);o=!0){var d=y(l.value,3),h=d[0],p=d[1],v=d[2];v=v||e.getEdgeData(h,p);var b={edge:[h,p],source:e.node.get(h)[O],target:e.node.get(p)[O],data:v,G:e};i.push(b),v[O]=b}}catch(g){u=!0,s=g}finally{try{!o&&c["return"]&&c["return"]()}finally{if(u)throw s}}n=n.data(i,f);var w=n.enter().append("g").classed("edge",!0);return w.append("path").classed("line",!0),a&&w.append("text").text(a),n}function s(e,t,r){if(null!=r){var n=new k.Set,a=!0,i=!1,o=void 0;try{for(var u,s=m(r);!(a=(u=s.next()).done);a=!0){var l=u.value;n.add(k.isArrayLike(l)?l[0]:l)}}catch(c){i=!0,o=c}finally{try{!a&&s["return"]&&s["return"]()}finally{if(i)throw o}}e=e.filter(function(e){return n.has(e.node)})}e.selectAll(".node-shape").attr(t.nodeAttr).style(t.nodeStyle),t.withLabels&&e.selectAll("text").attr(t.labelAttr).style(t.labelStyle)}function l(e,t,r,n){if(null!=r){var a=new k.Map,i=!0,o=!1,u=void 0;try{for(var s,l=m(r);!(i=(s=l.next()).done);i=!0){var c=y(s.value,2),f=c[0],d=c[1];a.set(f,d)}}catch(h){o=!0,u=h}finally{try{!i&&l["return"]&&l["return"]()}finally{if(o)throw u}}e=e.filter(function(e){var t=e.edge;return a.get(t[0])===t[1]||n||a.get(t[1])===t[0]})}e.selectAll(".line").attr(t.edgeAttr).style(t.edgeStyle).style("stroke-width",0),t.withEdgeLabels&&e.selectAll("text").attr(t.edgeLabelAttr).style(t.edgeLabelStyle)}function c(e){return e.node}function f(e){return e.edge}function d(e,t,r,n){var a=r.nodes(),i=!0,o=!1,u=void 0;try{for(var s,l=m(e.nbunchIter(t));!(i=(s=l.next()).done);i=!0){var f=s.value,d=a.indexOf(e.node.get(f)[O]);d>-1&&a.splice(d,1)}}catch(h){o=!0,u=h}finally{try{!i&&l["return"]&&l["return"]()}finally{if(o)throw u}}return n=n.data(a,c),n.exit().remove(),n}function h(e,t,r,n){var a=r.links(),i=!0,o=!1,u=void 0;try{for(var s,l=m(t);!(i=(s=l.next()).done);i=!0){var c=y(s.value,2),d=c[0],h=c[1],p=a.indexOf(e.getEdgeData(d,h,{})[O]);p>-1&&a.splice(p,1)}}catch(v){o=!0,u=v}finally{try{!i&&l["return"]&&l["return"]()}finally{if(o)throw u}}return n=n.data(a,f),n.exit().remove(),n}function p(e,t,r,n){b(e,!1);var i=e.constructor.prototype,p=r.edgeLabels,v=e.isDirected();e.addNode=function(e,a){var u=!this.hasNode(e);i.addNode.call(this,e,a),u&&(n.nodeSelection=o(this,[e],t,n.nodeSelection,r)),s(n.nodeSelection,r,[e]),t.start()},e.addNodesFrom=function(e,u){var l=this;e=a(e);var c=e.filter(function(e){return!l.hasNode(k.isArrayLike(e)?e[0]:e)});i.addNodesFrom.call(this,e,u),c.length>0&&(n.nodeSelection=o(this,c,t,n.nodeSelection,r)),s(n.nodeSelection,r,e),t.start()},e.addEdge=function(a,c,f){var d=this,h=!this.hasEdge(a,c),b=[[a,c]],g=h?(a===c?[a]:b[0]).filter(function(e){return!d.hasNode(e)}):[];i.addEdge.call(e,a,c,f),g.length>0&&(n.nodeSelection=o(this,g,t,n.nodeSelection,r),s(n.nodeSelection,r,g)),h&&(n.edgeSelection=u(this,b,t,n.edgeSelection,p)),l(n.edgeSelection,r,b,v),t.start()},e.addEdgesFrom=function(c,f){var d=[],h=[],b=new k.Map,g=new k.Set;c=a(c);var w=!0,x=!1,j=void 0;try{for(var E,_=m(c);!(w=(E=_.next()).done);w=!0){var S=y(E.value,2),O=S[0],M=S[1];this.hasEdge(O,M)||b.get(O)===M||!v&&b.get(M)!==O||(d.push([O,M]),b.set(O,M),this.hasNode(O)||g.has(O)||(h.push(O),g.add(O)),this.hasNode(M)||g.has(M)||(h.push(M),g.add(M)))}}catch(I){x=!0,j=I}finally{try{!w&&_["return"]&&_["return"]()}finally{if(x)throw j}}i.addEdgesFrom.call(e,c,f),h.length>0&&(n.nodeSelection=o(this,h,t,n.nodeSelection,r),s(n.nodeSelection,r,h)),d.length>0&&(n.edgeSelection=u(this,d,t,n.edgeSelection,p)),l(n.edgeSelection,r,d,v),t.start()},e.removeNode=function(e){if(this.hasNode(e)){n.nodeSelection=d(this,[e],t,n.nodeSelection);var r=this.edgesIter([e]);this.isDirected()&&(r=x.mark(function a(t,r){return x.wrap(function(n){for(;;)switch(n.prev=n.next){case 0:return n.delegateYield(r,"t0",1);case 1:return n.delegateYield(t.inEdgesIter([e]),"t1",2);case 2:case"end":return n.stop()}},a,this)})(this,r)),n.edgeSelection=h(this,r,t,n.edgeSelection),t.resume()}i.removeNode.call(this,e)},e.removeNodesFrom=function(e){e=a(e),n.nodeSelection=d(this,e,t,n.nodeSelection);var r=this.edgesIter(e);this.isDirected()&&(r=x.mark(function o(t,r){return x.wrap(function(n){for(;;)switch(n.prev=n.next){case 0:return n.delegateYield(r,"t0",1);case 1:return n.delegateYield(t.inEdgesIter(e),"t1",2);case 2:case"end":return n.stop()}},o,this)})(this,r)),n.edgeSelection=h(this,r,t,n.edgeSelection),t.resume(),i.removeNodesFrom.call(this,e)},e.removeEdge=function(e,r){n.edgeSelection=h(this,[[e,r]],t,n.edgeSelection),t.resume(),i.removeEdge.call(this,e,r)},e.removeEdgesFrom=function(r){r=a(r),n.edgeSelection=h(this,r,t,n.edgeSelection),t.resume(),i.removeEdgesFrom.call(e,r)},e.clear=function(){n.nodeSelection=n.nodeSelection.data([],c),n.nodeSelection.exit().remove(),n.edgeSelection=n.edgeSelection.data([],f),n.edgeSelection.exit().remove(),t.nodes([]).links([]).resume(),i.clear.call(this)},e.bound=!0}function v(e){return e.bound}function b(e){var t=arguments.length<=1||void 0===arguments[1]?!0:arguments[1];if(v(e)){var r=e.constructor.prototype;S.forEach(function(t){return e[t]=r[t]}),delete e.bound,t&&g(e)}}function g(e){var t=!0,r=!1,n=void 0;try{for(var a,i=m(e.nodesIter(!0));!(t=(a=i.next()).done);t=!0){var o=y(a.value,2),u=(o[0],o[1]);delete u[O]}}catch(s){r=!0,n=s}finally{try{!t&&i["return"]&&i["return"]()}finally{if(r)throw n}}var l=!0,c=!1,f=void 0; + try{for(var d,h=m(e.edgesIter(null,!0));!(l=(d=h.next()).done);l=!0){var p=y(d.value,3),u=(p[0],p[1],p[2]);delete u[O]}}catch(s){c=!0,f=s}finally{try{!l&&h["return"]&&h["return"]()}finally{if(c)throw f}}}var y=e("babel-runtime/helpers/sliced-to-array")["default"],m=e("babel-runtime/core-js/get-iterator")["default"],w=e("babel-runtime/core-js/array/from")["default"],x=e("babel-runtime/regenerator")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.draw=i;var k=e("../_internals"),j=function(){},E=null,_=null,S=["addNode","addNodesFrom","addEdge","addEdgesFrom","removeNode","removeNodesFrom","removeEdge","removeEdgesFrom","clear"],O="__d3datum__",M=t.d3,I={layoutAttr:{charge:-120,linkDistance:60},nodeShape:"circle",nodeAttr:{r:10},nodeStyle:{"stroke-width":2,stroke:"#333",fill:"#999",cursor:"pointer"},edgeAttr:{},edgeStyle:{fill:"#000","stroke-width":3},labelAttr:{},labelStyle:{"text-anchor":"middle","dominant-baseline":"central",cursor:"pointer","-webkit-user-select":"none",fill:"#000"},edgeLabelAttr:{},edgeLabelStyle:{"font-size":"0.8em","text-anchor":"middle","-webkit-user-select":"none"},edgeLabelOffset:{x:0,y:.5},withLabels:!1,withEdgeLabels:!1,edgeOffset:10,weighted:!1,weights:"weight",weightedStroke:!0,panZoom:{enabled:!0,scale:!0}}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"../_internals":20,"babel-runtime/core-js/array/from":88,"babel-runtime/core-js/get-iterator":89,"babel-runtime/helpers/sliced-to-array":109,"babel-runtime/regenerator":166}],72:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/get")["default"],a=e("babel-runtime/helpers/inherits")["default"],i=e("babel-runtime/helpers/class-call-check")["default"],o=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0});var u=e("./JSNetworkXException"),s=o(u),l=function(e){function t(e){i(this,t),n(Object.getPrototypeOf(t.prototype),"constructor",this).call(this,e),this.name="JSNetworkXAlgorithmError"}return a(t,e),t}(s["default"]);r["default"]=l,t.exports=r["default"]},{"./JSNetworkXException":74,"babel-runtime/helpers/class-call-check":101,"babel-runtime/helpers/get":105,"babel-runtime/helpers/inherits":106,"babel-runtime/helpers/interop-require-default":107}],73:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/get")["default"],a=e("babel-runtime/helpers/inherits")["default"],i=e("babel-runtime/helpers/class-call-check")["default"],o=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0});var u=e("./JSNetworkXException"),s=o(u),l=function(e){function t(e){i(this,t),n(Object.getPrototypeOf(t.prototype),"constructor",this).call(this,e),this.name="JSNetworkXError"}return a(t,e),t}(s["default"]);r["default"]=l,t.exports=r["default"]},{"./JSNetworkXException":74,"babel-runtime/helpers/class-call-check":101,"babel-runtime/helpers/get":105,"babel-runtime/helpers/inherits":106,"babel-runtime/helpers/interop-require-default":107}],74:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/class-call-check")["default"];Object.defineProperty(r,"__esModule",{value:!0});var a=function i(e){n(this,i),this.name="JSNetworkXException",this.message=e};r["default"]=a,t.exports=r["default"]},{"babel-runtime/helpers/class-call-check":101}],75:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/get")["default"],a=e("babel-runtime/helpers/inherits")["default"],i=e("babel-runtime/helpers/class-call-check")["default"],o=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0});var u=e("./JSNetworkXUnfeasible"),s=o(u),l=function(e){function t(e){i(this,t),n(Object.getPrototypeOf(t.prototype),"constructor",this).call(this,e),this.name="JSNetworkXNoPath"}return a(t,e),t}(s["default"]);r["default"]=l,t.exports=r["default"]},{"./JSNetworkXUnfeasible":76,"babel-runtime/helpers/class-call-check":101,"babel-runtime/helpers/get":105,"babel-runtime/helpers/inherits":106,"babel-runtime/helpers/interop-require-default":107}],76:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/get")["default"],a=e("babel-runtime/helpers/inherits")["default"],i=e("babel-runtime/helpers/class-call-check")["default"],o=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0});var u=e("./JSNetworkXAlgorithmError"),s=o(u),l=function(e){function t(e){i(this,t),n(Object.getPrototypeOf(t.prototype),"constructor",this).call(this,e),this.name="JSNetworkXUnfeasible"}return a(t,e),t}(s["default"]);r["default"]=l,t.exports=r["default"]},{"./JSNetworkXAlgorithmError":72,"babel-runtime/helpers/class-call-check":101,"babel-runtime/helpers/get":105,"babel-runtime/helpers/inherits":106,"babel-runtime/helpers/interop-require-default":107}],77:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/get")["default"],a=e("babel-runtime/helpers/inherits")["default"],i=e("babel-runtime/helpers/class-call-check")["default"];Object.defineProperty(r,"__esModule",{value:!0});var o=function(e){function t(e){i(this,t),n(Object.getPrototypeOf(t.prototype),"constructor",this).call(this),this.name="KeyError",this.message=e}return a(t,e),t}(Error);r["default"]=o,t.exports=r["default"]},{"babel-runtime/helpers/class-call-check":101,"babel-runtime/helpers/get":105,"babel-runtime/helpers/inherits":106}],78:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0});var a=e("./KeyError"),i=n(a),o=e("./JSNetworkXAlgorithmError"),u=n(o),s=e("./JSNetworkXError"),l=n(s),c=e("./JSNetworkXException"),f=n(c),d=e("./JSNetworkXNoPath"),h=n(d),p=e("./JSNetworkXUnfeasible"),v=n(p);r.KeyError=i["default"],r.JSNetworkXAlgorithmError=u["default"],r.JSNetworkXError=l["default"],r.JSNetworkXException=f["default"],r.JSNetworkXNoPath=h["default"],r.JSNetworkXUnfeasible=v["default"]},{"./JSNetworkXAlgorithmError":72,"./JSNetworkXError":73,"./JSNetworkXException":74,"./JSNetworkXNoPath":75,"./JSNetworkXUnfeasible":76,"./KeyError":77,"babel-runtime/helpers/interop-require-default":107}],79:[function(e,t,r){"use strict";function n(e,t){var r,n,a,i,o;return h.wrap(function(u){for(;;)switch(u.prev=u.next){case 0:if(r=y.genRange(e),0!==e){u.next=3;break}return u.abrupt("return");case 3:n=[y.next(r)];case 4:if(!(n.length>0)){u.next=20;break}a=n.shift(),i=0;case 7:if(!(t>i)){u.next=18;break}if(o=r.next(),!o.done){u.next=11;break}return u.abrupt("return");case 11:return o=o.value,n.push(o),u.next=15,y.tuple2(a,o);case 15:i++,u.next=7;break;case 18:u.next=4;break;case 20:case"end":return u.stop()}},v[0],this)}function a(e,t,r){var a=s(t,r);return a.addEdgesFrom(n(t,e)),a}function i(e,t,r){var a=1===e?t:Math.floor((1-Math.pow(e,t+1))/(1-e)),i=s(a,r);return i.addEdgesFrom(n(a,e)),i}function o(e,t){var r=s(e,t);return r.name="complete_graph("+e+")",e>1&&r.addEdgesFrom(r.isDirected()?y.genPermutations(y.range(e),2):y.genCombinations(y.range(e),2)),r}function u(e,t){var r=f(e,t);return r.name="cycle_graph("+e+")",e>1&&r.addEdge(e-1,0),r}function s(e,t){y.isGraph(e)&&(t=e,e=null),null==e&&(e=0);var r;return null==t?r=new g["default"]:(r=t,r.clear()),r.addNodesFrom(y.genRange(e)),r.name="emptyGraph("+e+")",r}function l(e,t){var r=arguments.length<=2||void 0===arguments[2]?!1:arguments[2],n=arguments.length<=3||void 0===arguments[3]?null:arguments[3],a=s(0,n);a.name="grid2dGraph";var i,o;for(i=0;e>i;i++)for(o=0;t>o;o++)a.addNode([i,o]);for(i=1;e>i;i++)for(o=0;t>o;o++)a.addEdge([i,o],[i-1,o]);for(i=0;e>i;i++)for(o=1;t>o;o++)a.addEdge([i,o],[i,o-1]);if(a.isDirected()){for(i=0;e-1>i;i++)for(o=0;t>o;o++)a.addEdge([i,o],[i+1,o]);for(i=0;e>i;i++)for(o=0;t-1>o;o++)a.addEdge([i,o],[i,o+1])}if(r){if(t>2){for(i=0;e>i;i++)a.addEdge([i,0],[i,t-1]);if(a.isDirected())for(i=0;e>i;i++)a.addEdge([i,t-1],[i,0])}if(e>2){for(o=0;t>o;o++)a.addEdge([0,o],[e-1,o]);if(a.isDirected())for(o=0;t>o;o++)a.addEdge([e-1,o],[0,o])}a.name="periodicGrid2dGraph("+e+", "+t+")"}return a}function c(e){var t=s(0,e);return t.name="nullGraph()",t}function f(e,t){var r=s(e,t);return r.name="pathGraph("+e+")",r.addEdgesFrom(y.mapIterator(y.genRange(e-1),function(e){return y.tuple2(e,e+1)})),r}function d(e){var t=s(1,e);return t.name="nullGraph()",t}var h=e("babel-runtime/regenerator")["default"],p=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.fullRaryTree=a,r.balancedTree=i,r.completeGraph=o,r.cycleGraph=u,r.emptyGraph=s,r.grid2dGraph=l,r.nullGraph=c,r.pathGraph=f,r.trivialGraph=d;var v=[n].map(h.mark),b=e("../classes/Graph"),g=p(b),y=e("../_internals")},{"../_internals":20,"../classes/Graph":61,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/regenerator":166}],80:[function(e,t,r){"use strict";function n(e,t){if(e=o(e),!d.isValidDegreeSequence(e))throw new f["default"]("Invalid degree sequence");if(null!=t&&t.isDirected())throw new f["default"]("Directed Graph not supported");for(var r=e.length,n=h.emptyGraph(r,t),a=new Array(r),u=0;r>u;u++)a[u]=[];var s=0,l=0,c=0;for(u=0;r>u;u++){var p=e[u];p>0&&(a[p].push(c),s=Math.max(s,p),l+=p,c+=1)}if(0===c)return n;var b=new Array(s+1);for(u=0;s+1>u;u++)b[u]=[0,0];for(;c>0;){for(;0===a[s].length;)s-=1;if(s>c-1)throw new f["default"]("Non-graphical integer sequence");var g=a[s].pop();c-=1;var y=0,m=s;for(u=0;s>u;u++){for(;0===a[m].length;)m-=1;var w=a[m].pop();n.addEdge(g,w),c-=1,m>1&&(b[y]=[m-1,w],y+=1)}for(u=0;y>u;u++){var x=i(b[u],2),k=x[0],j=x[1];a[k].push(j),c+=1}}return n.name=v["default"]("havelHakimiGraph %s nodes %d edges",n.order(),n.size()),n}function a(e,t){return l["default"]("havelHakimiGraph",[e,t])}var i=e("babel-runtime/helpers/sliced-to-array")["default"],o=e("babel-runtime/core-js/array/from")["default"],u=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.havelHakimiGraph=n,r.genHavelHakimiGraph=a;var s=e("../_internals/delegate"),l=u(s),c=e("../exceptions/JSNetworkXError"),f=u(c),d=e("../algorithms/graphical"),h=e("./classic"),p=e("../_internals/sprintf"),v=u(p)},{"../_internals/delegate":12,"../_internals/sprintf":38,"../algorithms/graphical":49,"../exceptions/JSNetworkXError":73,"./classic":79,"babel-runtime/core-js/array/from":88,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/sliced-to-array":109}],81:[function(e,t,r){"use strict";var n=e("babel-runtime/helpers/interop-require-wildcard")["default"],a=e("babel-runtime/helpers/defaults")["default"];Object.defineProperty(r,"__esModule",{value:!0});var i=e("./classic"),o=n(i),u=e("./degreeSequence"),s=n(u),l=e("./randomGraphs"),c=n(l),f=e("./small"),d=n(f),h=e("./social"),p=n(h);r.classic=o,r.degreeSequence=s,r.randomGraphs=c,r.small=d,r.social=p,a(r,n(i)),a(r,n(u)),a(r,n(l)),a(r,n(f)),a(r,n(h))},{"./classic":79,"./degreeSequence":80,"./randomGraphs":82,"./small":83,"./social":84,"babel-runtime/helpers/defaults":103,"babel-runtime/helpers/interop-require-wildcard":108}],82:[function(e,t,r){"use strict";function n(e,t){var r=arguments.length<=2||void 0===arguments[2]?!1:arguments[2],n=m.emptyGraph(e);if(n.name=w.sprintf("fastGnpRandomGraph(%s, %s)",e,t),0>=t||t>=1)return i(e,t,r);var a,o,u=-1,s=Math.log(1-t);if(r)for(a=0,n=new b["default"](n);e>a;){for(o=Math.log(1-Math.random()),u=u+1+Math.floor(o/s),a===u&&(u+=1);u>=e&&e>a;)u-=e,a+=1,a===u&&(u+=1);e>a&&n.addEdge(a,u)}else for(a=1;e>a;){for(o=Math.log(1-Math.random()),u=u+1+Math.floor(o/s);u>=a&&e>a;)u-=a,a+=1;e>a&&n.addEdge(a,u)}return n}function a(e,t,r){return p["default"]("fastGnpRandomGraph",[e,t,r])}function i(e,t){var r,n=arguments.length<=2||void 0===arguments[2]?!1:arguments[2],a=n?new b["default"]:new y["default"],i=w.range(e);if(a.addNodesFrom(i),a.name=w.sprintf("gnpRandomGraph(%s, %s)",e,t),0>=t)return a;if(t>=1)return m.completeGraph(e,a);r=a.isDirected()?w.genPermutations(i,2):w.genCombinations(i,2);var o=!0,u=!1,s=void 0;try{for(var l,c=f(r);!(o=(l=c.next()).done);o=!0){var d=l.value;Math.random()r||r>a-1||0>n||n>a-1)throw new c["default"]("invalid graphDescription");o.addEdge(r,n)});return o.name=n,o}function i(e){var t="adjacencylist",r="Bull Graph",a=5,i=[[2,3],[1,3,4],[1,2,5],[2],[3]];return n({type:t,name:r,n:a,list:i},e)}function o(e){var t="adjacencylist",r="Krackhardt Kite Social Network",a=10,i=[[2,3,4,6],[1,4,5,7],[1,4,6],[1,2,3,5,6,7],[2,4,7],[1,3,4,7,8],[2,4,5,6,8],[6,7,9],[8,10],[9]];return n({type:t,name:r,n:a,list:i},e)}var u=e("babel-runtime/helpers/sliced-to-array")["default"],s=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.makeSmallUndirectedGraph=n,r.makeSmallGraph=a,r.bullGraph=i,r.krackhardtKiteGraph=o;var l=e("../exceptions/JSNetworkXError"),c=s(l),f=e("./classic"),d=e("../_internals")},{"../_internals":20,"../exceptions/JSNetworkXError":73,"./classic":79,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/sliced-to-array":109}],84:[function(e,t,r){"use strict";function n(){var e=new s["default"];e.addNodesFrom(c["default"](34)),e.name="Zachary's Karate Club";var t=["0 1 1 1 1 1 1 1 1 0 1 1 1 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0","1 0 1 1 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0","1 1 0 1 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0","1 1 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0","1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0","1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0","1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0","1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0","1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1","0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1","1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0","1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0","1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0","1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1","0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0","1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1","1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1","1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1","0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1","0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 1","0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1","1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 1 1","0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 1 1 0 1","0 0 0 0 0 0 0 0 1 1 0 0 0 1 1 1 0 0 1 1 1 0 1 1 0 0 1 1 1 1 1 1 1 0"];return t.forEach(function(t,r){var n=t.split(" ");n.forEach(function(t,n){"1"===t&&e.addEdge(r,n)})}),e.addNodesFrom([0,1,2,3,4,5,6,7,8,10,11,12,13,16,17,19,21],{club:"Mr. Hi"}),e.addNodesFrom([9,14,15,18,20,22,23,24,25,26,27,28,29,30,31,32,33],{club:"Officer"}),e}function a(){var e=new s["default"];return e.addNodesFrom(["Evelyn Jefferson","Laura Mandeville","Theresa Anderson","Brenda Rogers","Charlotte McDowd","Frances Anderson","Eleanor Nye","Pearl Oglethorpe","Ruth DeSand","Verne Sanderson","Myra Liddel","Katherina Rogers","Sylvia Avondale","Nora Fayette","Helen Lloyd","Dorothy Murchison","Olivia Carleton","Flora Price"],{bipartite:0}),e.addNodesFrom(["E1","E2","E3","E4","E5","E6","E7","E8","E9","E10","E11","E12","E13","E14"],{bipartite:1}),e.add_edges_from([["Evelyn Jefferson","E1"],["Evelyn Jefferson","E2"],["Evelyn Jefferson","E3"],["Evelyn Jefferson","E4"],["Evelyn Jefferson","E5"],["Evelyn Jefferson","E6"],["Evelyn Jefferson","E8"],["Evelyn Jefferson","E9"],["Laura Mandeville","E1"],["Laura Mandeville","E2"],["Laura Mandeville","E3"],["Laura Mandeville","E5"],["Laura Mandeville","E6"],["Laura Mandeville","E7"],["Laura Mandeville","E8"],["Theresa Anderson","E2"],["Theresa Anderson","E3"],["Theresa Anderson","E4"],["Theresa Anderson","E5"],["Theresa Anderson","E6"],["Theresa Anderson","E7"],["Theresa Anderson","E8"],["Theresa Anderson","E9"],["Brenda Rogers","E1"],["Brenda Rogers","E3"],["Brenda Rogers","E4"],["Brenda Rogers","E5"],["Brenda Rogers","E6"],["Brenda Rogers","E7"],["Brenda Rogers","E8"],["Charlotte McDowd","E3"],["Charlotte McDowd","E4"],["Charlotte McDowd","E5"],["Charlotte McDowd","E7"],["Frances Anderson","E3"],["Frances Anderson","E5"],["Frances Anderson","E6"],["Frances Anderson","E8"],["Eleanor Nye","E5"],["Eleanor Nye","E6"],["Eleanor Nye","E7"],["Eleanor Nye","E8"],["Pearl Oglethorpe","E6"],["Pearl Oglethorpe","E8"],["Pearl Oglethorpe","E9"],["Ruth DeSand","E5"],["Ruth DeSand","E7"],["Ruth DeSand","E8"],["Ruth DeSand","E9"],["Verne Sanderson","E7"],["Verne Sanderson","E8"],["Verne Sanderson","E9"],["Verne Sanderson","E12"],["Myra Liddel","E8"],["Myra Liddel","E9"],["Myra Liddel","E10"],["Myra Liddel","E12"],["Katherina Rogers","E8"],["Katherina Rogers","E9"],["Katherina Rogers","E10"],["Katherina Rogers","E12"],["Katherina Rogers","E13"],["Katherina Rogers","E14"],["Sylvia Avondale","E7"],["Sylvia Avondale","E8"],["Sylvia Avondale","E9"],["Sylvia Avondale","E10"],["Sylvia Avondale","E12"],["Sylvia Avondale","E13"],["Sylvia Avondale","E14"],["Nora Fayette","E6"],["Nora Fayette","E7"],["Nora Fayette","E9"],["Nora Fayette","E10"],["Nora Fayette","E11"],["Nora Fayette","E12"],["Nora Fayette","E13"],["Nora Fayette","E14"],["Helen Lloyd","E7"],["Helen Lloyd","E8"],["Helen Lloyd","E10"],["Helen Lloyd","E11"],["Helen Lloyd","E12"],["Dorothy Murchison","E8"],["Dorothy Murchison","E9"],["Olivia Carleton","E9"],["Olivia Carleton","E11"],["Flora Price","E9"],["Flora Price","E11"]]),e}function i(){var e=new s["default"];return e.addEdge("Acciaiuoli","Medici"),e.addEdge("Castellani","Peruzzi"),e.addEdge("Castellani","Strozzi"),e.addEdge("Castellani","Barbadori"),e.addEdge("Medici","Barbadori"),e.addEdge("Medici","Ridolfi"),e.addEdge("Medici","Tornabuoni"),e.addEdge("Medici","Albizzi"),e.addEdge("Medici","Salviati"),e.addEdge("Salviati","Pazzi"),e.addEdge("Peruzzi","Strozzi"),e.addEdge("Peruzzi","Bischeri"),e.addEdge("Strozzi","Ridolfi"),e.addEdge("Strozzi","Bischeri"),e.addEdge("Ridolfi","Tornabuoni"),e.addEdge("Tornabuoni","Guadagni"),e.addEdge("Albizzi","Ginori"),e.addEdge("Albizzi","Guadagni"),e.addEdge("Bischeri","Guadagni"),e.addEdge("Guadagni","Lamberteschi"),e}var o=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.karateClubGraph=n,r.davisSouthernWomenGraph=a,r.florentineFamiliesGraph=i;var u=e("../classes/Graph"),s=o(u),l=e("../_internals/range"),c=o(l)},{"../_internals/range":35,"../classes/Graph":61,"babel-runtime/helpers/interop-require-default":107}],85:[function(e,t,r){"use strict";var n=e("babel-runtime/core-js/array/from")["default"],a=e("babel-runtime/helpers/interop-require-wildcard")["default"],i=e("babel-runtime/helpers/interop-require-default")["default"],o=e("babel-runtime/helpers/defaults")["default"];Object.defineProperty(r,"__esModule",{value:!0});var u=e("./algorithms"),s=a(u),l=e("./classes"),c=a(l),f=e("./convert"),d=a(f),h=e("./drawing"),p=a(h),v=e("./exceptions"),b=a(v),g=e("./generators"),y=a(g),m=e("./relabel"),w=a(m),x=e("./_internals/Map"),k=i(x),j=e("./_internals/Set"),E=i(j),_=e("./_internals/forEach"),S=i(_);r.Map=k["default"],r.Set=E["default"],r.forEach=S["default"],r.algorithms=s,r.classes=c,r.convert=d,r.drawing=p,r.exceptions=b,r.generators=y,r.relabel=w;var O=n;r.toArray=O,o(r,a(u)),o(r,a(l)),o(r,a(f)),o(r,a(h));var M=e("./contrib/observer");o(r,a(M)),o(r,a(v)),o(r,a(g)),o(r,a(m))},{"./_internals/Map":3,"./_internals/Set":5,"./_internals/forEach":14,"./algorithms":50,"./classes":65,"./contrib/observer":67,"./convert":69,"./drawing":70,"./exceptions":78,"./generators":81,"./relabel":87,"babel-runtime/core-js/array/from":88,"babel-runtime/helpers/defaults":103,"babel-runtime/helpers/interop-require-default":107,"babel-runtime/helpers/interop-require-wildcard":108}],86:[function(e,t,r){(function(n){"use strict";function a(){n.document||(n.onmessage=function(e){var t=e.data.args.map(o.deserialize),r=s["default"].methodLookupFunction(e.data.method).apply(null,t);n.postMessage(o.serialize(r)),n.close()})}var i=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=a;var o=e("./_internals/message"),u=e("./WorkerSettings"),s=i(u);t.exports=r["default"]}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./WorkerSettings":1,"./_internals/message":32,"babel-runtime/helpers/interop-require-default":107}],87:[function(e,t,r){"use strict";function n(e,t){var r=arguments.length<=2||void 0===arguments[2]?!0:arguments[2],n=t;return"function"!=typeof t?h.isMap(n)||(n=new h.Map(n)):n=new h.Map(h.mapIterator(e.nodesIter(),function(e){return h.tuple2(e,t(e))})),r?i(e,n):a(e,n)}function a(e,t){var r,n=new h.Set(t.keys());if(h.someIterator(t.values(),function(e){return n.has(e)})){var a=new f["default"](t);a.removeEdgesFrom(a.selfloopEdges());try{r=h.topologicalSort(a)}catch(i){if(i instanceof d.JSNetworkXUnfeasible)throw new d.JSNetworkXUnfeasible("The node label sets are overlapping and no ordering can resolve the mapping. Use copy=True.")}r.reverse()}else r=n.values();var o,u=e.isMultigraph(),s=e.isDirected();return h.forEach(r,function(r){var n;if(t.has(r)){if(n=t.get(r),!e.hasNode(r))throw new d.JSNetworkXError(h.sprintf("Node %j is not in the graph.",r));e.addNode(n,e.node.get(r)),u?(o=e.edges(r,!0,!0).map(function(e){return h.tuple4c(n,e[1],e[2],e[3],e)}),s&&(o=o.concat(e.inEdges(r,!0,!0).map(function(e){return h.tuple4c(e[0],n,e[2],e[3],e)})))):(o=e.edges(r,!0).map(function(e){return h.tuple3c(n,e[1],e[2],e)}),s&&(o=o.concat(e.inEdges(r,!0).map(function(e){return h.tuple3c(e[0],n,e[2],e)})))),e.removeNode(r),e.addEdgesFrom(o)}}),e}function i(e,t){var r=new e.constructor;return r.name="("+e.name+")",e.isMultigraph()?r.addEdgesFrom(h.mapIterator(e.edgesIter(null,!0,!0),function(e){return h.tuple4c(t.has(e[0])?t.get(e[0]):e[0],t.has(e[1])?t.get(e[1]):e[1],e[2],h.clone(e[3]),e)})):r.addEdgesFrom(h.mapIterator(e.edgesIter(null,!0),function(e){return h.tuple3c(t.has(e[0])?t.get(e[0]):e[0],t.has(e[1])?t.get(e[1]):e[1],h.clone(e[3]),e)})),e.node.forEach(function(e,n){return r.addNode(t.has(n)?t.get(n):n,h.clone(e))}),u(r.graph,h.clone(e.graph)),r}function o(e){var t=arguments.length<=1||void 0===arguments[1]?0:arguments[1],r=arguments.length<=2||void 0===arguments[2]?"default":arguments[2],a=arguments.length<=3||void 0===arguments[3]?!0:arguments[3];switch("boolean"==typeof r&&(a=r,r="default"),typeof t){case"string":r=t,t=0;break;case"boolean":a=t,t=0}var i,o,u,l,c,f=new h.Map;switch(r){case"default":for(i=e.nodes(),u=0,l=t,c=i.length;c>u;u++,l++)f.set(i[u],l);break;case"sorted":for(i=e.nodes(),i.sort(),u=0,l=t,c=i.length;c>u;u++,l++)f.set(i[u],l);break;case"increasing degree":for(o=s(e.degreeIter()),o.sort(function(e,t){return e[1]-t[1]}),u=0,l=t,c=o.length;c>u;u++,l++)f.set(o[u][0],l);break;case"decreasing degree":for(o=s(e.degreeIter()),o.sort(function(e,t){return t[1]-e[1]}),u=0,l=t,c=o.length;c>u;u++,l++)f.set(o[u][0],l);break;default:throw new d.JSNetworkXError(h.sprintf('Unkown node ordering: "%s"',r))}var p=n(e,f);return p.name="("+e.name+")WithIntLabels",a||(p.nodeLabels=f),p}var u=e("babel-runtime/core-js/object/assign")["default"],s=e("babel-runtime/core-js/array/from")["default"],l=e("babel-runtime/helpers/interop-require-default")["default"];Object.defineProperty(r,"__esModule",{value:!0}),r.relabelNodes=n,r.convertNodeLabelsToIntegers=o;var c=e("./classes/DiGraph"),f=l(c),d=e("./exceptions"),h=e("./_internals")},{"./_internals":20,"./classes/DiGraph":60,"./exceptions":78,"babel-runtime/core-js/array/from":88,"babel-runtime/core-js/object/assign":92,"babel-runtime/helpers/interop-require-default":107}],88:[function(e,t,r){t.exports={"default":e("core-js/library/fn/array/from"),__esModule:!0}},{"core-js/library/fn/array/from":110}],89:[function(e,t,r){t.exports={"default":e("core-js/library/fn/get-iterator"),__esModule:!0}},{"core-js/library/fn/get-iterator":111}],90:[function(e,t,r){t.exports={"default":e("core-js/library/fn/is-iterable"),__esModule:!0}},{"core-js/library/fn/is-iterable":112}],91:[function(e,t,r){t.exports={"default":e("core-js/library/fn/map"),__esModule:!0}},{"core-js/library/fn/map":113}],92:[function(e,t,r){t.exports={"default":e("core-js/library/fn/object/assign"),__esModule:!0}},{"core-js/library/fn/object/assign":114}],93:[function(e,t,r){t.exports={"default":e("core-js/library/fn/object/create"),__esModule:!0}},{"core-js/library/fn/object/create":115}],94:[function(e,t,r){t.exports={"default":e("core-js/library/fn/object/define-property"),__esModule:!0}},{"core-js/library/fn/object/define-property":116}],95:[function(e,t,r){t.exports={"default":e("core-js/library/fn/object/get-own-property-descriptor"),__esModule:!0}},{"core-js/library/fn/object/get-own-property-descriptor":117}],96:[function(e,t,r){t.exports={"default":e("core-js/library/fn/object/get-own-property-names"),__esModule:!0}},{"core-js/library/fn/object/get-own-property-names":118}],97:[function(e,t,r){t.exports={"default":e("core-js/library/fn/object/keys"),__esModule:!0}},{"core-js/library/fn/object/keys":119}],98:[function(e,t,r){t.exports={"default":e("core-js/library/fn/promise"),__esModule:!0}},{"core-js/library/fn/promise":120}],99:[function(e,t,r){t.exports={"default":e("core-js/library/fn/symbol"),__esModule:!0}},{"core-js/library/fn/symbol":121}],100:[function(e,t,r){t.exports={"default":e("core-js/library/fn/symbol/iterator"),__esModule:!0}},{"core-js/library/fn/symbol/iterator":122}],101:[function(e,t,r){"use strict";r["default"]=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},r.__esModule=!0},{}],102:[function(e,t,r){"use strict";var n=e("babel-runtime/core-js/object/define-property")["default"];r["default"]=function(){function e(e,t){for(var r=0;ro;)for(var u,s=n.ES5Object(arguments[o++]),l=a(s),c=l.length,f=0;c>f;)r[u=l[f++]]=s[u];return r}},{"./$":141,"./$.enum-keys":132}],125:[function(e,t,r){function n(e){return o.call(e).slice(8,-1)}var a=e("./$"),i=e("./$.wks")("toStringTag"),o={}.toString;n.classof=function(e){var t,r;return void 0==e?void 0===e?"Undefined":"Null":"string"==typeof(r=(t=Object(e))[i])?r:n(t)},n.set=function(e,t,r){e&&!a.has(e=r?e:e.prototype,i)&&a.hide(e,i,t)},t.exports=n},{"./$":141,"./$.wks":153}],126:[function(e,t,r){"use strict";function n(e,t){if(!h(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!f(e,b)){if(!v(e))return"F";if(!t)return"E";p(e,b,++k)}return"O"+e[b]}function a(e,t){var r,a=n(t);if("F"!==a)return e[g][a];for(r=e[m];r;r=r.n)if(r.k==t)return r}var i=e("./$"),o=e("./$.ctx"),u=e("./$.uid").safe,s=e("./$.assert"),l=e("./$.for-of"),c=e("./$.iter").step,f=i.has,d=i.set,h=i.isObject,p=i.hide,v=Object.isExtensible||h,b=u("id"),g=u("O1"),y=u("last"),m=u("first"),w=u("iter"),x=i.DESC?u("size"):"size",k=0;t.exports={getConstructor:function(t,r,n,u){var c=t(function(e,t){s.inst(e,c,r),d(e,g,i.create(null)),d(e,x,0),d(e,y,void 0),d(e,m,void 0),void 0!=t&&l(t,n,e[u],e)});return e("./$.mix")(c.prototype,{clear:function(){for(var e=this,t=e[g],r=e[m];r;r=r.n)r.r=!0,r.p&&(r.p=r.p.n=void 0),delete t[r.i];e[m]=e[y]=void 0,e[x]=0},"delete":function(e){var t=this,r=a(t,e);if(r){var n=r.n,i=r.p;delete t[g][r.i],r.r=!0,i&&(i.n=n),n&&(n.p=i),t[m]==r&&(t[m]=n),t[y]==r&&(t[y]=i),t[x]--}return!!r},forEach:function(e){for(var t,r=o(e,arguments[1],3);t=t?t.n:this[m];)for(r(t.v,t.k,this);t&&t.r;)t=t.p},has:function(e){return!!a(this,e)}}),i.DESC&&i.setDesc(c.prototype,"size",{get:function(){return s.def(this[x])}}),c},def:function(e,t,r){var i,o,u=a(e,t);return u?u.v=r:(e[y]=u={i:o=n(t,!0),k:t,v:r,p:i=e[y],n:void 0,r:!1},e[m]||(e[m]=u),i&&(i.n=u),e[x]++,"F"!==o&&(e[g][o]=u)),e},getEntry:a,setIter:function(t,r,n){e("./$.iter-define")(t,r,function(e,t){d(this,w,{o:e,k:t})},function(){for(var e=this[w],t=e.k,r=e.l;r&&r.r;)r=r.p;return e.o&&(e.l=r=r?r.n:e.o[m])?"keys"==t?c(0,r.k):"values"==t?c(0,r.v):c(0,[r.k,r.v]):(e.o=void 0,c(1))},n?"entries":"values",!n,!0)}}},{"./$":141,"./$.assert":123,"./$.ctx":129,"./$.for-of":133,"./$.iter":140,"./$.iter-define":138,"./$.mix":143,"./$.uid":151}],127:[function(e,t,r){var n=e("./$.def"),a=e("./$.for-of");t.exports=function(e){n(n.P,e,{toJSON:function(){var e=[];return a(this,!1,e.push,e),e}})}},{"./$.def":130,"./$.for-of":133}],128:[function(e,t,r){"use strict";var n=e("./$"),a=e("./$.def"),i=e("./$.iter"),o=i.BUGGY,u=e("./$.for-of"),s=e("./$.assert").inst,l=e("./$.uid").safe("internal");t.exports=function(t,r,i,c,f,d){var h=n.g[t],p=h,v=f?"set":"add",b=p&&p.prototype,g={};return n.DESC&&n.isFunction(p)&&(d||!o&&b.forEach&&b.entries)?(p=r(function(e,r){s(e,p,t),e[l]=new h,void 0!=r&&u(r,f,e[v],e)}),n.each.call("add,clear,delete,forEach,get,has,set,keys,values,entries".split(","),function(e){var t="add"==e||"set"==e;e in b&&n.hide(p.prototype,e,function(r,n){var a=this[l][e](0===r?0:r,n);return t?this:a})}),"size"in b&&n.setDesc(p.prototype,"size",{get:function(){return this[l].size}})):(p=c.getConstructor(r,t,f,v),e("./$.mix")(p.prototype,i)),e("./$.cof").set(p,t),g[t]=p,a(a.G+a.W+a.F,g),e("./$.species")(p),d||c.setIter(p,t,f),p}},{"./$":141,"./$.assert":123,"./$.cof":125,"./$.def":130,"./$.for-of":133,"./$.iter":140,"./$.mix":143,"./$.species":148,"./$.uid":151}],129:[function(e,t,r){var n=e("./$.assert").fn;t.exports=function(e,t,r){if(n(e),~r&&void 0===t)return e;switch(r){case 1:return function(r){return e.call(t,r)};case 2:return function(r,n){return e.call(t,r,n)};case 3:return function(r,n,a){return e.call(t,r,n,a)}}return function(){return e.apply(t,arguments)}}},{"./$.assert":123}],130:[function(e,t,r){function n(e,t){return function(){return e.apply(t,arguments)}}function a(e,t,r){var i,l,c,f,d=e&a.G,h=e&a.P,p=d?o:e&a.S?o[t]:(o[t]||{}).prototype,v=d?u:u[t]||(u[t]={});d&&(r=t);for(i in r)l=!(e&a.F)&&p&&i in p,l&&i in v||(c=l?p[i]:r[i],d&&!s(p[i])?f=r[i]:e&a.B&&l?f=n(c,o):e&a.W&&p[i]==c?!function(e){f=function(t){return this instanceof e?new e(t):e(t)},f.prototype=e.prototype}(c):f=h&&s(c)?n(Function.call,c):c,v[i]=f,h&&((v.prototype||(v.prototype={}))[i]=c))}var i=e("./$"),o=i.g,u=i.core,s=i.isFunction;a.F=1,a.G=2,a.S=4,a.P=8,a.B=16,a.W=32,t.exports=a},{"./$":141}],131:[function(e,t,r){var n=e("./$"),a=n.g.document,i=n.isObject,o=i(a)&&i(a.createElement);t.exports=function(e){return o?a.createElement(e):{}}},{"./$":141}],132:[function(e,t,r){var n=e("./$");t.exports=function(e){var t=n.getKeys(e),r=n.getDesc,a=n.getSymbols;return a&&n.each.call(a(e),function(n){r(e,n).enumerable&&t.push(n)}),t}},{"./$":141}],133:[function(e,t,r){var n=e("./$.ctx"),a=e("./$.iter").get,i=e("./$.iter-call");t.exports=function(e,t,r,o){for(var u,s=a(e),l=n(r,o,t?2:1);!(u=s.next()).done;)if(i(s,l,u.value,t)===!1)return i.close(s)}},{"./$.ctx":129,"./$.iter":140,"./$.iter-call":137}],134:[function(e,t,r){t.exports=function(e){return e.FW=!1,e.path=e.core,e}},{}],135:[function(e,t,r){function n(e){try{return o(e)}catch(t){return u.slice()}}var a=e("./$"),i={}.toString,o=a.getNames,u="object"==typeof window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];t.exports.get=function(e){return u&&"[object Window]"==i.call(e)?n(e):o(a.toObject(e))}},{"./$":141}],136:[function(e,t,r){t.exports=function(e,t,r){var n=void 0===r;switch(t.length){case 0:return n?e():e.call(r);case 1:return n?e(t[0]):e.call(r,t[0]);case 2:return n?e(t[0],t[1]):e.call(r,t[0],t[1]);case 3:return n?e(t[0],t[1],t[2]):e.call(r,t[0],t[1],t[2]);case 4:return n?e(t[0],t[1],t[2],t[3]):e.call(r,t[0],t[1],t[2],t[3]);case 5:return n?e(t[0],t[1],t[2],t[3],t[4]):e.call(r,t[0],t[1],t[2],t[3],t[4])}return e.apply(r,t)}},{}],137:[function(e,t,r){function n(e){var t=e["return"];void 0!==t&&i(t.call(e))}function a(e,t,r,a){try{return a?t(i(r)[0],r[1]):t(r)}catch(o){throw n(e),o}}var i=e("./$.assert").obj;a.close=n,t.exports=a},{"./$.assert":123}],138:[function(e,t,r){var n=e("./$.def"),a=e("./$.redef"),i=e("./$"),o=e("./$.cof"),u=e("./$.iter"),s=e("./$.wks")("iterator"),l="@@iterator",c="keys",f="values",d=u.Iterators;t.exports=function(e,t,r,h,p,v,b){function g(e){function t(t){return new r(t,e)}switch(e){case c:return function(){return t(this)};case f:return function(){return t(this)}}return function(){return t(this)}}u.create(r,t,h);var y,m,w=t+" Iterator",x=e.prototype,k=x[s]||x[l]||p&&x[p],j=k||g(p);if(k){var E=i.getProto(j.call(new e));o.set(E,w,!0),i.FW&&i.has(x,l)&&u.set(E,i.that)}if((i.FW||b)&&u.set(x,j),d[t]=j,d[w]=i.that,p)if(y={keys:v?j:g(c),values:p==f?j:g(f),entries:p!=f?j:g("entries")},b)for(m in y)m in x||a(x,m,y[m]);else n(n.P+n.F*u.BUGGY,t,y)}},{"./$":141,"./$.cof":125,"./$.def":130,"./$.iter":140,"./$.redef":144,"./$.wks":153}],139:[function(e,t,r){var n=e("./$.wks")("iterator"),a=!1;try{var i=[7][n]();i["return"]=function(){a=!0},Array.from(i,function(){throw 2})}catch(o){}t.exports=function(e){if(!a)return!1;var t=!1;try{var r=[7],i=r[n]();i.next=function(){t=!0},r[n]=function(){return i},e(r)}catch(o){}return t}},{"./$.wks":153}],140:[function(e,t,r){"use strict";function n(e,t){a.hide(e,l,t),c in[]&&a.hide(e,c,t)}var a=e("./$"),i=e("./$.cof"),o=i.classof,u=e("./$.assert"),s=u.obj,l=e("./$.wks")("iterator"),c="@@iterator",f=e("./$.shared")("iterators"),d={};n(d,a.that),t.exports={BUGGY:"keys"in[]&&!("next"in[].keys()),Iterators:f,step:function(e,t){return{value:t,done:!!e}},is:function(e){var t=Object(e),r=a.g.Symbol;return(r&&r.iterator||c)in t||l in t||a.has(f,o(t))},get:function(e){var t,r=a.g.Symbol;return void 0!=e&&(t=e[r&&r.iterator||c]||e[l]||f[o(e)]),u(a.isFunction(t),e," is not iterable!"),s(t.call(e))},set:n,create:function(e,t,r,n){e.prototype=a.create(n||d,{next:a.desc(1,r)}),i.set(e,t+" Iterator")}}},{"./$":141,"./$.assert":123,"./$.cof":125,"./$.shared":147,"./$.wks":153}],141:[function(e,t,r){"use strict";function n(e){return isNaN(e=+e)?0:(e>0?v:p)(e)}function a(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}function i(e,t,r){return e[t]=r,e}function o(e){return y?function(t,r,n){return w.setDesc(t,r,a(e,n))}:i}function u(e){return null!==e&&("object"==typeof e||"function"==typeof e)}function s(e){return"function"==typeof e}function l(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}var c="undefined"!=typeof self?self:Function("return this")(),f={},d=Object.defineProperty,h={}.hasOwnProperty,p=Math.ceil,v=Math.floor,b=Math.max,g=Math.min,y=!!function(){try{return 2==d({},"a",{get:function(){return 2}}).a}catch(e){}}(),m=o(1),w=t.exports=e("./$.fw")({g:c,core:f,html:c.document&&document.documentElement,isObject:u,isFunction:s,that:function(){return this},toInteger:n,toLength:function(e){return e>0?g(n(e),9007199254740991):0},toIndex:function(e,t){return e=n(e),0>e?b(e+t,0):g(e,t)},has:function(e,t){return h.call(e,t)},create:Object.create,getProto:Object.getPrototypeOf,DESC:y,desc:a,getDesc:Object.getOwnPropertyDescriptor,setDesc:d,setDescs:Object.defineProperties,getKeys:Object.keys,getNames:Object.getOwnPropertyNames,getSymbols:Object.getOwnPropertySymbols,assertDefined:l,ES5Object:Object,toObject:function(e){return w.ES5Object(l(e))},hide:m,def:o(0),set:c.Symbol?i:m,each:[].forEach});"undefined"!=typeof __e&&(__e=f),"undefined"!=typeof __g&&(__g=c)},{"./$.fw":134}],142:[function(e,t,r){var n=e("./$");t.exports=function(e,t){for(var r,a=n.toObject(e),i=n.getKeys(a),o=i.length,u=0;o>u;)if(a[r=i[u++]]===t)return r}},{"./$":141}],143:[function(e,t,r){var n=e("./$.redef");t.exports=function(e,t){for(var r in t)n(e,r,t[r]);return e}},{"./$.redef":144}],144:[function(e,t,r){t.exports=e("./$").hide},{"./$":141}],145:[function(e,t,r){t.exports=Object.is||function(e,t){return e===t?0!==e||1/e===1/t:e!=e&&t!=t}},{}],146:[function(e,t,r){function n(e,t){i.obj(e),i(null===t||a.isObject(t),t,": can't set as prototype!")}var a=e("./$"),i=e("./$.assert");t.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(t,r){try{r=e("./$.ctx")(Function.call,a.getDesc(Object.prototype,"__proto__").set,2),r({},[])}catch(i){t=!0}return function(e,a){return n(e,a),t?e.__proto__=a:r(e,a),e}}():void 0),check:n}},{"./$":141,"./$.assert":123,"./$.ctx":129}],147:[function(e,t,r){var n=e("./$"),a="__core-js_shared__",i=n.g[a]||(n.g[a]={});t.exports=function(e){return i[e]||(i[e]={})}},{"./$":141}],148:[function(e,t,r){var n=e("./$"),a=e("./$.wks")("species");t.exports=function(e){!n.DESC||a in e||n.setDesc(e,a,{configurable:!0,get:n.that})}},{"./$":141,"./$.wks":153}],149:[function(e,t,r){var n=e("./$");t.exports=function(e){return function(t,r){var a,i,o=String(n.assertDefined(t)),u=n.toInteger(r),s=o.length;return 0>u||u>=s?e?"":void 0:(a=o.charCodeAt(u),55296>a||a>56319||u+1===s||(i=o.charCodeAt(u+1))<56320||i>57343?e?o.charAt(u):a:e?o.slice(u,u+2):(a-55296<<10)+(i-56320)+65536)}}},{"./$":141}],150:[function(e,t,r){"use strict";function n(){var e=+this;if(s.has(x,e)){var t=x[e];delete x[e],t()}}function a(e){n.call(e.data)}var i,o,u,s=e("./$"),l=e("./$.ctx"),c=e("./$.cof"),f=e("./$.invoke"),d=e("./$.dom-create"),h=s.g,p=s.isFunction,v=s.html,b=h.process,g=h.setImmediate,y=h.clearImmediate,m=h.MessageChannel,w=0,x={},k="onreadystatechange";p(g)&&p(y)||(g=function(e){for(var t=[],r=1;arguments.length>r;)t.push(arguments[r++]);return x[++w]=function(){f(p(e)?e:Function(e),t)},i(w),w},y=function(e){delete x[e]},"process"==c(b)?i=function(e){b.nextTick(l(n,e,1))}:h.addEventListener&&p(h.postMessage)&&!h.importScripts?(i=function(e){h.postMessage(e,"*")},h.addEventListener("message",a,!1)):p(m)?(o=new m,u=o.port2,o.port1.onmessage=a,i=l(u.postMessage,u,1)):i=k in d("script")?function(e){v.appendChild(d("script"))[k]=function(){v.removeChild(this),n.call(e)}}:function(e){setTimeout(l(n,e,1),0)}),t.exports={set:g,clear:y}},{"./$":141,"./$.cof":125,"./$.ctx":129,"./$.dom-create":131,"./$.invoke":136}],151:[function(e,t,r){function n(e){return"Symbol(".concat(void 0===e?"":e,")_",(++a+Math.random()).toString(36))}var a=0;n.safe=e("./$").g.Symbol||n,t.exports=n},{"./$":141}],152:[function(e,t,r){t.exports=function(){}},{}],153:[function(e,t,r){var n=e("./$").g,a=e("./$.shared")("wks");t.exports=function(t){return a[t]||(a[t]=n.Symbol&&n.Symbol[t]||e("./$.uid").safe("Symbol."+t))}},{"./$":141,"./$.shared":147,"./$.uid":151}],154:[function(e,t,r){var n=e("./$").core,a=e("./$.iter");n.isIterable=a.is,n.getIterator=a.get},{"./$":141,"./$.iter":140}],155:[function(e,t,r){var n=e("./$"),a=e("./$.ctx"),i=e("./$.def"),o=e("./$.iter"),u=e("./$.iter-call");i(i.S+i.F*!e("./$.iter-detect")(function(e){Array.from(e)}),"Array",{from:function(e){var t,r,i,s,l=Object(n.assertDefined(e)),c=arguments[1],f=void 0!==c,d=f?a(c,arguments[2],2):void 0,h=0;if(o.is(l))for(s=o.get(l),r=new("function"==typeof this?this:Array);!(i=s.next()).done;h++)r[h]=f?u(s,d,[i.value,h],!0):i.value;else for(r=new("function"==typeof this?this:Array)(t=n.toLength(l.length));t>h;h++)r[h]=f?d(l[h],h):l[h];return r.length=h,r}})},{"./$":141,"./$.ctx":129,"./$.def":130,"./$.iter":140,"./$.iter-call":137,"./$.iter-detect":139}],156:[function(e,t,r){var n=e("./$"),a=e("./$.unscope"),i=e("./$.uid").safe("iter"),o=e("./$.iter"),u=o.step,s=o.Iterators;e("./$.iter-define")(Array,"Array",function(e,t){n.set(this,i,{o:n.toObject(e),i:0,k:t})},function(){var e=this[i],t=e.o,r=e.k,n=e.i++;return!t||n>=t.length?(e.o=void 0,u(1)):"keys"==r?u(0,n):"values"==r?u(0,t[n]):u(0,[n,t[n]])},"values"),s.Arguments=s.Array,a("keys"),a("values"),a("entries")},{"./$":141,"./$.iter":140,"./$.iter-define":138,"./$.uid":151,"./$.unscope":152}],157:[function(e,t,r){"use strict";var n=e("./$.collection-strong");e("./$.collection")("Map",function(e){return function(){return e(this,arguments[0])}},{get:function(e){var t=n.getEntry(this,e);return t&&t.v},set:function(e,t){return n.def(this,0===e?0:e,t)}},n,!0)},{"./$.collection":128,"./$.collection-strong":126}],158:[function(e,t,r){var n=e("./$.def");n(n.S,"Object",{assign:e("./$.assign")})},{"./$.assign":124,"./$.def":130}],159:[function(e,t,r){var n=e("./$"),a=e("./$.def"),i=n.isObject,o=n.toObject;n.each.call("freeze,seal,preventExtensions,isFrozen,isSealed,isExtensible,getOwnPropertyDescriptor,getPrototypeOf,keys,getOwnPropertyNames".split(","),function(t,r){var u=(n.core.Object||{})[t]||Object[t],s=0,l={};l[t]=0==r?function(e){return i(e)?u(e):e}:1==r?function(e){return i(e)?u(e):e}:2==r?function(e){return i(e)?u(e):e}:3==r?function(e){return i(e)?u(e):!0}:4==r?function(e){return i(e)?u(e):!0}:5==r?function(e){return i(e)?u(e):!1}:6==r?function(e,t){return u(o(e),t)}:7==r?function(e){return u(Object(n.assertDefined(e)))}:8==r?function(e){return u(o(e))}:e("./$.get-names").get;try{u("z")}catch(c){s=1}a(a.S+a.F*s,"Object",l)})},{"./$":141,"./$.def":130,"./$.get-names":135}],160:[function(e,t,r){"use strict";var n=e("./$.cof"),a={};a[e("./$.wks")("toStringTag")]="z",e("./$").FW&&"z"!=n(a)&&e("./$.redef")(Object.prototype,"toString",function(){return"[object "+n.classof(this)+"]"},!0)},{"./$":141,"./$.cof":125,"./$.redef":144,"./$.wks":153}],161:[function(e,t,r){"use strict";function n(e){var t=new I(function(){});return e&&(t.constructor=Object),I.resolve(t)===t}function a(e){return N(e)&&(q?"Promise"==v.classof(e):j in e)}function i(e,t){return h.FW||e!==I||t!==d?w(e,t):!0}function o(e){var t=$(e)[k];return void 0!=t?t:e}function u(e){var t;return N(e)&&(t=e.then),P(t)?t:!1}function s(e){var t=e.c;t.length&&M.call(_,function(){function r(t){var r,i,o=a?t.ok:t.fail;try{o?(a||(e.h=!0),r=o===!0?n:o(n),r===t.P?t.rej(TypeError("Promise-chain cycle")):(i=u(r))?i.call(r,t.res,t.rej):t.res(r)):t.rej(n)}catch(s){t.rej(s)}}for(var n=e.v,a=1==e.s,i=0;t.length>i;)r(t[i++]);t.length=0})}function l(e){var t,r=e[j],n=r.a||r.c,a=0;if(r.h)return!1;for(;n.length>a;)if(t=n[a++],t.fail||!l(t.P))return!1;return!0}function c(e){var t,r=this;r.d||(r.d=!0,r=r.r||r,r.v=e,r.s=2,r.a=r.c.slice(),setTimeout(function(){M.call(_,function(){l(t=r.p)&&(O?S.emit("unhandledRejection",e,t):_.console&&console.error&&console.error("Unhandled promise rejection",e)),r.a=void 0})},1),s(r))}function f(e){var t,r=this;if(!r.d){r.d=!0,r=r.r||r;try{(t=u(e))?M.call(_,function(){var n={r:r,d:!1};try{t.call(e,p(f,n,1),p(c,n,1))}catch(a){c.call(n,a)}}):(r.v=e,r.s=1,s(r))}catch(n){c.call({r:r,d:!1},n)}}}var d,h=e("./$"),p=e("./$.ctx"),v=e("./$.cof"),b=e("./$.def"),g=e("./$.assert"),y=e("./$.for-of"),m=e("./$.set-proto").set,w=e("./$.same"),x=e("./$.species"),k=e("./$.wks")("species"),j=e("./$.uid").safe("record"),E="Promise",_=h.g,S=_.process,O="process"==v(S),M=S&&S.nextTick||e("./$.task").set,I=_[E],P=h.isFunction,N=h.isObject,A=g.fn,$=g.obj,q=function(){function e(t){var r=new I(t);return m(r,e.prototype),r}var t=!1;try{if(t=P(I)&&P(I.resolve)&&n(),m(e,I),e.prototype=h.create(I.prototype,{constructor:{value:e}}),e.resolve(5).then(function(){})instanceof e||(t=!1),t&&h.DESC){var r=!1;I.resolve(h.setDesc({},"then",{get:function(){r=!0}})),t=r}}catch(a){t=!1}return t}();q||(I=function(e){A(e);var t={p:g.inst(this,I,E),c:[],a:void 0,s:0,d:!1,v:void 0,h:!1};h.hide(this,j,t);try{e(p(f,t,1),p(c,t,1))}catch(r){c.call(t,r)}},e("./$.mix")(I.prototype,{then:function(e,t){var r=$($(this).constructor)[k],n={ok:P(e)?e:!0,fail:P(t)?t:!1},a=n.P=new(void 0!=r?r:I)(function(e,t){n.res=A(e),n.rej=A(t)}),i=this[j];return i.c.push(n),i.a&&i.a.push(n),i.s&&s(i),a},"catch":function(e){return this.then(void 0,e)}})),b(b.G+b.W+b.F*!q,{Promise:I}),v.set(I,E),x(I),x(d=h.core[E]),b(b.S+b.F*!q,E,{reject:function(e){return new(o(this))(function(t,r){r(e)})}}),b(b.S+b.F*(!q||n(!0)),E,{resolve:function(e){return a(e)&&i(e.constructor,this)?e:new this(function(t){t(e)})}}),b(b.S+b.F*!(q&&e("./$.iter-detect")(function(e){I.all(e)["catch"](function(){})})),E,{all:function(e){var t=o(this),r=[];return new t(function(n,a){y(e,!1,r.push,r);var i=r.length,o=Array(i);i?h.each.call(r,function(e,r){t.resolve(e).then(function(e){o[r]=e,--i||n(o)},a)}):n(o)})},race:function(e){var t=o(this);return new t(function(r,n){y(e,!1,function(e){t.resolve(e).then(r,n)})})}})},{"./$":141,"./$.assert":123,"./$.cof":125,"./$.ctx":129,"./$.def":130,"./$.for-of":133,"./$.iter-detect":139,"./$.mix":143,"./$.same":145,"./$.set-proto":146,"./$.species":148,"./$.task":150,"./$.uid":151,"./$.wks":153}],162:[function(e,t,r){var n=e("./$").set,a=e("./$.string-at")(!0),i=e("./$.uid").safe("iter"),o=e("./$.iter"),u=o.step;e("./$.iter-define")(String,"String",function(e){n(this,i,{o:String(e),i:0})},function(){var e,t=this[i],r=t.o,n=t.i;return n>=r.length?u(1):(e=a(r,n),t.i+=e.length,u(0,e))})},{"./$":141,"./$.iter":140,"./$.iter-define":138,"./$.string-at":149,"./$.uid":151}],163:[function(e,t,r){"use strict";function n(e){var t=L[e]=f.set(j(P.prototype),A,e);return x&&N&&G(w,e,{configurable:!0,set:function(t){k(this,$)&&k(this[$],e)&&(this[$][e]=!1),G(this,e,S(1,t))}}),t}function a(e,t,r){return r&&k(L,t)?(r.enumerable?(k(e,$)&&e[$][t]&&(e[$][t]=!1),r=j(r,{enumerable:S(0,!1)})):(k(e,$)||_(e,$,S(1,{})),e[$][t]=!0),G(e,t,r)):_(e,t,r)}function i(e,t){m(e);for(var r,n=y(t=I(t)),i=0,o=n.length;o>i;)a(e,r=n[i++],t[r]);return e}function o(e,t){return void 0===t?j(e):i(j(e),t)}function u(e){var t=q.call(this,e);return t||!k(this,e)||!k(L,e)||k(this,$)&&this[$][e]?t:!0}function s(e,t){var r=E(e=I(e),t);return!r||!k(L,t)||k(e,$)&&e[$][t]||(r.enumerable=!0),r}function l(e){for(var t,r=M(I(e)),n=[],a=0;r.length>a;)k(L,t=r[a++])||t==$||n.push(t);return n}function c(e){for(var t,r=M(I(e)),n=[],a=0;r.length>a;)k(L,t=r[a++])&&n.push(L[t]);return n}var f=e("./$"),d=e("./$.cof").set,h=e("./$.uid"),p=e("./$.shared"),v=e("./$.def"),b=e("./$.redef"),g=e("./$.keyof"),y=e("./$.enum-keys"),m=e("./$.assert").obj,w=Object.prototype,x=f.DESC,k=f.has,j=f.create,E=f.getDesc,_=f.setDesc,S=f.desc,O=e("./$.get-names"),M=O.get,I=f.toObject,P=f.g.Symbol,N=!1,A=h("tag"),$=h("hidden"),q={}.propertyIsEnumerable,D=p("symbol-registry"),L=p("symbols"),F=f.isFunction(P),G=x?function(){try{return j(_({},$,{get:function(){return _(this,$,{value:!1})[$]}}))[$]||_}catch(e){return function(e,t,r){var n=E(w,t);n&&delete w[t],_(e,t,r),n&&e!==w&&_(w,t,n)}}}():_;F||(P=function(){if(this instanceof P)throw TypeError("Symbol is not a constructor");return n(h(arguments[0]))},b(P.prototype,"toString",function(){return this[A]}),f.create=o,f.setDesc=a,f.getDesc=s,f.setDescs=i,f.getNames=O.get=l,f.getSymbols=c,f.DESC&&f.FW&&b(w,"propertyIsEnumerable",u,!0));var C={"for":function(e){return k(D,e+="")?D[e]:D[e]=P(e)},keyFor:function(e){return g(D,e)},useSetter:function(){N=!0},useSimple:function(){N=!1}};f.each.call("hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),function(t){var r=e("./$.wks")(t);C[t]=F?r:n(r)}),N=!0,v(v.G+v.W,{Symbol:P}),v(v.S,"Symbol",C),v(v.S+v.F*!F,"Object",{create:o,defineProperty:a,defineProperties:i,getOwnPropertyDescriptor:s,getOwnPropertyNames:l,getOwnPropertySymbols:c}),d(P,"Symbol"),d(Math,"Math",!0),d(f.g.JSON,"JSON",!0)},{"./$":141,"./$.assert":123,"./$.cof":125,"./$.def":130,"./$.enum-keys":132,"./$.get-names":135,"./$.keyof":142,"./$.redef":144,"./$.shared":147,"./$.uid":151,"./$.wks":153}],164:[function(e,t,r){e("./$.collection-to-json")("Map")},{"./$.collection-to-json":127}],165:[function(e,t,r){e("./es6.array.iterator");var n=e("./$"),a=e("./$.iter").Iterators,i=e("./$.wks")("iterator"),o=a.Array,u=n.g.NodeList,s=n.g.HTMLCollection,l=u&&u.prototype,c=s&&s.prototype;n.FW&&(!u||i in l||n.hide(l,i,o),!s||i in c||n.hide(c,i,o)),a.NodeList=a.HTMLCollection=o},{"./$":141,"./$.iter":140,"./$.wks":153,"./es6.array.iterator":156}],166:[function(e,t,r){(function(r){var n="object"==typeof r?r:"object"==typeof window?window:"object"==typeof self?self:this,a=n.regeneratorRuntime&&Object.getOwnPropertyNames(n).indexOf("regeneratorRuntime")>=0,i=a&&n.regeneratorRuntime;n.regeneratorRuntime=void 0,t.exports=e("./runtime"),a?n.regeneratorRuntime=i:delete n.regeneratorRuntime,t.exports={"default":t.exports,__esModule:!0}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./runtime":167}],167:[function(e,t,r){(function(r,n){"use strict";var a=e("babel-runtime/core-js/symbol")["default"],i=e("babel-runtime/core-js/symbol/iterator")["default"],o=e("babel-runtime/core-js/object/create")["default"],u=e("babel-runtime/core-js/promise")["default"];!function(e){function n(e,t,r,n){var a=o((t||l).prototype);return a._invoke=v(e,r||null,new y(n||[])),a}function s(e,t,r){try{return{type:"normal",arg:e.call(t,r)}}catch(n){return{type:"throw",arg:n}}}function l(){}function c(){}function f(){}function d(e){["next","throw","return"].forEach(function(t){e[t]=function(e){return this._invoke(t,e)}})}function h(e){this.arg=e}function p(e){function t(t,r){var n=e[t](r),a=n.value;return a instanceof h?u.resolve(a.arg).then(i,o):u.resolve(a).then(function(e){return n.value=e,n})}function n(e,r){var n=a?a.then(function(){return t(e,r)}):new u(function(n){n(t(e,r))});return a=n["catch"](function(e){}),n}"object"==typeof r&&r.domain&&(t=r.domain.bind(t));var a,i=t.bind(e,"next"),o=t.bind(e,"throw");t.bind(e,"return");this._invoke=n}function v(e,t,r){var n=S;return function(a,i){if(n===M)throw new Error("Generator is already running");if(n===I){if("throw"===a)throw i;return w()}for(;;){var o=r.delegate;if(o){if("return"===a||"throw"===a&&o.iterator[a]===x){r.delegate=null;var u=o.iterator["return"];if(u){var l=s(u,o.iterator,i);if("throw"===l.type){a="throw",i=l.arg;continue}}if("return"===a)continue}var l=s(o.iterator[a],o.iterator,i);if("throw"===l.type){r.delegate=null,a="throw",i=l.arg;continue}a="next",i=x;var c=l.arg;if(!c.done)return n=O,c;r[o.resultName]=c.value,r.next=o.nextLoc,r.delegate=null}if("next"===a)n===O?r.sent=i:r.sent=x;else if("throw"===a){if(n===S)throw n=I,i;r.dispatchException(i)&&(a="next",i=x)}else"return"===a&&r.abrupt("return",i);n=M;var l=s(e,t,r);if("normal"===l.type){n=r.done?I:O;var c={value:l.arg,done:r.done};if(l.arg!==P)return c;r.delegate&&"next"===a&&(i=x)}else"throw"===l.type&&(n=I,a="throw",i=l.arg)}}}function b(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function g(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function y(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(b,this),this.reset(!0)}function m(e){if(e){var t=e[j];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var r=-1,n=function a(){for(;++r=0;--n){var a=this.tryEntries[n],i=a.completion;if("root"===a.tryLoc)return t("end");if(a.tryLoc<=this.prev){var o=k.call(a,"catchLoc"),u=k.call(a,"finallyLoc");if(o&&u){if(this.prev=0;--r){var n=this.tryEntries[r];if(n.tryLoc<=this.prev&&k.call(n,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),g(r),P}},"catch":function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var a=n.arg;g(r)}return a}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,r){return this.delegate={iterator:m(e),resultName:t,nextLoc:r},P}}}("object"==typeof n?n:"object"==typeof window?window:"object"==typeof self?self:void 0)}).call(this,e("_process"),"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{_process:168,"babel-runtime/core-js/object/create":93,"babel-runtime/core-js/promise":98,"babel-runtime/core-js/symbol":99,"babel-runtime/core-js/symbol/iterator":100}],168:[function(e,t,r){function n(){c=!1,u.length?l=u.concat(l):f=-1,l.length&&a()}function a(){if(!c){var e=setTimeout(n);c=!0;for(var t=l.length;t;){for(u=l,l=[];++f1)for(var r=1;r0?e[a(0,n-1)]:void 0}var l=-1,c=o(e),n=c.length,f=n-1;for(t=s(0>t?0:+t||0,n);++ln;)e=e[t[n++]];return n&&n==i?e:void 0}}var a=e("./toObject");t.exports=n},{"./toObject":221}],185:[function(e,t,r){function n(e,t,r,u,s,l){return e===t?!0:null==e||null==t||!i(e)&&!o(t)?e!==e&&t!==t:a(e,t,n,r,u,s,l)}var a=e("./baseIsEqualDeep"),i=e("../lang/isObject"),o=e("./isObjectLike");t.exports=n},{"../lang/isObject":229,"./baseIsEqualDeep":186,"./isObjectLike":217}],186:[function(e,t,r){function n(e,t,r,n,d,v,b){var g=u(e),y=u(t),m=c,w=c;g||(m=p.call(e),m==l?m=f:m!=f&&(g=s(e))),y||(w=p.call(t),w==l?w=f:w!=f&&(y=s(t)));var x=m==f,k=w==f,j=m==w;if(j&&!g&&!x)return i(e,t,m);if(!d){var E=x&&h.call(e,"__wrapped__"),_=k&&h.call(t,"__wrapped__");if(E||_)return r(E?e.value():e,_?t.value():t,n,d,v,b)}if(!j)return!1;v||(v=[]),b||(b=[]);for(var S=v.length;S--;)if(v[S]==e)return b[S]==t;v.push(e),b.push(t);var O=(g?a:o)(e,t,r,n,d,v,b);return v.pop(),b.pop(),O}var a=e("./equalArrays"),i=e("./equalByTag"),o=e("./equalObjects"),u=e("../lang/isArray"),s=e("../lang/isTypedArray"),l="[object Arguments]",c="[object Array]",f="[object Object]",d=Object.prototype,h=d.hasOwnProperty,p=d.toString;t.exports=n},{"../lang/isArray":225,"../lang/isTypedArray":232,"./equalArrays":203,"./equalByTag":204,"./equalObjects":205}],187:[function(e,t,r){function n(e,t,r){var n=t.length,o=n,u=!r;if(null==e)return!o;for(e=i(e);n--;){var s=t[n];if(u&&s[2]?s[1]!==e[s[0]]:!(s[0]in e))return!1}for(;++nt&&(t=-t>a?0:a+t),r=void 0===r||r>a?a:+r||0,0>r&&(r+=a),a=t>r?0:r-t>>>0,t>>>=0;for(var i=Array(a);++n2?r[o-2]:void 0,s=o>2?r[2]:void 0,l=o>1?r[o-1]:void 0;for("function"==typeof u?(u=a(u,l,5),o-=2):(u="function"==typeof l?l:void 0,o-=u?1:0),s&&i(r[0],r[1],s)&&(u=3>o?void 0:u,o=1);++nl))return!1;for(;++s-1&&e%1==0&&t>e}var a=/^\d+$/,i=9007199254740991;t.exports=n},{}],214:[function(e,t,r){function n(e,t,r){if(!o(r))return!1;var n=typeof t;if("number"==n?a(r)&&i(t,r.length):"string"==n&&t in r){var u=r[t];return e===e?e===u:u!==u}return!1}var a=e("./isArrayLike"),i=e("./isIndex"),o=e("../lang/isObject");t.exports=n},{"../lang/isObject":229,"./isArrayLike":212,"./isIndex":213}],215:[function(e,t,r){function n(e,t){var r=typeof e;if("string"==r&&u.test(e)||"number"==r)return!0;if(a(e))return!1;var n=!o.test(e);return n||null!=t&&e in i(t)}var a=e("../lang/isArray"),i=e("./toObject"),o=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,u=/^\w*$/;t.exports=n},{"../lang/isArray":225,"./toObject":221}],216:[function(e,t,r){function n(e){return"number"==typeof e&&e>-1&&e%1==0&&a>=e}var a=9007199254740991;t.exports=n},{}],217:[function(e,t,r){function n(e){return!!e&&"object"==typeof e}t.exports=n},{}],218:[function(e,t,r){function n(e){return e===e&&!a(e)}var a=e("../lang/isObject");t.exports=n},{"../lang/isObject":229}],219:[function(e,t,r){function n(e){for(var t=s(e),r=t.length,n=r&&e.length,l=!!n&&u(n)&&(i(e)||a(e)),f=-1,d=[];++f0;++no&&(t=r[4]?t.substr(0,o):t.substr(t.length-o)),l++}e=e.substr(0,o=r.index)+t+e.substr(a.lastIndex),a.lastIndex=t.length+o}return e};i.s=function(e,t){return t?n:e+""},t.exports=i},{}]},{},[59])(59)}); \ No newline at end of file diff --git a/tracker-server/public/stylesheets/demo.css b/tracker-server/public/stylesheets/demo.css new file mode 100644 index 0000000..60e7c84 --- /dev/null +++ b/tracker-server/public/stylesheets/demo.css @@ -0,0 +1,6 @@ +#demo-graph { + width: 1000px; + height: 600px; + display: block; + border: 2px solid black; +} \ No newline at end of file diff --git a/tracker-server/routes/index.js b/tracker-server/routes/index.js index e52c65d..baad178 100644 --- a/tracker-server/routes/index.js +++ b/tracker-server/routes/index.js @@ -73,6 +73,10 @@ router.get('/node', (req, res) => { } }); +router.get('/demo', (req, res) => { + res.render('demo'); +}); + /** * Reset the nodelist on the tracker server. */ diff --git a/tracker-server/views/demo.ejs b/tracker-server/views/demo.ejs new file mode 100644 index 0000000..b070217 --- /dev/null +++ b/tracker-server/views/demo.ejs @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + +
+
+ +
+ + + +

hoi

+ + +
+ + + +
+ +
+
\ No newline at end of file From b8d86eb083228ae93acaab810a3847c2f5676c54 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Tue, 23 Jan 2018 02:10:54 +0100 Subject: [PATCH 37/69] temporary fix for chainview --- .../blockchain/scaleoutdistributedledger/model/ChainView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java index 2f0170c..f5e4b73 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java @@ -100,7 +100,7 @@ private boolean checkIntegrity() { Block updatedBlock = updates.get(0); //TODO we might need a special equality check - if (!ownBlock.equals(updatedBlock)) { + if (ownBlock.getNumber() != updatedBlock.getNumber()) { this.valid = false; return false; } From da3728826abb7fa74e2f53034905234db2c570ce Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Tue, 23 Jan 2018 02:13:51 +0100 Subject: [PATCH 38/69] Use LightView --- .../model/Block.java | 2 +- .../model/LightView.java | 36 +++++++++++++++++++ .../model/Proof.java | 6 ++-- 3 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/LightView.java diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index 38d996b..a8df9d6 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -185,7 +185,7 @@ public boolean equals(Object obj) { if (this.number != other.number) return false; if (this.owner == null) { if (other.owner != null) return false; - } else if (other.owner == null || this.owner.getId() != other.owner.getId()) return false; + } else if (!this.owner.equals(other.owner)) return false; if (this.previousBlock == null) { if (other.previousBlock != null) return false; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/LightView.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/LightView.java new file mode 100644 index 0000000..5f0673f --- /dev/null +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/LightView.java @@ -0,0 +1,36 @@ +package nl.tudelft.blockchain.scaleoutdistributedledger.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class which provides an easy way of getting blocks. + */ +public class LightView { + private Chain chain; + private List updates; + + /** + * @param chain - the chain + * @param updates - the blocks of this chain that were sent with the proof + */ + public LightView(Chain chain, List updates) { + this.chain = chain; + this.updates = updates; + if (this.updates == null) this.updates = new ArrayList<>(); + } + + /** + * @param number - the number + * @return - the block with the given number + * @throws IndexOutOfBoundsException - If the block with the given number does not exist (yet). + */ + public Block getBlock(int number) { + if (number < chain.getBlocks().size()) { + return chain.getBlocks().get(number); + } else { + int index = number - updates.get(0).getNumber(); + return updates.get(index); + } + } +} diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 31994e9..8f337d9 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -100,10 +100,10 @@ private void fixPreviousBlockPointersAndOrder() { } private void fixTransactionSources(LocalStore localStore) { - HashMap chainViews = new HashMap<>(); + HashMap lightViews = new HashMap<>(); // Initialize the chainviews only once for (Node node : this.chainUpdates.keySet()) { - chainViews.put(node.getId(), getChainView(node)); + lightViews.put(node.getId(), new LightView(node.getChain(), chainUpdates.get(node))); } // For all transactions of all nodes do @@ -112,7 +112,7 @@ private void fixTransactionSources(LocalStore localStore) { for (Transaction tx : block.getTransactions()) { tx.getMessage().getSource().forEach(entry -> { Block sourceBlock; - if (!chainViews.containsKey(entry.getKey())) { + if (!lightViews.containsKey(entry.getKey())) { sourceBlock = localStore.getNode(entry.getKey()).getChain().getBlocks().get(entry.getValue()[0]); } else { sourceBlock = chainViews.get(entry.getKey()).getBlock(entry.getValue()[0]); From 7885d5576878f6252e311a8211cf9021827578db Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Tue, 23 Jan 2018 02:19:30 +0100 Subject: [PATCH 39/69] Fix bug --- .../blockchain/scaleoutdistributedledger/model/Proof.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 8f337d9..9cbd023 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -115,7 +115,7 @@ private void fixTransactionSources(LocalStore localStore) { if (!lightViews.containsKey(entry.getKey())) { sourceBlock = localStore.getNode(entry.getKey()).getChain().getBlocks().get(entry.getValue()[0]); } else { - sourceBlock = chainViews.get(entry.getKey()).getBlock(entry.getValue()[0]); + sourceBlock = lightViews.get(entry.getKey()).getBlock(entry.getValue()[0]); } tx.getSource().add(sourceBlock.getTransaction(entry.getValue()[1])); }); From 63663076a2926f8a80915f47b0af76de88a2290c Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Tue, 23 Jan 2018 02:21:17 +0100 Subject: [PATCH 40/69] removed quickfix --- .../blockchain/scaleoutdistributedledger/TransactionSender.java | 2 +- .../blockchain/scaleoutdistributedledger/model/ChainView.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index 65f6971..9105e9b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -36,7 +36,7 @@ public class TransactionSender { * The number of blocks (with the same or higher block number) that need to be committed before * we send a certain block. */ - public static final int REQUIRED_COMMITS = 6; + public static final int REQUIRED_COMMITS = 4; private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); private final LocalStore localStore; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java index 951dbf0..dcd0fdc 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java @@ -115,7 +115,7 @@ private boolean checkIntegrity() { Block updatedBlock = updates.get(startIndex); //TODO we might need a special equality check - if (ownBlock.getNumber() != updatedBlock.getNumber()) { + if (!ownBlock.equals(updatedBlock)) { this.valid = false; return false; } From 69701386975677c3e45c9e900abdc9f15f211745 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Tue, 23 Jan 2018 02:44:10 +0100 Subject: [PATCH 41/69] using treesets --- .../TransactionCreator.java | 10 ++------ .../TransactionTuple.java | 3 ++- .../message/TransactionMessage.java | 7 ++---- .../model/Transaction.java | 24 +++++++++++-------- .../tendermint/TendermintHelper.java | 11 ++------- .../TransactionCreatorTest.java | 12 ++++------ .../model/ProofTest.java | 11 ++++----- .../model/SerializationTest.java | 8 ++----- 8 files changed, 33 insertions(+), 53 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java index 0552425..18f3e86 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java @@ -1,12 +1,6 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; -import java.util.BitSet; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; +import java.util.*; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Chain; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; @@ -81,7 +75,7 @@ public Transaction createTransaction() { TransactionTuple sources = bestSources(); if (sources == null) throw new NotEnoughMoneyException(); - Set sourceSet = sources.getTransactions(); + TreeSet sourceSet = sources.getTransactions(); long remainder = sources.getAmount() - amount; //Mark sources as spent. diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionTuple.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionTuple.java index 09c563c..40a8419 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionTuple.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionTuple.java @@ -3,6 +3,7 @@ import java.util.BitSet; import java.util.HashSet; import java.util.Set; +import java.util.TreeSet; import java.util.stream.Collectors; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; @@ -21,7 +22,7 @@ public class TransactionTuple { private int amount; @Getter - private Set transactions = new HashSet<>(); + private TreeSet transactions = new TreeSet<>(); @Getter @Setter private BitSet chainsRequired; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java index e89ef94..4182c80 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java @@ -7,12 +7,9 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.model.Sha256Hash; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; +import java.util.*; import java.util.AbstractMap.SimpleEntry; -import java.util.ArrayList; -import java.util.HashSet; import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; /** * Transaction message for netty. @@ -85,7 +82,7 @@ public TransactionMessage(Transaction transaction, Node proofReceiver) { public Transaction toTransactionWithoutSources(LocalStore localStore) { Transaction tx = new Transaction(this.number, localStore.getNode(this.senderId), - localStore.getNode(this.receiverId), this.amount, this.remainder, new HashSet<>()); + localStore.getNode(this.receiverId), this.amount, this.remainder, new TreeSet<>()); tx.setMessage(this); return tx; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index 3a07b0d..d9e3d52 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -9,20 +9,15 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.OptionalInt; -import java.util.Set; import java.util.logging.Level; import nl.tudelft.blockchain.scaleoutdistributedledger.message.BlockMessage; /** * Transaction class. */ -public class Transaction { +public class Transaction implements Comparable { // Represent the sender of a genesis transaction public static final int GENESIS_SENDER = -1; @@ -41,7 +36,7 @@ public class Transaction { private final long amount, remainder; @Getter - private final Set source; + private final TreeSet source; // Custem getter private Sha256Hash hash; @@ -61,7 +56,7 @@ public class Transaction { * @param remainder - the remaining amount. * @param source - set of transactions that are used as source for this transaction. */ - public Transaction(int number, Node sender, Node receiver, long amount, long remainder, Set source) { + public Transaction(int number, Node sender, Node receiver, long amount, long remainder, TreeSet source) { this.sender = sender; this.receiver = receiver; this.amount = amount; @@ -147,7 +142,7 @@ private Sha256Hash calculateHash() { */ public Transaction genesisCopy() { if (!source.isEmpty()) throw new UnsupportedOperationException("Only genesis transactions can be copied"); - return new Transaction(number, sender, receiver, amount, remainder, new HashSet<>(0)); + return new Transaction(number, sender, receiver, amount, remainder, new TreeSet<>()); } @Override @@ -187,4 +182,13 @@ public String toString() { } } + @Override + public int compareTo(Transaction o) { + if (this.sender == null && o.getSender() != null) return -1; + if (this.sender != null && o.getSender() == null) return 1; + if (this.sender == null && o.getSender() == null) return 0; + int senderCompare = Integer.compare(this.sender.getId(), o.getSender().getId()); + if (senderCompare != 0) return senderCompare; + return Integer.compare(this.getNumber(), o.getNumber()); + } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java index c844158..429069f 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java @@ -8,14 +8,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.OptionalInt; +import java.util.*; import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -366,7 +359,7 @@ private static void enableLogging(Process ps, String logPrefix) { public static Block generateGenesisBlock(long amount, Map nodeList) { List initialTransactions = new ArrayList<>(); for (int i = 0; i < nodeList.size(); i++) { - Transaction t = new Transaction(i, null, nodeList.get(i), amount, 0, new HashSet<>(0)); + Transaction t = new Transaction(i, null, nodeList.get(i), amount, 0, new TreeSet<>()); initialTransactions.add(t); } diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java index 96a83a4..b8501b9 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java @@ -2,11 +2,7 @@ import static org.junit.Assert.*; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; import org.junit.Before; import org.junit.Test; @@ -78,8 +74,10 @@ public void addMetaKnowledge(Node node, int... chains) { * @return the transaction */ public Transaction addUnspent(Node sender, Node receiver, long amount, long remainder) { - Transaction genesis = new Transaction(0, null, sender, amount + remainder, 0, new HashSet<>()); - Transaction transaction = new Transaction(localStore.getNewTransactionId(), sender, receiver, amount, remainder, Collections.singleton(genesis)); + Transaction genesis = new Transaction(0, null, sender, amount + remainder, 0, new TreeSet<>()); + TreeSet source = new TreeSet<>(); + source.add(genesis); + Transaction transaction = new Transaction(localStore.getNewTransactionId(), sender, receiver, amount, remainder, source); localStore.addUnspentTransaction(transaction); return transaction; } diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java index 2272780..7668ebf 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java @@ -4,10 +4,7 @@ import static org.mockito.Mockito.*; import java.security.KeyPair; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; +import java.util.*; import org.junit.Before; import org.junit.Test; @@ -59,14 +56,14 @@ public Transaction basicScenario() { Chain ownChain = ownNode.getChain(); //3: 0 --> 1, source = GENESIS 0 - HashSet source0to1 = new HashSet<>(); + TreeSet source0to1 = new TreeSet<>(); source0to1.add(ownChain.getGenesisTransaction()); Transaction transaction0to1 = new Transaction(3, ownNode, node1, 100, 900, source0to1); Block block1node0 = ownChain.appendNewBlock(); block1node0.addTransaction(transaction0to1); //3: 1 --> 0, source = [3: 0 --> 1] - HashSet source1to0 = new HashSet<>(); + TreeSet source1to0 = new TreeSet<>(); source1to0.add(transaction0to1); Transaction transaction1to0 = new Transaction(3, node1, ownNode, 100, 0, source1to0); Block block1node1 = new Block(genesisBlock, node1); @@ -74,7 +71,7 @@ public Transaction basicScenario() { node1.getChain().getBlocks().add(block1node1); //4: 0 --> 2, source = [3: 1 --> 0] - HashSet source0to2 = new HashSet<>(); + TreeSet source0to2 = new TreeSet<>(); source0to2.add(transaction1to0); Transaction transaction0to2 = new Transaction(4, ownNode, node2, 100, 0, source0to2); Block block2node0 = ownChain.appendNewBlock(); diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java index 9ac15a5..c9abdd7 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java @@ -9,11 +9,7 @@ import org.junit.Test; import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import static org.junit.Assert.*; @@ -129,7 +125,7 @@ public LocalStore setupLocalStore(Node node) { */ public Transaction generateTransaction(Node sender, Node receiver, long amount, Transaction transactionSource) { // Create transaction - Set sources = new HashSet<>(); + TreeSet sources = new TreeSet<>(); sources.add(transactionSource); long remainder = transactionSource.getAmount() - amount; Transaction newtTransaction = new Transaction(this.transactionNumber++, sender, receiver, amount, remainder, sources); From 3861bda363031d2af651a2beb3c34ecbbe4a769f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20Jurasi=C5=84ski?= Date: Tue, 23 Jan 2018 03:05:37 +0100 Subject: [PATCH 42/69] Added lock to tendermint cache --- .../mocks/TendermintChainMock.java | 4 +- .../model/mainchain/MainChain.java | 10 ++--- .../mainchain/tendermint/TendermintChain.java | 42 +++++++++++++++---- .../UniformRandomTransactionPattern.java | 14 +++++-- 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/mocks/TendermintChainMock.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/mocks/TendermintChainMock.java index 5f82bc5..c876951 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/mocks/TendermintChainMock.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/mocks/TendermintChainMock.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; + +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; import nl.tudelft.blockchain.scaleoutdistributedledger.model.BlockAbstract; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Sha256Hash; import nl.tudelft.blockchain.scaleoutdistributedledger.model.mainchain.MainChain; @@ -36,7 +38,7 @@ public Sha256Hash commitAbstract(BlockAbstract abs) { } @Override - public boolean isPresent(Sha256Hash blockHash) { + public boolean isPresent(Block block) { return true; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/MainChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/MainChain.java index 6c16608..974ea78 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/MainChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/MainChain.java @@ -22,16 +22,16 @@ public interface MainChain { * @param block - the block to query for * @return - true when present, false otherwise */ - public default boolean isPresent(Block block) { - return isPresent(block.getHash()); - } +// public default boolean isPresent(Block block) { +// return isPresent(block.getHash()); +// } /** * Check whether the block (from a local chain) is on the main chain (in a form of BlockAbstract). - * @param blockHash the hash of the block + * @param block the hash of the block * @return true if there is a block abstract of the given block, false otherwise. */ - public boolean isPresent(Sha256Hash blockHash); + public boolean isPresent(Block block); /** * Initializes the tendermint chain. diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java index 6605953..25dfb74 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java @@ -9,10 +9,7 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.model.mainchain.MainChain; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; @@ -31,6 +28,9 @@ public final class TendermintChain implements MainChain { private TSocket socket; private ExecutorService threadPool; private Set cache; + private final Object cacheLock; + private Map> extraCache; + private Map superExtraCache; @Getter private long currentHeight; @Getter @@ -53,6 +53,9 @@ public TendermintChain(Block genesisBlock, Application app) { public TendermintChain(final int port, Block genesisBlock, Application app) { this.abciServerPort = port; this.cache = new HashSet<>(); + this.cacheLock = new Object(); + this.extraCache = new HashMap<>(); + this.superExtraCache = new HashMap<>(); this.app = app; this.socket = new TSocket(); @@ -132,13 +135,23 @@ private void updateCacheBlocking(long height) { Log.log(Level.WARNING, "Could not get block at height " + i + ", perhaps the tendermint rpc is not (yet) running (or broken)"); return; } - for (BlockAbstract abs : abstractsAtCurrentHeight) { - cache.add(abs.getBlockHash()); + synchronized (cacheLock) { + for (BlockAbstract abs : abstractsAtCurrentHeight) { + cache.add(abs.getBlockHash()); + int blockNum = abs.getBlockNumber(); + int owner = abs.getOwnerNodeId(); + Set setOfBlockNums = extraCache.getOrDefault(owner, new HashSet<>()); + setOfBlockNums.add(blockNum); + extraCache.put(owner, setOfBlockNums); + superExtraCache.putIfAbsent(owner + "-" + blockNum, abs.getBlockHash()); + } } } if (currentHeight < height) { Log.log(Level.FINE, "Successfully updated the Tendermint cache for node " + this.app.getLocalStore().getOwnNode().getId() + " from height " + currentHeight + " -> " + height + ", number of cached hashes of abstracts on main chain is now " + cache.size()); + Log.log(Level.INFO, "Node " + this.getApp().getLocalStore().getOwnNode().getId() + " current view of cache is " + this.getCurrentCache()); + Log.log(Level.INFO, "Node " + this.getApp().getLocalStore().getOwnNode().getId() + " current view of super cache is " + this.getSuperExtraCache()); } currentHeight = Math.max(currentHeight, height); // For concurrency reasons use the maximum } @@ -165,12 +178,15 @@ public Sha256Hash commitAbstract(BlockAbstract abs) { } @Override - public boolean isPresent(Sha256Hash blockHash) { + public boolean isPresent(Block block) { + Sha256Hash blockHash = block.getHash(); if (cache.contains(blockHash)) { return true; } else { +// Log.log(Level.INFO, "Node" + this.getApp().getLocalStore().getOwnNode() + " checking cache for " + block.getOwner().getId() + "-" + block.getNumber() + ": " + blockHash + ", the set is " + this.cache); // We could miss some blocks in our cache, so update and wait for the results updateCacheBlocking(-1); +// Log.log(Level.INFO, "Node" + this.getApp().getLocalStore().getOwnNode() + " did not find " + blockHash + ", so updated the cache and now the set is " + this.cache); return cache.contains(blockHash); //TODO: We might want to check the actual main chain in the false case // For when an abstract is in a block that is not yet closed by an ENDBLOCK @@ -187,4 +203,16 @@ boolean addToCache(Sha256Hash genesisBlockHash) { return cache.add(genesisBlockHash); } + public Map> getCurrentCache() { + synchronized (cacheLock) { + return new HashMap<>(this.extraCache); + } + } + + public Map getSuperExtraCache() { + synchronized (cacheLock) { + return new HashMap<>(this.superExtraCache); + } + } + } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/UniformRandomTransactionPattern.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/UniformRandomTransactionPattern.java index a73eb3d..2806cc1 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/UniformRandomTransactionPattern.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/UniformRandomTransactionPattern.java @@ -49,7 +49,11 @@ public String getName() { @Override public long timeUntilNextAction(LocalStore localStore) { - return minWaitTime + random.nextInt(maxWaitTime - minWaitTime); + if (maxWaitTime - minWaitTime > 0) { + return minWaitTime + random.nextInt(maxWaitTime - minWaitTime); + } else { + return minWaitTime; + } } @Override @@ -70,8 +74,12 @@ public Node selectNode(LocalStore localStore) { public long selectAmount(LocalStore localStore) { long available = localStore.getAvailableMoney(); if (available == 0 || minAmount > available) return -1; - - long amount = (long) minAmount + random.nextInt(maxAmount - minAmount); + long amount; + if (maxAmount - minAmount > 0) { + amount = (long) minAmount + random.nextInt(maxAmount - minAmount); + } else { + amount = (long) minAmount; + } return Math.min(amount, available); } From 6603c7f794b53af38d1c6ad857f46819cd828f58 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Tue, 23 Jan 2018 11:15:13 +0100 Subject: [PATCH 43/69] Use new constructor for chainview --- .../scaleoutdistributedledger/model/ChainViewTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainViewTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainViewTest.java index 7140cc7..d9a8ce2 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainViewTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainViewTest.java @@ -32,7 +32,7 @@ public void setUp() { chainMock = mock(Chain.class); when(chainMock.getBlocks()).thenReturn(blocks); - chainview = new ChainView(chainMock, updatedBlocks); + chainview = new ChainView(chainMock, updatedBlocks, false); } /** From 5d953df868f679a4184f400cd401f903e75eca49 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Tue, 23 Jan 2018 11:19:57 +0100 Subject: [PATCH 44/69] Fix compile error --- .../blockchain/scaleoutdistributedledger/model/Block.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index a8df9d6..665dd84 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -262,7 +262,7 @@ public boolean isOnMainChain(LocalStore localStore) { if (this.onMainChain) return true; //It is present, so store it and return - if (localStore.getMainChain().isPresent(this.getHash())) { + if (localStore.getMainChain().isPresent(this)) { this.onMainChain = true; return true; } From 86460613c7672360a0c8be67377517a641fa3caa Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Tue, 23 Jan 2018 11:30:13 +0100 Subject: [PATCH 45/69] Synchronize on message receive --- .../message/ProofMessage.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java index fc3dfbb..43284fc 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java @@ -57,10 +57,12 @@ public ProofMessage(Proof proof) { @Override public void handle(LocalStore localStore) { - try { - CommunicationHelper.receiveTransaction(new Proof(this, localStore), localStore); - } catch (IOException e) { - Log.log(Level.SEVERE, "Exception while handling proof message", e); + synchronized (localStore.getOwnNode().getChain()) { + try { + CommunicationHelper.receiveTransaction(new Proof(this, localStore), localStore); + } catch (IOException e) { + Log.log(Level.SEVERE, "Exception while handling proof message", e); + } } } From 0bf63c28c3299a6b1aa552725a25521b5bb90221 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Tue, 23 Jan 2018 11:59:26 +0100 Subject: [PATCH 46/69] removed tendermint logging' --- .../scaleoutdistributedledger/TransactionSender.java | 2 +- .../blockchain/scaleoutdistributedledger/model/Block.java | 5 +++-- .../model/mainchain/tendermint/TendermintChain.java | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index 9105e9b..01b8108 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -36,7 +36,7 @@ public class TransactionSender { * The number of blocks (with the same or higher block number) that need to be committed before * we send a certain block. */ - public static final int REQUIRED_COMMITS = 4; + public static final int REQUIRED_COMMITS = 2; private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); private final LocalStore localStore; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index a8df9d6..e5630f9 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -106,7 +106,7 @@ public synchronized void addTransaction(Transaction transaction) { * @return Hash SHA256 */ public synchronized Sha256Hash getHash() { - if (this.hash == null) { + if (true || this.hash == null) { this.hash = this.calculateHash(); } return this.hash; @@ -252,6 +252,7 @@ public Block genesisCopy() { * @return - boolean identifying if an abstract of this block is on the main chain. */ public boolean isOnMainChain(LocalStore localStore) { + if(true) return true; //TODO Remove hack? if (this.number == GENESIS_BLOCK_NUMBER) return true; @@ -262,7 +263,7 @@ public boolean isOnMainChain(LocalStore localStore) { if (this.onMainChain) return true; //It is present, so store it and return - if (localStore.getMainChain().isPresent(this.getHash())) { + if (localStore.getMainChain().isPresent(this)) { this.onMainChain = true; return true; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java index 25dfb74..0c4287c 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java @@ -150,8 +150,8 @@ private void updateCacheBlocking(long height) { if (currentHeight < height) { Log.log(Level.FINE, "Successfully updated the Tendermint cache for node " + this.app.getLocalStore().getOwnNode().getId() + " from height " + currentHeight + " -> " + height + ", number of cached hashes of abstracts on main chain is now " + cache.size()); - Log.log(Level.INFO, "Node " + this.getApp().getLocalStore().getOwnNode().getId() + " current view of cache is " + this.getCurrentCache()); - Log.log(Level.INFO, "Node " + this.getApp().getLocalStore().getOwnNode().getId() + " current view of super cache is " + this.getSuperExtraCache()); + Log.log(Level.FINE, "Node " + this.getApp().getLocalStore().getOwnNode().getId() + " current view of cache is " + this.getCurrentCache()); + Log.log(Level.FINE, "Node " + this.getApp().getLocalStore().getOwnNode().getId() + " current view of super cache is " + this.getSuperExtraCache()); } currentHeight = Math.max(currentHeight, height); // For concurrency reasons use the maximum } From ea6b3f55551639e81b9dbeb862638bb130063466 Mon Sep 17 00:00:00 2001 From: Chiel Bruin Date: Tue, 23 Jan 2018 12:01:11 +0100 Subject: [PATCH 47/69] Set some logging to fine --- .../model/mainchain/tendermint/TendermintChain.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java index 25dfb74..0c4287c 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java @@ -150,8 +150,8 @@ private void updateCacheBlocking(long height) { if (currentHeight < height) { Log.log(Level.FINE, "Successfully updated the Tendermint cache for node " + this.app.getLocalStore().getOwnNode().getId() + " from height " + currentHeight + " -> " + height + ", number of cached hashes of abstracts on main chain is now " + cache.size()); - Log.log(Level.INFO, "Node " + this.getApp().getLocalStore().getOwnNode().getId() + " current view of cache is " + this.getCurrentCache()); - Log.log(Level.INFO, "Node " + this.getApp().getLocalStore().getOwnNode().getId() + " current view of super cache is " + this.getSuperExtraCache()); + Log.log(Level.FINE, "Node " + this.getApp().getLocalStore().getOwnNode().getId() + " current view of cache is " + this.getCurrentCache()); + Log.log(Level.FINE, "Node " + this.getApp().getLocalStore().getOwnNode().getId() + " current view of super cache is " + this.getSuperExtraCache()); } currentHeight = Math.max(currentHeight, height); // For concurrency reasons use the maximum } From 6465b5ec135fd6abf7c9edabd8c02ac2129a3caf Mon Sep 17 00:00:00 2001 From: Chiel Bruin Date: Tue, 23 Jan 2018 12:33:25 +0100 Subject: [PATCH 48/69] Fixed broadcasting for slaves --- .../simulation/Simulation.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java index 184492c..6fb1c65 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java @@ -142,12 +142,10 @@ public void initialize() { } //Broadcast transaction pattern - if (this.isMaster) { - broadcastMessage(new TransactionPatternMessage(transactionPattern)); - } - + broadcastMessage(new TransactionPatternMessage(transactionPattern)); //Have everyone update their nodes list broadcastMessage(new UpdateNodesMessage()); + Log.log(Level.INFO, "[Simulation] Initialized"); state = SimulationState.INITIALIZED; @@ -162,9 +160,7 @@ public void initialize() { public void start() { checkState(SimulationState.INITIALIZED, "start"); - if (isMaster) { - broadcastMessage(new StartTransactingMessage()); - } + broadcastMessage(new StartTransactingMessage()); Log.log(Level.INFO, "[Simulation] Running"); state = SimulationState.RUNNING; } @@ -207,6 +203,7 @@ protected void checkState(SimulationState expected, String msg) { * @param msg - the message to send */ public void broadcastMessage(Message msg) { + if(!isMaster) return; Log.log(Level.INFO, "[Simulation] Sending " + msg + " to all nodes..."); for (Node node : nodes.values()) { From cc3bc509cc20c79bbb3cca64870f153748c2c841 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Tue, 23 Jan 2018 14:56:21 +0100 Subject: [PATCH 49/69] demo --- tracker-server/app.js | 3 + tracker-server/helpers/sse.js | 64 +++++++++++++++ tracker-server/package-lock.json | 46 +++++++++++ tracker-server/public/javascripts/demo.js | 79 +++++++++++-------- tracker-server/public/stylesheets/demo.css | 9 ++- .../public/stylesheets/odometer-theme-car.css | 20 ++--- tracker-server/routes/index.js | 34 ++++++++ tracker-server/views/demo.ejs | 6 +- 8 files changed, 217 insertions(+), 44 deletions(-) create mode 100644 tracker-server/helpers/sse.js diff --git a/tracker-server/app.js b/tracker-server/app.js index 4db8c6f..d6b1e78 100644 --- a/tracker-server/app.js +++ b/tracker-server/app.js @@ -7,10 +7,13 @@ import http from 'http'; import index from './routes/index'; import NodeList from './model/NodeList'; import path from "path"; +const sseMW = require('./helpers/sse'); const app = express(); const debug = Debug('test:app'); +app.use(sseMW.sseMiddleware); + app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ diff --git a/tracker-server/helpers/sse.js b/tracker-server/helpers/sse.js new file mode 100644 index 0000000..4c9fdd0 --- /dev/null +++ b/tracker-server/helpers/sse.js @@ -0,0 +1,64 @@ +"use strict"; + +console.log("loading sse.js"); + +// ... with this middleware: +function sseMiddleware(req, res, next) { + console.log(" sseMiddleware is activated with "+ req+" res: "+res); + res.sseConnection = new Connection(res); + console.log(" res has now connection res: "+res.sseConnection ); + next(); +} +exports.sseMiddleware = sseMiddleware; +/** + * A Connection is a simple SSE manager for 1 client. + */ +var Connection = (function () { + function Connection(res) { + console.log(" sseMiddleware construct connection for response "); + + this.res = res; + } + Connection.prototype.setup = function () { + console.log("set up SSE stream for response"); + this.res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive' + }); + }; + Connection.prototype.send = function (data) { + console.log("send event to SSE stream "+JSON.stringify(data)); + this.res.write("data: " + JSON.stringify(data) + "\n\n"); + }; + return Connection; +}()); + +exports.Connection = Connection; +/** + * A Topic handles a bundle of connections with cleanup after lost connection. + */ +var Topic = (function () { + function Topic() { + console.log(" constructor for Topic"); + + this.connections = []; + } + Topic.prototype.add = function (conn) { + var connections = this.connections; + connections.push(conn); + console.log('New client connected, now: ', connections.length); + conn.res.on('close', function () { + var i = connections.indexOf(conn); + if (i >= 0) { + connections.splice(i, 1); + } + console.log('Client disconnected, now: ', connections.length); + }); + }; + Topic.prototype.forEach = function (cb) { + this.connections.forEach(cb); + }; + return Topic; +}()); +exports.Topic = Topic; \ No newline at end of file diff --git a/tracker-server/package-lock.json b/tracker-server/package-lock.json index ecf3a00..bdcb550 100644 --- a/tracker-server/package-lock.json +++ b/tracker-server/package-lock.json @@ -865,6 +865,11 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "ejs": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.7.tgz", + "integrity": "sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo=" + }, "encodeurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", @@ -1286,6 +1291,37 @@ "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true }, + "jsnetworkx": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/jsnetworkx/-/jsnetworkx-0.3.4.tgz", + "integrity": "sha1-HAAl35QgjOtcxZ5lj5qb9f709vQ=", + "requires": { + "babel-runtime": "5.8.38", + "lodash": "3.10.1", + "through": "2.3.8", + "tiny-sprintf": "0.3.0" + }, + "dependencies": { + "babel-runtime": { + "version": "5.8.38", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.38.tgz", + "integrity": "sha1-HAsC62MxL18If/IEUIJ7QlydTBk=", + "requires": { + "core-js": "1.2.7" + } + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + } + } + }, "json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", @@ -1870,6 +1906,16 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "tiny-sprintf": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tiny-sprintf/-/tiny-sprintf-0.3.0.tgz", + "integrity": "sha1-QnL9XB0vkoByI/wW1yj98wWVoz4=" + }, "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", diff --git a/tracker-server/public/javascripts/demo.js b/tracker-server/public/javascripts/demo.js index 2d54abe..742194a 100644 --- a/tracker-server/public/javascripts/demo.js +++ b/tracker-server/public/javascripts/demo.js @@ -1,39 +1,49 @@ $(document).ready(function() { + window.odometerOptions = { + auto: false, // Don't automatically initialize everything with class 'odometer' + // selector: '.my-numbers', // Change the selector used to automatically find things to be animated + // format: '(,ddd).dd', // Change how digit groups are formatted, and how many digits are shown after the decimal point + duration: 100, // Change how long the javascript expects the CSS animation to take + // theme: 'car', // Specify the theme (if you have more than one theme css file on the page) + // animation: 'count' // Count is a simpler animation method which just increments the value, + // use it when you're looking for something more subtle. + }; + var nodes = null; var edges = null; var network = null; + // create people. + // value corresponds with the age of the person + nodes = [ + {id: 1, value: 2, label: 'Algie' }, + {id: 2, value: 31, label: 'Alston'}, + {id: 3, value: 12, label: 'Barney'}, + {id: 4, value: 16, label: 'Coley' }, + {id: 5, value: 17, label: 'Grant' }, + {id: 6, value: 15, label: 'Langdon'}, + {id: 7, value: 6, label: 'Lee'}, + {id: 8, value: 5, label: 'Merlin'}, + {id: 9, value: 30, label: 'Mick'}, + {id: 10, value: 18, label: 'Tod'}, + ]; - function draw() { - // create people. - // value corresponds with the age of the person - nodes = [ - {id: 1, value: 2, label: 'Algie' }, - {id: 2, value: 31, label: 'Alston'}, - {id: 3, value: 12, label: 'Barney'}, - {id: 4, value: 16, label: 'Coley' }, - {id: 5, value: 17, label: 'Grant' }, - {id: 6, value: 15, label: 'Langdon'}, - {id: 7, value: 6, label: 'Lee'}, - {id: 8, value: 5, label: 'Merlin'}, - {id: 9, value: 30, label: 'Mick'}, - {id: 10, value: 18, label: 'Tod'}, - ]; + // create connections between people + // value corresponds with the amount of contact between two people + edges = [ + {from: 2, to: 8, value: 3, title: '3 emails per week'}, + {from: 2, to: 9, value: 5, title: '5 emails per week'}, + {from: 2, to: 10,value: 1, title: '1 emails per week'}, + {from: 4, to: 6, value: 8, title: '8 emails per week'}, + {from: 5, to: 7, value: 2, title: '2 emails per week'}, + {from: 4, to: 5, value: 1, title: '1 emails per week'}, + {from: 9, to: 10,value: 2, title: '2 emails per week'}, + {from: 2, to: 3, value: 6, title: '6 emails per week'}, + {from: 3, to: 9, value: 4, title: '4 emails per week'}, + {from: 5, to: 3, value: 1, title: '1 emails per week'}, + {from: 2, to: 7, value: 4, title: '4 emails per week'} + ]; - // create connections between people - // value corresponds with the amount of contact between two people - edges = [ - {from: 2, to: 8, value: 3, title: '3 emails per week'}, - {from: 2, to: 9, value: 5, title: '5 emails per week'}, - {from: 2, to: 10,value: 1, title: '1 emails per week'}, - {from: 4, to: 6, value: 8, title: '8 emails per week'}, - {from: 5, to: 7, value: 2, title: '2 emails per week'}, - {from: 4, to: 5, value: 1, title: '1 emails per week'}, - {from: 9, to: 10,value: 2, title: '2 emails per week'}, - {from: 2, to: 3, value: 6, title: '6 emails per week'}, - {from: 3, to: 9, value: 4, title: '4 emails per week'}, - {from: 5, to: 3, value: 1, title: '1 emails per week'}, - {from: 2, to: 7, value: 4, title: '4 emails per week'} - ]; + function draw() { // Instantiate our network object. var container = document.getElementById('demo-graph'); @@ -48,8 +58,15 @@ $(document).ready(function() { }; network = new vis.Network(container, data, options); } + draw(); + + var counter = 12; $("#graph_button").on('click', function() { - draw() + edges[0].value += 1; + counter += 1; + $(".odometer").text(counter); + network.setData({nodes: nodes, edges: edges}); + network.redraw(); }); }); \ No newline at end of file diff --git a/tracker-server/public/stylesheets/demo.css b/tracker-server/public/stylesheets/demo.css index 60e7c84..13172b9 100644 --- a/tracker-server/public/stylesheets/demo.css +++ b/tracker-server/public/stylesheets/demo.css @@ -3,4 +3,11 @@ height: 600px; display: block; border: 2px solid black; -} \ No newline at end of file + margin-top: 30px; +} + +.odometer { + font-size: 50px; + margin-left: 20px; +} + diff --git a/tracker-server/public/stylesheets/odometer-theme-car.css b/tracker-server/public/stylesheets/odometer-theme-car.css index 5067d9d..bdebfec 100644 --- a/tracker-server/public/stylesheets/odometer-theme-car.css +++ b/tracker-server/public/stylesheets/odometer-theme-car.css @@ -48,11 +48,11 @@ position: absolute; } .odometer.odometer-auto-theme.odometer-animating-up .odometer-ribbon-inner, .odometer.odometer-theme-car.odometer-animating-up .odometer-ribbon-inner { - -webkit-transition: -webkit-transform 2s; - -moz-transition: -moz-transform 2s; - -ms-transition: -ms-transform 2s; - -o-transition: -o-transform 2s; - transition: transform 2s; + -webkit-transition: -webkit-transform 0.5s; + -moz-transition: -moz-transform 0.5s; + -ms-transition: -ms-transform 0.5s; + -o-transition: -o-transform 0.5s; + transition: transform 0.5s; } .odometer.odometer-auto-theme.odometer-animating-up.odometer-animating .odometer-ribbon-inner, .odometer.odometer-theme-car.odometer-animating-up.odometer-animating .odometer-ribbon-inner { -webkit-transform: translateY(-100%); @@ -69,11 +69,11 @@ transform: translateY(-100%); } .odometer.odometer-auto-theme.odometer-animating-down.odometer-animating .odometer-ribbon-inner, .odometer.odometer-theme-car.odometer-animating-down.odometer-animating .odometer-ribbon-inner { - -webkit-transition: -webkit-transform 2s; - -moz-transition: -moz-transform 2s; - -ms-transition: -ms-transform 2s; - -o-transition: -o-transform 2s; - transition: transform 2s; + -webkit-transition: -webkit-transform 0.5s; + -moz-transition: -moz-transform 0.5s; + -ms-transition: -ms-transform 0.5s; + -o-transition: -o-transform 0.5s; + transition: transform 0.5s; -webkit-transform: translateY(0); -moz-transform: translateY(0); -ms-transform: translateY(0); diff --git a/tracker-server/routes/index.js b/tracker-server/routes/index.js index baad178..228db95 100644 --- a/tracker-server/routes/index.js +++ b/tracker-server/routes/index.js @@ -7,6 +7,7 @@ import NodeList from '../model/NodeList'; * Gets all nodes. */ router.get('/', (req, res) => { + updateS res.json({ nodes: app.nodeList.getNodes() }); @@ -96,4 +97,37 @@ function isPresent(arg) { return !!(arg || arg === 0 || arg === "" || arg === false); } +var sseClients = new sseMW.Topic(); +// initial registration of SSE Client Connection +app.get('/topn/updates', function(req,res){ + var sseConnection = res.sseConnection; + sseConnection.setup(); + sseClients.add(sseConnection); +} ); +var m; +//send message to all registered SSE clients +alrighfunction updateSseClients(message) { + var msg = message; + this.m=message; + sseClients.forEach( + function(sseConnection) { + sseConnection.send(this.m); + } + , this // this second argument to forEach is the thisArg (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) + ); //forEach +}// updateSseClients + +// send a heartbeat signal to all SSE clients, once every interval seconds (or every 3 seconds if no interval is specified) +initHeartbeat = function(interval) { + setInterval(function() { + var msg = {"label":"The latest", "time":new Date()}; + updateSseClients( JSON.stringify(msg)); + }//interval function + , interval?interval*1000:3000 + ); // setInterval +}//initHeartbeat + +// initialize heartbeat at 10 second interval +initHeartbeat(10); + export default router; diff --git a/tracker-server/views/demo.ejs b/tracker-server/views/demo.ejs index b070217..7ca32e5 100644 --- a/tracker-server/views/demo.ejs +++ b/tracker-server/views/demo.ejs @@ -20,8 +20,10 @@ -

hoi

- +

Demo

+ + + 12
From 7dd80f92a284ac17bd17f6044014c5b8e3b2ae25 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Tue, 23 Jan 2018 16:57:24 +0100 Subject: [PATCH 50/69] Fix proof creation --- .../blockchain/scaleoutdistributedledger/model/Proof.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 9cbd023..afcde45 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -289,6 +289,12 @@ public static Proof createProof(LocalStore localStore, Transaction transaction) int blockRequired = transaction.getBlockNumber().getAsInt(); Chain senderChain = transaction.getSender().getChain(); + Map metaKnowledge = receiver.getMetaKnowledge(); + + //The from block is the already known + int alreadyKnownBlock = metaKnowledge.getOrDefault(transaction.getSender(), -1) + 1; + blockRequired = Math.min(blockRequired, alreadyKnownBlock); + Block fromBlock = senderChain.getBlocks().get(blockRequired); Block toBlock = getNextCommittedBlock(localStore, blockRequired, senderChain); @@ -296,7 +302,6 @@ public static Proof createProof(LocalStore localStore, Transaction transaction) Map chains = determineChains(transaction, fromBlock, toBlock); //Step 3: add only those blocks that are not yet known - Map metaKnowledge = receiver.getMetaKnowledge(); for (Entry entry : chains.entrySet()) { Chain chain = entry.getKey(); Node owner = chain.getOwner(); From 8edd7f79d7d325bb091860599b45cb53ef547326 Mon Sep 17 00:00:00 2001 From: Karol Jurasinski Date: Tue, 23 Jan 2018 17:29:28 +0100 Subject: [PATCH 51/69] Fixed array index out of bounds on borders + some extra debug stuff for the in-between --- .../SimulationMain.java | 11 +++--- .../TransactionSender.java | 10 ++++- .../scaleoutdistributedledger/model/Node.java | 18 +++++---- .../model/Proof.java | 39 +++++++++++++++---- .../mainchain/tendermint/ABCIClient.java | 2 +- .../UniformRandomTransactionPattern.java | 12 +++--- .../sockets/SocketServerHandler.java | 2 +- 7 files changed, 62 insertions(+), 32 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index d66cb1b..03e3cbf 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -30,16 +30,16 @@ private SimulationMain() {} //SETTINGS //number of local nodes to generate - public static final int LOCAL_NODES_NUMBER = 3; + public static final int LOCAL_NODES_NUMBER = 6; //number of total nodes in the system - public static final int TOTAL_NODES_NUMBER = 3; + public static final int TOTAL_NODES_NUMBER = 6; //number from which our nodes are (e.g if we run nodes (2, 3, 4), then this should be 2 public static final int NODES_FROM_NUMBER = 0; //Whether this main is the master coordinator of the simulation //Note that the master should always be started first public static final boolean IS_MASTER = true; //The duration of the simulation in seconds. Only has an effect when IS_MASTER == true. - public static final int SIMULATION_DURATION = 60; + public static final int SIMULATION_DURATION = 6000; /** * @param args - the program arguments @@ -81,9 +81,8 @@ public static void main(String[] args) throws Exception { // --- PHASE 3: start the actual simulation --- Simulation simulation = new Simulation(IS_MASTER); // ITransactionPattern itp = new OnlyNodeZeroTransactionPattern(10, 20, 1000, 2000, 1); - ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 2); -// UniformRandomTransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 1); -// itp.setSeed(1); + UniformRandomTransactionPattern itp = new UniformRandomTransactionPattern(10, 10, 2000, 2000, 2); + itp.setSeed(1); simulation.setTransactionPattern(itp); simulation.runNodesLocally(nodes, ownNodes, genesisBlock, nodeToKeyPair); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index 01b8108..f6187b4 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -36,7 +36,7 @@ public class TransactionSender { * The number of blocks (with the same or higher block number) that need to be committed before * we send a certain block. */ - public static final int REQUIRED_COMMITS = 2; + public static final int REQUIRED_COMMITS = 1; private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); private final LocalStore localStore; @@ -166,7 +166,13 @@ private void sendBlock(Block block) { */ private boolean sendTransaction(Transaction transaction) throws InterruptedException, IOException { Node to = transaction.getReceiver(); - Proof proof = Proof.createProof(localStore, transaction); + Proof proof; + synchronized (localStore.getOwnNode().getChain()) { + proof = Proof.createProof(localStore, transaction); + Log.debug("Proof created " + proof); + Log.debug("Metaknowledge it's based on " + to.getMetaKnowledge()); + + } ProofMessage msg = new ProofMessage(proof); // Log.debug("{0}: {1}", localStore.getOwnNode().getId(), msg); if (socketClient.sendMessage(to, msg)) { diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java index ba9110c..02699c4 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java @@ -75,14 +75,16 @@ public boolean verify(byte[] message, byte[] signature) throws Exception { * @param proof - the proof to update with */ public void updateMetaKnowledge(Proof proof) { - Map> updates = proof.getChainUpdates(); - for (Entry> entry : updates.entrySet()) { - //Don't include self - if (entry.getKey() == this) continue; - - int lastBlockNr = getLastBlockNumber(entry.getValue()); - if (lastBlockNr == -1) continue; - metaKnowledge.merge(entry.getKey(), lastBlockNr, Math::max); + synchronized (metaKnowledge) { + Map> updates = proof.getChainUpdates(); + for (Entry> entry : updates.entrySet()) { + //Don't include self + if (entry.getKey() == this) continue; + + int lastBlockNr = getLastBlockNumber(entry.getValue()); + if (lastBlockNr == -1) continue; + metaKnowledge.merge(entry.getKey(), lastBlockNr, Math::max); + } } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index afcde45..c8b565d 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -4,6 +4,7 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; import nl.tudelft.blockchain.scaleoutdistributedledger.message.ProofMessage; import nl.tudelft.blockchain.scaleoutdistributedledger.message.BlockMessage; +import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import nl.tudelft.blockchain.scaleoutdistributedledger.validation.ProofValidationException; import nl.tudelft.blockchain.scaleoutdistributedledger.validation.ValidationException; @@ -16,6 +17,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.logging.Level; +import java.util.stream.Collectors; /** * Proof class. @@ -110,7 +113,7 @@ private void fixTransactionSources(LocalStore localStore) { for (Node node : this.chainUpdates.keySet()) { for (Block block : this.chainUpdates.get(node)) { for (Transaction tx : block.getTransactions()) { - tx.getMessage().getSource().forEach(entry -> { + for (Entry entry : tx.getMessage().getSource()) { Block sourceBlock; if (!lightViews.containsKey(entry.getKey())) { sourceBlock = localStore.getNode(entry.getKey()).getChain().getBlocks().get(entry.getValue()[0]); @@ -118,7 +121,7 @@ private void fixTransactionSources(LocalStore localStore) { sourceBlock = lightViews.get(entry.getKey()).getBlock(entry.getValue()[0]); } tx.getSource().add(sourceBlock.getTransaction(entry.getValue()[1])); - }); + } } } } @@ -283,24 +286,42 @@ public static Proof createProof(LocalStore localStore, Transaction transaction) Node receiver = transaction.getReceiver(); synchronized (receiver.getMetaKnowledge()) { + Log.debug("Creating proof based on " + receiver.getMetaKnowledge()); Proof proof = new Proof(transaction); - + //Step 1: determine what blocks need to be sent int blockRequired = transaction.getBlockNumber().getAsInt(); Chain senderChain = transaction.getSender().getChain(); Map metaKnowledge = receiver.getMetaKnowledge(); - //The from block is the already known - int alreadyKnownBlock = metaKnowledge.getOrDefault(transaction.getSender(), -1) + 1; - blockRequired = Math.min(blockRequired, alreadyKnownBlock); + //The from block is the first unknown block + int firstUnknownBlock = metaKnowledge.getOrDefault(transaction.getSender(), -1) + 1; + blockRequired = Math.min(blockRequired, firstUnknownBlock); Block fromBlock = senderChain.getBlocks().get(blockRequired); Block toBlock = getNextCommittedBlock(localStore, blockRequired, senderChain); - + + List blocksToSend = senderChain.getBlocks().subList(fromBlock.getNumber(), toBlock.getNumber()); + //Step 2: determine the chains that need to be sent +// Map chains = determineChains(transaction, fromBlock, toBlock); + List sources = blocksToSend.stream().flatMap(block -> block.getTransactions().get(0).getSource().stream()).collect(Collectors.toList()); + Log.debug("Sources of from block: " + fromBlock.getTransactions().get(0).getSource()); + Set expected = sources.stream().map(Transaction::getSender).map(n -> n == null ? -1 : n.getId()).collect(Collectors.toSet()); + Map chains = determineChains(transaction, fromBlock, toBlock); - + Set actual = chains.keySet().stream().map(Chain::getOwner).map(n -> n == null ? -1 : n.getId()).collect(Collectors.toSet()); + expected.removeAll(actual); + expected.remove(-1); + Log.debug("Removing receiver id from expected: " + receiver.getId()); + Log.debug("Removed? " + expected.remove(receiver.getId())); + if (!expected.isEmpty()) { + System.out.println("badddd"); + } else { + Log.debug("actual set of node numbers required: " + actual); + } + //Step 3: add only those blocks that are not yet known for (Entry entry : chains.entrySet()) { Chain chain = entry.getKey(); @@ -311,6 +332,8 @@ public static Proof createProof(LocalStore localStore, Transaction transaction) int requiredKnown = entry.getValue(); if (alreadyKnown < requiredKnown) { proof.addBlocksOfChain(chain, alreadyKnown + 1, requiredKnown + 1); + } else { + Log.debug("Already known " + alreadyKnown + ", required known " + requiredKnown); } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIClient.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIClient.java index 5e55ec5..3c79f20 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIClient.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIClient.java @@ -117,7 +117,7 @@ public JSONObject status() { try { return result.getJSONObject("result"); } catch (NullPointerException e) { - Log.log(Level.SEVERE, "The main chain does not respond. Did you start tendermint?"); + Log.log(Level.WARNING, "The main chain does not respond. Perhaps the tendermint is not yet running?"); return new JSONObject(); } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/UniformRandomTransactionPattern.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/UniformRandomTransactionPattern.java index 2806cc1..8f465d0 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/UniformRandomTransactionPattern.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/UniformRandomTransactionPattern.java @@ -49,10 +49,10 @@ public String getName() { @Override public long timeUntilNextAction(LocalStore localStore) { - if (maxWaitTime - minWaitTime > 0) { - return minWaitTime + random.nextInt(maxWaitTime - minWaitTime); - } else { + if (maxWaitTime == minWaitTime) { return minWaitTime; + } else { + return minWaitTime + random.nextInt(maxWaitTime - minWaitTime); } } @@ -75,10 +75,10 @@ public long selectAmount(LocalStore localStore) { long available = localStore.getAvailableMoney(); if (available == 0 || minAmount > available) return -1; long amount; - if (maxAmount - minAmount > 0) { - amount = (long) minAmount + random.nextInt(maxAmount - minAmount); - } else { + if (maxAmount == minAmount) { amount = (long) minAmount; + } else { + amount = (long) minAmount + random.nextInt(maxAmount - minAmount); } return Math.min(amount, available); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketServerHandler.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketServerHandler.java index dac702c..c51d6d1 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketServerHandler.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketServerHandler.java @@ -27,7 +27,7 @@ public SocketServerHandler(LocalStore localStore) { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { - Log.log(Level.FINE, "Node " + localStore.getOwnNode().getId() + " Server: received message: " + msg); +// Log.log(Level.INFO, "Node " + localStore.getOwnNode().getId() + " Server: received message: " + msg); if(msg instanceof Message) { ((Message) msg).handle(localStore); } else { From 89afccefa8f5fea6668db9976e4755ab12e28e2b Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Wed, 24 Jan 2018 14:57:36 +0100 Subject: [PATCH 52/69] demo stuff --- tracker-server/app.js | 2 + tracker-server/model/NodeList.js | 18 ++++-- tracker-server/model/Transaction.js | 25 +++++++ tracker-server/model/TransactionList.js | 48 ++++++++++++++ tracker-server/public/javascripts/d3.min.js | 2 - tracker-server/public/javascripts/demo.js | 72 ++++++++++++--------- tracker-server/routes/index.js | 51 +++++++-------- 7 files changed, 155 insertions(+), 63 deletions(-) create mode 100644 tracker-server/model/Transaction.js create mode 100644 tracker-server/model/TransactionList.js delete mode 100644 tracker-server/public/javascripts/d3.min.js diff --git a/tracker-server/app.js b/tracker-server/app.js index d6b1e78..fa04659 100644 --- a/tracker-server/app.js +++ b/tracker-server/app.js @@ -6,6 +6,7 @@ import express from 'express'; import http from 'http'; import index from './routes/index'; import NodeList from './model/NodeList'; +import TransactionList from './model/TransactionList'; import path from "path"; const sseMW = require('./helpers/sse'); @@ -30,6 +31,7 @@ const server = http.createServer(app); app.use(cookieParser()); app.nodeList = new NodeList(); +app.transactionList = new TransactionList(); app.use('/', index); server.listen(port); diff --git a/tracker-server/model/NodeList.js b/tracker-server/model/NodeList.js index 40ef04a..bca9633 100644 --- a/tracker-server/model/NodeList.js +++ b/tracker-server/model/NodeList.js @@ -64,8 +64,8 @@ class NodeList { * @returns {int} - the number of nodes that is initialized */ getRunning() { - var count = 0; - for (var i = 0; i < this.nodes.length; ++i) { + let count = 0; + for (let i = 0; i < this.nodes.length; ++i) { if (!!this.nodes[i] && this.nodes[i].running) { count ++; } @@ -94,12 +94,12 @@ class NodeList { } /** Get the number of registered nodes. - * @returns {integer} - the number of registered nodes on the server + * @returns {number} - the number of registered nodes on the server */ getSize() { if (this.nodes) { - var count = 0; - for (var i = 0; i < this.nodes.length; ++i) { + let count = 0; + for (let i = 0; i < this.nodes.length; ++i) { if (this.nodes[i]) { count ++; } @@ -109,6 +109,14 @@ class NodeList { return 0; } } + + getGraphNodes() { + const nodes = []; + this.nodes.forEach(node => { + nodes.push({id: node.id, label: node.id.toString()}); + }); + return nodes; + } } module.exports = NodeList; diff --git a/tracker-server/model/Transaction.js b/tracker-server/model/Transaction.js new file mode 100644 index 0000000..bcb8e10 --- /dev/null +++ b/tracker-server/model/Transaction.js @@ -0,0 +1,25 @@ +/** + * Class to store transaction information. + */ +class Transaction { + + /** + * Constructor. + * @param from - id of sender node + * @param to - id of receiver node + * @param amount - amount of transaction + * @param remainder - remainder of transaction + * @param numberOfChains - number of chains sent in proof + * @param numberOfBlocks - number of blocks sent in proof + */ + constructor(from, to, amount, remainder, numberOfChains, numberOfBlocks) { + this.from = from; + this.to = to; + this.amount = amount; + this.remainder = remainder; + this.numberOfChains = numberOfChains; + this.numberOfBlocks = numberOfBlocks; + } +} + +module.exports = Transaction; \ No newline at end of file diff --git a/tracker-server/model/TransactionList.js b/tracker-server/model/TransactionList.js new file mode 100644 index 0000000..5609c13 --- /dev/null +++ b/tracker-server/model/TransactionList.js @@ -0,0 +1,48 @@ +/** + * Class to store a list of all transactions. + */ +class TransactionList { + + constructor() { + // Store transactions in a dictionary + this.transactions = {}; + } + + addTransaction(transaction) { + // Make sure we use a consistent key for all transactions between the same two nodes + let key = [transaction.from, transaction.to]; + if(transaction.from > transaction.to) + key = [transaction.to, transaction.from]; + + if(!this.transactions[key]) + this.transactions[key] = [transaction]; + else + this.transactions[key].push(transaction); + } + + getEdgeWeight(node1, node2) { + let key = [node1, node2]; + if(node1 > node2) { + key = [node2, node1]; + } + return this.getEdgeWeightWithKey(key); + } + + getEdgeWeightWithKey(key) { + if(!this.transactions[key]) return 0; + + // TODO: something other than number of transactions? + return this.transactions[key].length; + } + + getGraphEdges() { + const edges = []; + for (let key in this.transactions) { + const weight = this.getEdgeWeightWithKey(key); + edges.push({from: key[0], to: key[1], value: weight}); + } + return edges; + } +} + +module.exports = TransactionList; \ No newline at end of file diff --git a/tracker-server/public/javascripts/d3.min.js b/tracker-server/public/javascripts/d3.min.js deleted file mode 100644 index 97e9581..0000000 --- a/tracker-server/public/javascripts/d3.min.js +++ /dev/null @@ -1,2 +0,0 @@ -// https://d3js.org Version 4.12.2. Copyright 2017 Mike Bostock. -(function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(t.d3=t.d3||{})})(this,function(t){"use strict";function n(t,n){return tn?1:t>=n?0:NaN}function e(t){return 1===t.length&&(t=function(t){return function(e,r){return n(t(e),r)}}(t)),{left:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r>>1;t(n[o],e)<0?r=o+1:i=o}return r},right:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r>>1;t(n[o],e)>0?i=o:r=o+1}return r}}}function r(t,n){return[t,n]}function i(t){return null===t?NaN:+t}function o(t,n){var e,r,o=t.length,u=0,a=-1,c=0,s=0;if(null==n)for(;++a1)return s/(u-1)}function u(t,n){var e=o(t,n);return e?Math.sqrt(e):e}function a(t,n){var e,r,i,o=t.length,u=-1;if(null==n){for(;++u=e)for(r=i=e;++ue&&(r=e),i=e)for(r=i=e;++ue&&(r=e),i0)return[t];if((r=n0)for(t=Math.ceil(t/u),n=Math.floor(n/u),o=new Array(i=Math.ceil(n-t+1));++a=0?(o>=Ys?10:o>=Bs?5:o>=Hs?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=Ys?10:o>=Bs?5:o>=Hs?2:1)}function p(t,n,e){var r=Math.abs(n-t)/Math.max(0,e),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),o=r/i;return o>=Ys?i*=10:o>=Bs?i*=5:o>=Hs&&(i*=2),n=1)return+e(t[r-1],r-1,t);var r,o=(r-1)*n,u=Math.floor(o),a=+e(t[u],u,t);return a+(+e(t[u+1],u+1,t)-a)*(o-u)}}function g(t){for(var n,e,r,i=t.length,o=-1,u=0;++o=0;)for(n=(r=t[i]).length;--n>=0;)e[--u]=r[n];return e}function _(t,n){var e,r,i=t.length,o=-1;if(null==n){for(;++o=e)for(r=e;++oe&&(r=e)}else for(;++o=e)for(r=e;++oe&&(r=e);return r}function y(t){if(!(i=t.length))return[];for(var n=-1,e=_(t,m),r=new Array(e);++n=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),Js.hasOwnProperty(n)?{space:Js[n],local:t}:t}function A(t){var n=E(t);return(n.local?function(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}:function(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===Qs&&n.documentElement.namespaceURI===Qs?n.createElement(t):n.createElementNS(e,t)}})(n)}function C(){return new z}function z(){this._="@"+(++Ks).toString(36)}function P(t,n,e){return t=R(t,n,e),function(n){var e=n.relatedTarget;e&&(e===this||8&e.compareDocumentPosition(this))||t.call(this,n)}}function R(n,e,r){return function(i){var o=t.event;t.event=i;try{n.call(this,this.__data__,e,r)}finally{t.event=o}}}function L(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;rn?1:t>=n?0:NaN}function Z(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function G(t,n){return t.style.getPropertyValue(n)||Z(t).getComputedStyle(t,null).getPropertyValue(n)}function Q(t){return t.trim().split(/^|\s+/)}function J(t){return t.classList||new K(t)}function K(t){this._node=t,this._names=Q(t.getAttribute("class")||"")}function tt(t,n){for(var e=J(t),r=-1,i=n.length;++r>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1)):(n=hf.exec(t))?St(parseInt(n[1],16)):(n=pf.exec(t))?new zt(n[1],n[2],n[3],1):(n=df.exec(t))?new zt(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=vf.exec(t))?Et(n[1],n[2],n[3],n[4]):(n=gf.exec(t))?Et(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=_f.exec(t))?Pt(n[1],n[2]/100,n[3]/100,1):(n=yf.exec(t))?Pt(n[1],n[2]/100,n[3]/100,n[4]):mf.hasOwnProperty(t)?St(mf[t]):"transparent"===t?new zt(NaN,NaN,NaN,0):null}function St(t){return new zt(t>>16&255,t>>8&255,255&t,1)}function Et(t,n,e,r){return r<=0&&(t=n=e=NaN),new zt(t,n,e,r)}function At(t){return t instanceof Nt||(t=kt(t)),t?(t=t.rgb(),new zt(t.r,t.g,t.b,t.opacity)):new zt}function Ct(t,n,e,r){return 1===arguments.length?At(t):new zt(t,n,e,null==r?1:r)}function zt(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function Pt(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Lt(t,n,e,r)}function Rt(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Lt)return new Lt(t.h,t.s,t.l,t.opacity);if(t instanceof Nt||(t=kt(t)),!t)return new Lt;if(t instanceof Lt)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),u=NaN,a=o-i,c=(o+i)/2;return a?(u=n===o?(e-r)/a+6*(e0&&c<1?0:u,new Lt(u,a,c,t.opacity)}(t):new Lt(t,n,e,null==r?1:r)}function Lt(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function qt(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}function Dt(t){if(t instanceof Ot)return new Ot(t.l,t.a,t.b,t.opacity);if(t instanceof jt){var n=t.h*xf;return new Ot(t.l,Math.cos(n)*t.c,Math.sin(n)*t.c,t.opacity)}t instanceof zt||(t=At(t));var e=Bt(t.r),r=Bt(t.g),i=Bt(t.b),o=Ft((.4124564*e+.3575761*r+.1804375*i)/wf),u=Ft((.2126729*e+.7151522*r+.072175*i)/Mf);return new Ot(116*u-16,500*(o-u),200*(u-Ft((.0193339*e+.119192*r+.9503041*i)/Tf)),t.opacity)}function Ut(t,n,e,r){return 1===arguments.length?Dt(t):new Ot(t,n,e,null==r?1:r)}function Ot(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function Ft(t){return t>Ef?Math.pow(t,1/3):t/Sf+Nf}function It(t){return t>kf?t*t*t:Sf*(t-Nf)}function Yt(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Bt(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Ht(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof jt)return new jt(t.h,t.c,t.l,t.opacity);t instanceof Ot||(t=Dt(t));var n=Math.atan2(t.b,t.a)*bf;return new jt(n<0?n+360:n,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}(t):new jt(t,n,e,null==r?1:r)}function jt(t,n,e,r){this.h=+t,this.c=+n,this.l=+e,this.opacity=+r}function Xt(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Vt)return new Vt(t.h,t.s,t.l,t.opacity);t instanceof zt||(t=At(t));var n=t.r/255,e=t.g/255,r=t.b/255,i=(Lf*r+Pf*n-Rf*e)/(Lf+Pf-Rf),o=r-i,u=(zf*(e-i)-Af*o)/Cf,a=Math.sqrt(u*u+o*o)/(zf*i*(1-i)),c=a?Math.atan2(u,o)*bf-120:NaN;return new Vt(c<0?c+360:c,a,i,t.opacity)}(t):new Vt(t,n,e,null==r?1:r)}function Vt(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function $t(t,n,e,r,i){var o=t*t,u=o*t;return((1-3*t+3*o-u)*n+(4-6*o+3*u)*e+(1+3*t+3*o-3*u)*r+u*i)/6}function Wt(t){var n=t.length-1;return function(e){var r=e<=0?e=0:e>=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],u=r>0?t[r-1]:2*i-o,a=r180||e<-180?e-360*Math.round(e/360):e):Gt(isNaN(t)?n:t)}function Kt(t){return 1==(t=+t)?tn:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):Gt(isNaN(n)?e:n)}}function tn(t,n){var e=n-t;return e?Qt(t,e):Gt(isNaN(t)?n:t)}function nn(t){return function(n){var e,r,i=n.length,o=new Array(i),u=new Array(i),a=new Array(i);for(e=0;eo&&(i=n.slice(o,i),a[u]?a[u]+=i:a[++u]=i),(e=e[0])===(r=r[0])?a[u]?a[u]+=r:a[++u]=r:(a[++u]=null,c.push({i:u,x:on(e,r)})),o=Xf.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:on(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,u.rotate,a,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:on(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,u.skewX,a,c),function(t,n,e,r,o,u){if(t!==e||n!==r){var a=o.push(i(o)+"scale(",null,",",null,")");u.push({i:a-4,x:on(t,e)},{i:a-2,x:on(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,u.scaleX,u.scaleY,a,c),o=u=null,function(t){for(var n,e=-1,r=c.length;++e=0&&n._call.call(null,t),n=n._next;--ul}function wn(){ll=(fl=pl.now())+hl,ul=al=0;try{bn()}finally{ul=0,function(){var t,n,e=Ff,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:Ff=n);If=t,Tn(r)}(),ll=0}}function Mn(){var t=pl.now(),n=t-fl;n>sl&&(hl-=n,fl=t)}function Tn(t){if(!ul){al&&(al=clearTimeout(al));t-ll>24?(t<1/0&&(al=setTimeout(wn,t-pl.now()-hl)),cl&&(cl=clearInterval(cl))):(cl||(fl=pl.now(),cl=setInterval(Mn,sl)),ul=1,dl(wn))}}function Nn(t,n,e){var r=new mn;return n=null==n?0:+n,r.restart(function(e){r.stop(),t(e+n)},n,e),r}function kn(t,n,e,r,i,o){var u=t.__transition;if(u){if(e in u)return}else t.__transition={};(function(t,n,e){function r(c){var s,f,l,h;if(e.state!==yl)return o();for(s in a)if((h=a[s]).name===e.name){if(h.state===xl)return Nn(r);h.state===bl?(h.state=Ml,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete a[s]):+s_l)throw new Error("too late; already scheduled");return e}function En(t,n){var e=An(t,n);if(e.state>ml)throw new Error("too late; already started");return e}function An(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function Cn(t,n){var e,r,i,o=t.__transition,u=!0;if(o){n=null==n?null:n+"";for(i in o)(e=o[i]).name===n?(r=e.state>ml&&e.stateMath.abs(t[1]-D[1])?b=!0:x=!0),D=t,m=!0,Xn(),o()}function o(){var t;switch(_=D[0]-q[0],y=D[1]-q[1],T){case Jl:case Ql:N&&(_=Math.max(C-a,Math.min(P-p,_)),s=a+_,d=p+_),k&&(y=Math.max(z-l,Math.min(R-v,y)),h=l+y,g=v+y);break;case Kl:N<0?(_=Math.max(C-a,Math.min(P-a,_)),s=a+_,d=p):N>0&&(_=Math.max(C-p,Math.min(P-p,_)),s=a,d=p+_),k<0?(y=Math.max(z-l,Math.min(R-l,y)),h=l+y,g=v):k>0&&(y=Math.max(z-v,Math.min(R-v,y)),h=l,g=v+y);break;case th:N&&(s=Math.max(C,Math.min(P,a-_*N)),d=Math.max(C,Math.min(P,p+_*N))),k&&(h=Math.max(z,Math.min(R,l-y*k)),g=Math.max(z,Math.min(R,v+y*k)))}d0&&(a=s-_),k<0?v=g-y:k>0&&(l=h-y),T=Jl,I.attr("cursor",ih.selection),o());break;default:return}Xn()},!0).on("keyup.brush",function(){switch(t.event.keyCode){case 16:L&&(x=b=L=!1,o());break;case 18:T===th&&(N<0?p=d:N>0&&(a=s),k<0?v=g:k>0&&(l=h),T=Kl,o());break;case 32:T===Jl&&(t.event.altKey?(N&&(p=d-_*N,a=s+_*N),k&&(v=g-y*k,l=h+y*k),T=th):(N<0?p=d:N>0&&(a=s),k<0?v=g:k>0&&(l=h),T=Kl),I.attr("cursor",ih[M]),o());break;default:return}Xn()},!0).on("mousemove.brush",e,!0).on("mouseup.brush",u,!0);vt(t.event.view)}jn(),Cn(w),r.call(w),U.start()}}function a(){var t=this.__brush||{selection:null};return t.extent=s.apply(this,arguments),t.dim=n,t}var c,s=Wn,f=$n,l=N(e,"start","brush","end"),h=6;return e.move=function(t,e){t.selection?t.on("start.brush",function(){i(this,arguments).beforestart().start()}).on("interrupt.brush end.brush",function(){i(this,arguments).end()}).tween("brush",function(){function t(t){u.selection=1===t&&Gn(s)?null:f(t),r.call(o),a.brush()}var o=this,u=o.__brush,a=i(o,arguments),c=u.selection,s=n.input("function"==typeof e?e.apply(this,arguments):e,u.extent),f=cn(c,s);return c&&s?t:t(1)}):t.each(function(){var t=arguments,o=this.__brush,u=n.input("function"==typeof e?e.apply(this,t):e,o.extent),a=i(this,t).beforestart();Cn(this),o.selection=null==u||Gn(u)?null:u,r.call(this),a.start().brush().end()})},o.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(){return this.starting&&(this.starting=!1,this.emit("start")),this},brush:function(){return this.emit("brush"),this},end:function(){return 0==--this.active&&(delete this.state.emitter,this.emit("end")),this},emit:function(t){D(new function(t,n,e){this.target=t,this.type=n,this.selection=e}(e,t,n.output(this.state.selection)),l.apply,l,[t,this.that,this.args])}},e.extent=function(t){return arguments.length?(s="function"==typeof t?t:Hn([[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]]),e):s},e.filter=function(t){return arguments.length?(f="function"==typeof t?t:Hn(!!t),e):f},e.handleSize=function(t){return arguments.length?(h=+t,e):h},e.on=function(){var t=l.on.apply(l,arguments);return t===l?e:t},e}function Jn(t){return function(){return t}}function Kn(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function te(){return new Kn}function ne(t){return t.source}function ee(t){return t.target}function re(t){return t.radius}function ie(t){return t.startAngle}function oe(t){return t.endAngle}function ue(){}function ae(t,n){var e=new ue;if(t instanceof ue)t.each(function(t,n){e.set(n,t)});else if(Array.isArray(t)){var r,i=-1,o=t.length;if(null==n)for(;++i=u?s=!0:(e=t.charCodeAt(a++))===Mh?f=!0:e===Th&&(f=!0,t.charCodeAt(a)===Mh&&++a),t.slice(r+1,n-1).replace(/""/g,'"')}for(;a=(o=(v+_)/2))?v=o:_=o,(f=e>=(u=(g+y)/2))?g=u:y=u,i=p,!(p=p[l=f<<1|s]))return i[l]=d,t;if(a=+t._x.call(null,p.data),c=+t._y.call(null,p.data),n===a&&e===c)return d.next=p,i?i[l]=d:t._root=d,t;do{i=i?i[l]=new Array(4):t._root=new Array(4),(s=n>=(o=(v+_)/2))?v=o:_=o,(f=e>=(u=(g+y)/2))?g=u:y=u}while((l=f<<1|s)==(h=(c>=u)<<1|a>=o));return i[h]=p,i[l]=d,t}function me(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function xe(t){return t[0]}function be(t){return t[1]}function we(t,n,e){var r=new Me(null==n?xe:n,null==e?be:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function Me(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function Te(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}function Ne(t){return t.x+t.vx}function ke(t){return t.y+t.vy}function Se(t){return t.index}function Ee(t,n){var e=t.get(n);if(!e)throw new Error("missing: "+n);return e}function Ae(t){return t.x}function Ce(t){return t.y}function ze(t,n){if((e=(t=n?t.toExponential(n-1):t.toExponential()).indexOf("e"))<0)return null;var e,r=t.slice(0,e);return[r.length>1?r[0]+r.slice(2):r,+t.slice(e+1)]}function Pe(t){return(t=ze(Math.abs(t)))?t[1]:NaN}function Re(t,n){var e=ze(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}function Le(t){return new qe(t)}function qe(t){if(!(n=Ih.exec(t)))throw new Error("invalid format: "+t);var n,e=n[1]||" ",r=n[2]||">",i=n[3]||"-",o=n[4]||"",u=!!n[5],a=n[6]&&+n[6],c=!!n[7],s=n[8]&&+n[8].slice(1),f=n[9]||"";"n"===f?(c=!0,f="g"):Fh[f]||(f=""),(u||"0"===e&&"="===r)&&(u=!0,e="0",r="="),this.fill=e,this.align=r,this.sign=i,this.symbol=o,this.zero=u,this.width=a,this.comma=c,this.precision=s,this.type=f}function De(t){return t}function Ue(t){function n(t){function n(t){var n,r,u,f=g,x=_;if("c"===v)x=y(t)+x,t="";else{var b=(t=+t)<0;if(t=y(Math.abs(t),d),b&&0==+t&&(b=!1),f=(b?"("===s?s:"-":"-"===s||"("===s?"":s)+f,x=x+("s"===v?Bh[8+Dh/3]:"")+(b&&"("===s?")":""),m)for(n=-1,r=t.length;++n(u=t.charCodeAt(n))||u>57){x=(46===u?i+t.slice(n+1):t.slice(n))+x,t=t.slice(0,n);break}}p&&!l&&(t=e(t,1/0));var w=f.length+t.length+x.length,M=w>1)+f+t+x+M.slice(w);break;default:t=M+f+t+x}return o(t)}var a=(t=Le(t)).fill,c=t.align,s=t.sign,f=t.symbol,l=t.zero,h=t.width,p=t.comma,d=t.precision,v=t.type,g="$"===f?r[0]:"#"===f&&/[boxX]/.test(v)?"0"+v.toLowerCase():"",_="$"===f?r[1]:/[%p]/.test(v)?u:"",y=Fh[v],m=!v||/[defgprs%]/.test(v);return d=null==d?v?6:12:/[gprs]/.test(v)?Math.max(1,Math.min(21,d)):Math.max(0,Math.min(20,d)),n.toString=function(){return t+""},n}var e=t.grouping&&t.thousands?function(t,n){return function(e,r){for(var i=e.length,o=[],u=0,a=t[0],c=0;i>0&&a>0&&(c+a+1>r&&(a=Math.max(1,r-c)),o.push(e.substring(i-=a,i+a)),!((c+=a+1)>r));)a=t[u=(u+1)%t.length];return o.reverse().join(n)}}(t.grouping,t.thousands):De,r=t.currency,i=t.decimal,o=t.numerals?function(t){return function(n){return n.replace(/[0-9]/g,function(n){return t[+n]})}}(t.numerals):De,u=t.percent||"%";return{format:n,formatPrefix:function(t,e){var r=n((t=Le(t),t.type="f",t)),i=3*Math.max(-8,Math.min(8,Math.floor(Pe(e)/3))),o=Math.pow(10,-i),u=Bh[8+i/3];return function(t){return r(o*t)+u}}}}function Oe(n){return Yh=Ue(n),t.format=Yh.format,t.formatPrefix=Yh.formatPrefix,Yh}function Fe(t){return Math.max(0,-Pe(Math.abs(t)))}function Ie(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(Pe(n)/3)))-Pe(Math.abs(t)))}function Ye(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,Pe(n)-Pe(t))+1}function Be(){return new He}function He(){this.reset()}function je(t,n,e){var r=t.s=n+e,i=r-n,o=r-i;t.t=n-o+(e-i)}function Xe(t){return t>1?0:t<-1?Mp:Math.acos(t)}function Ve(t){return t>1?Tp:t<-1?-Tp:Math.asin(t)}function $e(t){return(t=Up(t/2))*t}function We(){}function Ze(t,n){t&&Bp.hasOwnProperty(t.type)&&Bp[t.type](t,n)}function Ge(t,n,e){var r,i=-1,o=t.length-e;for(n.lineStart();++i=0?1:-1,i=r*e,o=Pp(n),u=Up(n),a=$h*u,c=Vh*o+a*Pp(i),s=a*r*Up(i);Hp.add(zp(s,c)),Xh=t,Vh=o,$h=u}function rr(t){return[zp(t[1],t[0]),Ve(t[2])]}function ir(t){var n=t[0],e=t[1],r=Pp(e);return[r*Pp(n),r*Up(n),Up(e)]}function or(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function ur(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function ar(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function cr(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function sr(t){var n=Fp(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}function fr(t,n){ep.push(rp=[Wh=t,Gh=t]),nQh&&(Qh=n)}function lr(t,n){var e=ir([t*Ep,n*Ep]);if(np){var r=ur(np,e),i=ur([r[1],-r[0],0],r);sr(i),i=rr(i);var o,u=t-Jh,a=u>0?1:-1,c=i[0]*Sp*a,s=Ap(u)>180;s^(a*JhQh&&(Qh=o):(c=(c+360)%360-180,s^(a*JhQh&&(Qh=n))),s?t_r(Wh,Gh)&&(Gh=t):_r(t,Gh)>_r(Wh,Gh)&&(Wh=t):Gh>=Wh?(tGh&&(Gh=t)):t>Jh?_r(Wh,t)>_r(Wh,Gh)&&(Gh=t):_r(t,Gh)>_r(Wh,Gh)&&(Wh=t)}else ep.push(rp=[Wh=t,Gh=t]);nQh&&(Qh=n),np=e,Jh=t}function hr(){$p.point=lr}function pr(){rp[0]=Wh,rp[1]=Gh,$p.point=fr,np=null}function dr(t,n){if(np){var e=t-Jh;Vp.add(Ap(e)>180?e+(e>0?360:-360):e)}else Kh=t,tp=n;Xp.point(t,n),lr(t,n)}function vr(){Xp.lineStart()}function gr(){dr(Kh,tp),Xp.lineEnd(),Ap(Vp)>bp&&(Wh=-(Gh=180)),rp[0]=Wh,rp[1]=Gh,np=null}function _r(t,n){return(n-=t)<0?n+360:n}function yr(t,n){return t[0]-n[0]}function mr(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nMp?t-kp:t<-Mp?t+kp:t,n]}function Rr(t,n,e){return(t%=kp)?n||e?zr(qr(t),Dr(n,e)):qr(t):n||e?Dr(n,e):Pr}function Lr(t){return function(n,e){return n+=t,[n>Mp?n-kp:n<-Mp?n+kp:n,e]}}function qr(t){var n=Lr(t);return n.invert=Lr(-t),n}function Dr(t,n){function e(t,n){var e=Pp(n),a=Pp(t)*e,c=Up(t)*e,s=Up(n),f=s*r+a*i;return[zp(c*o-f*u,a*r-s*i),Ve(f*o+c*u)]}var r=Pp(t),i=Up(t),o=Pp(n),u=Up(n);return e.invert=function(t,n){var e=Pp(n),a=Pp(t)*e,c=Up(t)*e,s=Up(n),f=s*o-c*u;return[zp(c*o+s*u,a*r+f*i),Ve(f*r-a*i)]},e}function Ur(t){function n(n){return n=t(n[0]*Ep,n[1]*Ep),n[0]*=Sp,n[1]*=Sp,n}return t=Rr(t[0]*Ep,t[1]*Ep,t.length>2?t[2]*Ep:0),n.invert=function(n){return n=t.invert(n[0]*Ep,n[1]*Ep),n[0]*=Sp,n[1]*=Sp,n},n}function Or(t,n,e,r,i,o){if(e){var u=Pp(n),a=Up(n),c=r*e;null==i?(i=n+r*kp,o=n-c/2):(i=Fr(u,i),o=Fr(u,o),(r>0?io)&&(i+=r*kp));for(var s,f=i;r>0?f>o:f1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function Yr(t,n){return Ap(t[0]-n[0])=0;--o)i.point((f=s[o])[0],f[1]);else r(h.x,h.p.x,-1,i);h=h.p}s=(h=h.o).z,p=!p}while(!h.v);i.lineEnd()}}}function jr(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r=0?1:-1,T=M*w,N=T>Mp,k=d*x;if(ud.add(zp(k*M*Up(T),v*b+k*Pp(T))),o+=N?w+M*kp:w,N^h>=e^y>=e){var S=ur(ir(l),ir(_));sr(S);var E=ur(i,S);sr(E);var A=(N^w>=0?-1:1)*Ve(E[2]);(r>A||r===A&&(S[0]||S[1]))&&(u+=N^w>=0?1:-1)}}return(o<-bp||o0){for(m||(i.polygonStart(),m=!0),i.lineStart(),t=0;t1&&2&o&&u.push(u.pop().concat(u.shift())),p.push(u.filter($r))}var h,p,d,v=n(i),_=Ir(),y=n(_),m=!1,x={point:o,lineStart:a,lineEnd:c,polygonStart:function(){x.point=s,x.lineStart=f,x.lineEnd=l,p=[],h=[]},polygonEnd:function(){x.point=o,x.lineStart=a,x.lineEnd=c,p=g(p);var t=Xr(h,r);p.length?(m||(i.polygonStart(),m=!0),Hr(p,Wr,t,e,i)):t&&(m||(i.polygonStart(),m=!0),i.lineStart(),e(null,null,1,i),i.lineEnd()),m&&(i.polygonEnd(),m=!1),p=h=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}};return x}}function $r(t){return t.length>1}function Wr(t,n){return((t=t.x)[0]<0?t[1]-Tp-bp:Tp-t[1])-((n=n.x)[0]<0?n[1]-Tp-bp:Tp-n[1])}function Zr(t){function n(t,n){return Pp(t)*Pp(n)>i}function e(t,n,e){var r=[1,0,0],o=ur(ir(t),ir(n)),u=or(o,o),a=o[0],c=u-a*a;if(!c)return!e&&t;var s=i*u/c,f=-i*a/c,l=ur(r,o),h=cr(r,s);ar(h,cr(o,f));var p=l,d=or(h,p),v=or(p,p),g=d*d-v*(or(h,h)-1);if(!(g<0)){var _=Fp(g),y=cr(p,(-d-_)/v);if(ar(y,h),y=rr(y),!e)return y;var m,x=t[0],b=n[0],w=t[1],M=n[1];b0^y[1]<(Ap(y[0]-x)Mp^(x<=y[0]&&y[0]<=b)){var k=cr(p,(-d+_)/v);return ar(k,h),[y,rr(k)]}}}function r(n,e){var r=u?t:Mp-t,i=0;return n<-r?i|=1:n>r&&(i|=2),e<-r?i|=4:e>r&&(i|=8),i}var i=Pp(t),o=6*Ep,u=i>0,a=Ap(i)>bp;return Vr(n,function(t){var i,o,c,s,f;return{lineStart:function(){s=c=!1,f=1},point:function(l,h){var p,d=[l,h],v=n(l,h),g=u?v?0:r(l,h):v?r(l+(l<0?Mp:-Mp),h):0;if(!i&&(s=c=v)&&t.lineStart(),v!==c&&(!(p=e(i,d))||Yr(i,p)||Yr(d,p))&&(d[0]+=bp,d[1]+=bp,v=n(d[0],d[1])),v!==c)f=0,v?(t.lineStart(),p=e(d,i),t.point(p[0],p[1])):(p=e(i,d),t.point(p[0],p[1]),t.lineEnd()),i=p;else if(a&&i&&u^v){var _;g&o||!(_=e(d,i,!0))||(f=0,u?(t.lineStart(),t.point(_[0][0],_[0][1]),t.point(_[1][0],_[1][1]),t.lineEnd()):(t.point(_[1][0],_[1][1]),t.lineEnd(),t.lineStart(),t.point(_[0][0],_[0][1])))}!v||i&&Yr(i,d)||t.point(d[0],d[1]),i=d,c=v,o=g},lineEnd:function(){c&&t.lineEnd(),i=null},clean:function(){return f|(s&&c)<<1}}},function(n,e,r,i){Or(i,t,o,r,n,e)},u?[0,-t]:[-Mp,t-Mp])}function Gr(t,n,e,r){function i(i,o){return t<=i&&i<=e&&n<=o&&o<=r}function o(i,o,a,s){var f=0,l=0;if(null==i||(f=u(i,a))!==(l=u(o,a))||c(i,o)<0^a>0)do{s.point(0===f||3===f?t:e,f>1?r:n)}while((f=(f+a+4)%4)!==l);else s.point(o[0],o[1])}function u(r,i){return Ap(r[0]-t)0?0:3:Ap(r[0]-e)0?2:1:Ap(r[1]-n)0?1:0:i>0?3:2}function a(t,n){return c(t.x,n.x)}function c(t,n){var e=u(t,1),r=u(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(u){function c(t,n){i(t,n)&&w.point(t,n)}function s(o,u){var a=i(o,u);if(l&&h.push([o,u]),x)p=o,d=u,v=a,x=!1,a&&(w.lineStart(),w.point(o,u));else if(a&&m)w.point(o,u);else{var c=[_=Math.max(sd,Math.min(cd,_)),y=Math.max(sd,Math.min(cd,y))],s=[o=Math.max(sd,Math.min(cd,o)),u=Math.max(sd,Math.min(cd,u))];!function(t,n,e,r,i,o){var u,a=t[0],c=t[1],s=0,f=1,l=n[0]-a,h=n[1]-c;if(u=e-a,l||!(u>0)){if(u/=l,l<0){if(u0){if(u>f)return;u>s&&(s=u)}if(u=i-a,l||!(u<0)){if(u/=l,l<0){if(u>f)return;u>s&&(s=u)}else if(l>0){if(u0)){if(u/=h,h<0){if(u0){if(u>f)return;u>s&&(s=u)}if(u=o-c,h||!(u<0)){if(u/=h,h<0){if(u>f)return;u>s&&(s=u)}else if(h>0){if(u0&&(t[0]=a+s*l,t[1]=c+s*h),f<1&&(n[0]=a+f*l,n[1]=c+f*h),!0}}}}}(c,s,t,n,e,r)?a&&(w.lineStart(),w.point(o,u),b=!1):(m||(w.lineStart(),w.point(c[0],c[1])),w.point(s[0],s[1]),a||w.lineEnd(),b=!1)}_=o,y=u,m=a}var f,l,h,p,d,v,_,y,m,x,b,w=u,M=Ir(),T={point:c,lineStart:function(){T.point=s,l&&l.push(h=[]),x=!0,m=!1,_=y=NaN},lineEnd:function(){f&&(s(p,d),v&&m&&M.rejoin(),f.push(M.result())),T.point=c,m&&w.lineEnd()},polygonStart:function(){w=M,f=[],l=[],b=!0},polygonEnd:function(){var n=function(){for(var n=0,e=0,i=l.length;er&&(h-o)*(r-u)>(p-u)*(t-o)&&++n:p<=r&&(h-o)*(r-u)<(p-u)*(t-o)&&--n;return n}(),e=b&&n,i=(f=g(f)).length;(e||i)&&(u.polygonStart(),e&&(u.lineStart(),o(null,null,1,u),u.lineEnd()),i&&Hr(f,a,n,o,u),u.polygonEnd()),w=u,f=l=h=null}};return T}}function Qr(){ld.point=ld.lineEnd=We}function Jr(t,n){Zp=t*=Ep,Gp=Up(n*=Ep),Qp=Pp(n),ld.point=Kr}function Kr(t,n){t*=Ep;var e=Up(n*=Ep),r=Pp(n),i=Ap(t-Zp),o=Pp(i),u=r*Up(i),a=Qp*e-Gp*r*o,c=Gp*e+Qp*r*o;fd.add(zp(Fp(u*u+a*a),c)),Zp=t,Gp=e,Qp=r}function ti(t){return fd.reset(),Je(t,ld),+fd}function ni(t,n){return hd[0]=t,hd[1]=n,ti(pd)}function ei(t,n){return!(!t||!vd.hasOwnProperty(t.type))&&vd[t.type](t,n)}function ri(t,n){return 0===ni(t,n)}function ii(t,n){var e=ni(t[0],t[1]);return ni(t[0],n)+ni(n,t[1])<=e+bp}function oi(t,n){return!!Xr(t.map(ui),ai(n))}function ui(t){return(t=t.map(ai)).pop(),t}function ai(t){return[t[0]*Ep,t[1]*Ep]}function ci(t,n,e){var r=f(t,n-bp,e).concat(n);return function(t){return r.map(function(n){return[t,n]})}}function si(t,n,e){var r=f(t,n-bp,e).concat(n);return function(t){return r.map(function(n){return[n,t]})}}function fi(){function t(){return{type:"MultiLineString",coordinates:n()}}function n(){return f(Rp(o/_)*_,i,_).map(p).concat(f(Rp(s/y)*y,c,y).map(d)).concat(f(Rp(r/v)*v,e,v).filter(function(t){return Ap(t%_)>bp}).map(l)).concat(f(Rp(a/g)*g,u,g).filter(function(t){return Ap(t%y)>bp}).map(h))}var e,r,i,o,u,a,c,s,l,h,p,d,v=10,g=v,_=90,y=360,m=2.5;return t.lines=function(){return n().map(function(t){return{type:"LineString",coordinates:t}})},t.outline=function(){return{type:"Polygon",coordinates:[p(o).concat(d(c).slice(1),p(i).reverse().slice(1),d(s).reverse().slice(1))]}},t.extent=function(n){return arguments.length?t.extentMajor(n).extentMinor(n):t.extentMinor()},t.extentMajor=function(n){return arguments.length?(o=+n[0][0],i=+n[1][0],s=+n[0][1],c=+n[1][1],o>i&&(n=o,o=i,i=n),s>c&&(n=s,s=c,c=n),t.precision(m)):[[o,s],[i,c]]},t.extentMinor=function(n){return arguments.length?(r=+n[0][0],e=+n[1][0],a=+n[0][1],u=+n[1][1],r>e&&(n=r,r=e,e=n),a>u&&(n=a,a=u,u=n),t.precision(m)):[[r,a],[e,u]]},t.step=function(n){return arguments.length?t.stepMajor(n).stepMinor(n):t.stepMinor()},t.stepMajor=function(n){return arguments.length?(_=+n[0],y=+n[1],t):[_,y]},t.stepMinor=function(n){return arguments.length?(v=+n[0],g=+n[1],t):[v,g]},t.precision=function(n){return arguments.length?(m=+n,l=ci(a,u,90),h=si(r,e,m),p=ci(s,c,90),d=si(o,i,m),t):m},t.extentMajor([[-180,-90+bp],[180,90-bp]]).extentMinor([[-180,-80-bp],[180,80+bp]])}function li(t){return t}function hi(){yd.point=pi}function pi(t,n){yd.point=di,Jp=td=t,Kp=nd=n}function di(t,n){_d.add(nd*t-td*n),td=t,nd=n}function vi(){di(Jp,Kp)}function gi(t,n){Td+=t,Nd+=n,++kd}function _i(){Rd.point=yi}function yi(t,n){Rd.point=mi,gi(id=t,od=n)}function mi(t,n){var e=t-id,r=n-od,i=Fp(e*e+r*r);Sd+=i*(id+t)/2,Ed+=i*(od+n)/2,Ad+=i,gi(id=t,od=n)}function xi(){Rd.point=gi}function bi(){Rd.point=Mi}function wi(){Ti(ed,rd)}function Mi(t,n){Rd.point=Ti,gi(ed=id=t,rd=od=n)}function Ti(t,n){var e=t-id,r=n-od,i=Fp(e*e+r*r);Sd+=i*(id+t)/2,Ed+=i*(od+n)/2,Ad+=i,Cd+=(i=od*t-id*n)*(id+t),zd+=i*(od+n),Pd+=3*i,gi(id=t,od=n)}function Ni(t){this._context=t}function ki(t,n){Id.point=Si,qd=Ud=t,Dd=Od=n}function Si(t,n){Ud-=t,Od-=n,Fd.add(Fp(Ud*Ud+Od*Od)),Ud=t,Od=n}function Ei(){this._string=[]}function Ai(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function Ci(t){return function(n){var e=new zi;for(var r in t)e[r]=t[r];return e.stream=n,e}}function zi(){}function Pi(t,n,e){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),Je(e,t.stream(Md)),n(Md.result()),null!=r&&t.clipExtent(r),t}function Ri(t,n,e){return Pi(t,function(e){var r=n[1][0]-n[0][0],i=n[1][1]-n[0][1],o=Math.min(r/(e[1][0]-e[0][0]),i/(e[1][1]-e[0][1])),u=+n[0][0]+(r-o*(e[1][0]+e[0][0]))/2,a=+n[0][1]+(i-o*(e[1][1]+e[0][1]))/2;t.scale(150*o).translate([u,a])},e)}function Li(t,n,e){return Ri(t,[[0,0],n],e)}function qi(t,n,e){return Pi(t,function(e){var r=+n,i=r/(e[1][0]-e[0][0]),o=(r-i*(e[1][0]+e[0][0]))/2,u=-i*e[0][1];t.scale(150*i).translate([o,u])},e)}function Di(t,n,e){return Pi(t,function(e){var r=+n,i=r/(e[1][1]-e[0][1]),o=-i*e[0][0],u=(r-i*(e[1][1]+e[0][1]))/2;t.scale(150*i).translate([o,u])},e)}function Ui(t,n){return+n?function(t,n){function e(r,i,o,u,a,c,s,f,l,h,p,d,v,g){var _=s-r,y=f-i,m=_*_+y*y;if(m>4*n&&v--){var x=u+h,b=a+p,w=c+d,M=Fp(x*x+b*b+w*w),T=Ve(w/=M),N=Ap(Ap(w)-1)n||Ap((_*A+y*C)/m-.5)>.3||u*h+a*p+c*d2?t[2]%360*Ep:0,r()):[x*Sp,b*Sp,w*Sp]},n.precision=function(t){return arguments.length?(E=Ui(e,S=t*t),i()):Fp(S)},n.fitExtent=function(t,e){return Ri(n,t,e)},n.fitSize=function(t,e){return Li(n,t,e)},n.fitWidth=function(t,e){return qi(n,t,e)},n.fitHeight=function(t,e){return Di(n,t,e)},function(){return o=t.apply(this,arguments),n.invert=o.invert&&function(t){return(t=s.invert((t[0]-u)/v,(a-t[1])/v))&&[t[0]*Sp,t[1]*Sp]},r()}}function Ii(t){var n=0,e=Mp/3,r=Fi(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*Ep,e=t[1]*Ep):[n*Sp,e*Sp]},i}function Yi(t,n){function e(t,n){var e=Fp(o-2*i*Up(n))/i;return[e*Up(t*=i),u-e*Pp(t)]}var r=Up(t),i=(r+Up(n))/2;if(Ap(i)0?n<-Tp+bp&&(n=-Tp+bp):n>Tp-bp&&(n=Tp-bp);var e=o/Dp(Wi(n),i);return[e*Up(i*t),o-e*Pp(i*t)]}var r=Pp(t),i=t===n?Up(t):qp(r/Pp(n))/qp(Wi(n)/Wi(t)),o=r*Dp(Wi(t),i)/i;return i?(e.invert=function(t,n){var e=o-n,r=Op(i)*Fp(t*t+e*e);return[zp(t,Ap(e))/i*Op(e),2*Cp(Dp(o/r,1/i))-Tp]},e):Vi}function Gi(t,n){return[t,n]}function Qi(t,n){function e(t,n){var e=o-n,r=i*t;return[e*Up(r),o-e*Pp(r)]}var r=Pp(t),i=t===n?Up(t):(r-Pp(n))/(n-t),o=r/i+t;return Ap(i)=0;)n+=e[r].value;else n=1;t.value=n}function co(t,n){var e,r,i,o,u,a=new ho(t),c=+t.value&&(a.value=t.value),s=[a];for(null==n&&(n=so);e=s.pop();)if(c&&(e.value=+e.data.value),(i=n(e.data))&&(u=i.length))for(e.children=new Array(u),o=u-1;o>=0;--o)s.push(r=e.children[o]=new ho(i[o])),r.parent=e,r.depth=e.depth+1;return a.eachBefore(lo)}function so(t){return t.children}function fo(t){t.data=t.data.data}function lo(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function ho(t){this.data=t,this.depth=this.height=0,this.parent=null}function po(t){for(var n,e,r=0,i=(t=function(t){for(var n,e,r=t.length;r;)e=Math.random()*r--|0,n=t[r],t[r]=t[e],t[e]=n;return t}(Vd.call(t))).length,o=[];r0&&e*e>r*r+i*i}function _o(t,n){for(var e=0;ee*e+r*r}function wo(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function Mo(t){this._=t,this.next=null,this.previous=null}function To(t){if(!(i=t.length))return 0;var n,e,r,i,o,u,a,c,s,f,l;if(n=t[0],n.x=0,n.y=0,!(i>1))return n.r;if(e=t[1],n.x=-e.r,e.x=n.r,e.y=0,!(i>2))return n.r+e.r;xo(e,n,r=t[2]),n=new Mo(n),e=new Mo(e),r=new Mo(r),n.next=r.previous=e,e.next=n.previous=r,r.next=e.previous=n;t:for(a=3;ah&&(h=a),g=f*f*v,(p=Math.max(h/g,g/l))>d){f-=a;break}d=p}_.push(u={value:f,dice:c1&&jo(t[e[r-2]],t[e[r-1]],t[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function $o(t){this._size=t,this._call=this._error=null,this._tasks=[],this._data=[],this._waiting=this._active=this._ended=this._start=0}function Wo(t){if(!t._start)try{(function(t){for(;t._start=t._waiting&&t._active=0;)if((e=t._tasks[r])&&(t._tasks[r]=null,e.abort))try{e.abort()}catch(n){}t._active=NaN,Go(t)}function Go(t){if(!t._active&&t._call){var n=t._data;t._data=void 0,t._call(t._error,n)}}function Qo(t){if(null==t)t=1/0;else if(!((t=+t)>=1))throw new Error("invalid concurrency");return new $o(t)}function Jo(){return Math.random()}function Ko(t,n){function e(t){var n,e=s.status;if(!e&&function(t){var n=t.responseType;return n&&"text"!==n?t.response:t.responseText}(s)||e>=200&&e<300||304===e){if(o)try{n=o.call(r,s)}catch(t){return void a.call("error",r,t)}else n=s;a.call("load",r,n)}else a.call("error",r,t)}var r,i,o,u,a=N("beforesend","progress","load","error"),c=ae(),s=new XMLHttpRequest,f=null,l=null,h=0;if("undefined"==typeof XDomainRequest||"withCredentials"in s||!/^(http(s)?:)?\/\//.test(t)||(s=new XDomainRequest),"onload"in s?s.onload=s.onerror=s.ontimeout=e:s.onreadystatechange=function(t){s.readyState>3&&e(t)},s.onprogress=function(t){a.call("progress",r,t)},r={header:function(t,n){return t=(t+"").toLowerCase(),arguments.length<2?c.get(t):(null==n?c.remove(t):c.set(t,n+""),r)},mimeType:function(t){return arguments.length?(i=null==t?null:t+"",r):i},responseType:function(t){return arguments.length?(u=t,r):u},timeout:function(t){return arguments.length?(h=+t,r):h},user:function(t){return arguments.length<1?f:(f=null==t?null:t+"",r)},password:function(t){return arguments.length<1?l:(l=null==t?null:t+"",r)},response:function(t){return o=t,r},get:function(t,n){return r.send("GET",t,n)},post:function(t,n){return r.send("POST",t,n)},send:function(n,e,o){return s.open(n,t,!0,f,l),null==i||c.has("accept")||c.set("accept",i+",*/*"),s.setRequestHeader&&c.each(function(t,n){s.setRequestHeader(n,t)}),null!=i&&s.overrideMimeType&&s.overrideMimeType(i),null!=u&&(s.responseType=u),h>0&&(s.timeout=h),null==o&&"function"==typeof e&&(o=e,e=null),null!=o&&1===o.length&&(o=function(t){return function(n,e){t(null==n?e:null)}}(o)),null!=o&&r.on("error",o).on("load",function(t){o(null,t)}),a.call("beforesend",r,s),s.send(null==e?null:e),r},abort:function(){return s.abort(),r},on:function(){var t=a.on.apply(a,arguments);return t===a?r:t}},null!=n){if("function"!=typeof n)throw new Error("invalid callback: "+n);return r.get(n)}return r}function tu(t,n){return function(e,r){var i=Ko(e).mimeType(t).response(n);if(null!=r){if("function"!=typeof r)throw new Error("invalid callback: "+r);return i.get(r)}return i}}function nu(t,n){return function(e,r,i){arguments.length<3&&(i=r,r=null);var o=Ko(e).mimeType(t);return o.row=function(t){return arguments.length?o.response(function(t,n){return function(e){return t(e.responseText,n)}}(n,r=t)):r},o.row(r),i?o.get(i):o}}function eu(t){function n(n){var o=n+"",u=e.get(o);if(!u){if(i!==gv)return i;e.set(o,u=r.push(n))}return t[(u-1)%t.length]}var e=ae(),r=[],i=gv;return t=null==t?[]:vv.call(t),n.domain=function(t){if(!arguments.length)return r.slice();r=[],e=ae();for(var i,o,u=-1,a=t.length;++u2?su:cu,o=u=null,r}function r(n){return(o||(o=i(a,c,f?function(t){return function(n,e){var r=t(n=+n,e=+e);return function(t){return t<=n?0:t>=e?1:r(t)}}}(t):t,s)))(+n)}var i,o,u,a=_v,c=_v,s=cn,f=!1;return r.invert=function(t){return(u||(u=i(c,a,au,f?function(t){return function(n,e){var r=t(n=+n,e=+e);return function(t){return t<=0?n:t>=1?e:r(t)}}}(n):n)))(+t)},r.domain=function(t){return arguments.length?(a=dv.call(t,uu),e()):a.slice()},r.range=function(t){return arguments.length?(c=vv.call(t),e()):c.slice()},r.rangeRound=function(t){return c=vv.call(t),s=sn,e()},r.clamp=function(t){return arguments.length?(f=!!t,e()):f},r.interpolate=function(t){return arguments.length?(s=t,e()):s},e()}function hu(n){var e=n.domain;return n.ticks=function(t){var n=e();return l(n[0],n[n.length-1],null==t?10:t)},n.tickFormat=function(n,r){return function(n,e,r){var i,o=n[0],u=n[n.length-1],a=p(o,u,null==e?10:e);switch((r=Le(null==r?",f":r)).type){case"s":var c=Math.max(Math.abs(o),Math.abs(u));return null!=r.precision||isNaN(i=Ie(a,c))||(r.precision=i),t.formatPrefix(r,c);case"":case"e":case"g":case"p":case"r":null!=r.precision||isNaN(i=Ye(a,Math.max(Math.abs(o),Math.abs(u))))||(r.precision=i-("e"===r.type));break;case"f":case"%":null!=r.precision||isNaN(i=Fe(a))||(r.precision=i-2*("%"===r.type))}return t.format(r)}(e(),n,r)},n.nice=function(t){null==t&&(t=10);var r,i=e(),o=0,u=i.length-1,a=i[o],c=i[u];return c0?r=h(a=Math.floor(a/r)*r,c=Math.ceil(c/r)*r,t):r<0&&(r=h(a=Math.ceil(a*r)/r,c=Math.floor(c*r)/r,t)),r>0?(i[o]=Math.floor(a/r)*r,i[u]=Math.ceil(c/r)*r,e(i)):r<0&&(i[o]=Math.ceil(a*r)/r,i[u]=Math.floor(c*r)/r,e(i)),n},n}function pu(){var t=lu(au,on);return t.copy=function(){return fu(t,pu())},hu(t)}function du(){function t(t){return+t}var n=[0,1];return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=dv.call(e,uu),t):n.slice()},t.copy=function(){return du().domain(n)},hu(t)}function vu(t,n){var e,r=0,i=(t=t.slice()).length-1,o=t[r],u=t[i];return u0){for(;pc)break;g.push(h)}}else for(;p=1;--f)if(!((h=s*f)c)break;g.push(h)}}else g=l(p,d,Math.min(d-p,v)).map(u);return n?g.reverse():g},e.tickFormat=function(n,r){if(null==r&&(r=10===i?".0e":","),"function"!=typeof r&&(r=t.format(r)),n===1/0)return r;null==n&&(n=10);var a=Math.max(1,i*n/e.ticks().length);return function(t){var n=t/u(Math.round(o(t)));return n*i0?o[n-1]:r[0],n=i?[o[i-1],r]:[o[n-1],o[n]]},t.copy=function(){return ku().domain([e,r]).range(u)},hu(t)}function Su(){function t(t){if(t<=t)return e[Ds(n,t,0,r)]}var n=[.5],e=[0,1],r=1;return t.domain=function(i){return arguments.length?(n=vv.call(i),r=Math.min(n.length,e.length-1),t):n.slice()},t.range=function(i){return arguments.length?(e=vv.call(i),r=Math.min(n.length,e.length-1),t):e.slice()},t.invertExtent=function(t){var r=e.indexOf(t);return[n[r-1],n[r]]},t.copy=function(){return Su().domain(n).range(e)},t}function Eu(t,n,e,r){function i(n){return t(n=new Date(+n)),n}return i.floor=i,i.ceil=function(e){return t(e=new Date(e-1)),n(e,1),t(e),e},i.round=function(t){var n=i(t),e=i.ceil(t);return t-n0))return a;do{a.push(u=new Date(+e)),n(e,o),t(e)}while(u=n)for(;t(n),!e(n);)n.setTime(n-1)},function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););})},e&&(i.count=function(n,r){return yv.setTime(+n),mv.setTime(+r),t(yv),t(mv),Math.floor(e(yv,mv))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(n){return r(n)%t==0}:function(n){return i.count(0,n)%t==0}):i:null}),i}function Au(t){return Eu(function(n){n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+7*n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*wv)/Mv})}function Cu(t){return Eu(function(n){n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+7*n)},function(t,n){return(n-t)/Mv})}function zu(t){if(0<=t.y&&t.y<100){var n=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return n.setFullYear(t.y),n}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function Pu(t){if(0<=t.y&&t.y<100){var n=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return n.setUTCFullYear(t.y),n}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function Ru(t){return{y:t,m:0,d:1,H:0,M:0,S:0,L:0}}function Lu(t){function n(t,n){return function(e){var r,i,o,u=[],a=-1,c=0,s=t.length;for(e instanceof Date||(e=new Date(+e));++a53)return null;"w"in u||(u.w=1),"Z"in u?(i=(o=(i=Pu(Ru(u.y))).getUTCDay())>4||0===o?rg.ceil(i):rg(i),i=tg.offset(i,7*(u.V-1)),u.y=i.getUTCFullYear(),u.m=i.getUTCMonth(),u.d=i.getUTCDate()+(u.w+6)%7):(i=(o=(i=n(Ru(u.y))).getDay())>4||0===o?Rv.ceil(i):Rv(i),i=Cv.offset(i,7*(u.V-1)),u.y=i.getFullYear(),u.m=i.getMonth(),u.d=i.getDate()+(u.w+6)%7)}else("W"in u||"U"in u)&&("w"in u||(u.w="u"in u?u.u%7:"W"in u?1:0),o="Z"in u?Pu(Ru(u.y)).getUTCDay():n(Ru(u.y)).getDay(),u.m=0,u.d="W"in u?(u.w+6)%7+7*u.W-(o+5)%7:u.w+7*u.U-(o+6)%7);return"Z"in u?(u.H+=u.Z/100|0,u.M+=u.Z%100,Pu(u)):n(u)}}function r(t,n,e,r){for(var i,o,u=0,a=n.length,c=e.length;u=c)return-1;if(37===(i=n.charCodeAt(u++))){if(i=n.charAt(u++),!(o=T[i in bg?n.charAt(u++):i])||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}var i=t.dateTime,o=t.date,u=t.time,a=t.periods,c=t.days,s=t.shortDays,f=t.months,l=t.shortMonths,h=Uu(a),p=Ou(a),d=Uu(c),v=Ou(c),g=Uu(s),_=Ou(s),y=Uu(f),m=Ou(f),x=Uu(l),b=Ou(l),w={a:function(t){return s[t.getDay()]},A:function(t){return c[t.getDay()]},b:function(t){return l[t.getMonth()]},B:function(t){return f[t.getMonth()]},c:null,d:ia,e:ia,f:sa,H:oa,I:ua,j:aa,L:ca,m:fa,M:la,p:function(t){return a[+(t.getHours()>=12)]},Q:Fa,s:Ia,S:ha,u:pa,U:da,V:va,w:ga,W:_a,x:null,X:null,y:ya,Y:ma,Z:xa,"%":Oa},M={a:function(t){return s[t.getUTCDay()]},A:function(t){return c[t.getUTCDay()]},b:function(t){return l[t.getUTCMonth()]},B:function(t){return f[t.getUTCMonth()]},c:null,d:ba,e:ba,f:ka,H:wa,I:Ma,j:Ta,L:Na,m:Sa,M:Ea,p:function(t){return a[+(t.getUTCHours()>=12)]},Q:Fa,s:Ia,S:Aa,u:Ca,U:za,V:Pa,w:Ra,W:La,x:null,X:null,y:qa,Y:Da,Z:Ua,"%":Oa},T={a:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.w=_[r[0].toLowerCase()],e+r[0].length):-1},A:function(t,n,e){var r=d.exec(n.slice(e));return r?(t.w=v[r[0].toLowerCase()],e+r[0].length):-1},b:function(t,n,e){var r=x.exec(n.slice(e));return r?(t.m=b[r[0].toLowerCase()],e+r[0].length):-1},B:function(t,n,e){var r=y.exec(n.slice(e));return r?(t.m=m[r[0].toLowerCase()],e+r[0].length):-1},c:function(t,n,e){return r(t,i,n,e)},d:Wu,e:Wu,f:ta,H:Gu,I:Gu,j:Zu,L:Ku,m:$u,M:Qu,p:function(t,n,e){var r=h.exec(n.slice(e));return r?(t.p=p[r[0].toLowerCase()],e+r[0].length):-1},Q:ea,s:ra,S:Ju,u:Iu,U:Yu,V:Bu,w:Fu,W:Hu,x:function(t,n,e){return r(t,o,n,e)},X:function(t,n,e){return r(t,u,n,e)},y:Xu,Y:ju,Z:Vu,"%":na};return w.x=n(o,w),w.X=n(u,w),w.c=n(i,w),M.x=n(o,M),M.X=n(u,M),M.c=n(i,M),{format:function(t){var e=n(t+="",w);return e.toString=function(){return t},e},parse:function(t){var n=e(t+="",zu);return n.toString=function(){return t},n},utcFormat:function(t){var e=n(t+="",M);return e.toString=function(){return t},e},utcParse:function(t){var n=e(t,Pu);return n.toString=function(){return t},n}}}function qu(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o68?1900:2e3),e+r[0].length):-1}function Vu(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function $u(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function Wu(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function Zu(t,n,e){var r=wg.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function Gu(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function Qu(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function Ju(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function Ku(t,n,e){var r=wg.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function ta(t,n,e){var r=wg.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function na(t,n,e){var r=Mg.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function ea(t,n,e){var r=wg.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function ra(t,n,e){var r=wg.exec(n.slice(e));return r?(t.Q=1e3*+r[0],e+r[0].length):-1}function ia(t,n){return qu(t.getDate(),n,2)}function oa(t,n){return qu(t.getHours(),n,2)}function ua(t,n){return qu(t.getHours()%12||12,n,2)}function aa(t,n){return qu(1+Cv.count(Wv(t),t),n,3)}function ca(t,n){return qu(t.getMilliseconds(),n,3)}function sa(t,n){return ca(t,n)+"000"}function fa(t,n){return qu(t.getMonth()+1,n,2)}function la(t,n){return qu(t.getMinutes(),n,2)}function ha(t,n){return qu(t.getSeconds(),n,2)}function pa(t){var n=t.getDay();return 0===n?7:n}function da(t,n){return qu(Pv.count(Wv(t),t),n,2)}function va(t,n){var e=t.getDay();return t=e>=4||0===e?Dv(t):Dv.ceil(t),qu(Dv.count(Wv(t),t)+(4===Wv(t).getDay()),n,2)}function ga(t){return t.getDay()}function _a(t,n){return qu(Rv.count(Wv(t),t),n,2)}function ya(t,n){return qu(t.getFullYear()%100,n,2)}function ma(t,n){return qu(t.getFullYear()%1e4,n,4)}function xa(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+qu(n/60|0,"0",2)+qu(n%60,"0",2)}function ba(t,n){return qu(t.getUTCDate(),n,2)}function wa(t,n){return qu(t.getUTCHours(),n,2)}function Ma(t,n){return qu(t.getUTCHours()%12||12,n,2)}function Ta(t,n){return qu(1+tg.count(yg(t),t),n,3)}function Na(t,n){return qu(t.getUTCMilliseconds(),n,3)}function ka(t,n){return Na(t,n)+"000"}function Sa(t,n){return qu(t.getUTCMonth()+1,n,2)}function Ea(t,n){return qu(t.getUTCMinutes(),n,2)}function Aa(t,n){return qu(t.getUTCSeconds(),n,2)}function Ca(t){var n=t.getUTCDay();return 0===n?7:n}function za(t,n){return qu(eg.count(yg(t),t),n,2)}function Pa(t,n){var e=t.getUTCDay();return t=e>=4||0===e?ug(t):ug.ceil(t),qu(ug.count(yg(t),t)+(4===yg(t).getUTCDay()),n,2)}function Ra(t){return t.getUTCDay()}function La(t,n){return qu(rg.count(yg(t),t),n,2)}function qa(t,n){return qu(t.getUTCFullYear()%100,n,2)}function Da(t,n){return qu(t.getUTCFullYear()%1e4,n,4)}function Ua(){return"+0000"}function Oa(){return"%"}function Fa(t){return+t}function Ia(t){return Math.floor(+t/1e3)}function Ya(n){return mg=Lu(n),t.timeFormat=mg.format,t.timeParse=mg.parse,t.utcFormat=mg.utcFormat,t.utcParse=mg.utcParse,mg}function Ba(t){return new Date(t)}function Ha(t){return t instanceof Date?+t:+new Date(+t)}function ja(t,n,r,i,o,u,a,c,s){function f(e){return(a(e)=1?e_:t<=-1?-e_:Math.asin(t)}function Ga(t){return t.innerRadius}function Qa(t){return t.outerRadius}function Ja(t){return t.startAngle}function Ka(t){return t.endAngle}function tc(t){return t&&t.padAngle}function nc(t,n,e,r,i,o,u){var a=t-e,c=n-r,s=(u?o:-o)/Kg(a*a+c*c),f=s*c,l=-s*a,h=t+f,p=n+l,d=e+f,v=r+l,g=(h+d)/2,_=(p+v)/2,y=d-h,m=v-p,x=y*y+m*m,b=i-o,w=h*v-d*p,M=(m<0?-1:1)*Kg(Gg(0,b*b*x-w*w)),T=(w*m-y*M)/x,N=(-w*y-m*M)/x,k=(w*m+y*M)/x,S=(-w*y+m*M)/x,E=T-g,A=N-_,C=k-g,z=S-_;return E*E+A*A>C*C+z*z&&(T=k,N=S),{cx:T,cy:N,x01:-f,y01:-l,x11:T*(i/b-1),y11:N*(i/b-1)}}function ec(t){this._context=t}function rc(t){return new ec(t)}function ic(t){return t[0]}function oc(t){return t[1]}function uc(){function t(t){var a,c,s,f=t.length,l=!1;for(null==i&&(u=o(s=te())),a=0;a<=f;++a)!(a=f;--l)s.point(g[l],_[l]);s.lineEnd(),s.areaEnd()}v&&(g[n]=+e(h,n,t),_[n]=+i(h,n,t),s.point(r?+r(h,n,t):g[n],o?+o(h,n,t):_[n]))}if(p)return s=null,p+""||null}function n(){return uc().defined(u).curve(c).context(a)}var e=ic,r=null,i=Wa(0),o=oc,u=Wa(!0),a=null,c=rc,s=null;return t.x=function(n){return arguments.length?(e="function"==typeof n?n:Wa(+n),r=null,t):e},t.x0=function(n){return arguments.length?(e="function"==typeof n?n:Wa(+n),t):e},t.x1=function(n){return arguments.length?(r=null==n?null:"function"==typeof n?n:Wa(+n),t):r},t.y=function(n){return arguments.length?(i="function"==typeof n?n:Wa(+n),o=null,t):i},t.y0=function(n){return arguments.length?(i="function"==typeof n?n:Wa(+n),t):i},t.y1=function(n){return arguments.length?(o=null==n?null:"function"==typeof n?n:Wa(+n),t):o},t.lineX0=t.lineY0=function(){return n().x(e).y(i)},t.lineY1=function(){return n().x(e).y(o)},t.lineX1=function(){return n().x(r).y(i)},t.defined=function(n){return arguments.length?(u="function"==typeof n?n:Wa(!!n),t):u},t.curve=function(n){return arguments.length?(c=n,null!=a&&(s=c(a)),t):c},t.context=function(n){return arguments.length?(null==n?a=s=null:s=c(a=n),t):a},t}function cc(t,n){return nt?1:n>=t?0:NaN}function sc(t){return t}function fc(t){this._curve=t}function lc(t){function n(n){return new fc(t(n))}return n._curve=t,n}function hc(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(lc(t)):n()._curve},t}function pc(){return hc(uc().curve(i_))}function dc(){var t=ac().curve(i_),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return hc(e())},delete t.lineX0,t.lineEndAngle=function(){return hc(r())},delete t.lineX1,t.lineInnerRadius=function(){return hc(i())},delete t.lineY0,t.lineOuterRadius=function(){return hc(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(lc(t)):n()._curve},t}function vc(t,n){return[(n=+n)*Math.cos(t-=Math.PI/2),n*Math.sin(t)]}function gc(t){return t.source}function _c(t){return t.target}function yc(t){function n(){var n,a=o_.call(arguments),c=e.apply(this,a),s=r.apply(this,a);if(u||(u=n=te()),t(u,+i.apply(this,(a[0]=c,a)),+o.apply(this,a),+i.apply(this,(a[0]=s,a)),+o.apply(this,a)),n)return u=null,n+""||null}var e=gc,r=_c,i=ic,o=oc,u=null;return n.source=function(t){return arguments.length?(e=t,n):e},n.target=function(t){return arguments.length?(r=t,n):r},n.x=function(t){return arguments.length?(i="function"==typeof t?t:Wa(+t),n):i},n.y=function(t){return arguments.length?(o="function"==typeof t?t:Wa(+t),n):o},n.context=function(t){return arguments.length?(u=null==t?null:t,n):u},n}function mc(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n=(n+r)/2,e,n,i,r,i)}function xc(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n,e=(e+i)/2,r,e,r,i)}function bc(t,n,e,r,i){var o=vc(n,e),u=vc(n,e=(e+i)/2),a=vc(r,e),c=vc(r,i);t.moveTo(o[0],o[1]),t.bezierCurveTo(u[0],u[1],a[0],a[1],c[0],c[1])}function wc(){}function Mc(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function Tc(t){this._context=t}function Nc(t){this._context=t}function kc(t){this._context=t}function Sc(t,n){this._basis=new Tc(t),this._beta=n}function Ec(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function Ac(t,n){this._context=t,this._k=(1-n)/6}function Cc(t,n){this._context=t,this._k=(1-n)/6}function zc(t,n){this._context=t,this._k=(1-n)/6}function Pc(t,n,e){var r=t._x1,i=t._y1,o=t._x2,u=t._y2;if(t._l01_a>t_){var a=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*a-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*a-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>t_){var s=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,f=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*s+t._x1*t._l23_2a-n*t._l12_2a)/f,u=(u*s+t._y1*t._l23_2a-e*t._l12_2a)/f}t._context.bezierCurveTo(r,i,o,u,t._x2,t._y2)}function Rc(t,n){this._context=t,this._alpha=n}function Lc(t,n){this._context=t,this._alpha=n}function qc(t,n){this._context=t,this._alpha=n}function Dc(t){this._context=t}function Uc(t){return t<0?-1:1}function Oc(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),u=(e-t._y1)/(i||r<0&&-0),a=(o*i+u*r)/(r+i);return(Uc(o)+Uc(u))*Math.min(Math.abs(o),Math.abs(u),.5*Math.abs(a))||0}function Fc(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function Ic(t,n,e){var r=t._x0,i=t._y0,o=t._x1,u=t._y1,a=(o-r)/3;t._context.bezierCurveTo(r+a,i+a*n,o-a,u-a*e,o,u)}function Yc(t){this._context=t}function Bc(t){this._context=new Hc(t)}function Hc(t){this._context=t}function jc(t){this._context=t}function Xc(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),u=new Array(r);for(i[0]=0,o[0]=2,u[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(u[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n1)for(var e,r,i,o=1,u=t[n[0]],a=u.length;o=0;)e[n]=n;return e}function Zc(t,n){return t[n]}function Gc(t){var n=t.map(Qc);return Wc(t).sort(function(t,e){return n[t]-n[e]})}function Qc(t){for(var n,e=0,r=-1,i=t.length;++r0)){if(o/=h,h<0){if(o0){if(o>l)return;o>f&&(f=o)}if(o=r-c,h||!(o<0)){if(o/=h,h<0){if(o>l)return;o>f&&(f=o)}else if(h>0){if(o0)){if(o/=p,p<0){if(o0){if(o>l)return;o>f&&(f=o)}if(o=i-s,p||!(o<0)){if(o/=p,p<0){if(o>l)return;o>f&&(f=o)}else if(p>0){if(o0||l<1)||(f>0&&(t[0]=[c+f*h,s+f*p]),l<1&&(t[1]=[c+l*h,s+l*p]),!0)}}}}}function fs(t,n,e,r,i){var o=t[1];if(o)return!0;var u,a,c=t[0],s=t.left,f=t.right,l=s[0],h=s[1],p=f[0],d=f[1],v=(l+p)/2,g=(h+d)/2;if(d===h){if(v=r)return;if(l>p){if(c){if(c[1]>=i)return}else c=[v,e];o=[v,i]}else{if(c){if(c[1]1)if(l>p){if(c){if(c[1]>=i)return}else c=[(e-a)/u,e];o=[(i-a)/u,i]}else{if(c){if(c[1]=r)return}else c=[n,u*n+a];o=[r,u*r+a]}else{if(c){if(c[0]=-O_)){var p=c*c+s*s,d=f*f+l*l,v=(l*p-s*d)/h,g=(c*d-f*p)/h,_=q_.pop()||new function(){es(this),this.x=this.y=this.arc=this.site=this.cy=null};_.arc=t,_.site=i,_.x=v+u,_.y=(_.cy=g+a)+Math.sqrt(v*v+g*g),t.circle=_;for(var y=null,m=R_._;m;)if(_.yU_)a=a.L;else{if(!((i=o-function(t,n){var e=t.N;if(e)return xs(e,n);var r=t.site;return r[1]===n?r[0]:1/0}(a,u))>U_)){r>-U_?(n=a.P,e=a):i>-U_?(n=a,e=a.N):n=e=a;break}if(!a.R){n=a;break}a=a.R}(function(t){P_[t.index]={site:t,halfedges:[]}})(t);var c=gs(t);if(z_.insert(n,c),n||e){if(n===e)return vs(n),e=gs(n.site),z_.insert(c,e),c.edge=e.edge=us(n.site,c.site),ds(n),void ds(e);if(e){vs(n),vs(e);var s=n.site,f=s[0],l=s[1],h=t[0]-f,p=t[1]-l,d=e.site,v=d[0]-f,g=d[1]-l,_=2*(h*g-p*v),y=h*h+p*p,m=v*v+g*g,x=[(g*y-p*m)/_+f,(h*m-v*y)/_+l];cs(e.edge,s,d,x),c.edge=us(s,t,null,x),e.edge=us(t,d,null,x),ds(n),ds(e)}else c.edge=us(n.site,c.site)}}function xs(t,n){var e=t.site,r=e[0],i=e[1],o=i-n;if(!o)return r;var u=t.P;if(!u)return-1/0;var a=(e=u.site)[0],c=e[1],s=c-n;if(!s)return a;var f=a-r,l=1/o-1/s,h=f/s;return l?(-h+Math.sqrt(h*h-2*l*(f*f/(-2*s)-c+s/2+i-o/2)))/l+r:(r+a)/2}function bs(t,n,e){return(t[0]-e[0])*(n[1]-t[1])-(t[0]-n[0])*(e[1]-t[1])}function ws(t,n){return n[1]-t[1]||n[0]-t[0]}function Ms(t,n){var e,r,i,o=t.sort(ws).pop();for(L_=[],P_=new Array(t.length),z_=new ns,R_=new ns;;)if(i=C_,o&&(!i||o[1]U_||Math.abs(i[0][1]-i[1][1])>U_)||delete L_[o]})(u,a,c,s),function(t,n,e,r){var i,o,u,a,c,s,f,l,h,p,d,v,g=P_.length,_=!0;for(i=0;iU_||Math.abs(v-h)>U_)&&(c.splice(a,0,L_.push(as(u,p,Math.abs(d-t)U_?[t,Math.abs(l-t)U_?[Math.abs(h-r)U_?[e,Math.abs(l-e)U_?[Math.abs(h-n)r?(r+i)/2:Math.min(0,r)||Math.max(0,i),u>o?(o+u)/2:Math.min(0,o)||Math.max(0,u))}var qs=e(n),Ds=qs.right,Us=qs.left,Os=Array.prototype,Fs=Os.slice,Is=Os.map,Ys=Math.sqrt(50),Bs=Math.sqrt(10),Hs=Math.sqrt(2),js=Array.prototype.slice,Xs=1,Vs=2,$s=3,Ws=4,Zs=1e-6,Gs={value:function(){}};k.prototype=N.prototype={constructor:k,on:function(t,n){var e,r=this._,i=function(t,n){return t.trim().split(/^|\s+/).map(function(t){var e="",r=t.indexOf(".");if(r>=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}})}(t+"",r),o=-1,u=i.length;{if(!(arguments.length<2)){if(null!=n&&"function"!=typeof n)throw new Error("invalid callback: "+n);for(;++o0)for(var e,r,i=new Array(e),o=0;o=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var af=[null];st.prototype=ft.prototype={constructor:st,select:function(t){"function"!=typeof t&&(t=Y(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i=x&&(x=m+1);!(y=g[x])&&++x=0;)(r=i[o])&&(u&&u!==r.nextSibling&&u.parentNode.insertBefore(r,u),u=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=W);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?function(t){return function(){this.style.removeProperty(t)}}:"function"==typeof n?function(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}:function(t,n,e){return function(){this.style.setProperty(t,n,e)}})(t,n,null==e?"":e)):G(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?function(t){return function(){delete this[t]}}:"function"==typeof n?function(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}:function(t,n){return function(){this[t]=n}})(t,n)):this.node()[t]},classed:function(t,n){var e=Q(t+"");if(arguments.length<2){for(var r=J(this.node()),i=-1,o=e.length;++i=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}})}(t+""),u=o.length;if(!(arguments.length<2)){for(a=n?q:L,null==e&&(e=!1),r=0;r=240?t-240:t+120,i,r),qt(t,i,r),qt(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1}}));var xf=Math.PI/180,bf=180/Math.PI,wf=.95047,Mf=1,Tf=1.08883,Nf=4/29,kf=6/29,Sf=3*kf*kf,Ef=kf*kf*kf;Mt(Ot,Ut,Tt(Nt,{brighter:function(t){return new Ot(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new Ot(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,n=isNaN(this.a)?t:t+this.a/500,e=isNaN(this.b)?t:t-this.b/200;return t=Mf*It(t),n=wf*It(n),e=Tf*It(e),new zt(Yt(3.2404542*n-1.5371385*t-.4985314*e),Yt(-.969266*n+1.8760108*t+.041556*e),Yt(.0556434*n-.2040259*t+1.0572252*e),this.opacity)}})),Mt(jt,Ht,Tt(Nt,{brighter:function(t){return new jt(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new jt(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return Dt(this).rgb()}}));var Af=-.29227,Cf=-.90649,zf=1.97294,Pf=zf*Cf,Rf=1.78277*zf,Lf=1.78277*Af- -.14861*Cf;Mt(Vt,Xt,Tt(Nt,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new Vt(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new Vt(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*xf,n=+this.l,e=isNaN(this.s)?0:this.s*n*(1-n),r=Math.cos(t),i=Math.sin(t);return new zt(255*(n+e*(-.14861*r+1.78277*i)),255*(n+e*(Af*r+Cf*i)),255*(n+e*(zf*r)),this.opacity)}}));var qf,Df,Uf,Of,Ff,If,Yf=function t(n){function e(t,n){var e=r((t=Ct(t)).r,(n=Ct(n)).r),i=r(t.g,n.g),o=r(t.b,n.b),u=tn(t.opacity,n.opacity);return function(n){return t.r=e(n),t.g=i(n),t.b=o(n),t.opacity=u(n),t+""}}var r=Kt(n);return e.gamma=t,e}(1),Bf=nn(Wt),Hf=nn(Zt),jf=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Xf=new RegExp(jf.source,"g"),Vf=180/Math.PI,$f={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1},Wf=ln(function(t){return"none"===t?$f:(qf||(qf=document.createElement("DIV"),Df=document.documentElement,Uf=document.defaultView),qf.style.transform=t,t=Uf.getComputedStyle(Df.appendChild(qf),null).getPropertyValue("transform"),Df.removeChild(qf),t=t.slice(7,-1).split(","),fn(+t[0],+t[1],+t[2],+t[3],+t[4],+t[5]))},"px, ","px)","deg)"),Zf=ln(function(t){return null==t?$f:(Of||(Of=document.createElementNS("http://www.w3.org/2000/svg","g")),Of.setAttribute("transform",t),(t=Of.transform.baseVal.consolidate())?(t=t.matrix,fn(t.a,t.b,t.c,t.d,t.e,t.f)):$f)},", ",")",")"),Gf=Math.SQRT2,Qf=2,Jf=4,Kf=1e-12,tl=dn(Jt),nl=dn(tn),el=vn(Jt),rl=vn(tn),il=gn(Jt),ol=gn(tn),ul=0,al=0,cl=0,sl=1e3,fl=0,ll=0,hl=0,pl="object"==typeof performance&&performance.now?performance:Date,dl="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};mn.prototype=xn.prototype={constructor:mn,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?_n():+e)+(null==n?0:+n),this._next||If===this||(If?If._next=this:Ff=this,If=this),this._call=t,this._time=e,Tn()},stop:function(){this._call&&(this._call=null,this._time=1/0,Tn())}};var vl=N("start","end","interrupt"),gl=[],_l=0,yl=1,ml=2,xl=3,bl=4,wl=5,Ml=6,Tl=ft.prototype.constructor,Nl=0,kl=ft.prototype;Rn.prototype=Ln.prototype={constructor:Rn,select:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=Y(t));for(var r=this._groups,i=r.length,o=new Array(i),u=0;u=0&&(t=t.slice(0,n)),!t||"start"===t})}(n)?Sn:En;return function(){var u=o(this,t),a=u.on;a!==r&&(i=(r=a).copy()).on(n,e),u.on=i}}(e,t,n))},attr:function(t,n){var e=E(t),r="transform"===e?Zf:Pn;return this.attrTween(t,"function"==typeof n?(e.local?function(t,n,e){var r,i,o;return function(){var u,a=e(this);if(null!=a)return(u=this.getAttributeNS(t.space,t.local))===a?null:u===r&&a===i?o:o=n(r=u,i=a);this.removeAttributeNS(t.space,t.local)}}:function(t,n,e){var r,i,o;return function(){var u,a=e(this);if(null!=a)return(u=this.getAttribute(t))===a?null:u===r&&a===i?o:o=n(r=u,i=a);this.removeAttribute(t)}})(e,r,zn(this,"attr."+t,n)):null==n?(e.local?function(t){return function(){this.removeAttributeNS(t.space,t.local)}}:function(t){return function(){this.removeAttribute(t)}})(e):(e.local?function(t,n,e){var r,i;return function(){var o=this.getAttributeNS(t.space,t.local);return o===e?null:o===r?i:i=n(r=o,e)}}:function(t,n,e){var r,i;return function(){var o=this.getAttribute(t);return o===e?null:o===r?i:i=n(r=o,e)}})(e,r,n+""))},attrTween:function(t,n){var e="attr."+t;if(arguments.length<2)return(e=this.tween(e))&&e._value;if(null==n)return this.tween(e,null);if("function"!=typeof n)throw new Error;var r=E(t);return this.tween(e,(r.local?function(t,n){function e(){var e=this,r=n.apply(e,arguments);return r&&function(n){e.setAttributeNS(t.space,t.local,r(n))}}return e._value=n,e}:function(t,n){function e(){var e=this,r=n.apply(e,arguments);return r&&function(n){e.setAttribute(t,r(n))}}return e._value=n,e})(r,n))},style:function(t,n,e){var r="transform"==(t+="")?Wf:Pn;return null==n?this.styleTween(t,function(t,n){var e,r,i;return function(){var o=G(this,t),u=(this.style.removeProperty(t),G(this,t));return o===u?null:o===e&&u===r?i:i=n(e=o,r=u)}}(t,r)).on("end.style."+t,function(t){return function(){this.style.removeProperty(t)}}(t)):this.styleTween(t,"function"==typeof n?function(t,n,e){var r,i,o;return function(){var u=G(this,t),a=e(this);return null==a&&(this.style.removeProperty(t),a=G(this,t)),u===a?null:u===r&&a===i?o:o=n(r=u,i=a)}}(t,r,zn(this,"style."+t,n)):function(t,n,e){var r,i;return function(){var o=G(this,t);return o===e?null:o===r?i:i=n(r=o,e)}}(t,r,n+""),e)},styleTween:function(t,n,e){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==n)return this.tween(r,null);if("function"!=typeof n)throw new Error;return this.tween(r,function(t,n,e){function r(){var r=this,i=n.apply(r,arguments);return i&&function(n){r.style.setProperty(t,i(n),e)}}return r._value=n,r}(t,n,null==e?"":e))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var n=t(this);this.textContent=null==n?"":n}}(zn(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},remove:function(){return this.on("end.remove",function(t){return function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}}(this._id))},tween:function(t,n){var e=this._id;if(t+="",arguments.length<2){for(var r,i=An(this.node(),e).tween,o=0,u=i.length;o1e-6)if(Math.abs(f*a-c*s)>1e-6&&i){var h=e-o,p=r-u,d=a*a+c*c,v=h*h+p*p,g=Math.sqrt(d),_=Math.sqrt(l),y=i*Math.tan((gh-Math.acos((d+l-v)/(2*g*_)))/2),m=y/_,x=y/g;Math.abs(m-1)>1e-6&&(this._+="L"+(t+m*s)+","+(n+m*f)),this._+="A"+i+","+i+",0,0,"+ +(f*h>s*p)+","+(this._x1=t+x*a)+","+(this._y1=n+x*c)}else this._+="L"+(this._x1=t)+","+(this._y1=n);else;},arc:function(t,n,e,r,i,o){t=+t,n=+n;var u=(e=+e)*Math.cos(r),a=e*Math.sin(r),c=t+u,s=n+a,f=1^o,l=o?r-i:i-r;if(e<0)throw new Error("negative radius: "+e);null===this._x1?this._+="M"+c+","+s:(Math.abs(this._x1-c)>1e-6||Math.abs(this._y1-s)>1e-6)&&(this._+="L"+c+","+s),e&&(l<0&&(l=l%_h+_h),l>yh?this._+="A"+e+","+e+",0,1,"+f+","+(t-u)+","+(n-a)+"A"+e+","+e+",0,1,"+f+","+(this._x1=c)+","+(this._y1=s):l>1e-6&&(this._+="A"+e+","+e+",0,"+ +(l>=gh)+","+f+","+(this._x1=t+e*Math.cos(i))+","+(this._y1=n+e*Math.sin(i))))},rect:function(t,n,e,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+n)+"h"+ +e+"v"+ +r+"h"+-e+"Z"},toString:function(){return this._}};ue.prototype=ae.prototype={constructor:ue,has:function(t){return"$"+t in this},get:function(t){return this["$"+t]},set:function(t,n){return this["$"+t]=n,this},remove:function(t){var n="$"+t;return n in this&&delete this[n]},clear:function(){for(var t in this)"$"===t[0]&&delete this[t]},keys:function(){var t=[];for(var n in this)"$"===n[0]&&t.push(n.slice(1));return t},values:function(){var t=[];for(var n in this)"$"===n[0]&&t.push(this[n]);return t},entries:function(){var t=[];for(var n in this)"$"===n[0]&&t.push({key:n.slice(1),value:this[n]});return t},size:function(){var t=0;for(var n in this)"$"===n[0]&&++t;return t},empty:function(){for(var t in this)if("$"===t[0])return!1;return!0},each:function(t){for(var n in this)"$"===n[0]&&t(this[n],n.slice(1),this)}};var mh=ae.prototype;he.prototype=pe.prototype={constructor:he,has:mh.has,add:function(t){return t+="",this["$"+t]=t,this},remove:mh.remove,clear:mh.clear,values:mh.keys,size:mh.size,empty:mh.empty,each:mh.each};var xh={},bh={},wh=34,Mh=10,Th=13,Nh=ve(","),kh=Nh.parse,Sh=Nh.parseRows,Eh=Nh.format,Ah=Nh.formatRows,Ch=ve("\t"),zh=Ch.parse,Ph=Ch.parseRows,Rh=Ch.format,Lh=Ch.formatRows,qh=we.prototype=Me.prototype;qh.copy=function(){var t,n,e=new Me(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=Te(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=Te(n));return e},qh.add=function(t){var n=+this._x.call(null,t),e=+this._y.call(null,t);return ye(this.cover(n,e),n,e,t)},qh.addAll=function(t){var n,e,r,i,o=t.length,u=new Array(o),a=new Array(o),c=1/0,s=1/0,f=-1/0,l=-1/0;for(e=0;ef&&(f=r),il&&(l=i));for(ft||t>i||r>n||n>o))return this;var u,a,c=i-e,s=this._root;switch(a=(n<(r+o)/2)<<1|t<(e+i)/2){case 0:do{u=new Array(4),u[a]=s,s=u}while(c*=2,i=e+c,o=r+c,t>i||n>o);break;case 1:do{u=new Array(4),u[a]=s,s=u}while(c*=2,e=i-c,o=r+c,e>t||n>o);break;case 2:do{u=new Array(4),u[a]=s,s=u}while(c*=2,i=e+c,r=o-c,t>i||r>n);break;case 3:do{u=new Array(4),u[a]=s,s=u}while(c*=2,e=i-c,r=o-c,e>t||r>n)}this._root&&this._root.length&&(this._root=s)}return this._x0=e,this._y0=r,this._x1=i,this._y1=o,this},qh.data=function(){var t=[];return this.visit(function(n){if(!n.length)do{t.push(n.data)}while(n=n.next)}),t},qh.extent=function(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]},qh.find=function(t,n,e){var r,i,o,u,a,c,s,f=this._x0,l=this._y0,h=this._x1,p=this._y1,d=[],v=this._root;for(v&&d.push(new me(v,f,l,h,p)),null==e?e=1/0:(f=t-e,l=n-e,h=t+e,p=n+e,e*=e);c=d.pop();)if(!(!(v=c.node)||(i=c.x0)>h||(o=c.y0)>p||(u=c.x1)=_)<<1|t>=g)&&(c=d[d.length-1],d[d.length-1]=d[d.length-1-s],d[d.length-1-s]=c)}else{var y=t-+this._x.call(null,v.data),m=n-+this._y.call(null,v.data),x=y*y+m*m;if(x=(a=(d+g)/2))?d=a:g=a,(f=u>=(c=(v+_)/2))?v=c:_=c,n=p,!(p=p[l=f<<1|s]))return this;if(!p.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;p.data!==t;)if(r=p,!(p=p.next))return this;return(i=p.next)&&delete p.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(p=n[0]||n[1]||n[2]||n[3])&&p===(n[3]||n[2]||n[1]||n[0])&&!p.length&&(e?e[h]=p:this._root=p),this):(this._root=i,this)},qh.removeAll=function(t){for(var n=0,e=t.length;n0&&(o=0)}return o>0?t.slice(0,o)+t.slice(e+1):t},"%":function(t,n){return(100*t).toFixed(n)},b:function(t){return Math.round(t).toString(2)},c:function(t){return t+""},d:function(t){return Math.round(t).toString(10)},e:function(t,n){return t.toExponential(n)},f:function(t,n){return t.toFixed(n)},g:function(t,n){return t.toPrecision(n)},o:function(t){return Math.round(t).toString(8)},p:function(t,n){return Re(100*t,n)},r:Re,s:function(t,n){var e=ze(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(Dh=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,u=r.length;return o===u?r:o>u?r+new Array(o-u+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+ze(t,Math.max(0,n+o-1))[0]},X:function(t){return Math.round(t).toString(16).toUpperCase()},x:function(t){return Math.round(t).toString(16)}},Ih=/^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$/i;Le.prototype=qe.prototype,qe.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(null==this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(null==this.precision?"":"."+Math.max(0,0|this.precision))+this.type};var Yh,Bh=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];Oe({decimal:".",thousands:",",grouping:[3],currency:["$",""]}),He.prototype={constructor:He,reset:function(){this.s=this.t=0},add:function(t){je(xp,t,this.t),je(this,xp.s,this.s),this.s?this.t+=xp.t:this.s=xp.t},valueOf:function(){return this.s}};var Hh,jh,Xh,Vh,$h,Wh,Zh,Gh,Qh,Jh,Kh,tp,np,ep,rp,ip,op,up,ap,cp,sp,fp,lp,hp,pp,dp,vp,gp,_p,yp,mp,xp=new He,bp=1e-6,wp=1e-12,Mp=Math.PI,Tp=Mp/2,Np=Mp/4,kp=2*Mp,Sp=180/Mp,Ep=Mp/180,Ap=Math.abs,Cp=Math.atan,zp=Math.atan2,Pp=Math.cos,Rp=Math.ceil,Lp=Math.exp,qp=Math.log,Dp=Math.pow,Up=Math.sin,Op=Math.sign||function(t){return t>0?1:t<0?-1:0},Fp=Math.sqrt,Ip=Math.tan,Yp={Feature:function(t,n){Ze(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++rbp?Qh=90:Vp<-bp&&(Zh=-90),rp[0]=Wh,rp[1]=Gh}},Wp={sphere:We,point:xr,lineStart:wr,lineEnd:Nr,polygonStart:function(){Wp.lineStart=kr,Wp.lineEnd=Sr},polygonEnd:function(){Wp.lineStart=wr,Wp.lineEnd=Nr}};Pr.invert=Pr;var Zp,Gp,Qp,Jp,Kp,td,nd,ed,rd,id,od,ud=Be(),ad=Vr(function(){return!0},function(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,u){var a=o>0?Mp:-Mp,c=Ap(o-e);Ap(c-Mp)0?Tp:-Tp),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(a,r),t.point(o,r),n=0):i!==a&&c>=Mp&&(Ap(e-i)bp?Cp((Up(n)*(o=Pp(r))*Up(e)-Up(r)*(i=Pp(n))*Up(t))/(i*o*u)):(n+r)/2}(e,r,o,u),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(a,r),n=0),t.point(e=o,r=u),i=a},lineEnd:function(){t.lineEnd(),e=r=NaN},clean:function(){return 2-n}}},function(t,n,e,r){var i;if(null==t)i=e*Tp,r.point(-Mp,i),r.point(0,i),r.point(Mp,i),r.point(Mp,0),r.point(Mp,-i),r.point(0,-i),r.point(-Mp,-i),r.point(-Mp,0),r.point(-Mp,i);else if(Ap(t[0]-n[0])>bp){var o=t[0]bd&&(bd=t),nwd&&(wd=n)},lineStart:We,lineEnd:We,polygonStart:We,polygonEnd:We,result:function(){var t=[[md,xd],[bd,wd]];return bd=wd=-(xd=md=1/0),t}},Td=0,Nd=0,kd=0,Sd=0,Ed=0,Ad=0,Cd=0,zd=0,Pd=0,Rd={point:gi,lineStart:_i,lineEnd:xi,polygonStart:function(){Rd.lineStart=bi,Rd.lineEnd=wi},polygonEnd:function(){Rd.point=gi,Rd.lineStart=_i,Rd.lineEnd=xi},result:function(){var t=Pd?[Cd/Pd,zd/Pd]:Ad?[Sd/Ad,Ed/Ad]:kd?[Td/kd,Nd/kd]:[NaN,NaN];return Td=Nd=kd=Sd=Ed=Ad=Cd=zd=Pd=0,t}};Ni.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._context.moveTo(t,n),this._point=1;break;case 1:this._context.lineTo(t,n);break;default:this._context.moveTo(t+this._radius,n),this._context.arc(t,n,this._radius,0,kp)}},result:We};var Ld,qd,Dd,Ud,Od,Fd=Be(),Id={point:We,lineStart:function(){Id.point=ki},lineEnd:function(){Ld&&Si(qd,Dd),Id.point=We},polygonStart:function(){Ld=!0},polygonEnd:function(){Ld=null},result:function(){var t=+Fd;return Fd.reset(),t}};Ei.prototype={_radius:4.5,_circle:Ai(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._string.push("M",t,",",n),this._point=1;break;case 1:this._string.push("L",t,",",n);break;default:null==this._circle&&(this._circle=Ai(this._radius)),this._string.push("M",t,",",n,this._circle)}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}return null}},zi.prototype={constructor:zi,point:function(t,n){this.stream.point(t,n)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var Yd=16,Bd=Pp(30*Ep),Hd=Ci({point:function(t,n){this.stream.point(t*Ep,n*Ep)}}),jd=ji(function(t){return Fp(2/(1+t))});jd.invert=Xi(function(t){return 2*Ve(t/2)});var Xd=ji(function(t){return(t=Xe(t))&&t/Up(t)});Xd.invert=Xi(function(t){return t}),Vi.invert=function(t,n){return[t,2*Cp(Lp(n))-Tp]},Gi.invert=Gi,Ji.invert=Xi(Cp),to.invert=function(t,n){var e,r=n,i=25;do{var o=r*r,u=o*o;r-=e=(r*(1.007226+o*(.015085+u*(.028874*o-.044475-.005916*u)))-n)/(1.007226+o*(.045255+u*(.259866*o-.311325-.005916*11*u)))}while(Ap(e)>bp&&--i>0);return[t/(.8707+(o=r*r)*(o*(o*o*o*(.003971-.001529*o)-.013791)-.131979)),r]},no.invert=Xi(Ve),eo.invert=Xi(function(t){return 2*Cp(t)}),ro.invert=function(t,n){return[-n,2*Cp(Lp(t))-Tp]},ho.prototype=co.prototype={constructor:ho,count:function(){return this.eachAfter(ao)},each:function(t){var n,e,r,i,o=this,u=[o];do{for(n=u.reverse(),u=[];o=n.pop();)if(t(o),e=o.children)for(r=0,i=e.length;r=0;--e)i.push(n[e]);return this},sum:function(t){return this.eachAfter(function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e})},sort:function(t){return this.eachBefore(function(n){n.children&&n.children.sort(t)})},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;for(t=e.pop(),n=r.pop();t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){var t=[];return this.each(function(n){t.push(n)}),t},leaves:function(){var t=[];return this.eachBefore(function(n){n.children||t.push(n)}),t},links:function(){var t=this,n=[];return t.each(function(e){e!==t&&n.push({source:e.parent,target:e})}),n},copy:function(){return co(this).eachBefore(fo)}};var Vd=Array.prototype.slice,$d="$",Wd={depth:-1},Zd={};Yo.prototype=Object.create(ho.prototype);var Gd=(1+Math.sqrt(5))/2,Qd=function t(n){function e(t,e,r,i,o){Ho(n,t,e,r,i,o)}return e.ratio=function(n){return t((n=+n)>1?n:1)},e}(Gd),Jd=function t(n){function e(t,e,r,i,o){if((u=t._squarify)&&u.ratio===n)for(var u,a,c,s,f,l=-1,h=u.length,p=t.value;++l1?n:1)},e}(Gd),Kd=[].slice,tv={};$o.prototype=Qo.prototype={constructor:$o,defer:function(t){if("function"!=typeof t)throw new Error("invalid callback");if(this._call)throw new Error("defer after await");if(null!=this._error)return this;var n=Kd.call(arguments,1);return n.push(t),++this._waiting,this._tasks.push(n),Wo(this),this},abort:function(){return null==this._error&&Zo(this,new Error("abort")),this},await:function(t){if("function"!=typeof t)throw new Error("invalid callback");if(this._call)throw new Error("multiple await");return this._call=function(n,e){t.apply(null,[n].concat(e))},Go(this),this},awaitAll:function(t){if("function"!=typeof t)throw new Error("invalid callback");if(this._call)throw new Error("multiple await");return this._call=t,Go(this),this}};var nv=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,1===arguments.length?(e=t,t=0):e-=t,function(){return n()*e+t}}return e.source=t,e}(Jo),ev=function t(n){function e(t,e){var r,i;return t=null==t?0:+t,e=null==e?1:+e,function(){var o;if(null!=r)o=r,r=null;else do{r=2*n()-1,o=2*n()-1,i=r*r+o*o}while(!i||i>1);return t+e*o*Math.sqrt(-2*Math.log(i)/i)}}return e.source=t,e}(Jo),rv=function t(n){function e(){var t=ev.source(n).apply(this,arguments);return function(){return Math.exp(t())}}return e.source=t,e}(Jo),iv=function t(n){function e(t){return function(){for(var e=0,r=0;r0?t>1?Eu(function(n){n.setTime(Math.floor(n/t)*t)},function(n,e){n.setTime(+n+e*t)},function(n,e){return(e-n)/t}):xv:null};var bv=xv.range,wv=6e4,Mv=6048e5,Tv=Eu(function(t){t.setTime(1e3*Math.floor(t/1e3))},function(t,n){t.setTime(+t+1e3*n)},function(t,n){return(n-t)/1e3},function(t){return t.getUTCSeconds()}),Nv=Tv.range,kv=Eu(function(t){t.setTime(Math.floor(t/wv)*wv)},function(t,n){t.setTime(+t+n*wv)},function(t,n){return(n-t)/wv},function(t){return t.getMinutes()}),Sv=kv.range,Ev=Eu(function(t){var n=t.getTimezoneOffset()*wv%36e5;n<0&&(n+=36e5),t.setTime(36e5*Math.floor((+t-n)/36e5)+n)},function(t,n){t.setTime(+t+36e5*n)},function(t,n){return(n-t)/36e5},function(t){return t.getHours()}),Av=Ev.range,Cv=Eu(function(t){t.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*wv)/864e5},function(t){return t.getDate()-1}),zv=Cv.range,Pv=Au(0),Rv=Au(1),Lv=Au(2),qv=Au(3),Dv=Au(4),Uv=Au(5),Ov=Au(6),Fv=Pv.range,Iv=Rv.range,Yv=Lv.range,Bv=qv.range,Hv=Dv.range,jv=Uv.range,Xv=Ov.range,Vv=Eu(function(t){t.setDate(1),t.setHours(0,0,0,0)},function(t,n){t.setMonth(t.getMonth()+n)},function(t,n){return n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())},function(t){return t.getMonth()}),$v=Vv.range,Wv=Eu(function(t){t.setMonth(0,1),t.setHours(0,0,0,0)},function(t,n){t.setFullYear(t.getFullYear()+n)},function(t,n){return n.getFullYear()-t.getFullYear()},function(t){return t.getFullYear()});Wv.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Eu(function(n){n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)},function(n,e){n.setFullYear(n.getFullYear()+e*t)}):null};var Zv=Wv.range,Gv=Eu(function(t){t.setUTCSeconds(0,0)},function(t,n){t.setTime(+t+n*wv)},function(t,n){return(n-t)/wv},function(t){return t.getUTCMinutes()}),Qv=Gv.range,Jv=Eu(function(t){t.setUTCMinutes(0,0,0)},function(t,n){t.setTime(+t+36e5*n)},function(t,n){return(n-t)/36e5},function(t){return t.getUTCHours()}),Kv=Jv.range,tg=Eu(function(t){t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+n)},function(t,n){return(n-t)/864e5},function(t){return t.getUTCDate()-1}),ng=tg.range,eg=Cu(0),rg=Cu(1),ig=Cu(2),og=Cu(3),ug=Cu(4),ag=Cu(5),cg=Cu(6),sg=eg.range,fg=rg.range,lg=ig.range,hg=og.range,pg=ug.range,dg=ag.range,vg=cg.range,gg=Eu(function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCMonth(t.getUTCMonth()+n)},function(t,n){return n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())},function(t){return t.getUTCMonth()}),_g=gg.range,yg=Eu(function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCFullYear(t.getUTCFullYear()+n)},function(t,n){return n.getUTCFullYear()-t.getUTCFullYear()},function(t){return t.getUTCFullYear()});yg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Eu(function(n){n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)},function(n,e){n.setUTCFullYear(n.getUTCFullYear()+e*t)}):null};var mg,xg=yg.range,bg={"-":"",_:" ",0:"0"},wg=/^\s*\d+/,Mg=/^%/,Tg=/[\\^$*+?|[\]().{}]/g;Ya({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var Ng="%Y-%m-%dT%H:%M:%S.%LZ",kg=Date.prototype.toISOString?function(t){return t.toISOString()}:t.utcFormat(Ng),Sg=+new Date("2000-01-01T00:00:00.000Z")?function(t){var n=new Date(t);return isNaN(n)?null:n}:t.utcParse(Ng),Eg=1e3,Ag=60*Eg,Cg=60*Ag,zg=24*Cg,Pg=7*zg,Rg=30*zg,Lg=365*zg,qg=Xa("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"),Dg=Xa("393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6"),Ug=Xa("3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9"),Og=Xa("1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5"),Fg=ol(Xt(300,.5,0),Xt(-240,.5,1)),Ig=ol(Xt(-100,.75,.35),Xt(80,1.5,.8)),Yg=ol(Xt(260,.75,.35),Xt(80,1.5,.8)),Bg=Xt(),Hg=Va(Xa("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),jg=Va(Xa("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),Xg=Va(Xa("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),Vg=Va(Xa("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921")),$g=Math.abs,Wg=Math.atan2,Zg=Math.cos,Gg=Math.max,Qg=Math.min,Jg=Math.sin,Kg=Math.sqrt,t_=1e-12,n_=Math.PI,e_=n_/2,r_=2*n_;ec.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}};var i_=lc(rc);fc.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};var o_=Array.prototype.slice,u_={draw:function(t,n){var e=Math.sqrt(n/n_);t.moveTo(e,0),t.arc(0,0,e,0,r_)}},a_={draw:function(t,n){var e=Math.sqrt(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}},c_=Math.sqrt(1/3),s_=2*c_,f_={draw:function(t,n){var e=Math.sqrt(n/s_),r=e*c_;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},l_=Math.sin(n_/10)/Math.sin(7*n_/10),h_=Math.sin(r_/10)*l_,p_=-Math.cos(r_/10)*l_,d_={draw:function(t,n){var e=Math.sqrt(.8908130915292852*n),r=h_*e,i=p_*e;t.moveTo(0,-e),t.lineTo(r,i);for(var o=1;o<5;++o){var u=r_*o/5,a=Math.cos(u),c=Math.sin(u);t.lineTo(c*e,-a*e),t.lineTo(a*r-c*i,c*r+a*i)}t.closePath()}},v_={draw:function(t,n){var e=Math.sqrt(n),r=-e/2;t.rect(r,r,e,e)}},g_=Math.sqrt(3),__={draw:function(t,n){var e=-Math.sqrt(n/(3*g_));t.moveTo(0,2*e),t.lineTo(-g_*e,-e),t.lineTo(g_*e,-e),t.closePath()}},y_=Math.sqrt(3)/2,m_=1/Math.sqrt(12),x_=3*(m_/2+1),b_={draw:function(t,n){var e=Math.sqrt(n/x_),r=e/2,i=e*m_,o=r,u=e*m_+e,a=-o,c=u;t.moveTo(r,i),t.lineTo(o,u),t.lineTo(a,c),t.lineTo(-.5*r-y_*i,y_*r+-.5*i),t.lineTo(-.5*o-y_*u,y_*o+-.5*u),t.lineTo(-.5*a-y_*c,y_*a+-.5*c),t.lineTo(-.5*r+y_*i,-.5*i-y_*r),t.lineTo(-.5*o+y_*u,-.5*u-y_*o),t.lineTo(-.5*a+y_*c,-.5*c-y_*a),t.closePath()}},w_=[u_,a_,f_,v_,d_,__,b_];Tc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Mc(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Mc(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Nc.prototype={areaStart:wc,areaEnd:wc,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:Mc(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},kc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:Mc(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Sc.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],u=t[e]-i,a=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*u),this._beta*n[c]+(1-this._beta)*(o+r*a));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var M_=function t(n){function e(t){return 1===n?new Tc(t):new Sc(t,n)}return e.beta=function(n){return t(+n)},e}(.85);Ac.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Ec(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:Ec(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var T_=function t(n){function e(t){return new Ac(t,n)}return e.tension=function(n){return t(+n)},e}(0);Cc.prototype={areaStart:wc,areaEnd:wc,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Ec(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var N_=function t(n){function e(t){return new Cc(t,n)}return e.tension=function(n){return t(+n)},e}(0);zc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Ec(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var k_=function t(n){function e(t){return new zc(t,n)}return e.tension=function(n){return t(+n)},e}(0);Rc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3;default:Pc(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var S_=function t(n){function e(t){return n?new Rc(t,n):new Ac(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);Lc.prototype={areaStart:wc,areaEnd:wc,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Pc(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var E_=function t(n){function e(t){return n?new Lc(t,n):new Cc(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);qc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Pc(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var A_=function t(n){function e(t){return n?new qc(t,n):new zc(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);Dc.prototype={areaStart:wc,areaEnd:wc,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,n){t=+t,n=+n,this._point?this._context.lineTo(t,n):(this._point=1,this._context.moveTo(t,n))}},Yc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:Ic(this,this._t0,Fc(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){var e=NaN;if(t=+t,n=+n,t!==this._x1||n!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,Ic(this,Fc(this,e=Oc(this,t,n)),e);break;default:Ic(this,this._t0,e=Oc(this,t,n))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n,this._t0=e}}},(Bc.prototype=Object.create(Yc.prototype)).point=function(t,n){Yc.prototype.point.call(this,n,t)},Hc.prototype={moveTo:function(t,n){this._context.moveTo(n,t)},closePath:function(){this._context.closePath()},lineTo:function(t,n){this._context.lineTo(n,t)},bezierCurveTo:function(t,n,e,r,i,o){this._context.bezierCurveTo(n,t,r,e,o,i)}},jc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,n=this._y,e=t.length;if(e)if(this._line?this._context.lineTo(t[0],n[0]):this._context.moveTo(t[0],n[0]),2===e)this._context.lineTo(t[1],n[1]);else for(var r=Xc(t),i=Xc(n),o=0,u=1;u=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}},ns.prototype={constructor:ns,insert:function(t,n){var e,r,i;if(t){if(n.P=t,n.N=t.N,t.N&&(t.N.P=n),t.N=n,t.R){for(t=t.R;t.L;)t=t.L;t.L=n}else t.R=n;e=t}else this._?(t=os(this._),n.P=null,n.N=t,t.P=t.L=n,e=t):(n.P=n.N=null,this._=n,e=null);for(n.L=n.R=null,n.U=e,n.C=!0,t=n;e&&e.C;)e===(r=e.U).L?(i=r.R)&&i.C?(e.C=i.C=!1,r.C=!0,t=r):(t===e.R&&(rs(this,e),e=(t=e).U),e.C=!1,r.C=!0,is(this,r)):(i=r.L)&&i.C?(e.C=i.C=!1,r.C=!0,t=r):(t===e.L&&(is(this,e),e=(t=e).U),e.C=!1,r.C=!0,rs(this,r)),e=t.U;this._.C=!1},remove:function(t){t.N&&(t.N.P=t.P),t.P&&(t.P.N=t.N),t.N=t.P=null;var n,e,r,i=t.U,o=t.L,u=t.R;if(e=o?u?os(u):o:u,i?i.L===t?i.L=e:i.R=e:this._=e,o&&u?(r=e.C,e.C=t.C,e.L=o,o.U=e,e!==u?(i=e.U,e.U=t.U,t=e.R,i.L=t,e.R=u,u.U=e):(e.U=i,i=e,t=e.R)):(r=t.C,t=e),t&&(t.U=i),!r)if(t&&t.C)t.C=!1;else{do{if(t===this._)break;if(t===i.L){if((n=i.R).C&&(n.C=!1,i.C=!0,rs(this,i),n=i.R),n.L&&n.L.C||n.R&&n.R.C){n.R&&n.R.C||(n.L.C=!1,n.C=!0,is(this,n),n=i.R),n.C=i.C,i.C=n.R.C=!1,rs(this,i),t=this._;break}}else if((n=i.L).C&&(n.C=!1,i.C=!0,is(this,i),n=i.L),n.L&&n.L.C||n.R&&n.R.C){n.L&&n.L.C||(n.R.C=!1,n.C=!0,rs(this,n),n=i.L),n.C=i.C,i.C=n.L.C=!1,is(this,i),t=this._;break}n.C=!0,t=i,i=i.U}while(!t.C);t&&(t.C=!1)}}};var C_,z_,P_,R_,L_,q_=[],D_=[],U_=1e-6,O_=1e-12;Ms.prototype={constructor:Ms,polygons:function(){var t=this.edges;return this.cells.map(function(n){var e=n.halfedges.map(function(e){return hs(n,t[e])});return e.data=n.site.data,e})},triangles:function(){var t=[],n=this.edges;return this.cells.forEach(function(e,r){if(o=(i=e.halfedges).length)for(var i,o,u,a=e.site,c=-1,s=n[i[o-1]],f=s.left===a?s.right:s.left;++c=a)return null;var c=t-i.site[0],s=n-i.site[1],f=c*c+s*s;do{i=o.cells[r=u],u=null,i.halfedges.forEach(function(e){var r=o.edges[e],a=r.left;if(a!==i.site&&a||(a=r.right)){var c=t-a[0],s=n-a[1],l=c*c+s*s;lt?1:n>=t?0:NaN},t.deviation=u,t.extent=a,t.histogram=function(){function t(t){var i,o,u=t.length,a=new Array(u);for(i=0;il;)h.pop(),--d;var v,g=new Array(d+1);for(i=0;i<=d;++i)(v=g[i]=[]).x0=i>0?h[i-1]:s,v.x1=i=e)for(r=e;++or&&(r=e)}else for(;++o=e)for(r=e;++or&&(r=e);return r},t.mean=function(t,n){var e,r=t.length,o=r,u=-1,a=0;if(null==n)for(;++u=o.length)return null!=e&&n.sort(e),null!=r?r(n):n;for(var c,s,f,l=-1,h=n.length,p=o[i++],d=ae(),v=u();++lo.length)return t;var i,a=u[e-1];return null!=r&&e>=o.length?i=t.entries():(i=[],t.each(function(t,r){i.push({key:r,values:n(t,e)})})),null!=a?i.sort(function(t,n){return a(t.key,n.key)}):i}var e,r,i,o=[],u=[];return i={object:function(n){return t(n,0,ce,se)},map:function(n){return t(n,0,fe,le)},entries:function(e){return n(t(e,0,fe,le),0)},key:function(t){return o.push(t),i},sortKeys:function(t){return u[o.length-1]=t,i},sortValues:function(t){return e=t,i},rollup:function(t){return r=t,i}}},t.set=pe,t.map=ae,t.keys=function(t){var n=[];for(var e in t)n.push(e);return n},t.values=function(t){var n=[];for(var e in t)n.push(t[e]);return n},t.entries=function(t){var n=[];for(var e in t)n.push({key:e,value:t[e]});return n},t.color=kt,t.rgb=Ct,t.hsl=Rt,t.lab=Ut,t.hcl=Ht,t.cubehelix=Xt,t.dispatch=N,t.drag=function(){function n(t){t.on("mousedown.drag",e).filter(g).on("touchstart.drag",o).on("touchmove.drag",u).on("touchend.drag touchcancel.drag",a).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function e(){if(!h&&p.apply(this,arguments)){var n=c("mouse",d.apply(this,arguments),F,this,arguments);n&&(lt(t.event.view).on("mousemove.drag",r,!0).on("mouseup.drag",i,!0),vt(t.event.view),pt(),l=!1,s=t.event.clientX,f=t.event.clientY,n("start"))}}function r(){if(dt(),!l){var n=t.event.clientX-s,e=t.event.clientY-f;l=n*n+e*e>x}_.mouse("drag")}function i(){lt(t.event.view).on("mousemove.drag mouseup.drag",null),gt(t.event.view,l),dt(),_.mouse("end")}function o(){if(p.apply(this,arguments)){var n,e,r=t.event.changedTouches,i=d.apply(this,arguments),o=r.length;for(n=0;nc+p||is+p||or.index){var d=c-a.x-a.vx,v=s-a.y-a.vy,g=d*d+v*v;gt.r&&(t.r=t[n].r)}function r(){if(i){var n,e,r=i.length;for(o=new Array(r),n=0;n=f)){(t.data!==o||t.next)&&(0===i&&(i=_e(),p+=i*i),0===c&&(c=_e(),p+=c*c),p1?(null==n?l.remove(t):l.set(t,i(n)),o):l.get(t)},find:function(n,e,r){var i,o,u,a,c,s=0,f=t.length;for(null==r?r=1/0:r*=r,s=0;s1?(p.on(t,n),o):p.on(t)}}},t.forceX=function(t){function n(t){for(var n,e=0,u=r.length;e_r(r[0],r[1])&&(r[1]=i[1]),_r(i[0],r[1])>_r(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(u=-1/0,n=0,r=o[e=o.length-1];n<=e;r=i,++n)i=o[n],(a=_r(r[1],i[0]))>u&&(u=a,Wh=i[0],Gh=r[1])}return ep=rp=null,Wh===1/0||Zh===1/0?[[NaN,NaN],[NaN,NaN]]:[[Wh,Zh],[Gh,Qh]]},t.geoCentroid=function(t){ip=op=up=ap=cp=sp=fp=lp=hp=pp=dp=0,Je(t,Wp);var n=hp,e=pp,r=dp,i=n*n+e*e+r*r;return i=.12&&i<.234&&r>=-.425&&r<-.214?s:i>=.166&&i<.234&&r>=-.214&&r<-.115?f:c).invert(t)},t.stream=function(t){return e&&r===t?e:e=function(t){var n=t.length;return{point:function(e,r){for(var i=-1;++i2?t[2]+90:90]):(t=e(),[t[0],t[1],t[2]-90])},e([0,0,90]).scale(159.155)},t.geoTransverseMercatorRaw=ro,t.geoRotation=Ur,t.geoStream=Je,t.geoTransform=function(t){return{stream:Ci(t)}},t.cluster=function(){function t(t){var o,u=0;t.eachAfter(function(t){var e=t.children;e?(t.x=function(t){return t.reduce(oo,0)/t.length}(e),t.y=function(t){return 1+t.reduce(uo,0)}(e)):(t.x=o?u+=n(t,o):0,t.y=0,o=t)});var a=function(t){for(var n;n=t.children;)t=n[0];return t}(t),c=function(t){for(var n;n=t.children;)t=n[n.length-1];return t}(t),s=a.x-n(a,c)/2,f=c.x+n(c,a)/2;return t.eachAfter(i?function(n){n.x=(n.x-t.x)*e,n.y=(t.y-n.y)*r}:function(n){n.x=(n.x-s)/(f-s)*e,n.y=(1-(t.y?n.y/t.y:1))*r})}var n=io,e=1,r=1,i=!1;return t.separation=function(e){return arguments.length?(n=e,t):n},t.size=function(n){return arguments.length?(i=!1,e=+n[0],r=+n[1],t):i?null:[e,r]},t.nodeSize=function(n){return arguments.length?(i=!0,e=+n[0],r=+n[1],t):i?[e,r]:null},t},t.hierarchy=co,t.pack=function(){function t(t){return t.x=e/2,t.y=r/2,n?t.eachBefore(Ao(n)).eachAfter(Co(i,.5)).eachBefore(zo(1)):t.eachBefore(Ao(Eo)).eachAfter(Co(ko,1)).eachAfter(Co(i,t.r/Math.min(e,r))).eachBefore(zo(Math.min(e,r)/(2*t.r))),t}var n=null,e=1,r=1,i=ko;return t.radius=function(e){return arguments.length?(n=function(t){return null==t?null:No(t)}(e),t):n},t.size=function(n){return arguments.length?(e=+n[0],r=+n[1],t):[e,r]},t.padding=function(n){return arguments.length?(i="function"==typeof n?n:So(+n),t):i},t},t.packSiblings=function(t){return To(t),t},t.packEnclose=po,t.partition=function(){function t(t){var o=t.height+1;return t.x0=t.y0=r,t.x1=n,t.y1=e/o,t.eachBefore(function(t,n){return function(e){e.children&&Ro(e,e.x0,t*(e.depth+1)/n,e.x1,t*(e.depth+2)/n);var i=e.x0,o=e.y0,u=e.x1-r,a=e.y1-r;u0)throw new Error("cycle");return o}var n=Lo,e=qo;return t.id=function(e){return arguments.length?(n=No(e),t):n},t.parentId=function(n){return arguments.length?(e=No(n),t):e},t},t.tree=function(){function t(t){var c=function(t){for(var n,e,r,i,o,u=new Yo(t,0),a=[u];n=a.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)a.push(e=n.children[i]=new Yo(r[i],i)),e.parent=n;return(u.parent=new Yo(null,0)).children=[u],u}(t);if(c.eachAfter(n),c.parent.m=-c.z,c.eachBefore(e),a)t.eachBefore(r);else{var s=t,f=t,l=t;t.eachBefore(function(t){t.xf.x&&(f=t),t.depth>l.depth&&(l=t)});var h=s===f?1:i(s,f)/2,p=h-s.x,d=o/(f.x+h+p),v=u/(l.depth||1);t.eachBefore(function(t){t.x=(t.x+p)*d,t.y=t.depth*v})}return t}function n(t){var n=t.children,e=t.parent.children,r=t.i?e[t.i-1]:null;if(n){(function(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)(n=i[o]).z+=e,n.m+=e,e+=n.s+(r+=n.c)})(t);var o=(n[0].z+n[n.length-1].z)/2;r?(t.z=r.z+i(t._,r._),t.m=t.z-o):t.z=o}else r&&(t.z=r.z+i(t._,r._));t.parent.A=function(t,n,e){if(n){for(var r,o=t,u=t,a=n,c=o.parent.children[0],s=o.m,f=u.m,l=a.m,h=c.m;a=Oo(a),o=Uo(o),a&&o;)c=Uo(c),(u=Oo(u)).a=t,(r=a.z+l-o.z-s+i(a._,o._))>0&&(Fo(Io(a,t,e),t,r),s+=r,f+=r),l+=a.m,s+=o.m,h+=c.m,f+=u.m;a&&!Oo(u)&&(u.t=a,u.m+=l-f),o&&!Uo(c)&&(c.t=o,c.m+=s-h,e=t)}return e}(t,r,t.parent.A||e[0])}function e(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function r(t){t.x*=o,t.y=t.depth*u}var i=Do,o=1,u=1,a=null;return t.separation=function(n){return arguments.length?(i=n,t):i},t.size=function(n){return arguments.length?(a=!1,o=+n[0],u=+n[1],t):a?null:[o,u]},t.nodeSize=function(n){return arguments.length?(a=!0,o=+n[0],u=+n[1],t):a?[o,u]:null},t},t.treemap=function(){function t(t){return t.x0=t.y0=0,t.x1=i,t.y1=o,t.eachBefore(n),u=[0],r&&t.eachBefore(Po),t}function n(t){var n=u[t.depth],r=t.x0+n,i=t.y0+n,o=t.x1-n,h=t.y1-n;o=n-1){var s=c[t];return s.x0=r,s.y0=i,s.x1=u,void(s.y1=a)}for(var l=f[t],h=e/2+l,p=t+1,d=n-1;p>>1;f[v]a-i){var y=(r*_+u*g)/e;o(t,p,g,r,i,y,a),o(p,n,_,y,i,u,a)}else{var m=(i*_+a*g)/e;o(t,p,g,r,i,u,m),o(p,n,_,r,m,u,a)}}var u,a,c=t.children,s=c.length,f=new Array(s+1);for(f[0]=a=u=0;u=0;--n)s.push(t[r[o[n]][2]]);for(n=+a;na!=s>a&&u<(c-e)*(a-r)/(s-r)+e&&(f=!f),c=e,s=r;return f},t.polygonLength=function(t){for(var n,e,r=-1,i=t.length,o=t[i-1],u=o[0],a=o[1],c=0;++r1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return Bg.h=360*t-100,Bg.s=1.5-1.5*n,Bg.l=.8-.9*n,Bg+""},t.interpolateWarm=Ig,t.interpolateCool=Yg,t.interpolateViridis=Hg,t.interpolateMagma=jg,t.interpolateInferno=Xg,t.interpolatePlasma=Vg,t.scaleSequential=$a,t.creator=A,t.local=C,t.matcher=rf,t.mouse=F,t.namespace=E,t.namespaces=Js,t.clientPoint=O,t.select=lt,t.selectAll=function(t){return"string"==typeof t?new st([document.querySelectorAll(t)],[document.documentElement]):new st([null==t?[]:t],af)},t.selection=ft,t.selector=Y,t.selectorAll=H,t.style=G,t.touch=ht,t.touches=function(t,n){null==n&&(n=U().touches);for(var e=0,r=n?n.length:0,i=new Array(r);eh;if(c||(c=t=te()),lt_)if(d>r_-t_)c.moveTo(l*Zg(h),l*Jg(h)),c.arc(0,0,l,h,p,!v),f>t_&&(c.moveTo(f*Zg(p),f*Jg(p)),c.arc(0,0,f,p,h,v));else{var g,_,y=h,m=p,x=h,b=p,w=d,M=d,T=a.apply(this,arguments)/2,N=T>t_&&(i?+i.apply(this,arguments):Kg(f*f+l*l)),k=Qg($g(l-f)/2,+r.apply(this,arguments)),S=k,E=k;if(N>t_){var A=Za(N/f*Jg(T)),C=Za(N/l*Jg(T));(w-=2*A)>t_?(A*=v?1:-1,x+=A,b-=A):(w=0,x=b=(h+p)/2),(M-=2*C)>t_?(C*=v?1:-1,y+=C,m-=C):(M=0,y=m=(h+p)/2)}var z=l*Zg(y),P=l*Jg(y),R=f*Zg(b),L=f*Jg(b);if(k>t_){var q=l*Zg(m),D=l*Jg(m),U=f*Zg(x),O=f*Jg(x);if(dt_?function(t,n,e,r,i,o,u,a){var c=e-t,s=r-n,f=u-i,l=a-o,h=(f*(n-o)-l*(t-i))/(l*c-f*s);return[t+h*c,n+h*s]}(z,P,U,O,q,D,R,L):[R,L],I=z-F[0],Y=P-F[1],B=q-F[0],H=D-F[1],j=1/Jg(function(t){return t>1?0:t<-1?n_:Math.acos(t)}((I*B+Y*H)/(Kg(I*I+Y*Y)*Kg(B*B+H*H)))/2),X=Kg(F[0]*F[0]+F[1]*F[1]);S=Qg(k,(f-X)/(j-1)),E=Qg(k,(l-X)/(j+1))}}M>t_?E>t_?(g=nc(U,O,z,P,l,E,v),_=nc(q,D,R,L,l,E,v),c.moveTo(g.cx+g.x01,g.cy+g.y01),Et_&&w>t_?S>t_?(g=nc(R,L,q,D,f,-S,v),_=nc(z,P,U,O,f,-S,v),c.lineTo(g.cx+g.x01,g.cy+g.y01),S0&&(p+=l);for(null!=e?d.sort(function(t,n){return e(v[t],v[n])}):null!=r&&d.sort(function(n,e){return r(t[n],t[e])}),a=0,s=p?(_-h*m)/p:0;a0?l*s:0)+m,v[c]={data:t[c],index:a,value:l,startAngle:g,endAngle:f,padAngle:y};return v}var n=sc,e=cc,r=null,i=Wa(0),o=Wa(r_),u=Wa(0);return t.value=function(e){return arguments.length?(n="function"==typeof e?e:Wa(+e),t):n},t.sortValues=function(n){return arguments.length?(e=n,r=null,t):e},t.sort=function(n){return arguments.length?(r=n,e=null,t):r},t.startAngle=function(n){return arguments.length?(i="function"==typeof n?n:Wa(+n),t):i},t.endAngle=function(n){return arguments.length?(o="function"==typeof n?n:Wa(+n),t):o},t.padAngle=function(n){return arguments.length?(u="function"==typeof n?n:Wa(+n),t):u},t},t.areaRadial=dc,t.radialArea=dc,t.lineRadial=pc,t.radialLine=pc,t.pointRadial=vc,t.linkHorizontal=function(){return yc(mc)},t.linkVertical=function(){return yc(xc)},t.linkRadial=function(){var t=yc(bc);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t},t.symbol=function(){function t(){var t;if(r||(r=t=te()),n.apply(this,arguments).draw(r,+e.apply(this,arguments)),t)return r=null,t+""||null}var n=Wa(u_),e=Wa(64),r=null;return t.type=function(e){return arguments.length?(n="function"==typeof e?e:Wa(e),t):n},t.size=function(n){return arguments.length?(e="function"==typeof n?n:Wa(+n),t):e},t.context=function(n){return arguments.length?(r=null==n?null:n,t):r},t},t.symbols=w_,t.symbolCircle=u_,t.symbolCross=a_,t.symbolDiamond=f_,t.symbolSquare=v_,t.symbolStar=d_,t.symbolTriangle=__,t.symbolWye=b_,t.curveBasisClosed=function(t){return new Nc(t)},t.curveBasisOpen=function(t){return new kc(t)},t.curveBasis=function(t){return new Tc(t)},t.curveBundle=M_,t.curveCardinalClosed=N_,t.curveCardinalOpen=k_,t.curveCardinal=T_,t.curveCatmullRomClosed=E_,t.curveCatmullRomOpen=A_,t.curveCatmullRom=S_,t.curveLinearClosed=function(t){return new Dc(t)},t.curveLinear=rc,t.curveMonotoneX=function(t){return new Yc(t)},t.curveMonotoneY=function(t){return new Bc(t)},t.curveNatural=function(t){return new jc(t)},t.curveStep=function(t){return new Vc(t,.5)},t.curveStepAfter=function(t){return new Vc(t,1)},t.curveStepBefore=function(t){return new Vc(t,0)},t.stack=function(){function t(t){var o,u,a=n.apply(this,arguments),c=t.length,s=a.length,f=new Array(s);for(o=0;o0){for(var e,r,i,o=0,u=t[0].length;o1)for(var e,r,i,o,u,a,c=0,s=t[n[0]].length;c=0?(r[0]=o,r[1]=o+=i):i<0?(r[1]=u,r[0]=u+=i):r[0]=o},t.stackOffsetNone=$c,t.stackOffsetSilhouette=function(t,n){if((e=t.length)>0){for(var e,r=0,i=t[n[0]],o=i.length;r0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,u=1;uyl&&e.name===n)return new Rn([[t]],Gl,n,+r)}return null},t.interrupt=Cn,t.voronoi=function(){function t(t){return new Ms(t.map(function(r,i){var o=[Math.round(n(r,i,t)/U_)*U_,Math.round(e(r,i,t)/U_)*U_];return o.index=i,o.data=r,o}),r)}var n=Kc,e=ts,r=null;return t.polygons=function(n){return t(n).polygons()},t.links=function(n){return t(n).links()},t.triangles=function(n){return t(n).triangles()},t.x=function(e){return arguments.length?(n="function"==typeof e?e:Jc(+e),t):n},t.y=function(n){return arguments.length?(e="function"==typeof n?n:Jc(+n),t):e},t.extent=function(n){return arguments.length?(r=null==n?null:[[+n[0][0],+n[0][1]],[+n[1][0],+n[1][1]]],t):r&&[[r[0][0],r[0][1]],[r[1][0],r[1][1]]]},t.size=function(n){return arguments.length?(r=null==n?null:[[0,0],[+n[0],+n[1]]],t):r&&[r[1][0]-r[0][0],r[1][1]-r[0][1]]},t},t.zoom=function(){function n(t){t.property("__zoom",zs).on("wheel.zoom",c).on("mousedown.zoom",s).on("dblclick.zoom",f).filter(x).on("touchstart.zoom",l).on("touchmove.zoom",h).on("touchend.zoom touchcancel.zoom",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function e(t,n){return(n=Math.max(b[0],Math.min(b[1],n)))===t.k?t:new Ns(n,t.x,t.y)}function r(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new Ns(t.k,r,i)}function i(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function o(t,n,e){t.on("start.zoom",function(){u(this,arguments).start()}).on("interrupt.zoom end.zoom",function(){u(this,arguments).end()}).tween("zoom",function(){var t=arguments,r=u(this,t),o=_.apply(this,t),a=e||i(o),c=Math.max(o[1][0]-o[0][0],o[1][1]-o[0][1]),s=this.__zoom,f="function"==typeof n?n.apply(this,t):n,l=T(s.invert(a).concat(c/s.k),f.invert(a).concat(c/f.k));return function(t){if(1===t)t=f;else{var n=l(t),e=c/n[2];t=new Ns(e,a[0]-n[0]*e,a[1]-n[1]*e)}r.zoom(null,t)}})}function u(t,n){for(var e,r=0,i=k.length;rC}n.zoom("mouse",y(r(n.that.__zoom,n.mouse[0]=F(n.that),n.mouse[1]),n.extent,w))},!0).on("mouseup.zoom",function(){e.on("mousemove.zoom mouseup.zoom",null),gt(t.event.view,n.moved),Es(),n.end()},!0),i=F(this),o=t.event.clientX,a=t.event.clientY;vt(t.event.view),Ss(),n.mouse=[i,this.__zoom.invert(i)],Cn(this),n.start()}}function f(){if(g.apply(this,arguments)){var i=this.__zoom,u=F(this),a=i.invert(u),c=i.k*(t.event.shiftKey?.5:2),s=y(r(e(i,c),u,a),_.apply(this,arguments),w);Es(),M>0?lt(this).transition().duration(M).call(o,s,u):lt(this).call(n.transform,s)}}function l(){if(g.apply(this,arguments)){var n,e,r,i,o=u(this,arguments),a=t.event.changedTouches,c=a.length;for(Ss(),e=0;e { - updateS + updateSseClients("hoi"); res.json({ nodes: app.nodeList.getNodes() }); @@ -97,36 +97,35 @@ function isPresent(arg) { return !!(arg || arg === 0 || arg === "" || arg === false); } -var sseClients = new sseMW.Topic(); +/////////////////////////// TOPN stuff ////////////////////////// + +const sseClients = new sseMW.Topic(); // initial registration of SSE Client Connection -app.get('/topn/updates', function(req,res){ - var sseConnection = res.sseConnection; +router.get('/topn/updates', function(req,res){ + const sseConnection = res.sseConnection; sseConnection.setup(); sseClients.add(sseConnection); } ); -var m; -//send message to all registered SSE clients -alrighfunction updateSseClients(message) { - var msg = message; - this.m=message; - sseClients.forEach( - function(sseConnection) { - sseConnection.send(this.m); - } - , this // this second argument to forEach is the thisArg (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) - ); //forEach -}// updateSseClients -// send a heartbeat signal to all SSE clients, once every interval seconds (or every 3 seconds if no interval is specified) -initHeartbeat = function(interval) { - setInterval(function() { - var msg = {"label":"The latest", "time":new Date()}; - updateSseClients( JSON.stringify(msg)); - }//interval function - , interval?interval*1000:3000 - ); // setInterval -}//initHeartbeat +/** + * send message to all registered SSE clients + */ +function updateSseClients() { + const nodes = app.nodeList.getGraphNodes(); + const edges = app.transactionList.getGraphEdges(); + sseClients.forEach(sseConnection => sseConnection.send({nodes: nodes, edges: edges})); +} +/** + * send a heartbeat signal to all SSE clients, once every interval seconds (or every 3 seconds if no interval is specified) + * @param interval - interval in seconds + */ +function initHeartbeat(interval) { + setInterval(() => { + const msg = {"label":"The latest", "time":new Date()}; + updateSseClients( JSON.stringify(msg)); + }, interval?interval*1000:3000); +} // initialize heartbeat at 10 second interval initHeartbeat(10); From 9f2aa6a4717f418cf67bd32eb847588653e10f31 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Wed, 24 Jan 2018 15:39:58 +0100 Subject: [PATCH 53/69] live updating graph heuh --- .../CommunicationHelper.java | 6 ++++ .../SimulationMain.java | 6 ++-- .../TrackerHelper.java | 32 +++++++++++++++++++ tracker-server/model/TransactionList.js | 6 ++-- tracker-server/routes/index.js | 20 ++++++++++-- 5 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java index 2b65863..27366bf 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java @@ -1,5 +1,6 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; +import java.io.IOException; import java.util.logging.Level; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; @@ -37,6 +38,11 @@ public static boolean receiveTransaction(Proof proof, LocalStore localStore) { Log.log(Level.INFO, "Transaction " + proof.getTransaction() + " is valid, applying updates..."); proof.applyUpdates(localStore); + try { + TrackerHelper.registerTransaction(proof); + } catch (IOException e) { + Log.log(Level.WARNING, "Transaction registration failed", e); + } if (proof.getTransaction().getAmount() > 0) { localStore.addUnspentTransaction(proof.getTransaction()); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index 03e3cbf..a954d12 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -30,9 +30,9 @@ private SimulationMain() {} //SETTINGS //number of local nodes to generate - public static final int LOCAL_NODES_NUMBER = 6; + public static final int LOCAL_NODES_NUMBER = 3; //number of total nodes in the system - public static final int TOTAL_NODES_NUMBER = 6; + public static final int TOTAL_NODES_NUMBER = 3; //number from which our nodes are (e.g if we run nodes (2, 3, 4), then this should be 2 public static final int NODES_FROM_NUMBER = 0; //Whether this main is the master coordinator of the simulation @@ -82,7 +82,7 @@ public static void main(String[] args) throws Exception { Simulation simulation = new Simulation(IS_MASTER); // ITransactionPattern itp = new OnlyNodeZeroTransactionPattern(10, 20, 1000, 2000, 1); UniformRandomTransactionPattern itp = new UniformRandomTransactionPattern(10, 10, 2000, 2000, 2); - itp.setSeed(1); +// itp.setSeed(1); simulation.setTransactionPattern(itp); simulation.runNodesLocally(nodes, ownNodes, genesisBlock, nodeToKeyPair); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java index 06809bd..d683081 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java @@ -2,6 +2,7 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; import nl.tudelft.blockchain.scaleoutdistributedledger.model.OwnNode; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.HttpGet; @@ -206,4 +207,35 @@ private static byte[] jsonArrayToByteArray(JSONArray json) { } return res; } + + /** + * Registers a transaction to the tracker server. + * @param proof - the proof used to send the transaction + * @return - whether the registration was successful + * @throws IOException - exception while registering + */ + public static boolean registerTransaction(Proof proof) throws IOException { + JSONObject json = new JSONObject(); + json.put("from", proof.getTransaction().getSender().getId()); + json.put("to", proof.getTransaction().getReceiver().getId()); + json.put("amount", proof.getTransaction().getAmount()); + json.put("remainder", proof.getTransaction().getRemainder()); + json.put("numberOfChains", proof.getChainUpdates().size()); + json.put("numberOfBlocks", 1); + + try (CloseableHttpClient client = HttpClientBuilder.create().build()) { + StringEntity requestEntity = new StringEntity(json.toString(), ContentType.APPLICATION_JSON); + HttpPost request = new HttpPost(String.format("http://%s:%d/register-transaction", + Application.TRACKER_SERVER_ADDRESS, Application.TRACKER_SERVER_PORT)); + request.setEntity(requestEntity); + JSONObject response = new JSONObject(IOUtils.toString(client.execute(request).getEntity().getContent())); + if (response.getBoolean("success")) { + Log.log(Level.INFO, "Successfully registered transaction to tracker server"); + return true; + } else { + Log.log(Level.WARNING, "Error while registering transaction " + proof.getTransaction()); + return false; + } + } + } } diff --git a/tracker-server/model/TransactionList.js b/tracker-server/model/TransactionList.js index 5609c13..0653aef 100644 --- a/tracker-server/model/TransactionList.js +++ b/tracker-server/model/TransactionList.js @@ -37,9 +37,11 @@ class TransactionList { getGraphEdges() { const edges = []; - for (let key in this.transactions) { + for (const key of Object.keys(this.transactions)) { + console.log(key); + const keyArray = key.split(","); const weight = this.getEdgeWeightWithKey(key); - edges.push({from: key[0], to: key[1], value: weight}); + edges.push({from: keyArray[0], to: keyArray[1], value: weight}); } return edges; } diff --git a/tracker-server/routes/index.js b/tracker-server/routes/index.js index 7e0745f..5b1e380 100644 --- a/tracker-server/routes/index.js +++ b/tracker-server/routes/index.js @@ -3,11 +3,13 @@ const router = express.Router(); import app from '../app'; import NodeList from '../model/NodeList'; const sseMW = require('./../helpers/sse'); +import Transaction from '../model/Transaction'; +import TransactionList from '../model/TransactionList'; + /** * Gets all nodes. */ router.get('/', (req, res) => { - updateSseClients("hoi"); res.json({ nodes: app.nodeList.getNodes() }); @@ -39,10 +41,23 @@ router.post('/register-node', (req, res) => { res.json({success: false, err: 'Specify id, address, port and publicKey'}); } else { const id = app.nodeList.registerNode(req.body.id, req.body.address, req.body.port, req.body.publicKey); + updateSseClients(); res.json({success: true, id: id}); } }); +router.post('/register-transaction', (req, res) => { + if(!isPresent(req.body.from) || !isPresent(req.body.to) || !isPresent(req.body.amount)|| !isPresent(req.body.remainder)|| !isPresent(req.body.numberOfChains) || !isPresent(req.body.numberOfBlocks)) { + res.status(403); + res.json({success: false, err: 'Specify from, to, amount, remainder, remainder, numberOfChains and numberOfBlocks'}); + } else { + const tx = new Transaction(req.body.from, req.body.to, req.body.amount, req.body.remainder, req.body.numberOfChains, req.body.numberOfBlocks); + app.transactionList.addTransaction(tx); + updateSseClients(); + res.json({success: true}); + } +}); + /** * Update the running status of a node. */ @@ -51,7 +66,7 @@ router.post('/set-node-status', (req, res) => { res.status(403); res.json({success: false, err: 'Specify node ID and running status'}); } else { - app.nodeList.setNodeStatus(req.body.id, req.body.running) + app.nodeList.setNodeStatus(req.body.id, req.body.running); res.json({success: true, id: req.body.id}); } }); @@ -83,6 +98,7 @@ router.get('/demo', (req, res) => { */ router.post('/reset', (req, res) => { app.nodeList = new NodeList(); + app.transactionList = new TransactionList(); res.json({success: true}); }); From fb5a83f762cad6481195c7e2c95e964b1de7657b Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Wed, 24 Jan 2018 15:43:02 +0100 Subject: [PATCH 54/69] some cleanup --- tracker-server/helpers/sse.js | 12 ++---------- tracker-server/public/javascripts/demo.js | 2 -- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/tracker-server/helpers/sse.js b/tracker-server/helpers/sse.js index 4c9fdd0..f68712a 100644 --- a/tracker-server/helpers/sse.js +++ b/tracker-server/helpers/sse.js @@ -4,9 +4,7 @@ console.log("loading sse.js"); // ... with this middleware: function sseMiddleware(req, res, next) { - console.log(" sseMiddleware is activated with "+ req+" res: "+res); res.sseConnection = new Connection(res); - console.log(" res has now connection res: "+res.sseConnection ); next(); } exports.sseMiddleware = sseMiddleware; @@ -15,12 +13,9 @@ exports.sseMiddleware = sseMiddleware; */ var Connection = (function () { function Connection(res) { - console.log(" sseMiddleware construct connection for response "); - this.res = res; } Connection.prototype.setup = function () { - console.log("set up SSE stream for response"); this.res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', @@ -28,7 +23,6 @@ var Connection = (function () { }); }; Connection.prototype.send = function (data) { - console.log("send event to SSE stream "+JSON.stringify(data)); this.res.write("data: " + JSON.stringify(data) + "\n\n"); }; return Connection; @@ -40,16 +34,14 @@ exports.Connection = Connection; */ var Topic = (function () { function Topic() { - console.log(" constructor for Topic"); - this.connections = []; } Topic.prototype.add = function (conn) { - var connections = this.connections; + const connections = this.connections; connections.push(conn); console.log('New client connected, now: ', connections.length); conn.res.on('close', function () { - var i = connections.indexOf(conn); + const i = connections.indexOf(conn); if (i >= 0) { connections.splice(i, 1); } diff --git a/tracker-server/public/javascripts/demo.js b/tracker-server/public/javascripts/demo.js index 8f30aee..fe0afd5 100644 --- a/tracker-server/public/javascripts/demo.js +++ b/tracker-server/public/javascripts/demo.js @@ -1,9 +1,7 @@ $(document).ready(function() { var source = new EventSource("../topn/updates"); source.onmessage = function(event) { - console.log("Got message"); var data = JSON.parse(event.data); - console.log(data); // $(".odometer").text(counter); network.setData({nodes: data.nodes, edges: data.edges}); network.redraw(); From d00992a704fc22c925a536e96d548b6d3e3f0aa1 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Wed, 24 Jan 2018 17:41:26 +0100 Subject: [PATCH 55/69] Update tests --- .../model/Chain.java | 10 +++ .../model/Transaction.java | 24 +++-- .../TransactionCreatorTest.java | 14 ++- .../model/ProofTest.java | 90 ++++++++++++++++++- 4 files changed, 126 insertions(+), 12 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java index bbbcf52..28a9f6a 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java @@ -125,6 +125,16 @@ public synchronized Block getLastBlock() { return blocks.get(blocks.size() - 1); } + /** + * @return - the number of the last block + */ + public int getLastBlockNumber() { + //TODO Remove + Block last; + assert blocks.size() - 1 == ((last = getLastBlock()) == null ? -1 : last.getNumber()); + return blocks.size() - 1; + } + /** * @return - the last block that was committed to the main chain */ diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index d9e3d52..daa6f38 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -49,12 +49,12 @@ public class Transaction implements Comparable { /** * Constructor. - * @param number - the number of this transaction. - * @param sender - the sender of this transaction. - * @param receiver - the receiver of this transaction. - * @param amount - the amount to be transferred. + * @param number - the number of this transaction. + * @param sender - the sender of this transaction. + * @param receiver - the receiver of this transaction. + * @param amount - the amount to be transferred. * @param remainder - the remaining amount. - * @param source - set of transactions that are used as source for this transaction. + * @param source - set of transactions that are used as source for this transaction. */ public Transaction(int number, Node sender, Node receiver, long amount, long remainder, TreeSet source) { this.sender = sender; @@ -65,6 +65,20 @@ public Transaction(int number, Node sender, Node receiver, long amount, long rem this.number = number; this.blockNumber = OptionalInt.empty(); } + + /** + * Convenience constructor. The given sources are converted to a TreeSet with + *
new TreeSet<>(Arrays.asList(source))
. + * @param number - the number of this transaction. + * @param sender - the sender of this transaction. + * @param receiver - the receiver of this transaction. + * @param amount - the amount to be transferred. + * @param remainder - the remaining amount. + * @param source - the transaction that are used as sources for this transaction. + */ + public Transaction(int number, Node sender, Node receiver, long amount, long remainder, Transaction... source) { + this(number, sender, receiver, amount, remainder, new TreeSet<>(Arrays.asList(source))); + } /** * Returns the number of the block (if it is in a block). diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java index b8501b9..f38f6e1 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java @@ -7,6 +7,7 @@ import org.junit.Before; import org.junit.Test; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; import nl.tudelft.blockchain.scaleoutdistributedledger.model.OwnNode; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; @@ -40,6 +41,7 @@ public void setUp() { public void createNodes(int from, int to) { for (int i = from; i <= to; i++) { nodes.put(i, new Node(i)); + localStore.getNewTransactionId(); } } @@ -74,10 +76,16 @@ public void addMetaKnowledge(Node node, int... chains) { * @return the transaction */ public Transaction addUnspent(Node sender, Node receiver, long amount, long remainder) { + //Create genesis block Transaction genesis = new Transaction(0, null, sender, amount + remainder, 0, new TreeSet<>()); - TreeSet source = new TreeSet<>(); - source.add(genesis); - Transaction transaction = new Transaction(localStore.getNewTransactionId(), sender, receiver, amount, remainder, source); + Block genesisBlock = new Block(0, null, Arrays.asList(genesis)); + sender.getChain().getBlocks().add(genesisBlock); + + //Create transaction and block + Transaction transaction = new Transaction(localStore.getNewTransactionId(), sender, receiver, amount, remainder, genesis); + Block block1 = new Block(1, sender, Arrays.asList(transaction)); + sender.getChain().getBlocks().add(block1); + localStore.addUnspentTransaction(transaction); return transaction; } diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java index 7668ebf..894d111 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java @@ -33,7 +33,7 @@ public void setUp() throws Exception { ownNode.setPrivateKey(pair.getPrivate().getEncoded()); ownNode.setPublicKey(pair.getPublic().getEncoded()); - genesisBlock = TestHelper.generateGenesis(ownNode, 3, 1000); + genesisBlock = TestHelper.generateGenesis(ownNode, 4, 1000); Map nodes = TestHelper.getNodeList(genesisBlock); appMock = mock(Application.class); @@ -58,14 +58,14 @@ public Transaction basicScenario() { //3: 0 --> 1, source = GENESIS 0 TreeSet source0to1 = new TreeSet<>(); source0to1.add(ownChain.getGenesisTransaction()); - Transaction transaction0to1 = new Transaction(3, ownNode, node1, 100, 900, source0to1); + Transaction transaction0to1 = new Transaction(4, ownNode, node1, 100, 900, source0to1); Block block1node0 = ownChain.appendNewBlock(); block1node0.addTransaction(transaction0to1); //3: 1 --> 0, source = [3: 0 --> 1] TreeSet source1to0 = new TreeSet<>(); source1to0.add(transaction0to1); - Transaction transaction1to0 = new Transaction(3, node1, ownNode, 100, 0, source1to0); + Transaction transaction1to0 = new Transaction(4, node1, ownNode, 100, 0, source1to0); Block block1node1 = new Block(genesisBlock, node1); block1node1.addTransaction(transaction1to0); node1.getChain().getBlocks().add(block1node1); @@ -73,7 +73,7 @@ public Transaction basicScenario() { //4: 0 --> 2, source = [3: 1 --> 0] TreeSet source0to2 = new TreeSet<>(); source0to2.add(transaction1to0); - Transaction transaction0to2 = new Transaction(4, ownNode, node2, 100, 0, source0to2); + Transaction transaction0to2 = new Transaction(5, ownNode, node2, 100, 0, source0to2); Block block2node0 = ownChain.appendNewBlock(); block2node0.addTransaction(transaction0to2); @@ -83,6 +83,58 @@ public Transaction basicScenario() { return transaction0to2; } + /** + * Creates the following scenario. + *
+	 * 3: 0 --> 1, source = GENESIS 0
+	 * 3: 1 --> 0, source = [3: 0 --> 1]
+	 * 4: 0 --> 2, source = [3: 1 --> 0]
+	 * 
+ * @return - a transaction from node 0 to node 2 + */ + public Transaction basicScenario2() { + Node node1 = storeSpy.getNode(1); + Node node2 = storeSpy.getNode(2); + Node node3 = storeSpy.getNode(3); + Chain ownChain = ownNode.getChain(); + Chain node1Chain = node1.getChain(); + + //3: 0 --> 1, source = GENESIS 0 + TreeSet source0to1 = new TreeSet<>(); + source0to1.add(ownChain.getGenesisTransaction()); + Transaction transaction0to1 = new Transaction(4, ownNode, node1, 100, 900, source0to1); + Block block1node0 = ownChain.appendNewBlock(); + block1node0.addTransaction(transaction0to1); + + //3: 1 --> 0, source = [GENESIS 1] + TreeSet source1to0 = new TreeSet<>(); + source1to0.add(node1Chain.getGenesisTransaction()); + Transaction transaction1to0 = new Transaction(4, node1, ownNode, 100, 0, source1to0); + Block block1node1 = new Block(genesisBlock, node1); + block1node1.addTransaction(transaction1to0); + node1.getChain().getBlocks().add(block1node1); + + //5: 0 --> 3, source = [4: 0 --> 1] + TreeSet source0to3 = new TreeSet<>(); + source0to3.add(transaction0to1); + Transaction transaction0to3 = new Transaction(5, ownNode, node3, 100, 0, source0to3); + Block block2node0 = ownChain.appendNewBlock(); + block2node0.addTransaction(transaction0to3); + + //6: 0 --> 2, source = [3: 1 --> 0] + TreeSet source0to2 = new TreeSet<>(); + source0to2.add(transaction0to3); + source0to2.add(transaction1to0); + Transaction transaction0to2 = new Transaction(6, ownNode, node2, 100, 0, source0to2); + Block block3node0 = ownChain.appendNewBlock(); + block3node0.addTransaction(transaction0to2); + + //commit block 3 of node 0 + block3node0.commit(storeSpy); + + return transaction0to2; + } + /** * Test method for {@link Proof#createProof}. */ @@ -161,4 +213,34 @@ public void testCreateProof3() { assertFalse(proof.getChainUpdates().containsKey(node1)); } + /** + * Test method for {@link Proof#createProof}. + */ + @Test + public void testCreateProof4() { + Node node1 = storeSpy.getNode(1); + Node node3 = storeSpy.getNode(3); + Chain ownChain = ownNode.getChain(); + Chain node1Chain = node1.getChain(); + + Transaction transaction = basicScenario2(); + + //Node 2 knows about block 1 of node 0 and of block 1 of node 1 + + node1.getMetaKnowledge().put(ownNode, 1); + node3.getMetaKnowledge().put(ownNode, 2); + + //send to node 2 + Proof proof = Proof.createProof(storeSpy, transaction); + + //Proof should contain only the last block of node 0 + List ownNodeUpdates = proof.getChainUpdates().get(ownNode); + List expectedOwnNodeUpdates = ownChain.getBlocks(); + assertEquals(expectedOwnNodeUpdates, ownNodeUpdates); + + //No blocks of node1 should be included + List node1Updates = proof.getChainUpdates().get(node1); + List expectedChain1Updates = Arrays.asList(node1Chain.getGenesisBlock(), node1Chain.getBlocks().get(1)); + assertEquals(expectedChain1Updates, node1Updates); + } } From 4ea0a7469a2a9cd723276f1747708334ccda24cc Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Wed, 24 Jan 2018 20:37:16 +0100 Subject: [PATCH 56/69] Added new meta knowledge class --- .../model/MetaKnowledge.java | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/MetaKnowledge.java diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/MetaKnowledge.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/MetaKnowledge.java new file mode 100644 index 0000000..5911394 --- /dev/null +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/MetaKnowledge.java @@ -0,0 +1,114 @@ +package nl.tudelft.blockchain.scaleoutdistributedledger.model; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import lombok.Getter; + +/** + * A class for representing meta knowledge (what we know that they know). + */ +public class MetaKnowledge extends HashMap { + private static final long serialVersionUID = 1L; + + /** + * @return - the node that this meta knowledge belongs to + */ + @Getter + private final Node owner; + + /** + * @param owner - the owner of this meta knowledge + */ + public MetaKnowledge(Node owner) { + this.owner = owner; + } + + /** + * Determines the blocks that we have to send to owner in order to update them to the given + * end block number. + * + * This method returns {@link Collections#emptyList()} if the given node is the owner or if + * owner already knows about all the blocks up till the given block number. + * @param node - the node to send blocks of + * @param endBlockNr - the number of the last block we want to send + * @return - the list of blocks to send + * @throws IndexOutOfBoundsException - If the given node does not have a block with endBlockNr. + */ + public List getBlocksToSend(Node node, int endBlockNr) { + if (node == owner) return Collections.emptyList(); + + //Determine the first block they don't know + int firstUnknown = getFirstUnknownBlockNumber(node); + + //They already know everything we want to send + if (firstUnknown > endBlockNr) return Collections.emptyList(); + + //Get a sublist of the blocks on the chain. We need + 1 on endBlock because the end is exclusive. + return node.getChain().getBlocks().subList(firstUnknown, endBlockNr + 1); + } + + /** + * The number returned by this method is the number of the last block that {@link #getOwner()} + * knows from node {@code node}. + * + * This method returns -1 if the owner of this meta knowledge does not know about this block. + * @param node - the node + * @return - the number of the last block from the given node that is known by owner + */ + public int getLastKnownBlockNumber(Node node) { + return getLastKnownBlockNumber(node.getId()); + } + + /** + * The number returned by this method is the number of the last block that {@link #getOwner()} + * knows from the node with id {@code nodeId}. + * + * This method returns -1 if the owner of this meta knowledge does not know about this block. + * @param nodeId - the id of the node + * @return - the number of the last block from the given node that is known by owner + */ + public synchronized int getLastKnownBlockNumber(int nodeId) { + if (nodeId == owner.getId()) return owner.getChain().getLastBlockNumber(); + return getOrDefault(nodeId, -1); + } + + /** + * @param node - the node + * @return - the number of the first block from the given node that is unknown by owner + */ + public int getFirstUnknownBlockNumber(Node node) { + return getFirstUnknownBlockNumber(node.getId()); + } + + /** + * @param nodeId - the id of the node + * @return - the number of the first block from the given node that is unknown by owner + */ + public int getFirstUnknownBlockNumber(int nodeId) { + return getLastKnownBlockNumber(nodeId) + 1; + } + + /** + * Updates the last known block number of the given node to the given blockNumber. + * If the given block number is lower than the current last known block number, then this + * method does nothing. + * @param node - the node + * @param blockNumber - the block number + */ + public void updateLastKnownBlockNumber(Node node, int blockNumber) { + updateLastKnownBlockNumber(node.getId(), blockNumber); + } + + /** + * Updates the last known block number of the given node to the given blockNumber. + * If the given block number is lower than the current last known block number, then this + * method does nothing. + * @param nodeId - the id of the node + * @param blockNumber - the block number + */ + public synchronized void updateLastKnownBlockNumber(int nodeId, int blockNumber) { + merge(nodeId, blockNumber, (oldNr, newNr) -> Math.max(oldNr, newNr)); + } +} From c5df33e5893a1466c6dbae9f8bc50003cb779070 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Fri, 26 Jan 2018 02:51:57 +0100 Subject: [PATCH 57/69] finished live demo view --- .../TrackerHelper.java | 2 +- .../model/Proof.java | 10 ++++ tracker-server/model/TransactionList.js | 48 +++++++++++++++++- tracker-server/public/images/background.png | Bin 0 -> 169782 bytes tracker-server/public/images/background.psd | Bin 0 -> 510643 bytes tracker-server/public/javascripts/demo.js | 48 +++--------------- tracker-server/public/stylesheets/demo.css | 36 ++++++++++++- tracker-server/routes/index.js | 3 +- tracker-server/views/demo.ejs | 17 +++++-- 9 files changed, 115 insertions(+), 49 deletions(-) create mode 100644 tracker-server/public/images/background.png create mode 100644 tracker-server/public/images/background.psd diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java index d683081..4a46a98 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java @@ -221,7 +221,7 @@ public static boolean registerTransaction(Proof proof) throws IOException { json.put("amount", proof.getTransaction().getAmount()); json.put("remainder", proof.getTransaction().getRemainder()); json.put("numberOfChains", proof.getChainUpdates().size()); - json.put("numberOfBlocks", 1); + json.put("numberOfBlocks", proof.getNumberOfBlocks()); try (CloseableHttpClient client = HttpClientBuilder.create().build()) { StringEntity requestEntity = new StringEntity(json.toString(), ContentType.APPLICATION_JSON); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index c8b565d..d10fd5f 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -434,6 +434,16 @@ public static void appendChains2(Transaction transaction, Node receiver, Map res[0] += blocks.size()); + return res[0]; + } @Override public String toString() { diff --git a/tracker-server/model/TransactionList.js b/tracker-server/model/TransactionList.js index 0653aef..bbd9a73 100644 --- a/tracker-server/model/TransactionList.js +++ b/tracker-server/model/TransactionList.js @@ -3,12 +3,26 @@ */ class TransactionList { + /** + * Constructor. + */ constructor() { // Store transactions in a dictionary this.transactions = {}; + this.numberOfTransactions = 0; + this.numberOfChains = 0; + this.numberOfBlocks = 0; } + /** + * Add a transaction to the transaction list. + * @param transaction - the transaction to add + */ addTransaction(transaction) { + this.numberOfTransactions += 1; + this.numberOfChains += transaction.numberOfChains; + this.numberOfBlocks += transaction.numberOfBlocks; + // Make sure we use a consistent key for all transactions between the same two nodes let key = [transaction.from, transaction.to]; if(transaction.from > transaction.to) @@ -20,6 +34,12 @@ class TransactionList { this.transactions[key].push(transaction); } + /** + * Gets the edge weight between two nodes. + * @param node1 - the first node + * @param node2 - the second node + * @returns {number} + */ getEdgeWeight(node1, node2) { let key = [node1, node2]; if(node1 > node2) { @@ -28,6 +48,11 @@ class TransactionList { return this.getEdgeWeightWithKey(key); } + /** + * Gets the edge weight for a certain key. + * @param key - the key + * @returns {number} + */ getEdgeWeightWithKey(key) { if(!this.transactions[key]) return 0; @@ -35,16 +60,37 @@ class TransactionList { return this.transactions[key].length; } + /** + * Returns parsed edges for the graph. + * @returns {Array} + */ getGraphEdges() { const edges = []; for (const key of Object.keys(this.transactions)) { - console.log(key); const keyArray = key.split(","); const weight = this.getEdgeWeightWithKey(key); edges.push({from: keyArray[0], to: keyArray[1], value: weight}); } return edges; } + + /** + * Returns a JSON object containing some interesting number. + * @returns {{numberOfTransactions: number, averageNumberOfBlocks: number, averageNumberOfChains: number}} + */ + getNumbers() { + let averageNumberOfChains = 0, + averageNumberOfBlocks = 0; + if(this.numberOfTransactions !== 0) { + averageNumberOfBlocks = this.numberOfBlocks / this.numberOfTransactions; + averageNumberOfChains = this.numberOfChains / this.numberOfTransactions; + } + return { + numberOfTransactions: this.numberOfTransactions, + averageNumberOfBlocks: averageNumberOfBlocks, + averageNumberOfChains: averageNumberOfChains + }; + } } module.exports = TransactionList; \ No newline at end of file diff --git a/tracker-server/public/images/background.png b/tracker-server/public/images/background.png new file mode 100644 index 0000000000000000000000000000000000000000..20fe376a801c8376b6d2d0cabeb968a4245d14dd GIT binary patch literal 169782 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

5YjHN>DdFkd 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 z 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 literal 0 HcmV?d00001 diff --git a/tracker-server/public/images/background.psd b/tracker-server/public/images/background.psd new file mode 100644 index 0000000000000000000000000000000000000000..81642b4d02a28ac098c1532abb138dc74273ed0e GIT binary patch literal 510643 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 >`o=POoQB3 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 z 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 ?hn>*qM9(R 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 literal 0 HcmV?d00001 diff --git a/tracker-server/public/javascripts/demo.js b/tracker-server/public/javascripts/demo.js index fe0afd5..417ef5c 100644 --- a/tracker-server/public/javascripts/demo.js +++ b/tracker-server/public/javascripts/demo.js @@ -5,53 +5,21 @@ $(document).ready(function() { // $(".odometer").text(counter); network.setData({nodes: data.nodes, edges: data.edges}); network.redraw(); + $(".transactions").text(data.numbers.numberOfTransactions); + $(".chains").text(data.numbers.averageNumberOfChains); + $(".blocks").text(data.numbers.averageNumberOfBlocks); }; - - window.odometerOptions = { - auto: false, // Don't automatically initialize everything with class 'odometer' - // selector: '.my-numbers', // Change the selector used to automatically find things to be animated - // format: '(,ddd).dd', // Change how digit groups are formatted, and how many digits are shown after the decimal point - duration: 100, // Change how long the javascript expects the CSS animation to take - // theme: 'car', // Specify the theme (if you have more than one theme css file on the page) - // animation: 'count' // Count is a simpler animation method which just increments the value, - // use it when you're looking for something more subtle. + auto: false, + format: '(,ddd).ddd', // Change how digit groups are formatted, and how many digits are shown after the decimal point + duration: 100, + animation: 'count' }; var nodes = []; var edges = []; var network = null; - // create people. - // value corresponds with the age of the person - // nodes = [ - // {id: 1, value: 2, label: 'Algie' }, - // {id: 2, value: 31, label: 'Alston'}, - // {id: 3, value: 12, label: 'Barney'}, - // {id: 4, value: 16, label: 'Coley' }, - // {id: 5, value: 17, label: 'Grant' }, - // {id: 6, value: 15, label: 'Langdon'}, - // {id: 7, value: 6, label: 'Lee'}, - // {id: 8, value: 5, label: 'Merlin'}, - // {id: 9, value: 30, label: 'Mick'}, - // {id: 10, value: 18, label: 'Tod'}, - // ]; - // - // // create connections between people - // // value corresponds with the amount of contact between two people - // edges = [ - // {from: 2, to: 8, value: 3, title: '3 emails per week'}, - // {from: 2, to: 9, value: 5, title: '5 emails per week'}, - // {from: 2, to: 10,value: 1, title: '1 emails per week'}, - // {from: 4, to: 6, value: 8, title: '8 emails per week'}, - // {from: 5, to: 7, value: 2, title: '2 emails per week'}, - // {from: 4, to: 5, value: 1, title: '1 emails per week'}, - // {from: 9, to: 10,value: 2, title: '2 emails per week'}, - // {from: 2, to: 3, value: 6, title: '6 emails per week'}, - // {from: 3, to: 9, value: 4, title: '4 emails per week'}, - // {from: 5, to: 3, value: 1, title: '1 emails per week'}, - // {from: 2, to: 7, value: 4, title: '4 emails per week'} - // ]; function draw() { @@ -63,7 +31,7 @@ $(document).ready(function() { }; var options = { nodes: { - shape: 'dot', + shape: 'dot' } }; network = new vis.Network(container, data, options); diff --git a/tracker-server/public/stylesheets/demo.css b/tracker-server/public/stylesheets/demo.css index 13172b9..9e9e481 100644 --- a/tracker-server/public/stylesheets/demo.css +++ b/tracker-server/public/stylesheets/demo.css @@ -3,11 +3,43 @@ height: 600px; display: block; border: 2px solid black; - margin-top: 30px; + margin: 30px auto auto; } .odometer { font-size: 50px; - margin-left: 20px; + margin: auto; } +.odometer_wrapper { + text-align: center; +} + +body { + text-align: center; +} + +.numbers_table { + margin: auto; +} + +.numbers_table tr.labels { + font-size: 30px; + font-weight: bold; +} + +.numbers_table tr td { + padding: 10px 20px; +} + +h1 { + font-size: 45px; +} + +html { + background: rgba(0, 0, 0, 0.1) url(/images/background.png) no-repeat fixed center center; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} diff --git a/tracker-server/routes/index.js b/tracker-server/routes/index.js index 5b1e380..06c0708 100644 --- a/tracker-server/routes/index.js +++ b/tracker-server/routes/index.js @@ -129,7 +129,8 @@ router.get('/topn/updates', function(req,res){ function updateSseClients() { const nodes = app.nodeList.getGraphNodes(); const edges = app.transactionList.getGraphEdges(); - sseClients.forEach(sseConnection => sseConnection.send({nodes: nodes, edges: edges})); + sseClients.forEach(sseConnection => sseConnection.send( + {nodes: nodes, edges: edges, numbers: app.transactionList.getNumbers()})); } /** diff --git a/tracker-server/views/demo.ejs b/tracker-server/views/demo.ejs index 7ca32e5..83cbe99 100644 --- a/tracker-server/views/demo.ejs +++ b/tracker-server/views/demo.ejs @@ -20,10 +20,19 @@ -

Demo

- - - 12 +

Scale-Out Distributed Ledger

+ + + + + + + + + + + +
Total #transactionsAverage #chains per proofTotal #blocks per proof
From 70b065ad7e42923ef6269cab641866cd8069b0b5 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Fri, 26 Jan 2018 02:54:51 +0100 Subject: [PATCH 58/69] updated readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 1a89f38..e72a210 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,9 @@ BitSets are used as a convenient and efficient way to represent the chains that ## Tracker Server The tracker server can be installed by installing NodeJS and running `npm install` in the tracker-server folder. After this it can be run by running `npm start` in that same folder. +### Live demo view +A live demo view is available at http://localhost:3000/demo if the trackerserver is running. This demo view shows a live graph and counters for the total number of transactions and the average amount of blocks and chains sent per proof. + ## Main Chain For the main chain we use [Tendermint](https://tendermint.com/). We can communicate to this chain using the provided ABCI interface. From 0ca6485772402ba340ebb7137b3477d8f787e5bb Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Fri, 26 Jan 2018 19:03:51 +0100 Subject: [PATCH 59/69] Working version --- .../ProofConstructor.java | 224 ++++++++++++++++++ .../TransactionCreator.java | 16 +- .../TransactionSender.java | 9 +- .../message/ProofMessage.java | 5 +- .../model/Block.java | 16 +- .../model/Chain.java | 23 +- .../scaleoutdistributedledger/model/Node.java | 8 +- .../model/Proof.java | 109 ++------- .../model/Transaction.java | 18 +- .../mainchain/tendermint/TendermintChain.java | 1 + .../tendermint/TendermintHelper.java | 4 +- .../TransactionCreatorTest.java | 14 +- .../model/ProofTest.java | 33 ++- 13 files changed, 339 insertions(+), 141 deletions(-) create mode 100644 src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java new file mode 100644 index 0000000..8c676be --- /dev/null +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java @@ -0,0 +1,224 @@ +package nl.tudelft.blockchain.scaleoutdistributedledger; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.MetaKnowledge; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; + +/** + * Class for constructing proofs. + */ +public class ProofConstructor { + + private final Transaction mainTransaction; + private final Node receiver; + private final Node sender; + private final Map> toSend; + private final Map> checked = new HashMap<>(); + private final Map checked2 = new HashMap<>(); + private final Proof proof; + + /* + * The Problem: + * We want to send block A. + * We have committed block B. + * With metaknowledge, we can translate this to a list of blocks that need to be sent. + * + * From all these blocks, we have to determine "what is the last block of each node that needs to be sent." + * This is "loop over the blocks, loop over the sources of transactions (recursively): + * + * Block A + * Block B + * Node sender + * Node receiver + * List ownBlocks = MetaKnowledge.determineBlocks(sender, B) + * processBlocks(sender, ownBlocks) + * Map> toSend + * Map> alreadyChecked + * + * processBlocks(Node owner, List blocks): + * //newlyAdded is the list of all the blocks that were added (not already present) (HAS TO BE ORDERED) + * List newlyAdded = toSend.addAll(owner, blocks) + * for (Block b : newlyAdded) { + * alreadyChecked.add(owner, b.getNumber()) + * for (Transaction t : b.getTransactions()) { + * processSources(t) + * } + * } + * + * processSources(Transaction t): + * for (Transaction s : t.getSource()) + * Node owner = s.getOwner(); + * //Skip all sources in genesis blocks, our own blocks or in receiver blocks + * if (owner == null || owner == sender || owner == receiver) continue; + * + * Block b = s.getBlock() + * //Skip all blocks that were already checked + * if (b.getNumber() in alreadyChecked.get(owner)) continue; + * + * Block bCom = the first committed block at or after b + * if (bCom.getNumber() in alreadyChecked.get(owner)) continue; + * + * List blocksOfSource = MetaKnowledge.determineBlocks(owner, bCom) + * if (blocksOfSource is empty) { + * alreadyChecked.addAll(owner, 0 up to (inclusive) bCom.getNumber()) + * continue; + * } + * + * processBlocks(owner, blocksOfSource) + */ + + /** + * @param mainTransaction - the transaction to construct the proof for + */ + public ProofConstructor(Transaction mainTransaction) { + this.mainTransaction = mainTransaction; + this.receiver = mainTransaction.getReceiver(); + this.sender = mainTransaction.getSender(); + this.proof = new Proof(mainTransaction); + this.toSend = proof.getChainUpdates(); + } + + /** + * @return - the constructed proof + */ + public synchronized Proof constructProof() { + //If the proof was already constructed, return it. + if (!toSend.isEmpty()) return proof; + + MetaKnowledge metaKnowledge = receiver.getMetaKnowledge(); + int mainBlockNr = mainTransaction.getBlockNumber().getAsInt(); + Block nextCommitted = sender.getChain().getBlocks().get(mainBlockNr).getNextCommittedBlock(); + List ownBlocks = metaKnowledge.getBlocksToSend(sender, nextCommitted.getNumber()); + + //Base case: no blocks to send + if (ownBlocks.isEmpty()) { + return proof; + } + + //Recursively process all the blocks + processBlocks(sender, ownBlocks); + return proof; + } + + /** + * Processes the given list of blocks belonging to the given owner. + * The given list is expected to be non-empty. + * @param owner - the owner of the blocks + * @param blocks - the blocks + */ + protected void processBlocks(Node owner, List blocks) { + List newlyAdded = addBlocksToSend(owner, blocks); + for (Block block : newlyAdded) { + checked.computeIfAbsent(owner, n -> new HashSet<>()).add(block.getNumber()); + + //TODO Temp + Integer old = checked2.put(owner, block.getNumber()); + if (old != null && old > block.getNumber()) { + System.out.println("CHECKED 2 problem: old was higher (1)"); + } + //ENDTODO + + for (Transaction transaction : block.getTransactions()) { + processSources(transaction); + } + } + } + + /** + * Processes the sources of the given transaction. + * @param transaction - the transaction to process + */ + protected void processSources(Transaction transaction) { + for (Transaction source : transaction.getSource()) { + Node owner = source.getSender(); + //Skip all sources in genesis blocks, our own blocks and in receiver blocks + if (owner == null || owner == this.sender || owner == this.receiver) continue; + + int blockNumber = source.getBlockNumber().getAsInt(); + Set checkedSet = checked.computeIfAbsent(owner, n -> new HashSet<>()); + if (checkedSet.contains(blockNumber)) { + Integer checked22 = checked2.get(owner); + if (checked22 == null || checked22 < blockNumber) { + System.out.println("CHECKED 2 problem: null or less than block number (" + checked22 + ")"); + } + + continue; + } + + Block block = owner.getChain().getBlocks().get(blockNumber); + int nextCommittedBlockNr = block.getNextCommittedBlock().getNumber(); + if (checkedSet.contains(nextCommittedBlockNr)) { + System.out.println("I don't think this should be possible"); + //TODO Temp + Integer checked22 = checked2.get(owner); + if (checked22 == null || checked22 < nextCommittedBlockNr) { + System.out.println("CHECKED 2 problem: null or less than committed block number (" + checked22 + ")"); + } + //ENDTODO + continue; + } + + //Determine the blocks that we would need to send. + MetaKnowledge metaKnowledge = this.receiver.getMetaKnowledge(); + List blocksOfSource = metaKnowledge.getBlocksToSend(owner, nextCommittedBlockNr); + if (blocksOfSource.isEmpty()) { + for (int i = 0; i < nextCommittedBlockNr; i++) { + checkedSet.add(i); + } + + //TODO Temp + Integer old = checked2.put(owner, nextCommittedBlockNr); + if (old != null && old > nextCommittedBlockNr) { + System.out.println("CHECKED 2 problem: old was higher (2)"); + } + //ENDTODO + continue; + } + + processBlocks(owner, blocksOfSource); + } + } + + /** + * Adds the given blocks belonging to the given owner to the toSend map. + * @param owner - the owner of the blocks + * @param toAdd - the blocks to add + * @return - all the blocks that were added (not already in the toSend map) + */ + protected List addBlocksToSend(Node owner, List toAdd) { + List current = toSend.computeIfAbsent(owner, n -> new ArrayList<>()); + if (current.isEmpty()) { + current.addAll(toAdd); + return toAdd; + } + + if (current.size() >= toAdd.size()) { + //Nothing new + return Collections.EMPTY_LIST; + } + + //Since the blocks we are adding have been selected through the meta knowledge, they will go from firstUnknown up to a certain block. + //E.g. [0, 1, 2] or [2, 3] + //Also, we skip all blocks that we have already checked, we know that the entire history is effectively linear and that we check from + //low to high block numbers. This means that the given list will contain all elements that we already have + some extra. + //So we can start at the index equal to what we already have. + + assert current.containsAll(toAdd); + assert current.size() < toAdd.size(); + + int startBlockNr = current.size(); + List added = toAdd.subList(startBlockNr, toAdd.size()); + current.addAll(added); + return added; + } +} diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java index 18f3e86..d555a2f 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java @@ -2,7 +2,6 @@ import java.util.*; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Chain; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; @@ -51,7 +50,6 @@ private BitSet calculateKnowledge() { BitSet collected = receiver.getMetaKnowledge() .keySet() .stream() - .map(Node::getId) .collect(() -> new BitSet(nodesCount), (bs, i) -> bs.set(i), (bs1, bs2) -> bs1.or(bs2) @@ -224,19 +222,19 @@ private void cleanup(Collection tuples, int previousBest) { */ public BitSet chainsRequired(Transaction transaction) { //TODO Verify that this collection of chains is correct. - Set chains = new HashSet<>(); - Proof.appendChains(transaction, receiver, chains); - BitSet bitset = chains.stream() - .map(Chain::getOwner) + Map chains = new HashMap<>(); + + int nrOfNodes = localStore.getNodes().size(); + Proof.appendChains2(nrOfNodes, transaction, receiver, chains); + BitSet bitset = chains + .keySet() + .stream() .map(Node::getId) .collect(() -> new BitSet(nodesCount), (bs, i) -> bs.set(i), (bs1, bs2) -> bs1.or(bs2) ); - //Remove all chains that are already known - bitset.andNot(known); - return bitset; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index 01b8108..c8a3132 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -166,9 +166,14 @@ private void sendBlock(Block block) { */ private boolean sendTransaction(Transaction transaction) throws InterruptedException, IOException { Node to = transaction.getReceiver(); - Proof proof = Proof.createProof(localStore, transaction); + + ProofConstructor proofConstructor = new ProofConstructor(transaction); + Proof proof; + synchronized (localStore.getOwnNode().getChain()) { + proof = proofConstructor.constructProof(); + } + ProofMessage msg = new ProofMessage(proof); -// Log.debug("{0}: {1}", localStore.getOwnNode().getId(), msg); if (socketClient.sendMessage(to, msg)) { to.updateMetaKnowledge(proof); return true; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java index 43284fc..3518405 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java @@ -21,7 +21,8 @@ * Proof message for netty. */ public class ProofMessage extends Message { - + private static final long serialVersionUID = 1L; + @Getter private final TransactionMessage transactionMessage; @@ -40,7 +41,7 @@ public ProofMessage(Proof proof) { Node proofReceiver = proof.getTransaction().getReceiver(); this.transactionMessage = new TransactionMessage(proof.getTransaction(), proofReceiver); this.chainUpdates = new HashMap<>(); - for (Map.Entry> entry : proof.getChainUpdates().entrySet()) { + for (Entry> entry : proof.getChainUpdates().entrySet()) { Node node = entry.getKey(); List blockList = entry.getValue(); if (!blockList.isEmpty()) { diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index e5630f9..1bb5cb7 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -3,8 +3,6 @@ import lombok.Getter; import lombok.Setter; import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; -import nl.tudelft.blockchain.scaleoutdistributedledger.message.BlockMessage; -import nl.tudelft.blockchain.scaleoutdistributedledger.message.TransactionMessage; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Utils; @@ -12,7 +10,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.logging.Level; /** @@ -27,6 +24,9 @@ public class Block { @Getter @Setter private Block previousBlock; + + @Getter @Setter + private Block nextCommittedBlock; @Getter @Setter private Node owner; @@ -150,6 +150,7 @@ public BlockAbstract calculateBlockAbstract() { * @param localStore - the local store */ public synchronized void commit(LocalStore localStore) { + Log.debug("{0}: Committing block {1}", localStore.getOwnNode().getId(), this.getNumber()); if (finalized) { throw new IllegalStateException("This block has already been committed!"); } @@ -162,6 +163,13 @@ public synchronized void commit(LocalStore localStore) { } finalized = true; + + nextCommittedBlock = this; + Block prev = getPreviousBlock(); + while (prev.nextCommittedBlock == null) { + prev.nextCommittedBlock = this; + prev = prev.getPreviousBlock(); + } } @Override @@ -243,6 +251,7 @@ public Block genesisCopy() { block.onMainChain = true; block.finalized = true; + block.nextCommittedBlock = block; return block; } @@ -252,7 +261,6 @@ public Block genesisCopy() { * @return - boolean identifying if an abstract of this block is on the main chain. */ public boolean isOnMainChain(LocalStore localStore) { - if(true) return true; //TODO Remove hack? if (this.number == GENESIS_BLOCK_NUMBER) return true; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java index 28a9f6a..493356e 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java @@ -55,10 +55,15 @@ public synchronized void update(List updates, LocalStore localStore) { if (updates.isEmpty()) return; - ArrayList copy = new ArrayList<>(updates); - copy.sort((a, b) -> Integer.compare(a.getNumber(), b.getNumber())); - if (!copy.equals(updates)) { - throw new IllegalArgumentException("The blocks are not ordered correctly :("); + //TODO remove + boolean asserts = false; + assert asserts = true; + if (asserts) { + ArrayList copy = new ArrayList<>(updates); + copy.sort((a, b) -> Integer.compare(a.getNumber(), b.getNumber())); + if (!copy.equals(updates)) { + throw new IllegalArgumentException("The blocks are not ordered correctly :("); + } } int nextNr; @@ -74,15 +79,19 @@ public synchronized void update(List updates, LocalStore localStore) { previousBlock = lastBlock; } + //The last block in the updates must be a committed block + Block lastCommitted = updates.get(updates.size() - 1); for (Block block : updates) { //Skip any overlap if (block.getNumber() != nextNr) continue; block.setPreviousBlock(previousBlock); + block.setNextCommittedBlock(lastCommitted); blocks.add(block); nextNr++; previousBlock = block; } + //TODO Set last committed block for (Block block : ReversedIterator.reversed(this.blocks)) { if (block.isOnMainChain(localStore)) { setLastCommittedBlock(block); @@ -130,8 +139,10 @@ public synchronized Block getLastBlock() { */ public int getLastBlockNumber() { //TODO Remove - Block last; - assert blocks.size() - 1 == ((last = getLastBlock()) == null ? -1 : last.getNumber()); + { + Block last; + assert blocks.size() - 1 == ((last = getLastBlock()) == null ? -1 : last.getNumber()); + } return blocks.size() - 1; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java index ba9110c..b207d9e 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Node.java @@ -3,7 +3,6 @@ import lombok.Getter; import lombok.Setter; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -28,11 +27,8 @@ public class Node { @Getter @Setter private int port; - /** - * @return a map containing what we know that this node knows - */ @Getter - private Map metaKnowledge = new HashMap<>(); + private MetaKnowledge metaKnowledge = new MetaKnowledge(this); /** * Constructor. @@ -82,7 +78,7 @@ public void updateMetaKnowledge(Proof proof) { int lastBlockNr = getLastBlockNumber(entry.getValue()); if (lastBlockNr == -1) continue; - metaKnowledge.merge(entry.getKey(), lastBlockNr, Math::max); + metaKnowledge.updateLastKnownBlockNumber(entry.getKey(), lastBlockNr); } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index afcde45..7ee778d 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -160,6 +160,7 @@ public void verify(LocalStore localStore) throws ProofValidationException { throw new ProofValidationException("We directly received a transaction with a null sender."); } + //TODO [BFT] all the blocks that were sent but not required for the proof are not validated at all. verify(this.transaction, localStore); } @@ -273,85 +274,6 @@ public void applyUpdates(LocalStore localStore) { //Update the meta knowledge of the sender transaction.getSender().updateMetaKnowledge(this); } - - /** - * @param localStore - the local store - * @param transaction - the transaction - * @return the proof for the given transaction - */ - public static Proof createProof(LocalStore localStore, Transaction transaction) { - Node receiver = transaction.getReceiver(); - - synchronized (receiver.getMetaKnowledge()) { - Proof proof = new Proof(transaction); - - //Step 1: determine what blocks need to be sent - int blockRequired = transaction.getBlockNumber().getAsInt(); - Chain senderChain = transaction.getSender().getChain(); - - Map metaKnowledge = receiver.getMetaKnowledge(); - - //The from block is the already known - int alreadyKnownBlock = metaKnowledge.getOrDefault(transaction.getSender(), -1) + 1; - blockRequired = Math.min(blockRequired, alreadyKnownBlock); - - Block fromBlock = senderChain.getBlocks().get(blockRequired); - Block toBlock = getNextCommittedBlock(localStore, blockRequired, senderChain); - - //Step 2: determine the chains that need to be sent - Map chains = determineChains(transaction, fromBlock, toBlock); - - //Step 3: add only those blocks that are not yet known - for (Entry entry : chains.entrySet()) { - Chain chain = entry.getKey(); - Node owner = chain.getOwner(); - if (owner == receiver) continue; - - int alreadyKnown = metaKnowledge.getOrDefault(owner, -1); - int requiredKnown = entry.getValue(); - if (alreadyKnown < requiredKnown) { - proof.addBlocksOfChain(chain, alreadyKnown + 1, requiredKnown + 1); - } - } - - return proof; - } - } - - /** - * @param mainTransaction - the transaction that we want to send - * @param fromBlock - the first block that we want to send - * @param toBlock - the last block that we want to send - * @return - the chains required and the corresponding block number - */ - private static Map determineChains(Transaction mainTransaction, Block fromBlock, Block toBlock) { - //TODO We might want to do some kind of caching? - - Node receiver = mainTransaction.getReceiver(); - Map metaKnowledge = receiver.getMetaKnowledge(); - - //Determine what the sender already knows about us - int alreadyKnown = metaKnowledge.getOrDefault(mainTransaction.getSender(), -1); - - Map chains = new HashMap<>(); - - //Only consider the transactions that the receiver doesn't have yet. - Block current = toBlock; - while (current.getNumber() > alreadyKnown) { - for (Transaction transaction : current.getTransactions()) { - appendChains2(transaction, receiver, chains); - } - - if (current == fromBlock) break; - current = current.getPreviousBlock(); - } - - //Only add the chains of the transaction itself if it isn't in a known block - if (mainTransaction.getBlockNumber().getAsInt() > alreadyKnown) { - appendChains2(mainTransaction, receiver, chains); - } - return chains; - } /** * @param localStore @@ -374,41 +296,54 @@ private static Block getNextCommittedBlock(LocalStore localStore, int blockRequi /** * Recursively calls itself with all the sources of the given transaction. Transactions which * are in the chain of {@code receiver} are ignored. + * @param nrOfNodes - the total number of nodes * @param transaction - the transaction to check the sources of * @param receiver - the node receiving the transaction * @param chains - the list of chains to append to */ - public static void appendChains(Transaction transaction, Node receiver, Set chains) { + public static void appendChains(int nrOfNodes, Transaction transaction, Node receiver, MetaKnowledge metaKnowledge, Set chains) { Node owner = transaction.getSender(); if (owner == null || owner == receiver) return; + //TODO Do we want to cut off at known blocks? + int alreadyKnown = metaKnowledge.getFirstUnknownBlockNumber(owner); + int blockNumber = transaction.getBlockNumber().getAsInt(); + if (alreadyKnown >= blockNumber) return; + chains.add(owner.getChain()); + if (chains.size() >= nrOfNodes - 1) return; + for (Transaction source : transaction.getSource()) { - appendChains(source, receiver, chains); + appendChains(nrOfNodes, source, receiver, metaKnowledge, chains); } } /** * Recursively calls itself with all the sources of the given transaction. Transactions which * are in the chain of {@code receiver} are ignored. + * @param nrOfNodes - the total number of nodes * @param transaction - the transaction to check the sources of * @param receiver - the node receiving the transaction * @param chains - the map of chains to append to */ - public static void appendChains2(Transaction transaction, Node receiver, Map chains) { + public static void appendChains2(int nrOfNodes, Transaction transaction, Node receiver, Map chains) { Node owner = transaction.getSender(); if (owner == null || owner == receiver) return; //Skip transactions that are already known - Map metaKnowledge = receiver.getMetaKnowledge(); - int alreadyKnown = metaKnowledge.getOrDefault(owner, -1); + MetaKnowledge metaKnowledge = receiver.getMetaKnowledge(); + int lastKnown = metaKnowledge.getLastKnownBlockNumber(owner); int blockNumber = transaction.getBlockNumber().getAsInt(); - if (alreadyKnown >= blockNumber) return; + if (lastKnown >= blockNumber) return; //Store the highest block number. - chains.compute(owner.getChain(), (k, v) -> v == null ? blockNumber : Math.max(v, blockNumber)); + //Now consider this chain up to the last committed block + chains.merge(owner, blockNumber, Math::max); + if (chains.size() >= nrOfNodes - 1) return; + + //Check all the sources for (Transaction source : transaction.getSource()) { - appendChains2(source, receiver, chains); + appendChains2(nrOfNodes, source, receiver, chains); } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index daa6f38..37b459c 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -1,18 +1,18 @@ package nl.tudelft.blockchain.scaleoutdistributedledger.model; -import lombok.Getter; -import lombok.Setter; -import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.OptionalInt; +import java.util.TreeSet; +import java.util.logging.Level; + import nl.tudelft.blockchain.scaleoutdistributedledger.message.TransactionMessage; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Utils; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.*; -import java.util.Map.Entry; -import java.util.logging.Level; -import nl.tudelft.blockchain.scaleoutdistributedledger.message.BlockMessage; +import lombok.Getter; +import lombok.Setter; /** * Transaction class. diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java index 0c4287c..a902378 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java @@ -185,6 +185,7 @@ public boolean isPresent(Block block) { } else { // Log.log(Level.INFO, "Node" + this.getApp().getLocalStore().getOwnNode() + " checking cache for " + block.getOwner().getId() + "-" + block.getNumber() + ": " + blockHash + ", the set is " + this.cache); // We could miss some blocks in our cache, so update and wait for the results + //TODO We might not want to update here. The cache should be enough updateCacheBlocking(-1); // Log.log(Level.INFO, "Node" + this.getApp().getLocalStore().getOwnNode() + " did not find " + blockHash + ", so updated the cache and now the set is " + this.cache); return cache.contains(blockHash); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java index 429069f..b8ba8d1 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java @@ -363,7 +363,9 @@ public static Block generateGenesisBlock(long amount, Map nodeLis initialTransactions.add(t); } - return new Block(0, null, initialTransactions); + Block block = new Block(0, null, initialTransactions); + block.setNextCommittedBlock(block); + return block; } /** diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java index f38f6e1..5f5074f 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java @@ -2,12 +2,16 @@ import static org.junit.Assert.*; -import java.util.*; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; import org.junit.Before; import org.junit.Test; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.MetaKnowledge; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; import nl.tudelft.blockchain.scaleoutdistributedledger.model.OwnNode; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; @@ -28,7 +32,7 @@ public class TransactionCreatorTest { @Before public void setUp() { ownNode = new OwnNode(0); - + //TestHelper.generateGenesis(ownNode, 5, 100); localStore = new LocalStore(ownNode, null, null, false); nodes = localStore.getNodes(); } @@ -60,9 +64,9 @@ public Node getNode(int id) { * @param chains - the chains to add to the meta knowledge */ public void addMetaKnowledge(Node node, int... chains) { - Map meta = node.getMetaKnowledge(); + MetaKnowledge meta = node.getMetaKnowledge(); for (int chain : chains) { - meta.put(getNode(chain), 0); + meta.updateLastKnownBlockNumber(chain, 2); } } @@ -117,7 +121,7 @@ public Transaction addRemainderMoney(Node to, long remainder) { */ public void checkTransactionSources(Transaction transaction, Transaction... expectedSources) { Set actualSet = transaction.getSource(); - Set expectedSet = new HashSet<>(); + Set expectedSet = new TreeSet<>(); expectedSet.addAll(Arrays.asList(expectedSources)); assertEquals(expectedSet, actualSet); diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java index 894d111..d38a4ab 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java @@ -4,13 +4,17 @@ import static org.mockito.Mockito.*; import java.security.KeyPair; -import java.util.*; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; import org.junit.Before; import org.junit.Test; import nl.tudelft.blockchain.scaleoutdistributedledger.Application; import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; +import nl.tudelft.blockchain.scaleoutdistributedledger.ProofConstructor; import nl.tudelft.blockchain.scaleoutdistributedledger.test.utils.TestHelper; /** @@ -69,6 +73,7 @@ public Transaction basicScenario() { Block block1node1 = new Block(genesisBlock, node1); block1node1.addTransaction(transaction1to0); node1.getChain().getBlocks().add(block1node1); + block1node1.setNextCommittedBlock(block1node1); //4: 0 --> 2, source = [3: 1 --> 0] TreeSet source0to2 = new TreeSet<>(); @@ -80,6 +85,9 @@ public Transaction basicScenario() { //commit block 2 of node 0 block2node0.commit(storeSpy); + block1node0.setNextCommittedBlock(block2node0); + block2node0.setNextCommittedBlock(block2node0); + return transaction0to2; } @@ -113,6 +121,7 @@ public Transaction basicScenario2() { Block block1node1 = new Block(genesisBlock, node1); block1node1.addTransaction(transaction1to0); node1.getChain().getBlocks().add(block1node1); + block1node1.setNextCommittedBlock(block1node1); //5: 0 --> 3, source = [4: 0 --> 1] TreeSet source0to3 = new TreeSet<>(); @@ -132,6 +141,10 @@ public Transaction basicScenario2() { //commit block 3 of node 0 block3node0.commit(storeSpy); + block1node0.setNextCommittedBlock(block3node0); + block2node0.setNextCommittedBlock(block3node0); + block3node0.setNextCommittedBlock(block3node0); + return transaction0to2; } @@ -147,7 +160,7 @@ public void testCreateProof() { Transaction transaction = basicScenario(); - Proof proof = Proof.createProof(storeSpy, transaction); + Proof proof = new ProofConstructor(transaction).constructProof(); //Proof should contain all the blocks of our own chain List ownNodeUpdates = proof.getChainUpdates().get(ownNode); @@ -171,10 +184,10 @@ public void testCreateProof2() { Transaction transaction = basicScenario(); //Node 2 knows about the genesis of node 1 already - node2.getMetaKnowledge().put(node1, 0); + node2.getMetaKnowledge().updateLastKnownBlockNumber(node1, 0); //send to node 2 - Proof proof = Proof.createProof(storeSpy, transaction); + Proof proof = new ProofConstructor(transaction).constructProof(); //Proof should contain the first 3 blocks of the ownNode List ownNodeUpdates = proof.getChainUpdates().get(ownNode); @@ -198,11 +211,11 @@ public void testCreateProof3() { Transaction transaction = basicScenario(); //Node 2 knows about block 1 of node 0 and of block 1 of node 1 - node2.getMetaKnowledge().put(ownNode, 1); - node2.getMetaKnowledge().put(node1, 1); + node2.getMetaKnowledge().updateLastKnownBlockNumber(ownNode, 1); + node2.getMetaKnowledge().updateLastKnownBlockNumber(node1, 1); //send to node 2 - Proof proof = Proof.createProof(storeSpy, transaction); + Proof proof = new ProofConstructor(transaction).constructProof(); //Proof should contain only the last block of node 0 List ownNodeUpdates = proof.getChainUpdates().get(ownNode); @@ -227,11 +240,11 @@ public void testCreateProof4() { //Node 2 knows about block 1 of node 0 and of block 1 of node 1 - node1.getMetaKnowledge().put(ownNode, 1); - node3.getMetaKnowledge().put(ownNode, 2); + node1.getMetaKnowledge().updateLastKnownBlockNumber(ownNode, 1); + node3.getMetaKnowledge().updateLastKnownBlockNumber(ownNode, 2); //send to node 2 - Proof proof = Proof.createProof(storeSpy, transaction); + Proof proof = new ProofConstructor(transaction).constructProof(); //Proof should contain only the last block of node 0 List ownNodeUpdates = proof.getChainUpdates().get(ownNode); From 3bf75c3888307eecf17395c72ecacf52e2b2a3c7 Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Fri, 26 Jan 2018 21:04:34 +0100 Subject: [PATCH 60/69] updating some logs --- .../CommunicationHelper.java | 12 +++++----- .../ProofConstructor.java | 24 ++++++------------- .../SimulationMain.java | 16 ++++++------- .../TransactionSender.java | 22 ++++++++++------- .../model/Block.java | 2 +- .../ITransactionPattern.java | 13 ++++------ 6 files changed, 39 insertions(+), 50 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java index 2b65863..b5f1d26 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java @@ -1,11 +1,10 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; -import java.util.logging.Level; - import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import nl.tudelft.blockchain.scaleoutdistributedledger.validation.ValidationException; -import org.apache.commons.lang3.ObjectUtils; + +import java.util.logging.Level; /** * Helper class for communication. @@ -21,7 +20,7 @@ private CommunicationHelper() { * @return true if the transaction was accepted, false otherwise */ public static boolean receiveTransaction(Proof proof, LocalStore localStore) { - Log.log(Level.INFO, "Received transaction: " + proof.getTransaction()); + Log.log(Level.FINE, "Received transaction: " + proof.getTransaction()); if (proof.getTransaction().getReceiver().getId() != localStore.getOwnNode().getId()) { Log.log(Level.WARNING, "Received a transaction that isn't for us: " + proof.getTransaction()); @@ -34,8 +33,9 @@ public static boolean receiveTransaction(Proof proof, LocalStore localStore) { Log.log(Level.WARNING, "Received an invalid transaction/proof.", ex); return false; } - - Log.log(Level.INFO, "Transaction " + proof.getTransaction() + " is valid, applying updates..."); + + Log.log(Level.INFO, "Received and validated transaction: " + proof.getTransaction()); + Log.log(Level.FINE, "Transaction " + proof.getTransaction() + " is valid, applying updates..."); proof.applyUpdates(localStore); if (proof.getTransaction().getAmount() > 0) { diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java index 8c676be..5cda44d 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java @@ -1,18 +1,8 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.*; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.MetaKnowledge; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; +import java.util.*; /** * Class for constructing proofs. @@ -124,7 +114,7 @@ protected void processBlocks(Node owner, List blocks) { //TODO Temp Integer old = checked2.put(owner, block.getNumber()); if (old != null && old > block.getNumber()) { - System.out.println("CHECKED 2 problem: old was higher (1)"); +// System.out.println("CHECKED 2 problem: old was higher (1)"); } //ENDTODO @@ -149,7 +139,7 @@ protected void processSources(Transaction transaction) { if (checkedSet.contains(blockNumber)) { Integer checked22 = checked2.get(owner); if (checked22 == null || checked22 < blockNumber) { - System.out.println("CHECKED 2 problem: null or less than block number (" + checked22 + ")"); +// System.out.println("CHECKED 2 problem: null or less than block number (" + checked22 + ")"); } continue; @@ -158,11 +148,11 @@ protected void processSources(Transaction transaction) { Block block = owner.getChain().getBlocks().get(blockNumber); int nextCommittedBlockNr = block.getNextCommittedBlock().getNumber(); if (checkedSet.contains(nextCommittedBlockNr)) { - System.out.println("I don't think this should be possible"); +// System.out.println("I don't think this should be possible"); //TODO Temp Integer checked22 = checked2.get(owner); if (checked22 == null || checked22 < nextCommittedBlockNr) { - System.out.println("CHECKED 2 problem: null or less than committed block number (" + checked22 + ")"); +// System.out.println("CHECKED 2 problem: null or less than committed block number (" + checked22 + ")"); } //ENDTODO continue; @@ -179,7 +169,7 @@ protected void processSources(Transaction transaction) { //TODO Temp Integer old = checked2.put(owner, nextCommittedBlockNr); if (old != null && old > nextCommittedBlockNr) { - System.out.println("CHECKED 2 problem: old was higher (2)"); +// System.out.println("CHECKED 2 problem: old was higher (2)"); } //ENDTODO continue; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index d66cb1b..e7da866 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -7,8 +7,6 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.model.OwnNode; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.Simulation; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.tendermint.TendermintHelper; -import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern.ITransactionPattern; -import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern.OnlyNodeZeroTransactionPattern; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern.UniformRandomTransactionPattern; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Utils; @@ -30,16 +28,16 @@ private SimulationMain() {} //SETTINGS //number of local nodes to generate - public static final int LOCAL_NODES_NUMBER = 3; + public static final int LOCAL_NODES_NUMBER = 5; //number of total nodes in the system - public static final int TOTAL_NODES_NUMBER = 3; + public static final int TOTAL_NODES_NUMBER = 5; //number from which our nodes are (e.g if we run nodes (2, 3, 4), then this should be 2 public static final int NODES_FROM_NUMBER = 0; //Whether this main is the master coordinator of the simulation //Note that the master should always be started first public static final boolean IS_MASTER = true; //The duration of the simulation in seconds. Only has an effect when IS_MASTER == true. - public static final int SIMULATION_DURATION = 60; + public static final int SIMULATION_DURATION = 180; /** * @param args - the program arguments @@ -70,7 +68,7 @@ public static void main(String[] args) throws Exception { //update nodes from the tracker Map nodes = new HashMap<>(TOTAL_NODES_NUMBER); TrackerHelper.updateNodes(nodes, null); - final Block genesisBlock = TendermintHelper.generateGenesisBlock(1000, nodes); + final Block genesisBlock = TendermintHelper.generateGenesisBlock(1000000, nodes); //generate genesis.json for all local nodes TendermintHelper.generateGenesisFiles(new Date(), @@ -81,9 +79,9 @@ public static void main(String[] args) throws Exception { // --- PHASE 3: start the actual simulation --- Simulation simulation = new Simulation(IS_MASTER); // ITransactionPattern itp = new OnlyNodeZeroTransactionPattern(10, 20, 1000, 2000, 1); - ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 2); -// UniformRandomTransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 1); -// itp.setSeed(1); +// ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 2); + UniformRandomTransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 100, 200, 10); + itp.setSeed(1); simulation.setTransactionPattern(itp); simulation.runNodesLocally(nodes, ownNodes, genesisBlock, nodeToKeyPair); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index c8a3132..4f15d39 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -1,5 +1,10 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; +import nl.tudelft.blockchain.scaleoutdistributedledger.message.ProofMessage; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.*; +import nl.tudelft.blockchain.scaleoutdistributedledger.sockets.SocketClient; +import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; + import java.io.IOException; import java.util.ListIterator; import java.util.concurrent.Executors; @@ -9,15 +14,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; -import nl.tudelft.blockchain.scaleoutdistributedledger.message.ProofMessage; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Chain; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; -import nl.tudelft.blockchain.scaleoutdistributedledger.sockets.SocketClient; -import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; - /** * Class which handles sending of transactions. */ @@ -165,6 +161,8 @@ private void sendBlock(Block block) { * @throws InterruptedException - If the current thread was interrupted while sending. */ private boolean sendTransaction(Transaction transaction) throws InterruptedException, IOException { + Log.log(Level.FINE, "Node " + transaction.getSender().getId() + " starting sending transaction: " + transaction); + long startingTime = System.currentTimeMillis(); Node to = transaction.getReceiver(); ProofConstructor proofConstructor = new ProofConstructor(transaction); @@ -174,8 +172,14 @@ private boolean sendTransaction(Transaction transaction) throws InterruptedExcep } ProofMessage msg = new ProofMessage(proof); + long timeDelta = System.currentTimeMillis() - startingTime; + if(timeDelta > 5 * 1000) { + Log.log(Level.WARNING, "Proof creation took " + timeDelta + " ms for transaction: " + transaction); + } + Log.log(Level.FINE, "Node " + transaction.getSender().getId() + " now actually sending transaction: " + transaction); if (socketClient.sendMessage(to, msg)) { to.updateMetaKnowledge(proof); + Log.log(Level.FINE, "Node " + transaction.getSender().getId() + " done sending transaction: " + transaction); return true; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index 1bb5cb7..80af575 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -150,7 +150,7 @@ public BlockAbstract calculateBlockAbstract() { * @param localStore - the local store */ public synchronized void commit(LocalStore localStore) { - Log.debug("{0}: Committing block {1}", localStore.getOwnNode().getId(), this.getNumber()); +// Log.debug("{0}: Committing block {1}", localStore.getOwnNode().getId(), this.getNumber()); if (finalized) { throw new IllegalStateException("This block has already been committed!"); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java index 3f499d5..66c9e8b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java @@ -1,19 +1,15 @@ package nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern; -import java.io.Serializable; -import java.util.logging.Level; - import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; import nl.tudelft.blockchain.scaleoutdistributedledger.TransactionCreator; import nl.tudelft.blockchain.scaleoutdistributedledger.TransactionSender; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Chain; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.OwnNode; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.*; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.CancellableInfiniteRunnable; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; +import java.io.Serializable; +import java.util.logging.Level; + /** * Interface for a transaction pattern. */ @@ -71,6 +67,7 @@ public default void doAction(LocalStore localStore) throws InterruptedException //Add block to local chain newBlock = ownNode.getChain().appendNewBlock(); newBlock.addTransaction(transaction); + Log.log(Level.FINE, "Node " + ownNode.getId() + " added transaction " + transaction.getNumber() + " in block " + newBlock.getNumber()); } //Ensure that the block is sent at some point From 24c2c0b951a8a1a58cb99478fe665da9041f8b8d Mon Sep 17 00:00:00 2001 From: Chiel Bruin Date: Wed, 31 Jan 2018 18:16:13 +0100 Subject: [PATCH 61/69] Fixed the README and added some updates to it --- README.md | 149 +++++++++++++++++------------------------------------- 1 file changed, 47 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index e72a210..1ba598c 100644 --- a/README.md +++ b/README.md @@ -1,102 +1,47 @@ -# ScaleOutDistributedLedger -TU Delft Blockchain Engineering course project on scale-out distributed ledger. - -Technical report - -# Data Model # -## Node ## - -### Meta knowledge ### -We keep track of what each node knows. We call this `metaKnowlege`: what we know that they know. Each node contains a Map from nodes to integers, where the integer represents the last block that we know for sure that the receiver has. - -The meta knowledge is updated whenever we receive a transaction from that node and when we send a transaction to that node. - -## Chain ## - -## Block ## - -## BlockAbstract ## - -## Transaction ## - -## Proof ## - -### Smart proof collection ### -When sending a proof for a transaction, we use our metaKnowledge of the receiver to select the blocks that we send. We send only the part of the chain that we expect that the receiver doesn't have, up to the last committed block of that chain. Combined with Algorithm 3, this ensures that we send as little information as possible. - -# The algorithms # -## Algorithm 1 ## -### TODO ### - -## Algorithm 2 ## -### TODO ### - -## Algorithm 3: Non-interactive Smart Transacting Algorithm ## -The algorithm in the paper basically requires the use of a powerset of all unspent transactions. Since this requires O(2^n) sets, this is only feasible for small numbers of n. - -The algorithm implemented is smarter and eliminates bad combinations by keeping track of a best-so-far. It has the same worst case performance, O(2^n), e.g. if all unspent transactions are required for the transaction. If this is not the case however (you send a part of your money), then the performance of the implemented algorithm is much better. The less transactions are required as sources, the better this algorithm performs. The algorithm also benefits from sharding, as transactions with the same requirements (e.g. received from the same source) will be considered as a group instead of individually. - -#### Step 1 #### -First we collect all unspent transactions (these are tracked in application). We then create a TransactionTuple for each transaction, which will calculate which chains would need to be sent based on what the receiver already knows. -We then merge the tuples that have the same chain requirements in O(|transactions|) by hashing them to buckets. - -#### Step 2 #### -Check if there are single transactions / single groups that have a large enough amount to be used as the only source. If we find at least one of these, then all transactions that require more chains than the best single source are directly eliminated. (Combining those transactions with other transactions would only make the required set of chains bigger, so they will never be in the best choice) - -We call the resulting set `candidates`. - -If there are less than two candidates we stop and return the best-so-far. - -#### Step 3 #### -We have a set of TransactionTuples `currentRound`, which is initially set to `candidates`. -We have a set of TransactionTuples `nextRound`, which is initially empty. - -For at most `candidates.size() - 1` rounds, we do the following: -- We iterate over the candidates. We try to combine each candidate tuple with each element of `currentRound`. - - If the combination requires more than or the same number of chains than the best-so-far, then it is eliminated. - - Otherwise: - - If it is able to cover the costs, we set it as best-so-far. - - Otherwise, we add this combination to `nextRound`. -- If there are less than two combinations selected for the next round, then the algorithm returns the best-so-far. -- Otherwise, `currentRound := nextRound` and `nextRound := []` (empty). - -### Result ### -The final combination of transactions returned is the set of sources with minimum amount of chains required. The set found is the largest possible set for its chain requirements, but not necessarily the largest possible set. - -### Performance ### -The performance of the algorithm is still O(2^n) in the worst case. The worst case would be if all transactions are needed to get the amount of money required and none of them can be grouped. Then the algorithm will consider all possible combinations. - -### Future optimizations ### -* It would probably be useful to keep track of how much money we have, to prevent the costly case where we don't have enough money. -* There could be 2 algorithms, one for when we expect many transactions will be required, one for when we expect only a few transactions will be required. The current algorithm works bottom up, the other algorithm could work top down. (How many transactions do we need at minimum to cover the transaction?) - -### Implementation Details ### -BitSets are used as a convenient and efficient way to represent the chains that are required. They represent a sequence of bits of a particular size (e.g. 1000 bits). -* The bit at index 0 represents chain 0, the bit at index 1 represents chain 1, etc. -* A bit with a value of 1 means that the corresponding chain is required. -* Combining the chains required of 2 tuples is done with a bitwise or. -* The number of chains required is the cardinality of a bitset. -* Removing the chains that the receiver already knows is done with a bitwise andNot. - -## Tracker Server -The tracker server can be installed by installing NodeJS and running `npm install` in the tracker-server folder. After this it can be run by running `npm start` in that same folder. - -### Live demo view -A live demo view is available at http://localhost:3000/demo if the trackerserver is running. This demo view shows a live graph and counters for the total number of transactions and the average amount of blocks and chains sent per proof. - -## Main Chain -For the main chain we use [Tendermint](https://tendermint.com/). We can communicate to this chain using the provided ABCI interface. - -### Running tendermint -Tendermint process can be spawned using TendermintHelper class, which takes care of everything that is needed to start it up. -The steps are usually: -1. Generate priv_validator.json file for each node. -2. Generate genesis block, which is then passed to calculate the initial application hash. -3. Generate genesis.json file for each node. -4. Run tendermint process for each of the nodes (this means there is a tendermint processes running at the same time, one for each node). - -Alternatively, running tendermint can also be done with the following command: (the use of which is discouraged though) -`./tendermint node --consensus.create_empty_blocks=false` (Note that Tendermint creates empty blocks by default) -Note: For this method to work, you need to have a config.toml file, genesis.json and priv_validator.json for each node, and/or specify more parameters. - -See the [tendermint documentation](https://tendermint.readthedocs.io/en/master/using-tendermint.html) for more information. +

+ Scale-out Distributed Ledger +

+ +

TU Delft Blockchain Engineering course project on scale-out distributed ledger.

+ +## Paper +This project implements the system described in the following paper: + +> ## A Scale-out Blockchain for Value Transfer with Spontaneous Sharding +> #### By Zhijie Ren and Zekeriya Erkin [[PDF]](https://arxiv.org/abs/1801.02531) +> Blockchain technology, sometimes known by its applications like cryptocurrencies, suffers from the scalability problem mainly due to the unideal throughput of Byzantine fault tolerance consensus algorithms. Recently, many blockchains have been proposed to achieve scale-out throughput, i.e., the throughput of the system grows with the number of nodes. In this paper, we propose a novel scale-out blockchain system for the most considered type of ledgers, we call Value Transferring Ledgers, in which a transaction is a transfer of positive value from one node to another. In our system, nodes commonly agree on a main chain and individually generate their own chains. We propose a locally executable validation scheme with uncompromised validity and scalable throughput. Furthermore, a smart transacting algorithm is introduced so that the system is spontaneously sharded for individual transactions and achieves scale-out throughput. + +An in-depth explanation of the system is available as a technical report in the `docs` folder. + +## Running instructions +### Requirements +- [Java](https://java.com/en/download/) 8 or newer +- [NodeJS](https://nodejs.org/) 6 or newer +- [Tendermint](https://tendermint.com/) 0.14.0 + +### Setup +- Place a tendermint executable (V0.14) called `tendermint.exe` in the root folder of the project. The file-extension of the file must also be present on non-Windows systems. [Download here](https://tendermint.com/downloads) +- Install the tracker server + - In the folder `tracker-server` run `npm install` +- Determine the master machine (when only using a single machine this must also be the master) +- Determine the IP address of the tracker + +### Configuration +Apply these configuration steps for every machine +- In the `SimulationMain`-class: + - Give each machine its sets of node by changing the `LOCAL_NODES_NUMBER`, `TOTAL_NODES_NUMBER`, `NODES_FROM_NUMBER` values + - Specify the parameters for the transaction sending behaviour of each node using the fields `MAX_BLOCKS_PENDING`, `INITIAL_SENDING_DELAY`, `SENDING_WAIT_TIME` and `REQUIRED_COMMITS`. + - Set `IS_MASTER` to `true` for the master machine and to `false` for all the others + - If the current machine is the master, also specify the simulation time in seconds. +- In the `Application`-class: + - Set `TRACKER_SERVER_ADDRESS` and `TRACKER_SERVER_PORT` to point to the server location + +### Running +- Start the tracker server + - In folder `tracker-server` run `npm start` +- Start the master machine by calling the main method in `SimulationMain`. +- Start the other machines the same way as the master. +- A live visualization of the network can be seen by opening `/demo` in a browser. + +### Run Tests +From the root folder run `mvn test`. From 050be8f744d9d23577abb9d01247f7cce16f48d4 Mon Sep 17 00:00:00 2001 From: Chiel Bruin Date: Wed, 31 Jan 2018 18:23:43 +0100 Subject: [PATCH 62/69] Fixed non-terminating tendermint test --- .../model/mainchain/tendermint/TendermintChain.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java index 7411c1c..966f996 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java @@ -77,6 +77,9 @@ protected TendermintChain(ABCIClient client, TSocket socket, Set cac this.cache = cache; this.app = app; this.cacheLock = new Object(); + + this.extraCache = new HashMap<>(); + this.superExtraCache = new HashMap<>(); } /** From e1ce5d4ca9806e3bd7f40b299e534012c20dfe5a Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Wed, 31 Jan 2018 21:27:04 +0100 Subject: [PATCH 63/69] Rewrites and concurrency fixes --- .../scaleoutdistributedledger/LocalStore.java | 46 ++- .../ProofConstructor.java | 12 +- .../SimulationMain.java | 4 +- .../TransactionCreator.java | 50 +-- .../TransactionSender.java | 114 ++++--- .../message/ProofMessage.java | 11 +- .../model/Block.java | 51 ++- .../model/Chain.java | 90 +++-- .../model/ChainView.java | 32 +- .../model/MetaKnowledge.java | 2 +- .../model/Proof.java | 48 +-- .../model/Transaction.java | 1 + .../mainchain/tendermint/TendermintChain.java | 34 +- .../ITransactionPattern.java | 53 ++- .../utils/AppendOnlyArrayList.java | 323 ++++++++++++++++++ .../scaleoutdistributedledger/utils/Log.java | 10 + .../model/ChainViewTest.java | 20 +- .../model/SerializationTest.java | 6 +- .../tendermint/TendermintChainTest.java | 18 +- 19 files changed, 645 insertions(+), 280 deletions(-) create mode 100644 src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/AppendOnlyArrayList.java diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java index ca27496..92ccaf1 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStore.java @@ -33,7 +33,6 @@ public class LocalStore { @Getter private final Verification verification = new Verification(); - @Getter private final Set unspent = new HashSet<>(); @Getter @@ -145,19 +144,30 @@ public Transaction getTransactionFromNode(int nodeId, int blockId, int transacti throw new IllegalStateException("Transaction with id " + transactionId + " in block " + blockId + " from node " + nodeId + " not found."); } + /** + * @return - a copy of the unspent transactions + */ + public Set getUnspent() { + synchronized (unspent) { + return new HashSet<>(unspent); + } + } + /** * Adds the given transaction as unspent. * @param transaction - the transaction to add */ public void addUnspentTransaction(Transaction transaction) { - if (!unspent.add(transaction)) return; - - if (ownNode.equals(transaction.getReceiver())) { - availableMoney += transaction.getAmount(); - } - - if (ownNode.equals(transaction.getSender())) { - availableMoney += transaction.getRemainder(); + synchronized (unspent) { + if (!unspent.add(transaction)) return; + + if (ownNode.equals(transaction.getReceiver())) { + availableMoney += transaction.getAmount(); + } + + if (ownNode.equals(transaction.getSender())) { + availableMoney += transaction.getRemainder(); + } } } @@ -165,14 +175,16 @@ public void addUnspentTransaction(Transaction transaction) { * @param toRemove - the unspent transactions to remove */ public void removeUnspentTransactions(Collection toRemove) { - for (Transaction transaction : toRemove) { - if (!unspent.remove(transaction)) continue; - - if (ownNode.equals(transaction.getReceiver())) { - availableMoney -= transaction.getAmount(); - } - if (ownNode.equals(transaction.getSender())) { - availableMoney -= transaction.getRemainder(); + synchronized (unspent) { + for (Transaction transaction : toRemove) { + if (!unspent.remove(transaction)) continue; + + if (ownNode.equals(transaction.getReceiver())) { + availableMoney -= transaction.getAmount(); + } + if (ownNode.equals(transaction.getSender())) { + availableMoney -= transaction.getRemainder(); + } } } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java index 7df1c78..45a38c4 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java @@ -1,8 +1,15 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; -import java.util.*; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.MetaKnowledge; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; /** * Class for constructing proofs. @@ -131,6 +138,7 @@ protected void processSources(Transaction transaction) { //Determine the blocks that we would need to send. MetaKnowledge metaKnowledge = this.receiver.getMetaKnowledge(); List blocksOfSource = metaKnowledge.getBlocksToSend(owner, nextCommittedBlockNr); + if (blocksOfSource.isEmpty()) continue; processBlocks(owner, blocksOfSource); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index 90342ea..e0e4f9b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -41,9 +41,9 @@ private SimulationMain() {} // Maximum number of blocks waiting to be sent (no new transaction created in the mean time public static final int MAX_BLOCKS_PENDING = 30; // The initial delay in milliseconds to wait before checking for the first time. - public static final long INITIAL_SENDING_DELAY = 15000; + public static final long INITIAL_SENDING_DELAY = 5000; // The time in milliseconds to wait before checking again. - public static final long SENDING_WAIT_TIME = 15000; + public static final long SENDING_WAIT_TIME = 5000; // The number of blocks (with the same or higher block number) that need to be committed before we send a certain block. public static final int REQUIRED_COMMITS = 2; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java index d555a2f..caa6d88 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java @@ -1,6 +1,13 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; -import java.util.*; +import java.util.BitSet; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; @@ -21,7 +28,7 @@ public class TransactionCreator { private final Node sender; private final Node receiver; private final long amount; - private final BitSet known; + //private final BitSet known; private int currentBest = Integer.MAX_VALUE; private TransactionTuple currentBestTuple; @@ -37,27 +44,28 @@ public TransactionCreator(LocalStore localStore, Node receiver, long amount) { this.sender = localStore.getOwnNode(); this.receiver = receiver; this.amount = amount; - this.known = calculateKnowledge(); + //this.known = calculateKnowledge(); } - /** - * Calculates what the receiver already knows about. - * - * @return a bitset with the chains the receiver already knows about - */ - private BitSet calculateKnowledge() { - synchronized (receiver.getMetaKnowledge()) { - BitSet collected = receiver.getMetaKnowledge() - .keySet() - .stream() - .collect(() -> new BitSet(nodesCount), - (bs, i) -> bs.set(i), - (bs1, bs2) -> bs1.or(bs2) - ); - - return collected; - } - } +//TODO IMPORTANT Determine if the current transaction creation method is correct. +// /** +// * Calculates what the receiver already knows about. +// * +// * @return a bitset with the chains the receiver already knows about +// */ +// private BitSet calculateKnowledge() { +// synchronized (receiver.getMetaKnowledge()) { +// BitSet collected = receiver.getMetaKnowledge() +// .keySet() +// .stream() +// .collect(() -> new BitSet(nodesCount), +// (bs, i) -> bs.set(i), +// (bs1, bs2) -> bs1.or(bs2) +// ); +// +// return collected; +// } +// } /** * Creates a transaction. diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index fc244e2..ac7388b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -6,6 +6,8 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.ListIterator; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -17,13 +19,15 @@ /** * Class which handles sending of transactions. */ -public class TransactionSender { +public class TransactionSender implements Runnable { private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); private final LocalStore localStore; private final SocketClient socketClient; private final AtomicInteger taskCounter = new AtomicInteger(); + private final Chain chain; private boolean stopping; + private int alreadySent = -1; /** * Creates a new TransactionSender. @@ -32,6 +36,9 @@ public class TransactionSender { public TransactionSender(LocalStore localStore) { this.localStore = localStore; this.socketClient = new SocketClient(); + this.chain = localStore.getOwnNode().getChain(); + + this.executor.schedule(this, SimulationMain.INITIAL_SENDING_DELAY, TimeUnit.MILLISECONDS); } /** @@ -41,52 +48,59 @@ public TransactionSender(LocalStore localStore) { * @param toSend - the block to send */ public void scheduleBlockSending(Block toSend) { - final Chain chain = toSend.getOwner().getChain(); - Runnable runnable = new Runnable() { - //We need to start - private Block lastCheckedBlock = toSend.getPreviousBlock(); - private int committedBlocks; - - @Override - public void run() { - synchronized (chain) { - //We iterate over all the blocks since the last checked block and count how many are on the main chain. - //We have to check the same parts of the chain every time, since blocks can get committed after we checked them. - ListIterator lit = chain.getBlocks().listIterator(lastCheckedBlock.getNumber() - Block.GENESIS_BLOCK_NUMBER + 1); - while (lit.hasNext() && committedBlocks < SimulationMain.REQUIRED_COMMITS) { - Block block = lit.next(); - if (block.isOnMainChain(localStore)) { - committedBlocks++; - lastCheckedBlock = block; - } - } - } - - //If we didn't find enough blocks, then we reschedule to check again later. - if (!canSend(committedBlocks)) { - schedule(this); - return; - } - - //There are enough blocks on the chain, so we can send the block. - sendBlock(toSend); - taskCounter.decrementAndGet(); - } - }; + if (alreadySent == -1) { + alreadySent = 0; + } + taskCounter.incrementAndGet(); - executor.schedule(runnable, SimulationMain.INITIAL_SENDING_DELAY, TimeUnit.MILLISECONDS); + } + + @Override + public void run() { + //Send all and reschedule + try { + sendAllBlocksThatCanBeSent(); + } catch (Exception ex) { + Log.log(Level.SEVERE, "Uncaught exception in transaction sender!"); + } finally { + executor.schedule(this, SimulationMain.SENDING_WAIT_TIME, TimeUnit.MILLISECONDS); + } } /** - * @param committedBlocks - the number of blocks that have been committed - * @return - if a block can be sent + * Sends all blocks that can be sent. */ - public boolean canSend(int committedBlocks) { - if (stopping && committedBlocks >= 1) { - return true; + public void sendAllBlocksThatCanBeSent() { + if (alreadySent == -1) return; + + //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + //Committed: [0, 2, 4, 6, 8] + //Last sent: 2 + //Committed found: [3 -> 4, 5 -> 6, 7 -> 8] = [4, 6, 8] + //We can send up to 6 (inclusive) + //3 commits found, 2 required + + int lastBlockNr = chain.getLastBlockNumber(); + + //Determine what blocks have been committed since the last batch we sent + List committed = new ArrayList<>(); + for (int index = alreadySent + 1; index <= lastBlockNr; index++) { + Block current = chain.getBlocks().get(index); + Block next = current.getNextCommittedBlock(); + if (next == null || !next.isOnMainChain(localStore)) break; + + committed.add(next.getNumber()); + index = next.getNumber(); } - return committedBlocks >= SimulationMain.REQUIRED_COMMITS; + //Not enough commits + if (committed.size() < SimulationMain.REQUIRED_COMMITS) return; + + //Send all the blocks that we haven't sent up to the committed block (inclusive) + int lastToSend = committed.get(committed.size() - SimulationMain.REQUIRED_COMMITS); + for (int blockNr = alreadySent + 1; blockNr <= lastToSend; blockNr++) { + sendBlock(chain.getBlocks().get(blockNr)); + } } /** @@ -101,7 +115,7 @@ public int blocksWaiting() { * @throws InterruptedException - If we are interrupted while waiting. */ public void waitUntilDone() throws InterruptedException { - while (taskCounter.get() != 0) { + while (alreadySent < chain.getLastBlockNumber()) { Thread.sleep(1000L); } } @@ -121,7 +135,6 @@ public void stop() { public void shutdownNow() { executor.shutdownNow(); socketClient.shutdown(); - taskCounter.set(0); } /** @@ -129,6 +142,7 @@ public void shutdownNow() { * @param block - the block */ private void sendBlock(Block block) { + alreadySent = block.getNumber(); for (Transaction transaction : block.getTransactions()) { try { sendTransaction(transaction); @@ -150,15 +164,13 @@ private boolean sendTransaction(Transaction transaction) throws InterruptedExcep long startingTime = System.currentTimeMillis(); Node to = transaction.getReceiver(); + //TODO IMPORTANT Removed synchronization ProofConstructor proofConstructor = new ProofConstructor(transaction); - Proof proof; - synchronized (localStore.getOwnNode().getChain()) { - proof = proofConstructor.constructProof(); - } + Proof proof = proofConstructor.constructProof(); ProofMessage msg = new ProofMessage(proof); long timeDelta = System.currentTimeMillis() - startingTime; - if(timeDelta > 5 * 1000) { + if (timeDelta > 5 * 1000) { Log.log(Level.WARNING, "Proof creation took " + timeDelta + " ms for transaction: " + transaction); } Log.log(Level.FINE, "Node " + transaction.getSender().getId() + " now actually sending transaction: " + transaction); @@ -170,12 +182,4 @@ private boolean sendTransaction(Transaction transaction) throws InterruptedExcep return false; } - - /** - * @param runnable - the runnable to schedule - * @return - the ScheduledFuture - */ - private ScheduledFuture schedule(Runnable runnable) { - return executor.schedule(runnable, SimulationMain.SENDING_WAIT_TIME, TimeUnit.MILLISECONDS); - } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java index 3518405..52740f8 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java @@ -58,12 +58,11 @@ public ProofMessage(Proof proof) { @Override public void handle(LocalStore localStore) { - synchronized (localStore.getOwnNode().getChain()) { - try { - CommunicationHelper.receiveTransaction(new Proof(this, localStore), localStore); - } catch (IOException e) { - Log.log(Level.SEVERE, "Exception while handling proof message", e); - } + //TODO IMPORTANT Removed synchronized on own chain + try { + CommunicationHelper.receiveTransaction(new Proof(this, localStore), localStore); + } catch (IOException e) { + Log.log(Level.SEVERE, "Exception while handling proof message", e); } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index de0b7dc..4483847 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -98,17 +98,26 @@ public synchronized void addTransaction(Transaction transaction) { } transactions.add(transaction); - transaction.setBlockNumber(this.getNumber()); + transaction.setBlockNumber(getNumber()); } /** * Get hash of the block. * @return Hash SHA256 */ - public synchronized Sha256Hash getHash() { - if (true || this.hash == null) { - this.hash = this.calculateHash(); + public Sha256Hash getHash() { + if (this.hash == null) { + this.hash = calculateHash(); } + + //TODO IMPORTANT Remove this check + Sha256Hash oldHash = this.hash; + Sha256Hash newHash = calculateHash(); + if (!oldHash.equals(newHash)) { + Log.log(Level.SEVERE, "Hashes do not match! old: " + oldHash + ". new: " + newHash); + this.hash = newHash; + } + return this.hash; } @@ -128,7 +137,7 @@ public BlockAbstract calculateBlockAbstract() { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { outputStream.write(Utils.intToByteArray(this.owner.getId())); outputStream.write(Utils.intToByteArray(this.number)); - outputStream.write(this.getHash().getBytes()); + outputStream.write(getHash().getBytes()); attrInBytes = outputStream.toByteArray(); } catch (IOException ex) { throw new IllegalStateException("Unable to write to outputstream", ex); @@ -137,7 +146,7 @@ public BlockAbstract calculateBlockAbstract() { // Sign the attributes try { byte[] signature = ((OwnNode) this.owner).sign(attrInBytes); - BlockAbstract blockAbstract = new BlockAbstract(this.owner.getId(), this.number, this.getHash(), signature); + BlockAbstract blockAbstract = new BlockAbstract(this.owner.getId(), this.number, getHash(), signature); this.hasNoAbstract = false; return blockAbstract; } catch (Exception ex) { @@ -150,26 +159,25 @@ public BlockAbstract calculateBlockAbstract() { * @param localStore - the local store */ public synchronized void commit(LocalStore localStore) { -// Log.debug("{0}: Committing block {1}", localStore.getOwnNode().getId(), this.getNumber()); if (finalized) { throw new IllegalStateException("This block has already been committed!"); } - Chain chain = getOwner().getChain(); - synchronized (chain) { - BlockAbstract blockAbstract = calculateBlockAbstract(); - localStore.getMainChain().commitAbstract(blockAbstract); - getOwner().getChain().setLastCommittedBlock(this); - } + Log.log(Level.FINER, "Committing block " + getNumber(), getOwner().getId()); - finalized = true; + //Commit to the main chain, and set the last committed block + localStore.getMainChain().commitAbstract(calculateBlockAbstract()); + getOwner().getChain().setLastCommittedBlock(this); + //Set next committed block nextCommittedBlock = this; Block prev = getPreviousBlock(); - while (prev.nextCommittedBlock == null) { + while (prev != null && prev.nextCommittedBlock == null) { prev.nextCommittedBlock = this; prev = prev.getPreviousBlock(); } + + finalized = true; } @Override @@ -183,6 +191,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { + //TODO IMPORTANT Remove sysos if (this == obj) return true; if (!(obj instanceof Block)) { System.out.println("Block not equal because one is null"); @@ -195,6 +204,7 @@ public boolean equals(Object obj) { if (other.owner != null) return false; } else if (!this.owner.equals(other.owner)) return false; + //TODO IMPORTANT We might not want to use equals for the previous block (as it will recurse) if (this.previousBlock == null) { if (other.previousBlock != null) return false; } else if (!this.previousBlock.equals(other.previousBlock)) { @@ -220,11 +230,15 @@ private Sha256Hash calculateHash() { try { // Important to keep the order of writings outputStream.write(Utils.intToByteArray(this.number)); + + //TODO IMPORTANT Should we include the hash of the previous block? byte[] prevBlockHash = (this.previousBlock != null) ? this.previousBlock.getHash().getBytes() : new byte[0]; outputStream.write(prevBlockHash); if (this.owner != null) { outputStream.write(Utils.intToByteArray(this.owner.getId())); } + + //TODO IMPORTANT Remove the transactions for testing for (Transaction tx : this.transactions) { outputStream.write(tx.getHash().getBytes()); } @@ -249,6 +263,7 @@ public Block genesisCopy() { block.addTransaction(transaction.genesisCopy()); } + //The genesis block is on the main chain, cannot be modified and is its own committed block block.onMainChain = true; block.finalized = true; block.nextCommittedBlock = block; @@ -273,6 +288,12 @@ public boolean isOnMainChain(LocalStore localStore) { //It is present, so store it and return if (localStore.getMainChain().isPresent(this)) { this.onMainChain = true; + + //TODO Chance onMainChain to this.nextCommittedBlock + if (this.nextCommittedBlock != this) { + Log.debug("Block {0} doesn't have itself as next committed!", this.number); + } + return true; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java index 493356e..cd31f5b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java @@ -2,10 +2,12 @@ import lombok.Getter; -import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; +import nl.tudelft.blockchain.scaleoutdistributedledger.utils.AppendOnlyArrayList; +import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.ReversedIterator; /** @@ -17,7 +19,7 @@ public class Chain { private final Node owner; @Getter - private final List blocks; + private final AppendOnlyArrayList blocks; @Getter private Transaction genesisTransaction; @@ -30,17 +32,7 @@ public class Chain { */ public Chain(Node owner) { this.owner = owner; - this.blocks = new ArrayList<>(); - } - - /** - * Constructor. - * @param owner - the owner of this chain. - * @param blocks - list of blocks in this chain. - */ - public Chain(Node owner, List blocks) { - this.owner = owner; - this.blocks = blocks; + this.blocks = new AppendOnlyArrayList<>(); } /** @@ -50,47 +42,49 @@ public Chain(Node owner, List blocks) { * @param localStore - the localStore * @throws UnsupportedOperationException - If this chain is owned by us. */ - public synchronized void update(List updates, LocalStore localStore) { + public void update(List updates, LocalStore localStore) { if (owner instanceof OwnNode) throw new UnsupportedOperationException("You cannot use update to update your own chain"); if (updates.isEmpty()) return; - //TODO remove - boolean asserts = false; - assert asserts = true; - if (asserts) { - ArrayList copy = new ArrayList<>(updates); - copy.sort((a, b) -> Integer.compare(a.getNumber(), b.getNumber())); - if (!copy.equals(updates)) { - throw new IllegalArgumentException("The blocks are not ordered correctly :("); - } - } + Block lastCommitted = updates.get(updates.size() - 1); - int nextNr; - Block previousBlock; - if (blocks.isEmpty()) { - //Should start at 0, there is no previous block - nextNr = 0; - previousBlock = null; - } else { - //Should start with the first block after our last block. - Block lastBlock = blocks.get(blocks.size() - 1); - nextNr = lastBlock.getNumber() + 1; - previousBlock = lastBlock; + synchronized (this) { + int nextNr; + Block previousBlock; + if (blocks.isEmpty()) { + //Should start at 0, there is no previous block + nextNr = 0; + previousBlock = null; + } else { + //Should start with the first block after our last block. + Block lastBlock = blocks.get(blocks.size() - 1); + nextNr = lastBlock.getNumber() + 1; + previousBlock = lastBlock; + } + + //The last block in the updates must be a committed block + for (Block block : updates) { + //Skip any overlap + if (block.getNumber() != nextNr) continue; + block.setPreviousBlock(previousBlock); + + //TODO IMPORTANT We might want to set the next committed block in a better way. (Send it) + block.setNextCommittedBlock(lastCommitted); + blocks.add(block); + nextNr++; + previousBlock = block; + } + + setLastCommittedBlock(lastCommitted); } - //The last block in the updates must be a committed block - Block lastCommitted = updates.get(updates.size() - 1); - for (Block block : updates) { - //Skip any overlap - if (block.getNumber() != nextNr) continue; - block.setPreviousBlock(previousBlock); - block.setNextCommittedBlock(lastCommitted); - blocks.add(block); - nextNr++; - previousBlock = block; + //TODO IMPORTANT Remove after debugging + if (!lastCommitted.isOnMainChain(localStore)) { + Log.log(Level.SEVERE, "The last block received is not a committed block!"); } + //TODO IMPORTANT do we need this? //TODO Set last committed block for (Block block : ReversedIterator.reversed(this.blocks)) { if (block.isOnMainChain(localStore)) { @@ -103,7 +97,7 @@ public synchronized void update(List updates, LocalStore localStore) { /** * @return - the genesis block */ - public synchronized Block getGenesisBlock() { + public Block getGenesisBlock() { if (blocks.isEmpty()) return null; return blocks.get(0); @@ -128,7 +122,7 @@ public synchronized void setGenesisBlock(Block genesisBlock) { /** * @return the last block in this chain */ - public synchronized Block getLastBlock() { + public Block getLastBlock() { if (blocks.isEmpty()) return null; return blocks.get(blocks.size() - 1); @@ -149,7 +143,7 @@ public int getLastBlockNumber() { /** * @return - the last block that was committed to the main chain */ - public synchronized Block getLastCommittedBlock() { + public Block getLastCommittedBlock() { return lastCommittedBlock; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java index dcd0fdc..2c972d8 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java @@ -17,6 +17,7 @@ public class ChainView implements Iterable { private Boolean valid; private boolean trim; private int startIndex; + private int chainSize; /** * @param chain @@ -25,10 +26,7 @@ public class ChainView implements Iterable { * the blocks of this chain that were sent with the proof */ public ChainView(Chain chain, List updates) { - this.chain = chain; - this.updates = updates; - if (this.updates == null) this.updates = new ArrayList<>(); - this.trim = true; + this(chain, updates, true); } /** @@ -41,6 +39,7 @@ public ChainView(Chain chain, List updates, boolean trim) { this.updates = updates; if (this.updates == null) this.updates = new ArrayList<>(); this.trim = trim; + this.chainSize = chain.getBlocks().size(); } /** @@ -92,14 +91,15 @@ private boolean checkIntegrity() { return true; } + chainSize = chain.getBlocks().size(); + //If we had no blocks, then we only need to check for gaps - List blocks = chain.getBlocks(); int firstUpdateNumber = updates.get(0).getNumber(); - if (blocks.isEmpty()) { + if (chainSize == 0) { return checkNoGaps(1, firstUpdateNumber); } - int lastOwnNumber = blocks.get(blocks.size() - 1).getNumber(); + int lastOwnNumber = chainSize - 1; if (firstUpdateNumber - lastOwnNumber > 1) { //We are missing blocks between what we know and what we were sent! Can never happen with an honest node. System.out.println("HERE1"); @@ -109,9 +109,9 @@ private boolean checkIntegrity() { //There is overlap, check if exactly matches our view //At the same time, we will remove the overlapping elements int overlap = lastOwnNumber + 1 - firstUpdateNumber; - int baseI = blocks.size() - overlap; + int baseI = chainSize - overlap; for (int i = 0; i < overlap && !updates.isEmpty() && startIndex < updates.size(); i++) { - Block ownBlock = blocks.get(baseI + i); + Block ownBlock = chain.getBlocks().get(baseI + i); Block updatedBlock = updates.get(startIndex); //TODO we might need a special equality check @@ -173,10 +173,11 @@ private boolean checkNoGaps(int startIndex, int previousNr) { * of this ChainView. */ public Block getBlock(int number) { + //TODO IMPORTANT chainSize instead of the actual size?? if (number < chain.getBlocks().size()) { return chain.getBlocks().get(number); } else if (isValid()) { - int index = number - chain.getBlocks().size() + startIndex; + int index = number - chainSize + startIndex; return updates.get(index); } else { throw new IllegalStateException( @@ -218,20 +219,19 @@ private class ChainViewIterator implements ListIterator { private int currentIndex; ChainViewIterator() { - chainIterator = chain.getBlocks().listIterator(); + chainIterator = chain.getBlocks().listIterator(0, chainSize); updatesIterator = updates.subList(startIndex, updates.size()).listIterator(); currentIndex = -1; } ChainViewIterator(int number) { - int chainLength = chain.getBlocks().size(); - if (number < chainLength) { - chainIterator = chain.getBlocks().listIterator(number); + if (number < chainSize) { + chainIterator = chain.getBlocks().listIterator(number, chainSize); updatesIterator = updates.subList(startIndex, updates.size()).listIterator(); } else { - int index = number - chainLength; + int index = number - chainSize; updatesIterator = updates.subList(startIndex, updates.size()).listIterator(index); - chainIterator = chain.getBlocks().listIterator(chainLength); + chainIterator = chain.getBlocks().listIterator(chainSize, chainSize); updatesReached = true; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/MetaKnowledge.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/MetaKnowledge.java index 5911394..8b8ed27 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/MetaKnowledge.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/MetaKnowledge.java @@ -69,7 +69,7 @@ public int getLastKnownBlockNumber(Node node) { * @param nodeId - the id of the node * @return - the number of the last block from the given node that is known by owner */ - public synchronized int getLastKnownBlockNumber(int nodeId) { + public int getLastKnownBlockNumber(int nodeId) { if (nodeId == owner.getId()) return owner.getChain().getLastBlockNumber(); return getOrDefault(nodeId, -1); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index ad4836b..121477c 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -4,21 +4,16 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; import nl.tudelft.blockchain.scaleoutdistributedledger.message.ProofMessage; import nl.tudelft.blockchain.scaleoutdistributedledger.message.BlockMessage; -import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import nl.tudelft.blockchain.scaleoutdistributedledger.validation.ProofValidationException; import nl.tudelft.blockchain.scaleoutdistributedledger.validation.ValidationException; import java.io.IOException; import java.util.ArrayList; -import java.util.Comparator; import java.util.HashMap; import java.util.List; -import java.util.ListIterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.logging.Level; -import java.util.stream.Collectors; /** * Proof class. @@ -42,16 +37,6 @@ public Proof(Transaction transaction) { this.chainUpdates = new HashMap<>(); } - /** - * Constructor. - * @param transaction - the transaction to be proven. - * @param chainUpdates - a map of chain updates - */ - public Proof(Transaction transaction, Map> chainUpdates) { - this.transaction = transaction; - this.chainUpdates = chainUpdates; - } - /** * Constructor to decode a proof message. * @param proofMessage - proof received from the network @@ -64,12 +49,13 @@ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOExceptio // Decode the transactions while skipping sources for (Map.Entry> entry : proofMessage.getChainUpdates().entrySet()) { List blocks = new ArrayList<>(); + //TODO Turn into normal for loop entry.getValue().forEach(blockMessage -> blocks.add(blockMessage.toBlockWithoutSources(localStore))); chainUpdates.put(localStore.getNode(entry.getKey()), blocks); } // Fix backlinks - this.fixPreviousBlockPointersAndOrder(); + this.fixPreviousBlockPointers(); // Fix the sources this.fixTransactionSources(localStore); @@ -80,12 +66,11 @@ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOExceptio .getTransaction(proofMessage.getTransactionMessage().getNumber()); } - private void fixPreviousBlockPointersAndOrder() { + private void fixPreviousBlockPointers() { for (Entry> entry : this.chainUpdates.entrySet()) { Node node = entry.getKey(); List updates = entry.getValue(); - - updates.sort(Comparator.comparingInt(Block::getNumber)); + if (updates.isEmpty()) continue; Block previousBlock = null; for (int i = 0; i < updates.size(); i++) { @@ -143,6 +128,7 @@ public void addBlock(Block block) { * @param end - the block to end at (exclusive) */ public void addBlocksOfChain(Chain chain, int start, int end) { + //TODO IMPORTANT Is this check correct? Shouldn't it be start > end? And the end also seems strange. if (start >= end || end > chain.getBlocks().size()) return; List blocks = chainUpdates.get(chain.getOwner()); @@ -277,24 +263,6 @@ public void applyUpdates(LocalStore localStore) { //Update the meta knowledge of the sender transaction.getSender().updateMetaKnowledge(this); } - - /** - * @param localStore - * @param blockRequired - * @param senderChain - * @return - */ - private static Block getNextCommittedBlock(LocalStore localStore, int blockRequired, Chain senderChain) { - ListIterator it = senderChain.getBlocks().listIterator(blockRequired); - while (it.hasNext()) { - Block block = it.next(); - if (block.isOnMainChain(localStore)) { - return block; - } - } - - throw new IllegalStateException("There is no next committed block!"); - } /** * Recursively calls itself with all the sources of the given transaction. Transactions which @@ -352,12 +320,10 @@ public static void appendChains2(int nrOfNodes, Transaction transaction, Node re /** * Gets the number of blocks used in the proof. - * @return - the number of blocks; + * @return - the number of blocks */ public int getNumberOfBlocks() { - final int[] res = {0}; - chainUpdates.values().forEach(blocks -> res[0] += blocks.size()); - return res[0]; + return chainUpdates.values().stream().mapToInt(List::size).sum(); } @Override diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index 3bba731..b625ac9 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -91,6 +91,7 @@ public OptionalInt getBlockNumber() { if (this.sender == null) { this.blockNumber = OptionalInt.of(Block.GENESIS_BLOCK_NUMBER); } else { + //TODO IMPORTANT We don't want this to be called really. for (Block block : sender.getChain().getBlocks()) { if (block.getTransactions().contains(this)) { this.blockNumber = OptionalInt.of(block.getNumber()); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java index 966f996..ec5083f 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java @@ -28,9 +28,9 @@ public final class TendermintChain implements MainChain { private TSocket socket; private ExecutorService threadPool; private Set cache; - private final Object cacheLock; - private Map> extraCache; - private Map superExtraCache; + private final Object cacheLock = new Object(); + private Map> extraCache = new HashMap<>(); + private Map superExtraCache = new HashMap<>(); @Getter private long currentHeight; @Getter @@ -40,22 +40,22 @@ public final class TendermintChain implements MainChain { * Create and start the ABCI app (server) to connect with Tendermint on the default port (46658). * Also uses (port - 1), which Tendermint should listen on for RPC (rpc.laddr) * @param genesisBlock - the genesis (initial) block for the entire system + * @param app - the application */ public TendermintChain(Block genesisBlock, Application app) { this(DEFAULT_ABCI_SERVER_PORT, genesisBlock, app); } + /** * Create and start the ABCI app (server) to connect with Tendermint on the given port. * Also uses (port - 1), which Tendermint should listen on for RPC (rpc.laddr) - * @param port - the port on which we run the server + * @param port - the port on which we run the server * @param genesisBlock - the genesis (initial) block for the entire system + * @param app - the application */ public TendermintChain(final int port, Block genesisBlock, Application app) { this.abciServerPort = port; this.cache = new HashSet<>(); - this.cacheLock = new Object(); - this.extraCache = new HashMap<>(); - this.superExtraCache = new HashMap<>(); this.app = app; this.socket = new TSocket(); @@ -76,10 +76,6 @@ protected TendermintChain(ABCIClient client, TSocket socket, Set cac this.socket = socket; this.cache = cache; this.app = app; - this.cacheLock = new Object(); - - this.extraCache = new HashMap<>(); - this.superExtraCache = new HashMap<>(); } /** @@ -108,7 +104,7 @@ protected void initialUpdateCache() { boolean updated = false; do { try { - updateCacheBlocking(-1); + updateCacheBlocking(-1, false); updated = true; } catch (Exception e) { int retryTime = 3; @@ -132,9 +128,10 @@ protected void initialUpdateCache() { */ protected void updateCache(long height) { if (client == null) return; // If in startup - this.threadPool.submit(() -> updateCacheBlocking(height)); + this.threadPool.submit(() -> updateCacheBlocking(height, false)); } + //TODO IMPORTANT Remove the extra /** * Update the cache of the chain. * Note that this method is blocking and execution may therefore take a while, @@ -142,7 +139,7 @@ protected void updateCache(long height) { * * @param height - The height to update to, if -1 check the needed height with Tendermint */ - private void updateCacheBlocking(long height) { + private void updateCacheBlocking(long height, boolean extra) { if (height == -1) { height = this.client.status().getLong("latest_block_height"); } @@ -155,7 +152,9 @@ private void updateCacheBlocking(long height) { } synchronized (cacheLock) { for (BlockAbstract abs : abstractsAtCurrentHeight) { - cache.add(abs.getBlockHash()); + if (cache.add(abs.getBlockHash()) && extra) { + Log.debug("{0}: updateCacheBlocking caused new block to be added", getApp().getLocalStore().getOwnNode().getId()); + } int blockNum = abs.getBlockNumber(); int owner = abs.getOwnerNodeId(); Set setOfBlockNums = extraCache.getOrDefault(owner, new HashSet<>()); @@ -198,13 +197,16 @@ public Sha256Hash commitAbstract(BlockAbstract abs) { @Override public boolean isPresent(Block block) { Sha256Hash blockHash = block.getHash(); +// return cache.contains(blockHash); + + //TODO IMPORTANT Decide what to do here if (cache.contains(blockHash)) { return true; } else { // Log.log(Level.INFO, "Node" + this.getApp().getLocalStore().getOwnNode() + " checking cache for " + block.getOwner().getId() + "-" + block.getNumber() + ": " + blockHash + ", the set is " + this.cache); // We could miss some blocks in our cache, so update and wait for the results //TODO We might not want to update here. The cache should be enough - updateCacheBlocking(-1); + updateCacheBlocking(-1, true); // Log.log(Level.INFO, "Node" + this.getApp().getLocalStore().getOwnNode() + " did not find " + blockHash + ", so updated the cache and now the set is " + this.cache); return cache.contains(blockHash); //TODO: We might want to check the actual main chain in the false case diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java index 99d52e7..52e00ec 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java @@ -49,8 +49,8 @@ public default void doAction(LocalStore localStore) throws InterruptedException // Make sure we have some room if (localStore.getApplication().getTransactionSender().blocksWaiting() >= SimulationMain.MAX_BLOCKS_PENDING) { - Log.log(Level.FINE, "Too many blocks pending, skipping transaction creation!"); - return; + Log.log(Level.FINE, "Too many blocks pending, maybe slow down!"); +// return; } //Select receiver and amount @@ -65,17 +65,17 @@ public default void doAction(LocalStore localStore) throws InterruptedException OwnNode ownNode = localStore.getOwnNode(); Block newBlock; Log.log(Level.FINE, "Going to make transaction: $ " + amount + " from " + ownNode.getId() + " -> " + receiver.getId()); - synchronized (ownNode.getChain()) { - //Create the transaction - TransactionCreator creator = new TransactionCreator(localStore, receiver, amount); - Transaction transaction = creator.createTransaction(); - //TODO how many transactions do we put in one block? - //Add block to local chain - newBlock = ownNode.getChain().appendNewBlock(); - newBlock.addTransaction(transaction); - Log.log(Level.FINE, "Node " + ownNode.getId() + " added transaction " + transaction.getNumber() + " in block " + newBlock.getNumber()); - } + //TODO IMPORTANT Removed synchronization on own chain. Transaction creator should be safe. + //Create the transaction + TransactionCreator creator = new TransactionCreator(localStore, receiver, amount); + Transaction transaction = creator.createTransaction(); + + //TODO how many transactions do we put in one block? + //Add block to local chain + newBlock = ownNode.getChain().appendNewBlock(); + newBlock.addTransaction(transaction); + Log.log(Level.FINE, "Node " + ownNode.getId() + " added transaction " + transaction.getNumber() + " in block " + newBlock.getNumber()); //Ensure that the block is sent at some point localStore.getApplication().getTransactionSender().scheduleBlockSending(newBlock); @@ -102,16 +102,16 @@ public default boolean shouldCommitBlocks(Block lastBlock, Block lastCommitted) */ public default void commitBlocks(LocalStore localStore, boolean force) throws InterruptedException { Chain ownChain = localStore.getOwnNode().getChain(); - synchronized (ownChain) { - Block lastBlock = ownChain.getLastBlock(); - Block lastCommitted = ownChain.getLastCommittedBlock(); - - //Don't commit if we don't have anything to commit - if (lastBlock == lastCommitted) return; - - if (force || shouldCommitBlocks(lastBlock, lastCommitted)) { - lastBlock.commit(localStore); - } + + //TODO IMPORTANT Removed synchronization on own chain + Block lastBlock = ownChain.getLastBlock(); + Block lastCommitted = ownChain.getLastCommittedBlock(); + + //Don't commit if we don't have anything to commit + if (lastBlock == lastCommitted) return; + + if (force || shouldCommitBlocks(lastBlock, lastCommitted)) { + lastBlock.commit(localStore); } } @@ -121,11 +121,10 @@ public default void commitBlocks(LocalStore localStore, boolean force) throws In */ public default void commitExtraEmpty(LocalStore localStore) { Chain ownChain = localStore.getOwnNode().getChain(); - for (int i = 0; i < SimulationMain.REQUIRED_COMMITS; i++) { - synchronized (ownChain) { - Block block = ownChain.appendNewBlock(); - block.commit(localStore); - } + for (int i = 0; i < SimulationMain.REQUIRED_COMMITS + 1; i++) { + //TODO IMPORTANT (LOW) Removed synchronization on own chain (methods perform the synchronization already). + Block block = ownChain.appendNewBlock(); + block.commit(localStore); } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/AppendOnlyArrayList.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/AppendOnlyArrayList.java new file mode 100644 index 0000000..acdcfb0 --- /dev/null +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/AppendOnlyArrayList.java @@ -0,0 +1,323 @@ +package nl.tudelft.blockchain.scaleoutdistributedledger.utils; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.RandomAccess; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +/** + * An ArrayList to which you can only append. + * The iterators of this class all reflect the list from the moment of creation. Any elements + * appended to the list after the iterator was created will not be reflected. + * + * @param - the type of elements in the list + */ +public class AppendOnlyArrayList extends ArrayList { + private static final long serialVersionUID = 1L; + + @Override + public void add(int index, E element) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(int index, Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public E set(int index, E element) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public E remove(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeIf(Predicate filter) { + throw new UnsupportedOperationException(); + } + + @Override + public void replaceAll(UnaryOperator operator) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + protected void removeRange(int fromIndex, int toIndex) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public Iterator iterator() { + return listIterator(); + } + + @Override + public ListIterator listIterator() { + return new AppendOnlyArrayListIterator(0); + } + + @Override + public ListIterator listIterator(int index) { + return new AppendOnlyArrayListIterator(index); + } + + /** + * This method returns a list iterator that provides a view from index 0 to the given size, + * starting at the given index. + * @param index - the index where the list iterator starts + * @param size - the size that the list iterator will use as maximum + * @return - a new List Iterator starting at the given index + */ + public ListIterator listIterator(int index, int size) { + return new AppendOnlyArrayListIterator(index, size); + } + + /** + * List iterator for this class. + */ + private class AppendOnlyArrayListIterator implements ListIterator { + private int index; + private int size; + + /** + * @param index - the index to start at + */ + AppendOnlyArrayListIterator(int index) { + this(index, size()); + } + + /** + * @param index - the index to start at + * @param size - the size to use + */ + AppendOnlyArrayListIterator(int index, int size) { + this.index = index; + this.size = size; + + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException("Index: " + index); + } + } + + @Override + public boolean hasNext() { + return index != size; + } + + @Override + public E next() { + int i = index; + if (i >= size) throw new NoSuchElementException(); + + E element = get(i); + index = i + 1; + return element; + } + + @Override + public boolean hasPrevious() { + return index != 0; + } + + @Override + public E previous() { + int i = index - 1; + if (i < 0) throw new NoSuchElementException(); + + E element = get(i); + index = i; + return element; + } + + @Override + public int nextIndex() { + return index; + } + + @Override + public int previousIndex() { + return index - 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public void set(E e) { + throw new UnsupportedOperationException(); + } + + @Override + public void add(E e) { + throw new UnsupportedOperationException(); + } + } + + @Override + public List subList(int fromIndex, int toIndex) { + if (fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); + if (toIndex > size()) throw new IndexOutOfBoundsException("toIndex = " + toIndex); + if (fromIndex > toIndex) throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); + return new SubList(0, fromIndex, toIndex); + } + + /** + * Sublist implementation for AppendOnlyArrayList. + */ + private class SubList extends AbstractList implements RandomAccess { + private final int offset; + private final int size; + + SubList(int offset, int fromIndex, int toIndex) { + this.offset = offset + fromIndex; + this.size = toIndex - fromIndex; + } + + @Override + public E set(int index, E e) { + throw new UnsupportedOperationException(); + } + + @Override + public E get(int index) { + if (index < 0 || index >= this.size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size); + return AppendOnlyArrayList.this.get(offset + index); + } + + @Override + public int size() { + return this.size; + } + + @Override + public void add(int index, E e) { + throw new UnsupportedOperationException(); + } + + @Override + public E remove(int index) { + throw new UnsupportedOperationException(); + } + + @Override + protected void removeRange(int fromIndex, int toIndex) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(int index, Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public Iterator iterator() { + return listIterator(); + } + + @Override + public ListIterator listIterator(final int index) { + if (index < 0 || index > this.size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size); + final int offset = this.offset; + + return new ListIterator() { + int cursor = index; + + @Override + public boolean hasNext() { + return cursor != SubList.this.size; + } + + @Override + public E next() { + int i = cursor; + if (i >= SubList.this.size) throw new NoSuchElementException(); + cursor = i + 1; + return AppendOnlyArrayList.this.get(offset + i); + } + + @Override + public boolean hasPrevious() { + return cursor != 0; + } + + @Override + public E previous() { + int i = cursor - 1; + if (i < 0) throw new NoSuchElementException(); + cursor = i; + return AppendOnlyArrayList.this.get(offset + i); + } + + @Override + public int nextIndex() { + return cursor; + } + + @Override + public int previousIndex() { + return cursor - 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public void set(E e) { + throw new UnsupportedOperationException(); + } + + @Override + public void add(E e) { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public List subList(int fromIndex, int toIndex) { + if (fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); + if (toIndex > size) throw new IndexOutOfBoundsException("toIndex = " + toIndex); + if (fromIndex > toIndex) throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); + return new SubList(offset, fromIndex, toIndex); + } + } +} diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/Log.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/Log.java index 79149bb..31478b4 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/Log.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/Log.java @@ -79,6 +79,16 @@ public static void log(Level level, String str) { Logger.getLogger(getCallerClassName()).log(level, str); } + /** + * Logs the given message for the given node id. + * @param level - level to log at + * @param str - message to log + * @param nodeId - the id of the node + */ + public static void log(Level level, String str, int nodeId) { + Logger.getLogger(getCallerClassName()).log(level, "[" + nodeId + "] " + str); + } + /** * Logs the given debug message. * @param msg - the message diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainViewTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainViewTest.java index d9a8ce2..d946112 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainViewTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainViewTest.java @@ -10,6 +10,8 @@ import org.junit.Before; import org.junit.Test; +import nl.tudelft.blockchain.scaleoutdistributedledger.utils.AppendOnlyArrayList; + /** * Test class for {@link ChainView}. */ @@ -17,7 +19,7 @@ public class ChainViewTest { private ChainView chainview; private Chain chainMock; private Node nodeMock; - private List blocks; + private AppendOnlyArrayList blocks; private List updatedBlocks; /** @@ -25,7 +27,7 @@ public class ChainViewTest { */ @Before public void setUp() { - blocks = new ArrayList(); + blocks = new AppendOnlyArrayList(); updatedBlocks = new ArrayList(); nodeMock = mock(Node.class); @@ -65,6 +67,7 @@ public void testIsValid_Valid() { addBlock(0, true); addBlock(1, true); addBlock(2, false); + chainview.isValid(); assertTrue(chainview.isValid()); } @@ -76,6 +79,7 @@ public void testIsValid_Valid() { public void testIsValid_EmptyUpdate() { addBlock(0, true); addBlock(1, true); + chainview.isValid(); assertTrue(chainview.isValid()); } @@ -88,6 +92,7 @@ public void testIsValid_Missing() { addBlock(0, true); addBlock(1, true); addBlock(3, false); + chainview.isValid(); assertFalse(chainview.isValid()); } @@ -101,6 +106,7 @@ public void testIsValid_Gap() { addBlock(1, true); addBlock(2, false); addBlock(4, false); + chainview.isValid(); assertFalse(chainview.isValid()); } @@ -116,6 +122,7 @@ public void testIsValid_OverlapCorrect() { addBlock(1, false); addBlock(2, false); addBlock(3, false); + chainview.isValid(); assertTrue(chainview.isValid()); } @@ -130,6 +137,7 @@ public void testIsValid_OverlapIncorrect() { addBlock(2, true); addBlock(1, false); addBlock(3, false); + chainview.isValid(); assertFalse(chainview.isValid()); } @@ -141,6 +149,7 @@ public void testIsValid_OverlapIncorrect() { public void testGetBlock() { addBlock(0, true); addBlock(1, true); + chainview.isValid(); assertEquals(0, chainview.getBlock(0).getNumber()); assertEquals(1, chainview.getBlock(1).getNumber()); @@ -155,6 +164,7 @@ public void testGetBlock_Overlap() { addBlock(0, true); Block good1 = addBlock(1, true); addBlock(1, false); + chainview.isValid(); assertSame(good1, chainview.getBlock(1)); } @@ -168,6 +178,7 @@ public void testGetBlock_UpdatePart() { addBlock(0, true); addBlock(1, true); addBlock(2, false); + chainview.isValid(); assertEquals(2, chainview.getBlock(2).getNumber()); } @@ -179,6 +190,7 @@ public void testGetBlock_UpdatePart() { public void testIteratorNextIndex() { addBlock(0, true); addBlock(1, false); + chainview.isValid(); ListIterator it = chainview.iterator(); assertEquals(0, it.nextIndex()); @@ -196,6 +208,7 @@ public void testListIteratorNextIndex() { addBlock(0, true); addBlock(1, false); addBlock(2, false); + chainview.isValid(); ListIterator it = chainview.listIterator(2); assertEquals(2, it.nextIndex()); @@ -212,6 +225,7 @@ public void testListIteratorNextIndex() { public void testIteratorPreviousIndex() { addBlock(0, true); addBlock(1, false); + chainview.isValid(); ListIterator it = chainview.iterator(); assertEquals(-1, it.previousIndex()); @@ -228,6 +242,7 @@ public void testIteratorPreviousIndex() { public void testListIteratorNextPrevious() { addBlock(0, true); addBlock(1, false); + chainview.isValid(); ListIterator it = chainview.listIterator(); assertEquals(0, it.next().getNumber()); @@ -243,6 +258,7 @@ public void testListIteratorPreviousIndex() { addBlock(0, true); addBlock(1, false); addBlock(2, false); + chainview.isValid(); ListIterator it = chainview.listIterator(2); assertEquals(1, it.previousIndex()); diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java index c9abdd7..924f1f9 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java @@ -78,7 +78,7 @@ public void setUp() { // Add Proof this.proof = new Proof(this.transaction); // Manually change the chainUpdates - List listBlockUpdate = this.bobNode.getChain().getBlocks(); + List listBlockUpdate = new ArrayList<>(this.bobNode.getChain().getBlocks()); listBlockUpdate.remove(0); this.proof.getChainUpdates().put(this.bobNode, listBlockUpdate); } @@ -219,8 +219,8 @@ public void testDecoding_Valid() throws IOException { // Create Proof Proof originalProof = new Proof(newTransaction); // Manually change the chainUpdates - originalProof.getChainUpdates().put(this.aliceNode, this.aliceNode.getChain().getBlocks()); - originalProof.getChainUpdates().put(this.bobNode, this.bobNode.getChain().getBlocks()); + originalProof.getChainUpdates().put(this.aliceNode, new ArrayList<>(this.aliceNode.getChain().getBlocks())); + originalProof.getChainUpdates().put(this.bobNode, new ArrayList<>(this.bobNode.getChain().getBlocks())); // Encode Proof into ProofMessage ProofMessage encodedProof = new ProofMessage(originalProof); // Decode ProofMessage into Proof diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChainTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChainTest.java index 4bc397d..4f70696 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChainTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChainTest.java @@ -21,7 +21,6 @@ import java.util.Set; import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.*; /** @@ -57,10 +56,11 @@ public void setUp() { */ @Test public void testInitialUpdateCache() { - when(clientMock.query(anyLong())).thenAnswer(new Answer() { + when(clientMock.query(anyLong())).thenAnswer(new Answer>() { private int c; - public Object answer(InvocationOnMock invocation) { + @Override + public List answer(InvocationOnMock invocation) { List data = new ArrayList<>(); if (c == 0) { data.add(new BlockAbstract(0, 0, new Sha256Hash(Utils.hexStringToBytes("FF44")), null)); @@ -73,14 +73,16 @@ public Object answer(InvocationOnMock invocation) { } }); - when(clientMock.status()).thenAnswer(new Answer() { + when(clientMock.status()).thenAnswer(new Answer() { private JSONObject json; - public Object answer(InvocationOnMock invocation) { + @Override + public JSONObject answer(InvocationOnMock invocation) { + if (json == null) { json = new JSONObject(); } else { - json.put("latest_block_height", 2l); + json.put("latest_block_height", 2L); } return json; } @@ -106,7 +108,7 @@ public void testStop() { */ @Test public void testCommitAbstract() { - BlockAbstract abs = new BlockAbstract(0, 0, Sha256Hash.withHash(Utils.hexStringToBytes("11FF")) , null); + BlockAbstract abs = new BlockAbstract(0, 0, Sha256Hash.withHash(Utils.hexStringToBytes("11FF")), null); Sha256Hash hash = Sha256Hash.withHash(Utils.hexStringToBytes("FF11")); when(clientMock.commit(any(BlockAbstract.class))).thenReturn(Utils.hexStringToBytes("FF11")); @@ -122,7 +124,7 @@ public void testCommitAbstract() { */ @Test public void testCommitAbstractFail() { - BlockAbstract abs = new BlockAbstract(0, 0, Sha256Hash.withHash(Utils.hexStringToBytes("11FF")) , null); + BlockAbstract abs = new BlockAbstract(0, 0, Sha256Hash.withHash(Utils.hexStringToBytes("11FF")), null); when(clientMock.commit(any(BlockAbstract.class))).thenReturn(null); assertNull(instance.commitAbstract(abs)); From 67b92027de9eae8dccad3dcb914821a4688d92c7 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Wed, 31 Jan 2018 21:46:03 +0100 Subject: [PATCH 64/69] Cache hashes + mainchain check caching --- .../model/Block.java | 8 ----- .../mainchain/tendermint/TendermintChain.java | 30 +++++++++---------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index 4483847..1fa6728 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -110,14 +110,6 @@ public Sha256Hash getHash() { this.hash = calculateHash(); } - //TODO IMPORTANT Remove this check - Sha256Hash oldHash = this.hash; - Sha256Hash newHash = calculateHash(); - if (!oldHash.equals(newHash)) { - Log.log(Level.SEVERE, "Hashes do not match! old: " + oldHash + ". new: " + newHash); - this.hash = newHash; - } - return this.hash; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java index ec5083f..206c19b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java @@ -197,22 +197,22 @@ public Sha256Hash commitAbstract(BlockAbstract abs) { @Override public boolean isPresent(Block block) { Sha256Hash blockHash = block.getHash(); -// return cache.contains(blockHash); + return cache.contains(blockHash); - //TODO IMPORTANT Decide what to do here - if (cache.contains(blockHash)) { - return true; - } else { -// Log.log(Level.INFO, "Node" + this.getApp().getLocalStore().getOwnNode() + " checking cache for " + block.getOwner().getId() + "-" + block.getNumber() + ": " + blockHash + ", the set is " + this.cache); - // We could miss some blocks in our cache, so update and wait for the results - //TODO We might not want to update here. The cache should be enough - updateCacheBlocking(-1, true); -// Log.log(Level.INFO, "Node" + this.getApp().getLocalStore().getOwnNode() + " did not find " + blockHash + ", so updated the cache and now the set is " + this.cache); - return cache.contains(blockHash); - //TODO: We might want to check the actual main chain in the false case - // For when an abstract is in a block that is not yet closed by an ENDBLOCK - // This now works because the block size is 1 - } +// //TODO IMPORTANT Decide what to do here +// if (cache.contains(blockHash)) { +// return true; +// } else { +//// Log.log(Level.INFO, "Node" + this.getApp().getLocalStore().getOwnNode() + " checking cache for " + block.getOwner().getId() + "-" + block.getNumber() + ": " + blockHash + ", the set is " + this.cache); +// // We could miss some blocks in our cache, so update and wait for the results +// //TODO We might not want to update here. The cache should be enough +// updateCacheBlocking(-1, true); +//// Log.log(Level.INFO, "Node" + this.getApp().getLocalStore().getOwnNode() + " did not find " + blockHash + ", so updated the cache and now the set is " + this.cache); +// return cache.contains(blockHash); +// //TODO: We might want to check the actual main chain in the false case +// // For when an abstract is in a block that is not yet closed by an ENDBLOCK +// // This now works because the block size is 1 +// } } /** From 5ddbce6f1c98278a82f377ecba7ccabd7983487d Mon Sep 17 00:00:00 2001 From: Bart de Jonge Date: Wed, 31 Jan 2018 22:45:02 +0100 Subject: [PATCH 65/69] proof verification caching --- .../blockchain/scaleoutdistributedledger/model/Proof.java | 4 ++++ .../scaleoutdistributedledger/model/Transaction.java | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 121477c..61c4792 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -159,6 +159,8 @@ public void verify(LocalStore localStore) throws ProofValidationException { * @throws ProofValidationException - If the proof is invalid. */ private void verify(Transaction transaction, LocalStore localStore) throws ProofValidationException { + if (transaction.isLocallyVerified()) return; + int blockNumber = transaction.getBlockNumber().orElse(-1); if (blockNumber == -1) { throw new ProofValidationException("The transaction has no block number, so we cannot validate it."); @@ -166,6 +168,7 @@ private void verify(Transaction transaction, LocalStore localStore) throws Proof if (transaction.getSender() == null) { verifyGenesisTransaction(transaction, localStore); + transaction.setLocallyVerified(true); return; } @@ -203,6 +206,7 @@ private void verify(Transaction transaction, LocalStore localStore) throws Proof throw new ProofValidationException("Source " + sourceTransaction + " is not valid", ex); } } + transaction.setLocallyVerified(true); } /** diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index b625ac9..64f5f4c 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -47,6 +47,9 @@ public class Transaction implements Comparable { @Getter @Setter private TransactionMessage message; + @Getter @Setter + private boolean locallyVerified; + /** * Constructor. * @param number - the number of this transaction. From b70b35de0bf3e16691d77943238ccfb41ae347f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20Jurasi=C5=84ski?= Date: Wed, 31 Jan 2018 23:17:42 +0100 Subject: [PATCH 66/69] Parallelization of tendermint --- .../mainchain/tendermint/TendermintChain.java | 3 +- .../simulation/Simulation.java | 37 ++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java index 206c19b..727a14e 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java @@ -104,10 +104,11 @@ protected void initialUpdateCache() { boolean updated = false; do { try { + Thread.sleep(1000); updateCacheBlocking(-1, false); updated = true; } catch (Exception e) { - int retryTime = 3; + int retryTime = 2; Log.log(Level.INFO, "Could not update cache on startup, trying again in " + retryTime + "s."); Log.log(Level.FINE, "", e); try { diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java index a9b4275..6c0a543 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java @@ -17,10 +17,8 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.sockets.SocketClient; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; /** @@ -79,23 +77,28 @@ public void runNodesLocally(Map nodes, Map ownN this.nodes = nodes; //Init the applications localApplications = new Application[ownNodes.size()]; - int counter = 0; + AtomicInteger counter = new AtomicInteger(0); + List startingThreads = new LinkedList<>(); for (Map.Entry nodeEntry : ownNodes.entrySet()) { - Node node = nodeEntry.getValue(); - int nodeNumber = nodeEntry.getKey(); - - Application app = new Application(true); - List addressesForThisNode = generateAddressesForNodeForTendermintP2P(nodeNumber, nodes); + startingThreads.add(new Thread(() -> { + Node node = nodeEntry.getValue(); + int nodeNumber = nodeEntry.getKey(); + + Application app = new Application(true); + List addressesForThisNode = generateAddressesForNodeForTendermintP2P(nodeNumber, nodes); + + try { + TendermintHelper.runTendermintNode(node.getPort(), addressesForThisNode, nodeNumber); + app.init(node.getPort(), genesisBlock.genesisCopy(), nodeToKeyPair.get(nodeNumber), ownNodes.get(nodeNumber)); + } catch (Exception ex) { + Log.log(Level.SEVERE, "Unable to initialize local node " + nodeNumber + " on port " + node.getPort() + "!", ex); + } - try { - TendermintHelper.runTendermintNode(node.getPort(), addressesForThisNode, nodeNumber); - app.init(node.getPort(), genesisBlock.genesisCopy(), nodeToKeyPair.get(nodeNumber), ownNodes.get(nodeNumber)); - } catch (Exception ex) { - Log.log(Level.SEVERE, "Unable to initialize local node " + nodeNumber + " on port " + node.getPort() + "!", ex); - } + localApplications[counter.getAndIncrement()] = app; + })); - localApplications[counter++] = app; } + startingThreads.stream().forEach(Thread::start); } private List generateAddressesForNodeForTendermintP2P(Integer i, Map nodes) { From e696d356e75ad4ab9e4cfbb5567e3f167429dc13 Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Wed, 31 Jan 2018 22:46:39 +0100 Subject: [PATCH 67/69] Debug --- .../blockchain/scaleoutdistributedledger/model/Transaction.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index 64f5f4c..646a548 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -95,6 +95,7 @@ public OptionalInt getBlockNumber() { this.blockNumber = OptionalInt.of(Block.GENESIS_BLOCK_NUMBER); } else { //TODO IMPORTANT We don't want this to be called really. + System.out.println("Looking up block number!"); for (Block block : sender.getChain().getBlocks()) { if (block.getTransactions().contains(this)) { this.blockNumber = OptionalInt.of(block.getNumber()); From 9476c19ee095aed119b0e542f168f0f0d5c5ba7b Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Thu, 1 Feb 2018 03:49:03 +0100 Subject: [PATCH 68/69] Don't use main --- pom.xml | 2 +- .../scaleoutdistributedledger/Main.java | 61 ------------------- .../NodeRegisterFailedException.java | 0 .../NotEnoughMoneyException.java | 0 4 files changed, 1 insertion(+), 62 deletions(-) delete mode 100644 src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Main.java rename src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/{ => exceptions}/NodeRegisterFailedException.java (100%) rename src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/{ => exceptions}/NotEnoughMoneyException.java (100%) diff --git a/pom.xml b/pom.xml index 7652ac5..4524d2e 100644 --- a/pom.xml +++ b/pom.xml @@ -145,7 +145,7 @@ false - nl.tudelft.blockchain.scaleoutdistributedledger.Main + nl.tudelft.blockchain.scaleoutdistributedledger.SimulationMain ${project.name} diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Main.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Main.java deleted file mode 100644 index f23d04d..0000000 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Main.java +++ /dev/null @@ -1,61 +0,0 @@ -package nl.tudelft.blockchain.scaleoutdistributedledger; - -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.OwnNode; -import nl.tudelft.blockchain.scaleoutdistributedledger.sockets.SocketClient; -import nl.tudelft.blockchain.scaleoutdistributedledger.sockets.SocketServer; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; - -/** - * Class to handle multiple applications. - */ -public final class Main { - - private Main() { - // prevents instantiation - throw new UnsupportedOperationException(); - } - - /** - * Main method, starting point of the application. - * @param args - command line arguments. - * @throws IOException - error while registering nodes. - */ - public static void main(String[] args) { - if (args[0].equalsIgnoreCase("s")) { - try { - SimulationMain.main(args); - } catch (Exception ex) { - ex.printStackTrace(); - } - return; - } - // Start a new node - // TODO: Make an example transaction? - Application app = new Application(true); - } - - /** - * Manual testing method for sockets. - */ - private static void testSockets() { - try { - Thread t = new Thread(new SocketServer(8007, new LocalStore(new OwnNode(0), null, null, false))); - t.start(); - - Node node = new Node(1, null, "localhost", 8007); - - SocketClient client = new SocketClient(); - client.sendMessage(node, new ArrayList()); - Thread.sleep(2500); - client.sendMessage(node, new ArrayList<>()); - Thread.sleep(7500); - client.sendMessage(node, new ArrayList<>()); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/NodeRegisterFailedException.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NodeRegisterFailedException.java similarity index 100% rename from src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/NodeRegisterFailedException.java rename to src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NodeRegisterFailedException.java diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/NotEnoughMoneyException.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NotEnoughMoneyException.java similarity index 100% rename from src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/NotEnoughMoneyException.java rename to src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NotEnoughMoneyException.java From e72c8925cfbc268375a27dcf1f6545d4a45f290c Mon Sep 17 00:00:00 2001 From: Taico Aerts Date: Thu, 1 Feb 2018 13:25:27 +0100 Subject: [PATCH 69/69] Cleanup, refactoring, optimizations --- .../Application.java | 2 +- .../CommunicationHelper.java | 2 +- .../SimulationMain.java | 25 ++--- .../TrackerHelper.java | 16 ++- .../TransactionCreator.java | 1 + .../TransactionSender.java | 61 ++++------ .../TransactionTuple.java | 2 - .../NodeRegisterFailedException.java | 6 +- .../exceptions/NotEnoughMoneyException.java | 2 +- .../exceptions/TrackerException.java | 22 ++++ .../message/TransactionMessage.java | 99 +++++++++++++---- .../mocks/TendermintChainMock.java | 5 + .../model/Block.java | 20 +--- .../model/Chain.java | 80 ++++++++----- .../model/ChainView.java | 10 +- .../model/OwnNode.java | 12 ++ .../model/Proof.java | 105 +++++++++++------- .../model/Transaction.java | 10 +- .../model/mainchain/MainChain.java | 18 ++- .../mainchain/tendermint/ABCIServer.java | 1 - .../mainchain/tendermint/TendermintChain.java | 38 +++++-- .../simulation/Simulation.java | 8 +- .../tendermint/TendermintHelper.java | 35 ++---- .../ITransactionPattern.java | 30 +++-- .../OnlyNodeZeroTransactionPattern.java | 12 ++ .../UniformRandomTransactionPattern.java | 2 +- .../sockets/SocketClient.java | 22 ++-- .../sockets/SocketServerHandler.java | 3 +- .../utils/ReversedIterator.java | 45 -------- .../TransactionCreatorTest.java | 1 + .../model/ChainViewTest.java | 13 +++ 31 files changed, 400 insertions(+), 308 deletions(-) create mode 100644 src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/TrackerException.java delete mode 100644 src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/ReversedIterator.java diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java index 8cdcc99..6937ec6 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java @@ -25,7 +25,7 @@ public class Application { @Getter private LocalStore localStore; private Thread executor; - private CancellableInfiniteRunnable transactionExecutable; + private CancellableInfiniteRunnable transactionExecutable; private final boolean isProduction; @Getter diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java index a39263b..de9be8c 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/CommunicationHelper.java @@ -31,7 +31,7 @@ public static boolean receiveTransaction(Proof proof, LocalStore localStore) { try { localStore.getVerification().validateNewMessage(proof, localStore); } catch (ValidationException ex) { - Log.log(Level.WARNING, "Received an invalid transaction/proof.", ex); + Log.log(Level.WARNING, "Received an invalid transaction/proof " + proof.getTransaction() + ": " + ex.getMessage()); return false; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index e0e4f9b..4c2e63e 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -1,12 +1,12 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; -import nl.tudelft.blockchain.scaleoutdistributedledger.message.StopTransactingMessage; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Ed25519Key; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; import nl.tudelft.blockchain.scaleoutdistributedledger.model.OwnNode; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.Simulation; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.tendermint.TendermintHelper; +import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern.RandomTransactionPattern; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern.UniformRandomTransactionPattern; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Utils; @@ -24,13 +24,11 @@ * Main class for running a simulation. */ public final class SimulationMain { - private SimulationMain() {} - //SETTINGS //number of local nodes to generate - public static final int LOCAL_NODES_NUMBER = 5; + public static final int LOCAL_NODES_NUMBER = 6; //number of total nodes in the system - public static final int TOTAL_NODES_NUMBER = 5; + public static final int TOTAL_NODES_NUMBER = 6; //number from which our nodes are (e.g if we run nodes (2, 3, 4), then this should be 2 public static final int NODES_FROM_NUMBER = 0; //Whether this main is the master coordinator of the simulation @@ -39,7 +37,7 @@ private SimulationMain() {} //The duration of the simulation in seconds. Only has an effect when IS_MASTER == true. public static final int SIMULATION_DURATION = 600; // Maximum number of blocks waiting to be sent (no new transaction created in the mean time - public static final int MAX_BLOCKS_PENDING = 30; + public static final int MAX_BLOCKS_PENDING = 50; // The initial delay in milliseconds to wait before checking for the first time. public static final long INITIAL_SENDING_DELAY = 5000; // The time in milliseconds to wait before checking again. @@ -47,6 +45,8 @@ private SimulationMain() {} // The number of blocks (with the same or higher block number) that need to be committed before we send a certain block. public static final int REQUIRED_COMMITS = 2; + private SimulationMain() {} + /** * @param args - the program arguments * @throws Exception - If an exception occurs. @@ -86,11 +86,10 @@ public static void main(String[] args) throws Exception { // --- PHASE 3: start the actual simulation --- Simulation simulation = new Simulation(IS_MASTER); -// ITransactionPattern itp = new OnlyNodeZeroTransactionPattern(10, 20, 1000, 2000, 1); -// ITransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 1000, 2000, 2); - UniformRandomTransactionPattern itp = new UniformRandomTransactionPattern(10, 20, 100, 200, 10); - itp.setSeed(1); - simulation.setTransactionPattern(itp); + + RandomTransactionPattern rtp = new UniformRandomTransactionPattern(10, 20, 100, 200, 10); +// rtp.setSeed(1); + simulation.setTransactionPattern(rtp); simulation.runNodesLocally(nodes, ownNodes, genesisBlock, nodeToKeyPair); // Wait for all nodes to have initialized @@ -106,12 +105,12 @@ public static void main(String[] args) throws Exception { if (IS_MASTER) { Thread.sleep(SIMULATION_DURATION * 1000); - simulation.broadcastMessage(new StopTransactingMessage()); } // Wait for all the other nodes to stop - waitForStop(); + simulation.stop(); + waitForStop(); simulation.stopLocalNodes(); simulation.cleanup(); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java index b5c40ab..0ea3c1d 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java @@ -1,5 +1,7 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; +import nl.tudelft.blockchain.scaleoutdistributedledger.exceptions.NodeRegisterFailedException; +import nl.tudelft.blockchain.scaleoutdistributedledger.exceptions.TrackerException; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; import nl.tudelft.blockchain.scaleoutdistributedledger.model.OwnNode; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; @@ -53,7 +55,8 @@ public static boolean resetTrackerServer() throws IOException { * Registers this node with the given public key. * @param nodePort - the port of the node * @param publicKey - the publicKey of the new node - * @return - the registered node + * @param id - the id of the node + * @return - the registered node * @throws IOException - IOException while registering node * @throws NodeRegisterFailedException - Server side exception while registering node */ @@ -82,10 +85,11 @@ public static OwnNode registerNode(int nodePort, byte[] publicKey, int id) throw /** * Mark a node with the given id as initialized on the tracker. * @param id - the id of the node to mark + * @param running - if the node is running or not * @throws IOException - IOException while registering node - * @throws NodeRegisterFailedException - Server side exception while registering node + * @throws TrackerException - Server side exception while updating running status */ - public static void setRunning(int id, boolean running) throws IOException { + public static void setRunning(int id, boolean running) throws IOException, TrackerException { JSONObject json = new JSONObject(); json.put("id", id); json.put("running", running); @@ -100,8 +104,7 @@ public static void setRunning(int id, boolean running) throws IOException { return; } Log.log(Level.SEVERE, "Error while updating the running status of the node"); - //TODO: Create new excepton for this - throw new NodeRegisterFailedException(); + throw new TrackerException("Unable to update to running."); } } @@ -120,7 +123,7 @@ public static String getIP() { while (addrss.hasMoreElements()) { String addr = addrss.nextElement().getHostAddress(); if (addr.contains(":") || addr.startsWith("127.")) continue; // IPv6 or Local - return (addr); + return addr; } } } catch (SocketException e) { } // Intentionally empty catch block @@ -155,6 +158,7 @@ public static void updateNodes(Map nodes, OwnNode ownNode) throws } else { Node node = new Node(i, publicKey, address, port); + //TODO Check if we need this. if (ownNode != null) { node.getChain().setGenesisBlock(ownNode.getChain().getGenesisBlock()); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java index caa6d88..e23db34 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java @@ -9,6 +9,7 @@ import java.util.Set; import java.util.TreeSet; +import nl.tudelft.blockchain.scaleoutdistributedledger.exceptions.NotEnoughMoneyException; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index ac7388b..55445ae 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -1,21 +1,22 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; -import nl.tudelft.blockchain.scaleoutdistributedledger.message.ProofMessage; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.*; -import nl.tudelft.blockchain.scaleoutdistributedledger.sockets.SocketClient; -import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; - import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.ListIterator; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; +import nl.tudelft.blockchain.scaleoutdistributedledger.message.ProofMessage; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Chain; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Proof; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; +import nl.tudelft.blockchain.scaleoutdistributedledger.sockets.SocketClient; +import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; + /** * Class which handles sending of transactions. */ @@ -24,10 +25,8 @@ public class TransactionSender implements Runnable { private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); private final LocalStore localStore; private final SocketClient socketClient; - private final AtomicInteger taskCounter = new AtomicInteger(); private final Chain chain; - private boolean stopping; - private int alreadySent = -1; + private int alreadySent; /** * Creates a new TransactionSender. @@ -41,20 +40,6 @@ public TransactionSender(LocalStore localStore) { this.executor.schedule(this, SimulationMain.INITIAL_SENDING_DELAY, TimeUnit.MILLISECONDS); } - /** - * Schedules the {@code toSend} block to be sent. - * The block will only be sent when at least {@link SimulationMain#REQUIRED_COMMITS} have - * been committed. - * @param toSend - the block to send - */ - public void scheduleBlockSending(Block toSend) { - if (alreadySent == -1) { - alreadySent = 0; - } - - taskCounter.incrementAndGet(); - } - @Override public void run() { //Send all and reschedule @@ -71,8 +56,7 @@ public void run() { * Sends all blocks that can be sent. */ public void sendAllBlocksThatCanBeSent() { - if (alreadySent == -1) return; - + //TODO Add explanation in readme? //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //Committed: [0, 2, 4, 6, 8] //Last sent: 2 @@ -107,7 +91,16 @@ public void sendAllBlocksThatCanBeSent() { * @return - the number of blocks currently waiting to be sent */ public int blocksWaiting() { - return taskCounter.get(); + Block block = chain.getLastBlock(); + if (block == null) return 0; + + Block prev = block; + while (block != null && block.getTransactions().isEmpty()) { + prev = block; + block = block.getPreviousBlock(); + } + + return prev.getNumber() - alreadySent; } /** @@ -115,19 +108,11 @@ public int blocksWaiting() { * @throws InterruptedException - If we are interrupted while waiting. */ public void waitUntilDone() throws InterruptedException { - while (alreadySent < chain.getLastBlockNumber()) { + while (blocksWaiting() > 0) { Thread.sleep(1000L); } } - /** - * Indicates that we want to stop. This reduces the REQUIRED_COMMITS to 1, to ensure that all - * remaining blocks get flushed whenever that is possible. - */ - public void stop() { - stopping = true; - } - /** * Shuts down this Transaction sender as quickly as possible. * Pending blocks will not be sent. @@ -167,8 +152,8 @@ private boolean sendTransaction(Transaction transaction) throws InterruptedExcep //TODO IMPORTANT Removed synchronization ProofConstructor proofConstructor = new ProofConstructor(transaction); Proof proof = proofConstructor.constructProof(); - ProofMessage msg = new ProofMessage(proof); + long timeDelta = System.currentTimeMillis() - startingTime; if (timeDelta > 5 * 1000) { Log.log(Level.WARNING, "Proof creation took " + timeDelta + " ms for transaction: " + transaction); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionTuple.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionTuple.java index 40a8419..3adc20d 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionTuple.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionTuple.java @@ -1,8 +1,6 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; import java.util.BitSet; -import java.util.HashSet; -import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NodeRegisterFailedException.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NodeRegisterFailedException.java index eb65e29..8384fb0 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NodeRegisterFailedException.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NodeRegisterFailedException.java @@ -1,9 +1,9 @@ -package nl.tudelft.blockchain.scaleoutdistributedledger; +package nl.tudelft.blockchain.scaleoutdistributedledger.exceptions; /** - * Exception for indicating that the user doesn't have enough money. + * Exception for indicating that registering with the tracker failed. */ -public class NodeRegisterFailedException extends RuntimeException { +public class NodeRegisterFailedException extends TrackerException { private static final long serialVersionUID = 8271135988867023425L; /** diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NotEnoughMoneyException.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NotEnoughMoneyException.java index bc06a7e..33e046e 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NotEnoughMoneyException.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NotEnoughMoneyException.java @@ -1,4 +1,4 @@ -package nl.tudelft.blockchain.scaleoutdistributedledger; +package nl.tudelft.blockchain.scaleoutdistributedledger.exceptions; /** * Exception for indicating that the user doesn't have enough money. diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/TrackerException.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/TrackerException.java new file mode 100644 index 0000000..55ef450 --- /dev/null +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/TrackerException.java @@ -0,0 +1,22 @@ +package nl.tudelft.blockchain.scaleoutdistributedledger.exceptions; + +/** + * Exception for the tracker. + */ +public class TrackerException extends RuntimeException { + private static final long serialVersionUID = -3346246279993022763L; + + /** + * Exception without message. + */ + public TrackerException() { + super(); + } + + /** + * @param msg - the message + */ + public TrackerException(String msg) { + super(msg); + } +} diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java index 4182c80..d3b2dde 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java @@ -1,21 +1,24 @@ package nl.tudelft.blockchain.scaleoutdistributedledger.message; -import lombok.Getter; +import java.io.Serializable; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Sha256Hash; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; -import java.util.*; -import java.util.AbstractMap.SimpleEntry; -import java.util.Map.Entry; +import lombok.Getter; /** * Transaction message for netty. */ public class TransactionMessage extends Message { - + private static final long serialVersionUID = 1L; + @Getter private final int number; @@ -25,12 +28,8 @@ public class TransactionMessage extends Message { @Getter private final long amount, remainder; - /** - * Transactions known by the receiver. - * Entry: node id, [block number, transaction number] - */ @Getter - private final Set> source; + private final Set source; @Getter private final Sha256Hash hash; @@ -64,13 +63,15 @@ public TransactionMessage(Transaction transaction, Node proofReceiver) { if (sourceTransaction.getBlockNumber().isPresent()) { if (sourceSender == null) { // Genesis transaction - this.source.add(new SimpleEntry<>(sourceTransaction.getReceiver().getId(), - new int[]{sourceTransaction.getBlockNumber().getAsInt(), - sourceTransaction.getNumber()})); + this.source.add(new TransactionSource( + sourceTransaction.getReceiver().getId(), + sourceTransaction.getBlockNumber().getAsInt(), + sourceTransaction.getNumber())); } else { - this.source.add(new SimpleEntry<>(sourceSender.getId(), - new int[]{sourceTransaction.getBlockNumber().getAsInt(), - sourceTransaction.getNumber()})); + this.source.add(new TransactionSource( + sourceSender.getId(), + sourceTransaction.getBlockNumber().getAsInt(), + sourceTransaction.getNumber())); } } else { throw new IllegalStateException("Transaction without blocknumber found"); @@ -80,6 +81,11 @@ public TransactionMessage(Transaction transaction, Node proofReceiver) { this.blockNumber = transaction.getBlockNumber().getAsInt(); } + /** + * Converts this message into a transaction without any sources. + * @param localStore - the local store + * @return - the transaction represented by this message, without sources + */ public Transaction toTransactionWithoutSources(LocalStore localStore) { Transaction tx = new Transaction(this.number, localStore.getNode(this.senderId), localStore.getNode(this.receiverId), this.amount, this.remainder, new TreeSet<>()); @@ -103,9 +109,9 @@ public boolean equals(Object obj) { if (receiverId != other.receiverId) return false; if (amount != other.amount) return false; if (remainder != other.remainder) return false; - if (source.equals(other.source)) return false; - if (hash.equals(other.hash)) return false; - return blockNumber == other.blockNumber; + if (blockNumber != other.blockNumber) return false; + if (!source.equals(other.source)) return false; + return hash.equals(other.hash); } @Override @@ -125,7 +131,7 @@ public int hashCode() { @Override public String toString() { - StringBuilder sb = new StringBuilder(64); + StringBuilder sb = new StringBuilder(128); sb.append("TransactionMessage entry : source) { - sb.append("\n ").append(entry.getKey()) - .append(": block=").append(entry.getValue()[0]) - .append(", id=").append(entry.getValue()[1]); + for (TransactionSource ts : source) { + sb.append("\n ").append(ts.getOwner()) + .append(": block=").append(ts.getBlockNumber()) + .append(", id=").append(ts.getId()); } sb.append("\n ]"); return sb.toString(); } + + /** + * Class to represent a transaction source. + */ + @Getter + public static class TransactionSource implements Serializable { + private static final long serialVersionUID = 1L; + + private final int owner; + private final int id; + private final int blockNumber; + + /** + * @param owner - the id of the owner of the transaction + * @param blockNumber - the number of the block in which this transaction resides + * @param id - the id of the transaction + */ + public TransactionSource(int owner, int blockNumber, int id) { + this.owner = owner; + this.id = id; + this.blockNumber = blockNumber; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = prime * result + blockNumber; + result = prime * result + owner; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof TransactionSource)) return false; + + TransactionSource other = (TransactionSource) obj; + if (id != other.id) return false; + if (blockNumber != other.blockNumber) return false; + if (owner != other.owner) return false; + return true; + } + } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/mocks/TendermintChainMock.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/mocks/TendermintChainMock.java index c876951..2bb29ca 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/mocks/TendermintChainMock.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/mocks/TendermintChainMock.java @@ -42,6 +42,11 @@ public boolean isPresent(Block block) { return true; } + @Override + public boolean isInCache(Block block) { + return true; + } + @Override public void stop() {} } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index 1fa6728..dd4a1e3 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -183,12 +183,8 @@ public int hashCode() { @Override public boolean equals(Object obj) { - //TODO IMPORTANT Remove sysos if (this == obj) return true; - if (!(obj instanceof Block)) { - System.out.println("Block not equal because one is null"); - return false; - } + if (!(obj instanceof Block)) return false; Block other = (Block) obj; if (this.number != other.number) return false; @@ -196,13 +192,10 @@ public boolean equals(Object obj) { if (other.owner != null) return false; } else if (!this.owner.equals(other.owner)) return false; - //TODO IMPORTANT We might not want to use equals for the previous block (as it will recurse) + //TODO IMPORTANT We might not want to use equals for the previous block (as it will recurse further) if (this.previousBlock == null) { if (other.previousBlock != null) return false; - } else if (!this.previousBlock.equals(other.previousBlock)) { - System.out.println("Blocks not equals because of previousBlockPointer"); - return false; - } + } else if (!this.previousBlock.equals(other.previousBlock)) return false; return this.transactions.equals(other.transactions); } @@ -280,12 +273,7 @@ public boolean isOnMainChain(LocalStore localStore) { //It is present, so store it and return if (localStore.getMainChain().isPresent(this)) { this.onMainChain = true; - - //TODO Chance onMainChain to this.nextCommittedBlock - if (this.nextCommittedBlock != this) { - Log.debug("Block {0} doesn't have itself as next committed!", this.number); - } - + this.nextCommittedBlock = this; return true; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java index cd31f5b..6cdb768 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java @@ -2,13 +2,11 @@ import lombok.Getter; +import java.util.ArrayList; import java.util.List; -import java.util.logging.Level; import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.AppendOnlyArrayList; -import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; -import nl.tudelft.blockchain.scaleoutdistributedledger.utils.ReversedIterator; /** * Chain class. @@ -48,50 +46,79 @@ public void update(List updates, LocalStore localStore) { if (updates.isEmpty()) return; Block lastCommitted = updates.get(updates.size() - 1); - synchronized (this) { + //Figure out where to start updating int nextNr; Block previousBlock; if (blocks.isEmpty()) { //Should start at 0, there is no previous block - nextNr = 0; previousBlock = null; + nextNr = 0; } else { //Should start with the first block after our last block. - Block lastBlock = blocks.get(blocks.size() - 1); - nextNr = lastBlock.getNumber() + 1; - previousBlock = lastBlock; + previousBlock = blocks.get(blocks.size() - 1); + nextNr = previousBlock.getNumber() + 1; } - //The last block in the updates must be a committed block + //Actually apply the updates + ArrayList toAdd = new ArrayList<>(); + int lastBlockNr = nextNr - 1; for (Block block : updates) { //Skip any overlap if (block.getNumber() != nextNr) continue; block.setPreviousBlock(previousBlock); - - //TODO IMPORTANT We might want to set the next committed block in a better way. (Send it) - block.setNextCommittedBlock(lastCommitted); - blocks.add(block); + lastBlockNr = fixNextCommitted(block, lastCommitted.getNumber(), lastBlockNr, localStore); + toAdd.add(block); nextNr++; previousBlock = block; } - setLastCommittedBlock(lastCommitted); + blocks.addAll(toAdd); } - //TODO IMPORTANT Remove after debugging - if (!lastCommitted.isOnMainChain(localStore)) { - Log.log(Level.SEVERE, "The last block received is not a committed block!"); - } + //The last block in the updates must be a committed block + setLastCommittedBlock(lastCommitted); + } + + /** + * Fixes the next committed block pointers. + * @param updates - the block updates + * @param localStore - the local store + */ + private void fixNextCommitted(List updates, LocalStore localStore) { + if (updates.isEmpty()) return; - //TODO IMPORTANT do we need this? - //TODO Set last committed block - for (Block block : ReversedIterator.reversed(this.blocks)) { - if (block.isOnMainChain(localStore)) { - setLastCommittedBlock(block); - return; + int lastBlockNr = updates.get(0).getNumber(); + for (Block block : updates) { + if (!localStore.getMainChain().isInCache(block)) continue; + + Block prev = block.getPreviousBlock(); + while (prev != null && prev.getNumber() > lastBlockNr) { + prev.setNextCommittedBlock(block); + prev = prev.getPreviousBlock(); } + + lastBlockNr = block.getNumber(); + } + } + + /** + * @param block - the block + * @param lastUpdateBlockNr - the number of the last block in the list of updates + * @param lastBlockNr - the number of the last checked block that was actually committed + * @param localStore - the local store + * @return - the new lastBlockNr + */ + private int fixNextCommitted(Block block, int lastUpdateBlockNr, int lastBlockNr, LocalStore localStore) { + if (block.getNumber() != lastUpdateBlockNr && !localStore.getMainChain().isInCache(block)) return lastBlockNr; + + Block prev = block; + while (prev != null && prev.getNumber() > lastBlockNr) { + prev.setNextCommittedBlock(block); + prev = prev.getPreviousBlock(); } + + return block.getNumber(); } /** @@ -132,11 +159,6 @@ public Block getLastBlock() { * @return - the number of the last block */ public int getLastBlockNumber() { - //TODO Remove - { - Block last; - assert blocks.size() - 1 == ((last = getLastBlock()) == null ? -1 : last.getNumber()); - } return blocks.size() - 1; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java index 2c972d8..675c89a 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java @@ -173,7 +173,6 @@ private boolean checkNoGaps(int startIndex, int previousNr) { * of this ChainView. */ public Block getBlock(int number) { - //TODO IMPORTANT chainSize instead of the actual size?? if (number < chain.getBlocks().size()) { return chain.getBlocks().get(number); } else if (isValid()) { @@ -185,6 +184,15 @@ public Block getBlock(int number) { } } + /** + * @return - the amount of blocks in this chainview + */ + public int size() { + if (!isValid()) throw new IllegalStateException("This chainview is invalid"); + + return chainSize + updates.size() - startIndex; + } + @Override public ListIterator iterator() { return listIterator(); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/OwnNode.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/OwnNode.java index 1e8d921..5a671a9 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/OwnNode.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/OwnNode.java @@ -3,6 +3,9 @@ import lombok.Getter; import lombok.Setter; +/** + * Class to represent our own node. + */ public class OwnNode extends Node { /** * Only used by the node himself. @@ -11,10 +14,19 @@ public class OwnNode extends Node { @Getter @Setter private transient byte[] privateKey; + /** + * @param id - the id of our node + */ public OwnNode(int id) { super(id); } + /** + * @param id - the id of our node + * @param publicKey - the public key + * @param address - the address + * @param port - the port + */ public OwnNode(int id, byte[] publicKey, String address, int port) { super(id, publicKey, address, port); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index 61c4792..b3220f5 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -3,6 +3,7 @@ import lombok.Getter; import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; import nl.tudelft.blockchain.scaleoutdistributedledger.message.ProofMessage; +import nl.tudelft.blockchain.scaleoutdistributedledger.message.TransactionMessage.TransactionSource; import nl.tudelft.blockchain.scaleoutdistributedledger.message.BlockMessage; import nl.tudelft.blockchain.scaleoutdistributedledger.validation.ProofValidationException; import nl.tudelft.blockchain.scaleoutdistributedledger.validation.ValidationException; @@ -47,10 +48,11 @@ public Proof(ProofMessage proofMessage, LocalStore localStore) throws IOExceptio this.chainUpdates = new HashMap<>(); // Decode the transactions while skipping sources - for (Map.Entry> entry : proofMessage.getChainUpdates().entrySet()) { + for (Entry> entry : proofMessage.getChainUpdates().entrySet()) { List blocks = new ArrayList<>(); - //TODO Turn into normal for loop - entry.getValue().forEach(blockMessage -> blocks.add(blockMessage.toBlockWithoutSources(localStore))); + for (BlockMessage blockMessage : entry.getValue()) { + blocks.add(blockMessage.toBlockWithoutSources(localStore)); + } chainUpdates.put(localStore.getNode(entry.getKey()), blocks); } @@ -95,17 +97,17 @@ private void fixTransactionSources(LocalStore localStore) { } // For all transactions of all nodes do - for (Node node : this.chainUpdates.keySet()) { - for (Block block : this.chainUpdates.get(node)) { + for (List blocks : this.chainUpdates.values()) { + for (Block block : blocks) { for (Transaction tx : block.getTransactions()) { - for (Entry entry : tx.getMessage().getSource()) { + for (TransactionSource ts : tx.getMessage().getSource()) { Block sourceBlock; - if (!lightViews.containsKey(entry.getKey())) { - sourceBlock = localStore.getNode(entry.getKey()).getChain().getBlocks().get(entry.getValue()[0]); + if (!lightViews.containsKey(ts.getOwner())) { + sourceBlock = localStore.getNode(ts.getOwner()).getChain().getBlocks().get(ts.getBlockNumber()); } else { - sourceBlock = lightViews.get(entry.getKey()).getBlock(entry.getValue()[0]); + sourceBlock = lightViews.get(ts.getOwner()).getBlock(ts.getBlockNumber()); } - tx.getSource().add(sourceBlock.getTransaction(entry.getValue()[1])); + tx.getSource().add(sourceBlock.getTransaction(ts.getId())); } } } @@ -120,24 +122,6 @@ public void addBlock(Block block) { List blocks = chainUpdates.computeIfAbsent(block.getOwner(), k -> new ArrayList<>()); blocks.add(block); } - - /** - * Adds the blocks with numbers start to end of the given chain to the proof. - * @param chain - the chain - * @param start - the block to start at (inclusive) - * @param end - the block to end at (exclusive) - */ - public void addBlocksOfChain(Chain chain, int start, int end) { - //TODO IMPORTANT Is this check correct? Shouldn't it be start > end? And the end also seems strange. - if (start >= end || end > chain.getBlocks().size()) return; - - List blocks = chainUpdates.get(chain.getOwner()); - if (blocks == null) { - blocks = new ArrayList<>(); - chainUpdates.put(chain.getOwner(), blocks); - } - blocks.addAll(chain.getBlocks().subList(start, end)); - } /** * Verifies this proof. @@ -172,41 +156,75 @@ private void verify(Transaction transaction, LocalStore localStore) throws Proof return; } - int absmark = 0; - boolean seen = false; + verifyChainWithTransaction(transaction, localStore, blockNumber); + verifySourceTransactions(transaction, localStore); + transaction.setLocallyVerified(true); + } + /** + * Performs the first part of the verification of a transaction. + * This method first checks if the chain is consistent with the updates in this proof, and then + * checks that the transaction only appears once. Finally, it checks if the transaction is in + * a block that before a committed block, or is itself committed. + * @param transaction - the transaction + * @param localStore - the local store + * @param blockNumber - the block number of transaction + */ + private void verifyChainWithTransaction(Transaction transaction, LocalStore localStore, int blockNumber) { ChainView chainView = getChainView(transaction.getSender()); if (!chainView.isValid()) { throw new ProofValidationException("ChainView of node " + transaction.getSender().getId() + " is invalid."); } - + + boolean seen = false; + boolean absmark = false; for (Block block : chainView) { + //TODO This containment check will not report transactions with the same id in different blocks (they will be unequal). + //It is therefore impossible to find a duplicate transaction if (block.getTransactions().contains(transaction)) { if (seen) { throw new ProofValidationException("Duplicate transaction found."); } seen = true; } - if (block.isOnMainChain(localStore)) absmark = block.getNumber(); + + //If a block at or after the block in question is committed, then we have found a valid absmark + if (!absmark && block.getNumber() >= blockNumber) { + Block nextCommitted = block.getNextCommittedBlock(); + + if (nextCommitted != null || block.isOnMainChain(localStore)) { + absmark = true; + } + } } - - if (absmark < blockNumber) { - System.out.println(this.getTransaction()); - throw new ProofValidationException("No suitable committed block found"); + + if (!seen) { + throw new ProofValidationException("Transaction not found in any block!"); } - - // Verify source transaction + + if (!absmark) { + throw new ProofValidationException("No suitable committed block found for block " + blockNumber); + } + } + + /** + * Performs the second part of the verification of a transaction. + * This method verifies all source transactions. + * @param transaction - the transaction + * @param localStore - the local store + */ + private void verifySourceTransactions(Transaction transaction, LocalStore localStore) { for (Transaction sourceTransaction : transaction.getSource()) { try { verify(sourceTransaction, localStore); } catch (ValidationException ex) { + //TODO Remove debugging stuff ex.printStackTrace(); System.out.println(this); System.exit(1); throw new ProofValidationException("Source " + sourceTransaction + " is not valid", ex); } } - transaction.setLocallyVerified(true); } /** @@ -271,10 +289,11 @@ public void applyUpdates(LocalStore localStore) { /** * Recursively calls itself with all the sources of the given transaction. Transactions which * are in the chain of {@code receiver} are ignored. - * @param nrOfNodes - the total number of nodes - * @param transaction - the transaction to check the sources of - * @param receiver - the node receiving the transaction - * @param chains - the list of chains to append to + * @param nrOfNodes - the total number of nodes + * @param transaction - the transaction to check the sources of + * @param receiver - the node receiving the transaction + * @param metaKnowledge - the meta knowledge + * @param chains - the list of chains to append to */ public static void appendChains(int nrOfNodes, Transaction transaction, Node receiver, MetaKnowledge metaKnowledge, Set chains) { Node owner = transaction.getSender(); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index 646a548..bf7ba00 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -28,7 +28,6 @@ public class Transaction implements Comparable { @Getter private final Node sender; - // TODO: change back to final somehow @Getter @Setter private Node receiver; @@ -143,7 +142,6 @@ private Sha256Hash calculateHash() { outputStream.write(Utils.longToByteArray(this.amount)); outputStream.write(Utils.longToByteArray(this.remainder)); - // TODO: check if we really need to do this for (Transaction tx : this.source) { outputStream.write(tx.getHash().getBytes()); } @@ -203,10 +201,10 @@ public String toString() { @Override public int compareTo(Transaction o) { - if (this.sender == null && o.getSender() != null) return -1; - if (this.sender != null && o.getSender() == null) return 1; - if (this.sender == null && o.getSender() == null) return 0; - int senderCompare = Integer.compare(this.sender.getId(), o.getSender().getId()); + if (this.sender == null && o.sender != null) return -1; + if (this.sender != null && o.sender == null) return 1; + if (this.sender == null && o.sender == null) return 0; + int senderCompare = Integer.compare(this.sender.getId(), o.sender.getId()); if (senderCompare != 0) return senderCompare; return Integer.compare(this.getNumber(), o.getNumber()); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/MainChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/MainChain.java index 974ea78..0d7a0c4 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/MainChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/MainChain.java @@ -16,22 +16,18 @@ public interface MainChain { */ public Sha256Hash commitAbstract(BlockAbstract abs); - /** - * Query the main chain for the presence of a block. - * - * @param block - the block to query for - * @return - true when present, false otherwise - */ -// public default boolean isPresent(Block block) { -// return isPresent(block.getHash()); -// } - /** * Check whether the block (from a local chain) is on the main chain (in a form of BlockAbstract). - * @param block the hash of the block + * @param block the block to check * @return true if there is a block abstract of the given block, false otherwise. */ public boolean isPresent(Block block); + + /** + * @param block - the block to check + * @return - true if the given block is in the cache, false otherwise + */ + public boolean isInCache(Block block); /** * Initializes the tendermint chain. diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIServer.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIServer.java index dc42998..66db06c 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIServer.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIServer.java @@ -1,7 +1,6 @@ package nl.tudelft.blockchain.scaleoutdistributedledger.model.mainchain.tendermint; import com.github.jtendermint.jabci.api.ABCIAPI; -import com.github.jtendermint.jabci.types.Types; import com.github.jtendermint.jabci.types.Types.CodeType; import com.github.jtendermint.jabci.types.Types.RequestBeginBlock; import com.github.jtendermint.jabci.types.Types.RequestCheckTx; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java index 727a14e..4bbe524 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java @@ -1,7 +1,16 @@ package nl.tudelft.blockchain.scaleoutdistributedledger.model.mainchain.tendermint; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Level; + import com.github.jtendermint.jabci.socket.TSocket; -import lombok.Getter; + import nl.tudelft.blockchain.scaleoutdistributedledger.Application; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; import nl.tudelft.blockchain.scaleoutdistributedledger.model.BlockAbstract; @@ -9,10 +18,7 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.model.mainchain.MainChain; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; -import java.util.*; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.logging.Level; +import lombok.Getter; /** * Class implementing {@link MainChain} for a Tendermint chain. @@ -100,12 +106,15 @@ private void initClient() { Log.log(Level.INFO, "Started ABCI Client on " + DEFAULT_ADDRESS + ":" + (abciServerPort - 1)); } + /** + * Performs the initial update of the cache. + */ protected void initialUpdateCache() { boolean updated = false; do { try { Thread.sleep(1000); - updateCacheBlocking(-1, false); + updateCacheBlocking(-1); updated = true; } catch (Exception e) { int retryTime = 2; @@ -129,10 +138,9 @@ protected void initialUpdateCache() { */ protected void updateCache(long height) { if (client == null) return; // If in startup - this.threadPool.submit(() -> updateCacheBlocking(height, false)); + this.threadPool.submit(() -> updateCacheBlocking(height)); } - //TODO IMPORTANT Remove the extra /** * Update the cache of the chain. * Note that this method is blocking and execution may therefore take a while, @@ -140,7 +148,7 @@ protected void updateCache(long height) { * * @param height - The height to update to, if -1 check the needed height with Tendermint */ - private void updateCacheBlocking(long height, boolean extra) { + private void updateCacheBlocking(long height) { if (height == -1) { height = this.client.status().getLong("latest_block_height"); } @@ -153,9 +161,9 @@ private void updateCacheBlocking(long height, boolean extra) { } synchronized (cacheLock) { for (BlockAbstract abs : abstractsAtCurrentHeight) { - if (cache.add(abs.getBlockHash()) && extra) { - Log.debug("{0}: updateCacheBlocking caused new block to be added", getApp().getLocalStore().getOwnNode().getId()); - } + cache.add(abs.getBlockHash()); + + //TODO Remove extra cache and super extra cache int blockNum = abs.getBlockNumber(); int owner = abs.getOwnerNodeId(); Set setOfBlockNums = extraCache.getOrDefault(owner, new HashSet<>()); @@ -215,6 +223,12 @@ public boolean isPresent(Block block) { // // This now works because the block size is 1 // } } + + @Override + public boolean isInCache(Block block) { + Sha256Hash blockHash = block.getHash(); + return cache.contains(blockHash); + } /** * Only to be used for initial block. diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java index 6c0a543..e28645b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java @@ -6,6 +6,7 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.Application; import nl.tudelft.blockchain.scaleoutdistributedledger.message.Message; import nl.tudelft.blockchain.scaleoutdistributedledger.message.StartTransactingMessage; +import nl.tudelft.blockchain.scaleoutdistributedledger.message.StopTransactingMessage; import nl.tudelft.blockchain.scaleoutdistributedledger.message.TransactionPatternMessage; import nl.tudelft.blockchain.scaleoutdistributedledger.message.UpdateNodesMessage; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; @@ -42,6 +43,7 @@ public class Simulation { /** * Creates a new simulation. + * @param isMaster - if this simulation is the master */ public Simulation(boolean isMaster) { this.socketClient = new SocketClient(); @@ -180,7 +182,9 @@ public void start() { */ public void stop() { checkState(SimulationState.RUNNING, "stop"); - Log.log(Level.INFO, "[Simulation] Stopped"); + + broadcastMessage(new StopTransactingMessage()); + Log.log(Level.INFO, "[Simulation] Stopping"); state = SimulationState.STOPPED; } @@ -212,7 +216,7 @@ protected void checkState(SimulationState expected, String msg) { * @param msg - the message to send */ public void broadcastMessage(Message msg) { - if(!isMaster) return; + if (!isMaster) return; Log.log(Level.INFO, "[Simulation] Sending " + msg + " to all nodes..."); for (Node node : nodes.values()) { diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java index 0207656..edec264 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java @@ -65,8 +65,7 @@ public static Ed25519Key generatePrivValidatorFile(Ed25519Key keyPair, int nodeN try { final Process ps = rt.exec(script.toString(), envVariables); ps.waitFor(); - BufferedReader stdInput = new BufferedReader(new - InputStreamReader(ps.getInputStream())); + BufferedReader stdInput = new BufferedReader(new InputStreamReader(ps.getInputStream())); // read the output from the command String s; while ((s = stdInput.readLine()) != null) { @@ -108,15 +107,12 @@ public static Ed25519Key generatePrivValidatorFile(Ed25519Key keyPair, int nodeN keyPair = new Ed25519Key(privateKey, publicKey); } - try ( - BufferedWriter writer = Files.newBufferedWriter(Paths.get(nodeFilesLocation, "priv_validator.json")) - ) { + try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(nodeFilesLocation, "priv_validator.json"))) { if (!ensureDirectoryExists(nodeFilesLocation)) { return null; } writer.write(privValidator.toString()); - writer.close(); } catch (IOException e) { Log.log(Level.WARNING, "Could not generate priv_validator.json due to IO Exception", e); return null; @@ -193,15 +189,12 @@ public static boolean generateGenesisFile(Date genesisTime, Map genesis.put("validators", validators); String nodeFilesLocation = getNodeFilesLocation(nodeNumber); - try ( - BufferedWriter writer = Files.newBufferedWriter(Paths.get(nodeFilesLocation, "genesis.json")) - ) { + try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(nodeFilesLocation, "genesis.json"))) { if (!ensureDirectoryExists(nodeFilesLocation)) { return false; } writer.write(genesis.toString()); - writer.close(); } catch (IOException e) { Log.log(Level.WARNING, "Could not generate genesis.json due to IO exception.", e); return false; @@ -270,11 +263,11 @@ public static void runTendermintNode(int nodeBasePort, List peerAddresse } String nodeFilesLocation = getNodeFilesLocation(nodeNumber); //add arguments - script.append("--home ").append(nodeFilesLocation).append(" "); - script.append("--p2p.laddr=tcp://0.0.0.0:").append(nodeBasePort + 1).append(" "); - script.append("--rpc.laddr=tcp://0.0.0.0:").append(nodeBasePort + 2).append(" "); - script.append("--proxy_app=tcp://127.0.0.1:").append(nodeBasePort + 3).append(" "); - script.append("--moniker=Node").append(nodeNumber).append(" "); + script.append("--home ").append(nodeFilesLocation).append(' '); + script.append("--p2p.laddr=tcp://0.0.0.0:").append(nodeBasePort + 1).append(' '); + script.append("--rpc.laddr=tcp://0.0.0.0:").append(nodeBasePort + 2).append(' '); + script.append("--proxy_app=tcp://127.0.0.1:").append(nodeBasePort + 3).append(' '); + script.append("--moniker=Node").append(nodeNumber).append(' '); //add other seeds if (peerAddresses != null && !peerAddresses.isEmpty()) { @@ -313,31 +306,23 @@ private static boolean ensureDirectoryExists(String location) { */ private static void enableLogging(Process ps, String logPrefix) { Thread stdOutThread = new Thread(() -> { - try ( - BufferedReader stdInput = new BufferedReader(new - InputStreamReader(ps.getInputStream())) - ) { + try (BufferedReader stdInput = new BufferedReader(new InputStreamReader(ps.getInputStream()))) { // read the output from the command String s; while ((s = stdInput.readLine()) != null) { Log.log(Level.FINE, "[TM STDIN " + logPrefix + " ] " + s); } - stdInput.close(); } catch (Exception e) { e.printStackTrace(); } }); Thread stdErrThread = new Thread(() -> { - try ( - BufferedReader stdError = new BufferedReader(new - InputStreamReader(ps.getErrorStream())) - ) { + try (BufferedReader stdError = new BufferedReader(new InputStreamReader(ps.getErrorStream()))) { // read any errors from the attempted command String s; while ((s = stdError.readLine()) != null) { Log.log(Level.FINE, "[TM STDERROR " + logPrefix + " ] " + s); } - stdError.close(); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java index 52e00ec..34bc172 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java @@ -3,8 +3,11 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; import nl.tudelft.blockchain.scaleoutdistributedledger.SimulationMain; import nl.tudelft.blockchain.scaleoutdistributedledger.TransactionCreator; -import nl.tudelft.blockchain.scaleoutdistributedledger.TransactionSender; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.*; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Chain; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.OwnNode; +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.CancellableInfiniteRunnable; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; @@ -45,26 +48,25 @@ public interface ITransactionPattern extends Serializable { * @throws InterruptedException - if the action is interrupted */ public default void doAction(LocalStore localStore) throws InterruptedException { - Log.log(Level.FINER, "Start doAction of node " + localStore.getOwnNode().getId()); + Log.log(Level.FINER, "Start doAction", localStore.getOwnNode().getId()); + OwnNode ownNode = localStore.getOwnNode(); + int ownNodeId = ownNode.getId(); // Make sure we have some room if (localStore.getApplication().getTransactionSender().blocksWaiting() >= SimulationMain.MAX_BLOCKS_PENDING) { - Log.log(Level.FINE, "Too many blocks pending, maybe slow down!"); -// return; + Log.log(Level.INFO, "Too many blocks pending, skipping transaction creation!", ownNodeId); + return; } //Select receiver and amount long amount = selectAmount(localStore); if (amount == -1) { - Log.log(Level.INFO, "Not enough money to make transaction!"); + Log.log(Level.INFO, "Not enough money to make transaction!", ownNodeId); return; } Node receiver = selectNode(localStore); - - OwnNode ownNode = localStore.getOwnNode(); - Block newBlock; - Log.log(Level.FINE, "Going to make transaction: $ " + amount + " from " + ownNode.getId() + " -> " + receiver.getId()); + Log.log(Level.FINE, "Going to make transaction: $ " + amount + " from " + ownNodeId + " -> " + receiver.getId()); //TODO IMPORTANT Removed synchronization on own chain. Transaction creator should be safe. //Create the transaction @@ -73,12 +75,9 @@ public default void doAction(LocalStore localStore) throws InterruptedException //TODO how many transactions do we put in one block? //Add block to local chain - newBlock = ownNode.getChain().appendNewBlock(); + Block newBlock = ownNode.getChain().appendNewBlock(); newBlock.addTransaction(transaction); - Log.log(Level.FINE, "Node " + ownNode.getId() + " added transaction " + transaction.getNumber() + " in block " + newBlock.getNumber()); - - //Ensure that the block is sent at some point - localStore.getApplication().getTransactionSender().scheduleBlockSending(newBlock); + Log.log(Level.FINE, "Node " + ownNodeId + " added transaction " + transaction.getNumber() + " in block " + newBlock.getNumber()); //Check if we want to commit the new block, and commit it if we do. commitBlocks(localStore, false); @@ -122,7 +121,6 @@ public default void commitBlocks(LocalStore localStore, boolean force) throws In public default void commitExtraEmpty(LocalStore localStore) { Chain ownChain = localStore.getOwnNode().getChain(); for (int i = 0; i < SimulationMain.REQUIRED_COMMITS + 1; i++) { - //TODO IMPORTANT (LOW) Removed synchronization on own chain (methods perform the synchronization already). Block block = ownChain.appendNewBlock(); block.commit(localStore); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/OnlyNodeZeroTransactionPattern.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/OnlyNodeZeroTransactionPattern.java index bf408c6..2dd4c70 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/OnlyNodeZeroTransactionPattern.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/OnlyNodeZeroTransactionPattern.java @@ -2,8 +2,20 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; +/** + * Transaction pattern where only node 0 makes transactions. + * Only used for debugging. + */ public class OnlyNodeZeroTransactionPattern extends UniformRandomTransactionPattern { + private static final long serialVersionUID = 1L; + /** + * @param minAmount - the minimum amount of money to send + * @param maxAmount - the maximum amount of money to send + * @param minWaitTime - the minimum wait time between making transactions + * @param maxWaitTime - the maximum wait time between making transactions + * @param commitEvery - commit every x blocks + */ public OnlyNodeZeroTransactionPattern(int minAmount, int maxAmount, int minWaitTime, int maxWaitTime, int commitEvery) { super(minAmount, maxAmount, minWaitTime, maxWaitTime, commitEvery); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/UniformRandomTransactionPattern.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/UniformRandomTransactionPattern.java index 8f465d0..3c96c6f 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/UniformRandomTransactionPattern.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/UniformRandomTransactionPattern.java @@ -76,7 +76,7 @@ public long selectAmount(LocalStore localStore) { if (available == 0 || minAmount > available) return -1; long amount; if (maxAmount == minAmount) { - amount = (long) minAmount; + amount = minAmount; } else { amount = (long) minAmount + random.nextInt(maxAmount - minAmount); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketClient.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketClient.java index e559858..923b684 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketClient.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketClient.java @@ -1,21 +1,24 @@ package nl.tudelft.blockchain.scaleoutdistributedledger.sockets; +import java.util.HashMap; +import java.util.logging.Level; + +import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; +import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; + import io.netty.bootstrap.Bootstrap; -import io.netty.channel.*; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; import io.netty.handler.codec.serialization.ObjectEncoder; -import io.netty.handler.stream.ChunkedWriteHandler; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; -import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; - -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.util.HashMap; -import java.util.logging.Level; /** * Socket client. @@ -70,6 +73,7 @@ public void shutdown() { * @param node - the node to send the message to. * @param msg - the message object to send * @return - whether the message was sent successfully + * @throws InterruptedException - If message sending is interrupted. */ public boolean sendMessage(Node node, Object msg) throws InterruptedException { Channel channel = connections.get(node); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketServerHandler.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketServerHandler.java index c51d6d1..b0cf3a3 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketServerHandler.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/sockets/SocketServerHandler.java @@ -27,8 +27,7 @@ public SocketServerHandler(LocalStore localStore) { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { -// Log.log(Level.INFO, "Node " + localStore.getOwnNode().getId() + " Server: received message: " + msg); - if(msg instanceof Message) { + if (msg instanceof Message) { ((Message) msg).handle(localStore); } else { Log.log(Level.SEVERE, "Invalid message, not a message instance"); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/ReversedIterator.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/ReversedIterator.java deleted file mode 100644 index 8121d00..0000000 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/utils/ReversedIterator.java +++ /dev/null @@ -1,45 +0,0 @@ -package nl.tudelft.blockchain.scaleoutdistributedledger.utils; - -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; - -/** - * Iterates over an iterable list in reversed order. - * @param - */ -public class ReversedIterator implements Iterable { - private final List original; - - /** - * Constructor. - * @param original - the original iterable list. - */ - public ReversedIterator(List original) { - this.original = original; - } - - /** - * Implementation of the iterator. - * @return - the iterator. - */ - public Iterator iterator() { - final ListIterator i = original.listIterator(original.size()); - - return new Iterator() { - public boolean hasNext() { return i.hasPrevious(); } - public T next() { return i.previous(); } - public void remove() { i.remove(); } - }; - } - - /** - * Gets a reversed iterator for an iterable list. - * @param original - the original list - * @param - the type of the original list - * @return - the reversed iterable. - */ - public static ReversedIterator reversed(List original) { - return new ReversedIterator(original); - } -} \ No newline at end of file diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java index 5f5074f..90fdf06 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreatorTest.java @@ -10,6 +10,7 @@ import org.junit.Before; import org.junit.Test; +import nl.tudelft.blockchain.scaleoutdistributedledger.exceptions.NotEnoughMoneyException; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; import nl.tudelft.blockchain.scaleoutdistributedledger.model.MetaKnowledge; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainViewTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainViewTest.java index d946112..ef9b444 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainViewTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainViewTest.java @@ -183,6 +183,19 @@ public void testGetBlock_UpdatePart() { assertEquals(2, chainview.getBlock(2).getNumber()); } + /** + * Test for {@link ChainView#size()}. + */ + @Test + public void testSize() { + addBlock(0, true); + addBlock(1, true); + addBlock(1, false); + addBlock(2, false); + + assertEquals(3, chainview.size()); + } + /** * Test for {@link ChainView#iterator()}, for next and nextIndex. */