data/RelationGraphHouseholds.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // The households of a generation row (declared in RelationGraphRows.h, | ||
| 2 | // split from RelationGraphRows.cpp). | ||
| 3 | #include "RelationGraphRows.h" | ||
| 4 | |||
| 5 | #include <QMap> | ||
| 6 | #include <QSet> | ||
| 7 | #include <algorithm> | ||
| 8 | |||
| 9 | namespace RelationGraphLayout { | ||
| 10 | |||
| 11 | namespace { | ||
| 12 | |||
| 13 | 1843 | bool byName(const RelationGraph::Graph &graph, int a, int b) { | |
| 14 | 1843 | const int c = graph.nodes.at(a).name.localeAwareCompare(graph.nodes.at(b).name); | |
| 15 |
1/2✓ Branch 5 → 6 taken 1843 times.
✗ Branch 5 → 7 not taken.
|
1843 | return c != 0 ? c < 0 : graph.nodes.at(a).key < graph.nodes.at(b).key; |
| 16 | } | ||
| 17 | |||
| 18 | // The birthday with a year ("1988-04-12"), else "" (unknown or "--06-14"). | ||
| 19 | 4322 | QString datedBirthday(const RelationGraph::Node &node) { | |
| 20 |
5/5✓ Branch 3 → 4 taken 480 times.
✓ Branch 3 → 8 taken 3842 times.
✓ Branch 5 → 6 taken 480 times.
✓ Branch 6 → 7 taken 415 times.
✓ Branch 6 → 8 taken 65 times.
|
4322 | return node.birthday.size() >= 4 && !node.birthday.startsWith(QLatin1String("--")) ? node.birthday : QString(); |
| 21 | } | ||
| 22 | |||
| 23 | // Siblings as a family tree orders them: the older first, those without a | ||
| 24 | // known year after them by name. | ||
| 25 | 2161 | bool byAge(const RelationGraph::Graph &graph, int a, int b) { | |
| 26 |
2/2✓ Branch 3 → 4 taken 2161 times.
✓ Branch 5 → 6 taken 2161 times.
|
2161 | const QString da = datedBirthday(graph.nodes.at(a)), db = datedBirthday(graph.nodes.at(b)); |
| 27 |
2/2✓ Branch 8 → 9 taken 221 times.
✓ Branch 8 → 11 taken 1940 times.
|
2161 | if (da.isEmpty() != db.isEmpty()) |
| 28 | 221 | return !da.isEmpty(); | |
| 29 |
2/2✓ Branch 12 → 13 taken 97 times.
✓ Branch 12 → 15 taken 1843 times.
|
1940 | if (da != db) |
| 30 | 97 | return da < db; | |
| 31 |
1/1✓ Branch 15 → 16 taken 1843 times.
|
1843 | return byName(graph, a, b); |
| 32 | 2161 | } | |
| 33 | |||
| 34 | } // namespace | ||
| 35 | |||
| 36 | 763 | QList<QList<int>> households(const RelationGraph::Graph &graph, const QList<int> &row, | |
| 37 | const QSet<QPair<int, int>> &siblings, const QHash<int, int> &partnerOf) { | ||
| 38 |
1/1✓ Branch 4 → 5 taken 763 times.
|
763 | const QSet<int> inRow(row.cbegin(), row.cend()); |
| 39 | 763 | QHash<int, int> group; // sibling group: the smallest index of the group | |
| 40 | 5687 | const auto find = [&group](int i) { | |
| 41 |
2/2✓ Branch 6 → 3 taken 787 times.
✓ Branch 6 → 7 taken 5687 times.
|
6474 | while (group.value(i, i) != i) |
| 42 | 787 | i = group.value(i, i); | |
| 43 | 5687 | return i; | |
| 44 | 763 | }; | |
| 45 |
2/2✓ Branch 30 → 7 taken 1475 times.
✓ Branch 30 → 31 taken 763 times.
|
2238 | for (const auto &[a, b] : siblings) |
| 46 |
7/8✓ Branch 11 → 12 taken 538 times.
✓ Branch 11 → 18 taken 937 times.
✓ Branch 13 → 14 taken 538 times.
✗ Branch 13 → 18 not taken.
✓ Branch 16 → 17 taken 510 times.
✓ Branch 16 → 18 taken 28 times.
✓ Branch 19 → 20 taken 510 times.
✓ Branch 19 → 28 taken 965 times.
|
1475 | if (inRow.contains(a) && inRow.contains(b) && find(a) != find(b)) |
| 47 |
1/1✓ Branch 26 → 27 taken 510 times.
|
510 | group.insert(std::max(find(a), find(b)), std::min(find(a), find(b))); |
| 48 | 763 | QMap<int, QList<int>> groups; // group id → members, the oldest first | |
| 49 |
2/2✓ Branch 39 → 33 taken 1891 times.
✓ Branch 39 → 40 taken 763 times.
|
2654 | for (int node : row) |
| 50 |
2/2✓ Branch 35 → 36 taken 1891 times.
✓ Branch 36 → 37 taken 1891 times.
|
1891 | groups[find(node)].append(node); |
| 51 |
4/4✓ Branch 40 → 41 taken 763 times.
✓ Branch 41 → 42 taken 763 times.
✓ Branch 49 → 43 taken 1381 times.
✓ Branch 49 → 50 taken 763 times.
|
2144 | for (QList<int> &members : groups) |
| 52 |
3/3✓ Branch 44 → 45 taken 1381 times.
✓ Branch 45 → 46 taken 1381 times.
✓ Branch 46 → 47 taken 1381 times.
|
2382 | std::sort(members.begin(), members.end(), [&graph](int a, int b) { return byAge(graph, a, b); }); |
| 53 | 7247 | const auto partnerIn = [&](int node) { | |
| 54 | 7247 | const int partner = partnerOf.value(node, -1); | |
| 55 |
2/2✓ Branch 4 → 5 taken 3171 times.
✓ Branch 4 → 6 taken 4076 times.
|
7247 | return inRow.contains(partner) ? partner : -1; |
| 56 | 763 | }; | |
| 57 | // Groups in order of their first (oldest) person — so children of one | ||
| 58 | // couple who are not joined as siblings stand oldest first, too; each | ||
| 59 | // starts a chain unless it is reached through a partner. | ||
| 60 |
1/1✓ Branch 50 → 51 taken 763 times.
|
763 | QList<int> starts = groups.keys(); |
| 61 |
3/3✓ Branch 51 → 52 taken 763 times.
✓ Branch 52 → 53 taken 763 times.
✓ Branch 53 → 54 taken 763 times.
|
763 | std::sort(starts.begin(), starts.end(), [&](int a, int b) { |
| 62 |
3/3✓ Branch 3 → 4 taken 1265 times.
✓ Branch 6 → 7 taken 1265 times.
✓ Branch 8 → 9 taken 1265 times.
|
2797 | const int la = int(std::count_if(groups.value(a).cbegin(), groups.value(a).cend(), [&](int n) { return partnerIn(n) >= 0; })); |
| 63 |
3/3✓ Branch 14 → 15 taken 1265 times.
✓ Branch 17 → 18 taken 1265 times.
✓ Branch 19 → 20 taken 1265 times.
|
3208 | const int lb = int(std::count_if(groups.value(b).cbegin(), groups.value(b).cend(), [&](int n) { return partnerIn(n) >= 0; })); |
| 64 |
2/2✓ Branch 24 → 25 taken 105 times.
✓ Branch 24 → 26 taken 1160 times.
|
1265 | if ((la <= 1) != (lb <= 1)) |
| 65 | 105 | return la <= 1; // a chain's end first | |
| 66 |
5/5✓ Branch 27 → 28 taken 1160 times.
✓ Branch 28 → 29 taken 1160 times.
✓ Branch 30 → 31 taken 1160 times.
✓ Branch 31 → 32 taken 1160 times.
✓ Branch 32 → 33 taken 1160 times.
|
1160 | return byAge(graph, groups.value(a).first(), groups.value(b).first()); |
| 67 | }); | ||
| 68 | 763 | QSet<int> done; // people placed | |
| 69 | // How many people of a sibling group are still to be placed. | ||
| 70 | 1061 | const auto open = [&](int groupId) { | |
| 71 |
3/3✓ Branch 3 → 4 taken 1061 times.
✓ Branch 6 → 7 taken 1061 times.
✓ Branch 8 → 9 taken 1061 times.
|
2122 | return int(std::count_if(groups.value(groupId).cbegin(), groups.value(groupId).cend(), |
| 72 | 3556 | [&](int n) { return !done.contains(n); })); | |
| 73 | 763 | }; | |
| 74 | 763 | QList<QList<int>> order; | |
| 75 | 763 | QSet<int> visited; // sibling groups | |
| 76 |
2/2✓ Branch 163 → 59 taken 1381 times.
✓ Branch 163 → 164 taken 763 times.
|
2144 | for (int start : std::as_const(starts)) { |
| 77 |
7/7✓ Branch 61 → 62 taken 1051 times.
✓ Branch 61 → 64 taken 330 times.
✓ Branch 62 → 63 taken 1051 times.
✓ Branch 63 → 64 taken 5 times.
✓ Branch 63 → 65 taken 1046 times.
✓ Branch 66 → 67 taken 335 times.
✓ Branch 66 → 68 taken 1046 times.
|
1381 | if (visited.contains(start) || open(start) == 0) // all taken in by their partners |
| 78 | 335 | continue; | |
| 79 |
1/1✓ Branch 69 → 70 taken 1046 times.
|
1046 | order.append(QList<int>{}); |
| 80 | 1046 | int current = start, entry = -1; | |
| 81 |
5/6✓ Branch 154 → 155 taken 1376 times.
✓ Branch 154 → 158 taken 1046 times.
✓ Branch 156 → 157 taken 1376 times.
✗ Branch 156 → 158 not taken.
✓ Branch 159 → 72 taken 1376 times.
✓ Branch 159 → 160 taken 1046 times.
|
2422 | while (current >= 0 && !visited.contains(current)) { |
| 82 |
1/1✓ Branch 72 → 73 taken 1376 times.
|
1376 | visited.insert(current); |
| 83 | 1376 | QList<int> members; | |
| 84 |
5/5✓ Branch 74 → 75 taken 1376 times.
✓ Branch 76 → 77 taken 1376 times.
✓ Branch 77 → 78 taken 1376 times.
✓ Branch 85 → 79 taken 1886 times.
✓ Branch 85 → 86 taken 1376 times.
|
3262 | for (int node : groups.value(current)) |
| 85 |
1/2✓ Branch 81 → 82 taken 1886 times.
✗ Branch 81 → 83 not taken.
|
1886 | if (!done.contains(node)) |
| 86 |
1/1✓ Branch 82 → 83 taken 1886 times.
|
3262 | members.append(node); |
| 87 | // The chain goes on through the partner whose own siblings are most. | ||
| 88 | 1376 | int exit = -1, next = -1; | |
| 89 |
2/2✓ Branch 116 → 90 taken 1886 times.
✓ Branch 116 → 117 taken 1376 times.
|
3262 | for (int node : std::as_const(members)) { |
| 90 |
1/1✓ Branch 91 → 92 taken 1886 times.
|
1886 | const int partner = partnerIn(node); |
| 91 |
8/10✓ Branch 92 → 93 taken 1556 times.
✓ Branch 92 → 99 taken 330 times.
✓ Branch 93 → 94 taken 335 times.
✓ Branch 93 → 99 taken 1221 times.
✓ Branch 95 → 96 taken 335 times.
✗ Branch 95 → 99 not taken.
✗ Branch 98 → 99 not taken.
✓ Branch 98 → 100 taken 335 times.
✓ Branch 101 → 102 taken 1551 times.
✓ Branch 101 → 103 taken 335 times.
|
1886 | if (node == entry || partner < 0 || done.contains(partner) || visited.contains(find(partner))) |
| 92 | 1551 | continue; | |
| 93 |
8/8✓ Branch 103 → 104 taken 5 times.
✓ Branch 103 → 109 taken 330 times.
✓ Branch 105 → 106 taken 5 times.
✓ Branch 107 → 108 taken 5 times.
✓ Branch 108 → 109 taken 1 time.
✓ Branch 108 → 110 taken 4 times.
✓ Branch 111 → 112 taken 331 times.
✓ Branch 111 → 113 taken 4 times.
|
335 | if (next < 0 || open(find(partner)) > open(find(next))) { |
| 94 | 331 | exit = node; | |
| 95 | 331 | next = partner; | |
| 96 | } | ||
| 97 | } | ||
| 98 |
2/2✓ Branch 117 → 118 taken 330 times.
✓ Branch 117 → 120 taken 1046 times.
|
1376 | if (entry >= 0) |
| 99 |
1/1✓ Branch 119 → 120 taken 330 times.
|
330 | members.move(int(members.indexOf(entry)), 0); |
| 100 |
2/2✓ Branch 120 → 121 taken 330 times.
✓ Branch 120 → 124 taken 1046 times.
|
1376 | if (exit >= 0) |
| 101 |
1/1✓ Branch 123 → 124 taken 330 times.
|
330 | members.move(int(members.indexOf(exit)), int(members.size()) - 1); |
| 102 | // Every other partner stands right beside their partner (and leaves | ||
| 103 | // their own siblings): a couple is never split. | ||
| 104 |
2/2✓ Branch 148 → 127 taken 1886 times.
✓ Branch 148 → 149 taken 1376 times.
|
3262 | for (int node : std::as_const(members)) { |
| 105 |
2/2✓ Branch 128 → 129 taken 1886 times.
✓ Branch 129 → 130 taken 1886 times.
|
1886 | order.last().append(node); |
| 106 |
1/1✓ Branch 130 → 131 taken 1886 times.
|
1886 | done.insert(node); |
| 107 |
1/1✓ Branch 131 → 132 taken 1886 times.
|
1886 | const int partner = partnerIn(node); |
| 108 |
10/12✓ Branch 132 → 133 taken 1556 times.
✓ Branch 132 → 140 taken 330 times.
✓ Branch 133 → 134 taken 1226 times.
✓ Branch 133 → 140 taken 330 times.
✓ Branch 134 → 135 taken 5 times.
✓ Branch 134 → 140 taken 1221 times.
✓ Branch 136 → 137 taken 5 times.
✗ Branch 136 → 140 not taken.
✓ Branch 138 → 139 taken 5 times.
✗ Branch 138 → 140 not taken.
✓ Branch 141 → 142 taken 5 times.
✓ Branch 141 → 146 taken 1881 times.
|
1886 | if (node != entry && node != exit && partner >= 0 && !done.contains(partner) && find(partner) != current) { |
| 109 |
2/2✓ Branch 142 → 143 taken 5 times.
✓ Branch 143 → 144 taken 5 times.
|
5 | order.last().append(partner); |
| 110 |
1/1✓ Branch 144 → 145 taken 5 times.
|
5 | done.insert(partner); |
| 111 | } | ||
| 112 | } | ||
| 113 | 1376 | entry = next; | |
| 114 |
2/2✓ Branch 149 → 150 taken 330 times.
✓ Branch 149 → 151 taken 1046 times.
|
1376 | current = next >= 0 ? find(next) : -1; |
| 115 | 1376 | } | |
| 116 | } | ||
| 117 | 763 | return order; | |
| 118 | 763 | } | |
| 119 | |||
| 120 | } // namespace RelationGraphLayout | ||
| 121 |