1 module tagion.hashgraphview.Compare;
2 
3 import std.algorithm.iteration : map;
4 import std.array : array;
5 import std.range : lockstep;
6 import tagion.hashgraph.Event;
7 import tagion.hashgraph.HashGraph;
8 import tagion.hashgraph.HashGraphBasic : higher;
9 
10 @safe
11 struct Compare {
12     enum ErrorCode {
13         NONE,
14         NODES_DOES_NOT_MATCH,
15         FINGERPRINT_NOT_THE_SAME,
16         MOTHER_NOT_THE_SAME,
17         FATHER_NOT_THE_SAME,
18         ALTITUDE_NOT_THE_SAME,
19         ORDER_NOT_THE_SAME,
20         ROUND_NOT_THE_SAME,
21         ROUND_RECEIVED_NOT_THE_SAME,
22         WITNESS_CONFLICT,
23     }
24 
25     alias ErrorCallback = bool delegate(const Event e1, const Event e2, const ErrorCode code) nothrow @safe;
26     const HashGraph h1, h2;
27     const ErrorCallback error_callback;
28     int order_offset;
29     long round_offset;
30     uint count;
31     this(const HashGraph h1, const HashGraph h2, const ErrorCallback error_callback) {
32         this.h1 = h1;
33         this.h2 = h2;
34         this.error_callback = error_callback;
35     }
36 
37     bool compare() @trusted {
38         count = 0;
39         auto h1_nodes = h1.nodes
40             .byValue
41             .map!((n) => n[])
42             .array;
43         typeof(h1_nodes) h2_nodes;
44         try {
45             h2_nodes = h1.nodes
46                 .byValue
47                 .map!((n) => h2.nodes[n.channel][])
48                 .array;
49         }
50         catch (Exception e) {
51             if (error_callback) {
52                 error_callback(null, null, ErrorCode.NODES_DOES_NOT_MATCH);
53             }
54             return false;
55         }
56         bool ok = true;
57 
58         foreach (ref h1_events, ref h2_events; lockstep(h1_nodes, h2_nodes)) {
59             while (!h1_events.empty && higher(h1_events.front.altitude, h2_events
60                     .front.altitude)) {
61                 h1_events.popFront;
62             }
63             while (!h2_events.empty && higher(h2_events.front.altitude, h1_events
64                     .front.altitude)) {
65                 h2_events.popFront;
66             }
67             bool check(bool ok, const ErrorCode code) {
68                 if (!ok && error_callback) {
69                     return error_callback(h1_events.front, h2_events.front, code);
70                 }
71                 return ok;
72             }
73 
74             if (!h1_events.empty && !h2_events.empty) {
75                 order_offset = h1_events.front.order - h2_events.front.order;
76                 if (!h1_events.front.hasRound || !h2_events.front.hasRound) {
77                     return error_callback(null, null, ErrorCode.NODES_DOES_NOT_MATCH);
78                 }
79                 round_offset = h1_events.front.round.number - h2_events.front.round.number;
80             }
81             //error_callback(h1_events.front, h2_events.front, ErrorCode.NONE);
82             while (!h1_events.empty && !h2_events.empty) {
83                 const e1 = h1_events.front;
84                 const e2 = h2_events.front;
85 
86                 with (ErrorCode) {
87                     ok &= check(e1.fingerprint == e2.fingerprint, FINGERPRINT_NOT_THE_SAME);
88                     ok &= check(e1.event_body.mother == e2.event_body.mother, MOTHER_NOT_THE_SAME);
89                     ok &= check(e1.event_body.father == e2.event_body.father, FATHER_NOT_THE_SAME);
90                     ok &= check(e1.altitude == e2.altitude, ALTITUDE_NOT_THE_SAME);
91                     ok &= check(e1.order - e2.order == order_offset, ORDER_NOT_THE_SAME);
92                     ok &= check(e1.round.number - e2.round.number == round_offset, ROUND_NOT_THE_SAME);
93                     if ((e1.round_received) && (e2.round_received)) {
94                         ok &= check(e1.round_received.number - e2.round_received.number == round_offset,
95                                 ROUND_RECEIVED_NOT_THE_SAME);
96                     }
97                     ok &= check((e1.witness is null) == (e2.witness is null), WITNESS_CONFLICT);
98                 }
99                 // if (!ok) {
100                 //     return ok;
101                 // }
102                 count++;
103                 h1_events.popFront;
104                 h2_events.popFront;
105             }
106         }
107         return ok;
108     }
109 }