/// <summary> /// Returns the current representative of the set this node is in. /// Note that the representative is only accurate untl the next Union operation. /// </summary> public UnionFindNode Find() { if (!ReferenceEquals(_parent, this)) { _parent = _parent.Find(); } return(_parent); }
/// <summary> /// Determines whether or not this node and the other node are in the same set. /// </summary> public bool IsUnionedWith(UnionFindNode other) { if (other == null) { throw new ArgumentNullException("other"); } return(ReferenceEquals(Find(), other.Find())); }
//TODO: for testnet we only have a single chain, thus grouper only take care of txList in one chain (hence Process has chainId as parameter) public async Task <Tuple <List <List <Transaction> >, Dictionary <Transaction, Exception> > > ProcessNaive(Hash chainId, List <Transaction> transactions) { var txResourceHandle = new Dictionary <Transaction, string>(); var failedTxs = new Dictionary <Transaction, Exception>(); if (transactions.Count == 0) { return(new Tuple <List <List <Transaction> >, Dictionary <Transaction, Exception> >(new List <List <Transaction> >(), failedTxs)); } Dictionary <string, UnionFindNode> resourceUnionSet = new Dictionary <string, UnionFindNode>(); //set up the union find set as the representation of graph and the connected components will be the resulting groups foreach (var tx in transactions) { UnionFindNode first = null; List <string> resources; try { resources = (await _resourceUsageDetectionService.GetResources(chainId, tx)).ToList(); } catch (Exception e) { failedTxs.Add(tx, e); continue; } //_logger.Debug(string.Format("tx {0} have resource [{1}]", tx.From, string.Join(" ||| ", resources))); foreach (var resource in resources) { if (!resourceUnionSet.TryGetValue(resource, out var node)) { node = new UnionFindNode(); resourceUnionSet.Add(resource, node); } if (first == null) { first = node; txResourceHandle.Add(tx, resource); } else { node.Union(first); } } } Dictionary <int, List <Transaction> > grouped = new Dictionary <int, List <Transaction> >(); List <List <Transaction> > result = new List <List <Transaction> >(); foreach (var tx in transactions) { if (txResourceHandle.TryGetValue(tx, out var firstResource)) { int nodeId = resourceUnionSet[firstResource].Find().NodeId; if (!grouped.TryGetValue(nodeId, out var group)) { group = new List <Transaction>(); grouped.Add(nodeId, group); } group.Add(tx); } else { if (!failedTxs.ContainsKey(tx)) { //each "resource-free" transaction have its own group result.Add(new List <Transaction>() { tx }); } } } result.AddRange(grouped.Values); _logger?.Info(string.Format( "Grouper on chainId \"{0}\" group [{1}] transactions into [{2}] groups with sizes [{3}], There are also {4} transactions failed retriving resource", chainId.DumpHex(), transactions.Count, result.Count, string.Join(", ", result.Select(a => a.Count)), failedTxs.Count)); return(new Tuple <List <List <Transaction> >, Dictionary <Transaction, Exception> >(result, failedTxs));; }
/// <summary> /// Creates a new disjoint node, representative of a set containing only the new node. /// </summary> public UnionFindNode() { _parent = this; NodeId = Interlocked.Increment(ref _nextId); }