Skip to content

Commit 0028410

Browse files
author
Marcel Nadzam
committed
Fix settlement algorithm - transitive payments
1 parent e2f4d2f commit 0028410

File tree

1 file changed

+55
-19
lines changed

1 file changed

+55
-19
lines changed

FairSplit/Utils/TransactionSettler.cs

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public static HashSet<Payment> CalculateBestSettleOptions(Group group, List<Tran
7272

7373
graph.EliminateLoops();
7474
graph.EliminateSplits();
75+
graph.EliminateChains();
7576

7677
return graph.ToPayments();
7778
}
@@ -123,23 +124,6 @@ public void AddEdge(Member source, Member destination, decimal amount)
123124
sourceNode.Edges[destinationNode] = amount;
124125
}
125126

126-
public string ToGraphviz()
127-
{
128-
var sb = new StringBuilder();
129-
sb.AppendLine("digraph G {");
130-
131-
foreach (var node in _nodes)
132-
{
133-
foreach (var (destination, amount) in node.Edges)
134-
{
135-
sb.AppendLine($" \"{node.Person.Name}\" -> \"{destination.Person.Name}\" [label=\"{amount}\"];");
136-
}
137-
}
138-
139-
sb.AppendLine("}");
140-
return sb.ToString();
141-
}
142-
143127
public void EliminateSplits()
144128
{
145129
foreach (var node in _nodes)
@@ -177,7 +161,7 @@ private bool EliminateSplitBFS(Node start)
177161
return false;
178162
}
179163

180-
private void RemoveFoundSplit(Node shorterRouteWithFinalNode, Node longerRoute, decimal fromLongerToFinal)
164+
private static void RemoveFoundSplit(Node shorterRouteWithFinalNode, Node longerRoute, decimal fromLongerToFinal)
181165
{
182166
HashSet<Node> longerRouteNodes = [];
183167

@@ -267,7 +251,6 @@ private bool EliminateLoopDFS(Node currentNode, HashSet<Node> visited)
267251
if (visited.Contains(edge.Key))
268252
{
269253
RemoveFoundLoop(currentNode);
270-
var deez = ToGraphviz();
271254
return true;
272255
}
273256
visited.Add(currentNode);
@@ -317,6 +300,59 @@ private static void RemoveFoundLoop(Node currentNode)
317300
currentNode.Edges.Remove(currentNodeSuccessor);
318301
}
319302
}
303+
304+
public void EliminateChains()
305+
{
306+
while (true)
307+
{
308+
var changed = false;
309+
310+
foreach (var u in _nodes.ToList())
311+
{
312+
foreach (var uvPair in u.Edges.ToList())
313+
{
314+
var v = uvPair.Key;
315+
var uvValue = uvPair.Value;
316+
if (uvValue <= 0)
317+
{
318+
continue;
319+
}
320+
321+
foreach (var vwPair in v.Edges.ToList())
322+
{
323+
var w = vwPair.Key;
324+
var vwValue = vwPair.Value;
325+
if (w == u)
326+
{
327+
continue;
328+
}
329+
330+
if (Math.Abs(uvValue - vwValue) <= 0.0000001m)
331+
{
332+
u.Edges.Remove(v);
333+
v.Edges.Remove(w);
334+
335+
if (u.Edges.TryGetValue(w, out var uw))
336+
{
337+
u.Edges[w] = uw + uvValue;
338+
}
339+
else
340+
{
341+
u.Edges[w] = uvValue;
342+
}
343+
344+
changed = true;
345+
break;
346+
}
347+
}
348+
if (changed) break;
349+
}
350+
if (changed) break;
351+
}
352+
if (!changed) break;
353+
}
354+
RemovePredecesors();
355+
}
320356
}
321357

322358
private class Node

0 commit comments

Comments
 (0)