Repository: bitcoin/bitcoin
File: src/test/fuzz/feefrac.cpp (C++)

Code before:

    // Multiply the 4 individual limbs (schoolbook multiply, with base 2^32).
    add_fn((a & 0xffffffff) * (b & 0xffffffff), 0);
    add_fn((a >> 32) * (b & 0xffffffff), 1);
    add_fn((a & 0xffffffff) * (b >> 32), 1);
    add_fn((a >> 32) * (b >> 32), 2);
    return ret;
}

/* comparison helper for std::array */
std::strong_ordering compare_arrays(const std::array<uint32_t, 4>& a, const std::array<uint32_t, 4>& b) {
    for (size_t i = 0; i < a.size(); ++i) {
        if (a[i] != b[i]) return a[i] <=> b[i];
    }
    return std::strong_ordering::equal;
}

std::strong_ordering MulCompare(int64_t a1, int64_t a2, int64_t b1, int64_t b2)
{
    // Compute and compare signs.
    int sign_a = (a1 == 0 ? 0 : a1 < 0 ? -1 : 1) * (a2 == 0 ? 0 : a2 < 0 ? -1 : 1);
    int sign_b = (b1 == 0 ? 0 : b1 < 0 ? -1 : 1) * (b2 == 0 ? 0 : b2 < 0 ? -1 : 1);
    if (sign_a != sign_b) return sign_a <=> sign_b;

    // Compute absolute values.
    uint64_t abs_a1 = static_cast<uint64_t>(a1), abs_a2 = static_cast<uint64_t>(a2);
    uint64_t abs_b1 = static_cast<uint64_t>(b1), abs_b2 = static_cast<uint64_t>(b2);
    // Use (~x + 1) instead of the equivalent (-x) to silence the linter; mod 2^64 behavior is
    // intentional here.
    if (a1 < 0) abs_a1 = ~abs_a1 + 1;
    if (a2 < 0) abs_a2 = ~abs_a2 + 1;
    if (b1 < 0) abs_b1 = ~abs_b1 + 1;
    if (b2 < 0) abs_b2 = ~abs_b2 + 1;

    // Compute products of absolute values.
    auto mul_abs_a = Mul128(abs_a1, abs_a2);
    auto mul_abs_b = Mul128(abs_b1, abs_b2);
    if (sign_a < 0) {
        return compare_arrays(mul_abs_b, mul_abs_a);
    } else {
        return compare_arrays(mul_abs_a, mul_abs_b);
    }
}

} // namespace

FUZZ_TARGET(feefrac)
{
    FuzzedDataProvider provider(buffer.data(), buffer.size());

    int64_t f1 = provider.ConsumeIntegral<int64_t>();

Review comment (refactor):
can we move this to the top to make it available for `Abs256`?

Code after:
arith_uint256 Abs256(std::pair<int64_t, uint32_t> x)
{
    if (x.first >= 0) {
        // x.first and x.second are both non-negative; sum their absolute values.
        return (Abs256(x.first) << 32) + Abs256(x.second);
    } else {
        // x.first is negative and x.second is non-negative; subtract the absolute values.
        return (Abs256(x.first) << 32) - Abs256(x.second);
    }
}

std::strong_ordering MulCompare(int64_t a1, int64_t a2, int64_t b1, int64_t b2)
{
    // Compute and compare signs.
    int sign_a = (a1 == 0 ? 0 : a1 < 0 ? -1 : 1) * (a2 == 0 ? 0 : a2 < 0 ? -1 : 1);
    int sign_b = (b1 == 0 ? 0 : b1 < 0 ? -1 : 1) * (b2 == 0 ? 0 : b2 < 0 ? -1 : 1);
    if (sign_a != sign_b) return sign_a <=> sign_b;

    // Compute absolute values of products.
    auto mul_abs_a = Abs256(a1) * Abs256(a2), mul_abs_b = Abs256(b1) * Abs256(b2);

    // Compute products of absolute values.
    if (sign_a < 0) {
        return mul_abs_b <=> mul_abs_a;
    } else {
        return mul_abs_a <=> mul_abs_b;
    }
}

} // namespace

FUZZ_TARGET(feefrac)
{
    FuzzedDataProvider provider(buffer.data(), buffer.size());

    int64_t f1 = provider.ConsumeIntegral<int64_t>();
    int32_t s1 = provider.ConsumeIntegral<int32_t>();
    if (s1 == 0) f1 = 0;
    FeeFrac fr1(f1, s1);
    assert(fr1.IsEmpty() == (s1 == 0));

    int64_t f2 = provider.ConsumeIntegral<int64_t>();
    int32_t s2 = provider.ConsumeIntegral<int32_t>();
    if (s2 == 0) f2 = 0;
    FeeFrac fr2(f2, s2);
    assert(fr2.IsEmpty() == (s2 == 0));

    // Feerate comparisons
    auto cmp_feerate = MulCompare(f1, s2, f2, s1);
    assert(FeeRateCompare(fr1, fr2) == cmp_feerate);
    assert((fr1 << fr2) == std::is_lt(cmp_feerate));