aboutsummaryrefslogtreecommitdiff
path: root/src/util/feefrac.cpp
blob: 68fb836936005ec66db90ff5015cae5dae65a031 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// Copyright (c) The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <util/feefrac.h>
#include <algorithm>
#include <array>
#include <vector>

std::vector<FeeFrac> BuildDiagramFromChunks(const Span<const FeeFrac> chunks)
{
    std::vector<FeeFrac> diagram;
    diagram.reserve(chunks.size() + 1);

    diagram.emplace_back(0, 0);
    for (auto& chunk : chunks) {
        diagram.emplace_back(diagram.back() + chunk);
    }
    return diagram;
}

std::partial_ordering CompareFeerateDiagram(Span<const FeeFrac> dia0, Span<const FeeFrac> dia1)
{
    /** Array to allow indexed access to input diagrams. */
    const std::array<Span<const FeeFrac>, 2> dias = {dia0, dia1};
    /** How many elements we have processed in each input. */
    size_t next_index[2] = {1, 1};
    /** Whether the corresponding input is strictly better than the other at least in one place. */
    bool better_somewhere[2] = {false, false};
    /** Get the first unprocessed point in diagram number dia. */
    const auto next_point = [&](int dia) { return dias[dia][next_index[dia]]; };
    /** Get the last processed point in diagram number dia. */
    const auto prev_point = [&](int dia) { return dias[dia][next_index[dia] - 1]; };

    // Diagrams should be non-empty, and first elements zero in size and fee
    Assert(!dia0.empty() && !dia1.empty());
    Assert(prev_point(0).IsEmpty());
    Assert(prev_point(1).IsEmpty());

    do {
        bool done_0 = next_index[0] == dias[0].size();
        bool done_1 = next_index[1] == dias[1].size();
        if (done_0 && done_1) break;

        // Determine which diagram has the first unprocessed point. If a single side is finished, use the
        // other one. Only up to one can be done due to check above.
        const int unproc_side = (done_0 || done_1) ? done_0 : next_point(0).size > next_point(1).size;

        // Let `P` be the next point on diagram unproc_side, and `A` and `B` the previous and next points
        // on the other diagram. We want to know if P lies above or below the line AB. To determine this, we
        // compute the slopes of line AB and of line AP, and compare them. These slopes are fee per size,
        // and can thus be expressed as FeeFracs.
        const FeeFrac& point_p = next_point(unproc_side);
        const FeeFrac& point_a = prev_point(!unproc_side);

        const auto slope_ap = point_p - point_a;
        Assume(slope_ap.size > 0);
        std::weak_ordering cmp = std::weak_ordering::equivalent;
        if (done_0 || done_1) {
            // If a single side has no points left, act as if AB has slope tail_feerate(of 0).
            Assume(!(done_0 && done_1));
            cmp = FeeRateCompare(slope_ap, FeeFrac(0, 1));
        } else {
            // If both sides have points left, compute B, and the slope of AB explicitly.
            const FeeFrac& point_b = next_point(!unproc_side);
            const auto slope_ab = point_b - point_a;
            Assume(slope_ab.size >= slope_ap.size);
            cmp = FeeRateCompare(slope_ap, slope_ab);

            // If B and P have the same size, B can be marked as processed (in addition to P, see
            // below), as we've already performed a comparison at this size.
            if (point_b.size == point_p.size) ++next_index[!unproc_side];
        }
        // If P lies above AB, unproc_side is better in P. If P lies below AB, then !unproc_side is
        // better in P.
        if (std::is_gt(cmp)) better_somewhere[unproc_side] = true;
        if (std::is_lt(cmp)) better_somewhere[!unproc_side] = true;
        ++next_index[unproc_side];

        // If both diagrams are better somewhere, they are incomparable.
        if (better_somewhere[0] && better_somewhere[1]) return std::partial_ordering::unordered;

    } while(true);

    // Otherwise compare the better_somewhere values.
    return better_somewhere[0] <=> better_somewhere[1];
}