/*
 * Copyright (c) 2017 Fastly, Kazuho Oku
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */
#include "quicly/ranges.h"
#include "test.h"

#define CHECK(...)                                                                                                                 \
    do {                                                                                                                           \
        static const struct st_quicly_range_t expected[] = {__VA_ARGS__};                                                          \
        ok(ranges.num_ranges == PTLS_ELEMENTSOF(expected));                                                                        \
        size_t i;                                                                                                                  \
        for (i = 0; i != ranges.num_ranges; ++i) {                                                                                 \
            ok(ranges.ranges[i].start == expected[i].start);                                                                       \
            ok(ranges.ranges[i].end == expected[i].end);                                                                           \
        }                                                                                                                          \
    } while (0)

static void test_add(void)
{
    quicly_ranges_t ranges;
    int ret;

    quicly_ranges_init(&ranges);
    ok(ranges.num_ranges == 0);

    ret = quicly_ranges_add(&ranges, 40, 100);
    ok(ret == 0);
    CHECK({40, 100});

    ret = quicly_ranges_add(&ranges, 30, 40);
    ok(ret == 0);
    CHECK({30, 100});

    ret = quicly_ranges_add(&ranges, 0, 10);
    ok(ret == 0);
    CHECK({0, 10}, {30, 100});

    ret = quicly_ranges_add(&ranges, 10, 30);
    ok(ret == 0);
    CHECK({0, 100});

    ret = quicly_ranges_add(&ranges, 200, 300);
    ok(ret == 0);
    CHECK({0, 100}, {200, 300});

    ret = quicly_ranges_add(&ranges, 100, 110);
    ok(ret == 0);
    CHECK({0, 110}, {200, 300});

    ret = quicly_ranges_add(&ranges, 190, 200);
    ok(ret == 0);
    CHECK({0, 110}, {190, 300});

    ret = quicly_ranges_add(&ranges, 100, 120);
    ok(ret == 0);
    CHECK({0, 120}, {190, 300});

    ret = quicly_ranges_add(&ranges, 180, 200);
    ok(ret == 0);
    CHECK({0, 120}, {180, 300});

    ret = quicly_ranges_add(&ranges, 130, 150);
    ok(ret == 0);
    CHECK({0, 120}, {130, 150}, {180, 300});

    ret = quicly_ranges_add(&ranges, 160, 170);
    ok(ret == 0);
    CHECK({0, 120}, {130, 150}, {160, 170}, {180, 300});

    ret = quicly_ranges_add(&ranges, 170, 180);
    ok(ret == 0);
    CHECK({0, 120}, {130, 150}, {160, 300});

    ret = quicly_ranges_add(&ranges, 110, 180);
    ok(ret == 0);
    CHECK({0, 300});
}

static void test_subtract(void)
{
    quicly_ranges_t ranges;
    int ret;

    quicly_ranges_init(&ranges);

    ret = quicly_ranges_add(&ranges, 100, 200);
    ok(ret == 0);

    ret = quicly_ranges_subtract(&ranges, 0, 50);
    ok(ret == 0);
    CHECK({100, 200});

    ret = quicly_ranges_subtract(&ranges, 0, 100);
    ok(ret == 0);
    CHECK({100, 200});

    ret = quicly_ranges_subtract(&ranges, 250, 300);
    ok(ret == 0);
    CHECK({100, 200});

    ret = quicly_ranges_subtract(&ranges, 200, 300);
    ok(ret == 0);
    CHECK({100, 200});

    ret = quicly_ranges_subtract(&ranges, 100, 110);
    ok(ret == 0);
    CHECK({110, 200});

    ret = quicly_ranges_subtract(&ranges, 120, 130);
    ok(ret == 0);
    CHECK({110, 120}, {130, 200});

    ret = quicly_ranges_subtract(&ranges, 120, 130);
    ok(ret == 0);
    CHECK({110, 120}, {130, 200});

    ret = quicly_ranges_subtract(&ranges, 121, 129);
    ok(ret == 0);
    CHECK({110, 120}, {130, 200});

    ret = quicly_ranges_subtract(&ranges, 119, 120);
    ok(ret == 0);
    CHECK({110, 119}, {130, 200});

    ret = quicly_ranges_subtract(&ranges, 118, 120);
    ok(ret == 0);
    CHECK({110, 118}, {130, 200});

    ret = quicly_ranges_subtract(&ranges, 130, 132);
    ok(ret == 0);
    CHECK({110, 118}, {132, 200});

    ret = quicly_ranges_subtract(&ranges, 130, 133);
    ok(ret == 0);
    CHECK({110, 118}, {133, 200});

    ret = quicly_ranges_subtract(&ranges, 118, 134);
    ok(ret == 0);
    CHECK({110, 118}, {134, 200});

    ret = quicly_ranges_subtract(&ranges, 117, 134);
    ok(ret == 0);
    CHECK({110, 117}, {134, 200});

    ret = quicly_ranges_subtract(&ranges, 116, 135);
    ok(ret == 0);
    CHECK({110, 116}, {135, 200});

    ret = quicly_ranges_subtract(&ranges, 110, 140);
    ok(ret == 0);
    CHECK({140, 200});

    ret = quicly_ranges_add(&ranges, 100, 120);
    ok(ret == 0);
    CHECK({100, 120}, {140, 200});

    ret = quicly_ranges_subtract(&ranges, 80, 150);
    ok(ret == 0);
    CHECK({150, 200});

    ret = quicly_ranges_add(&ranges, 100, 120);
    ok(ret == 0);
    CHECK({100, 120}, {150, 200});

    ret = quicly_ranges_subtract(&ranges, 118, 200);
    ok(ret == 0);
    CHECK({100, 118});

    ret = quicly_ranges_add(&ranges, 140, 200);
    ok(ret == 0);
    CHECK({100, 118}, {140, 200});

    ret = quicly_ranges_subtract(&ranges, 118, 240);
    ok(ret == 0);
    CHECK({100, 118});

    ret = quicly_ranges_add(&ranges, 140, 200);
    ok(ret == 0);
    CHECK({100, 118}, {140, 200});

    ret = quicly_ranges_subtract(&ranges, 117, 240);
    ok(ret == 0);
    CHECK({100, 117});
}

static void test_next_missing(void)
{
    quicly_ranges_t ranges;
    int ret;

    quicly_ranges_init(&ranges);
    ok(ranges.num_ranges == 0);

    ret = quicly_ranges_add(&ranges, 1, 3);
    ok(ret == 0);
    CHECK({1, 3});

    ret = quicly_ranges_add(&ranges, 6, 10);
    ok(ret == 0);
    CHECK({1, 3}, {6, 10});

    ret = quicly_ranges_add(&ranges, 12, 15);
    ok(ret == 0);
    CHECK({1, 3}, {6, 10}, {12, 15});

    struct st_missing_test_case {
        uint64_t lower_bound;
        uint64_t expected_missing;
        uint64_t expected_slots_traversed;
    };

    struct st_missing_test_case missing_test_cases[] = {
        {1, 3, 3},  {2, 3, 3},   {3, 3, 3},   {4, 4, 2},   {5, 5, 2},   {6, 10, 2},  {7, 10, 2},  {8, 10, 2},
        {9, 10, 2}, {10, 10, 2}, {11, 11, 1}, {12, 15, 1}, {13, 15, 1}, {15, 15, 1}, {15, 15, 1}, {16, 16, 0},
    };

    uint64_t missing;
    size_t slot_traversed;
    for (size_t i = 0; i < PTLS_ELEMENTSOF(missing_test_cases); ++i) {
        missing = quicly_ranges_next_missing(&ranges, missing_test_cases[i].lower_bound, &slot_traversed);
        ok(missing == missing_test_cases[i].expected_missing);
        ok(slot_traversed == missing_test_cases[i].expected_slots_traversed);
    }
}

void test_ranges(void)
{
    subtest("add", test_add);
    subtest("subtract", test_subtract);
    subtest("next_missing", test_next_missing);
}
