/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include "percent-util.h"
#include "tests.h"
#include "time-util.h"

static void test_parse_percent(void) {
        assert_se(parse_percent("") == -EINVAL);
        assert_se(parse_percent("foo") == -EINVAL);
        assert_se(parse_percent("0") == -EINVAL);
        assert_se(parse_percent("0.1") == -EINVAL);
        assert_se(parse_percent("50") == -EINVAL);
        assert_se(parse_percent("100") == -EINVAL);
        assert_se(parse_percent("-1") == -EINVAL);
        assert_se(parse_percent("0%") == 0);
        assert_se(parse_percent("55%") == 55);
        assert_se(parse_percent("100%") == 100);
        assert_se(parse_percent("-7%") == -ERANGE);
        assert_se(parse_percent("107%") == -ERANGE);
        assert_se(parse_percent("%") == -EINVAL);
        assert_se(parse_percent("%%") == -EINVAL);
        assert_se(parse_percent("%1") == -EINVAL);
        assert_se(parse_percent("1%%") == -EINVAL);
        assert_se(parse_percent("3.2%") == -EINVAL);
}

static void test_parse_percent_unbounded(void) {
        assert_se(parse_percent_unbounded("101%") == 101);
        assert_se(parse_percent_unbounded("400%") == 400);
}

static void test_parse_permille(void) {
        assert_se(parse_permille("") == -EINVAL);
        assert_se(parse_permille("foo") == -EINVAL);
        assert_se(parse_permille("0") == -EINVAL);
        assert_se(parse_permille("50") == -EINVAL);
        assert_se(parse_permille("100") == -EINVAL);
        assert_se(parse_permille("-1") == -EINVAL);
        assert_se(parse_permille("0.1") == -EINVAL);
        assert_se(parse_permille("5%") == 50);
        assert_se(parse_permille("5.5%") == 55);
        assert_se(parse_permille("5.12%") == -EINVAL);

        assert_se(parse_permille("0‰") == 0);
        assert_se(parse_permille("555‰") == 555);
        assert_se(parse_permille("1000‰") == 1000);
        assert_se(parse_permille("-7‰") == -ERANGE);
        assert_se(parse_permille("1007‰") == -ERANGE);
        assert_se(parse_permille("‰") == -EINVAL);
        assert_se(parse_permille("‰‰") == -EINVAL);
        assert_se(parse_permille("‰1") == -EINVAL);
        assert_se(parse_permille("1‰‰") == -EINVAL);
        assert_se(parse_permille("3.2‰") == -EINVAL);
        assert_se(parse_permille("0.1‰") == -EINVAL);

        assert_se(parse_permille("0%") == 0);
        assert_se(parse_permille("55%") == 550);
        assert_se(parse_permille("55.5%") == 555);
        assert_se(parse_permille("100%") == 1000);
        assert_se(parse_permille("-7%") == -ERANGE);
        assert_se(parse_permille("107%") == -ERANGE);
        assert_se(parse_permille("%") == -EINVAL);
        assert_se(parse_permille("%%") == -EINVAL);
        assert_se(parse_permille("%1") == -EINVAL);
        assert_se(parse_permille("1%%") == -EINVAL);
        assert_se(parse_permille("3.21%") == -EINVAL);
        assert_se(parse_permille("0.1%") == 1);
}

static void test_parse_permille_unbounded(void) {
        assert_se(parse_permille_unbounded("1001‰") == 1001);
        assert_se(parse_permille_unbounded("4000‰") == 4000);
        assert_se(parse_permille_unbounded("2147483647‰") == 2147483647);
        assert_se(parse_permille_unbounded("2147483648‰") == -ERANGE);
        assert_se(parse_permille_unbounded("4294967295‰") == -ERANGE);
        assert_se(parse_permille_unbounded("4294967296‰") == -ERANGE);

        assert_se(parse_permille_unbounded("101%") == 1010);
        assert_se(parse_permille_unbounded("400%") == 4000);
        assert_se(parse_permille_unbounded("214748364.7%") == 2147483647);
        assert_se(parse_permille_unbounded("214748364.8%") == -ERANGE);
        assert_se(parse_permille_unbounded("429496729.5%") == -ERANGE);
        assert_se(parse_permille_unbounded("429496729.6%") == -ERANGE);
}

static void test_parse_permyriad(void) {
        assert_se(parse_permyriad("") == -EINVAL);
        assert_se(parse_permyriad("foo") == -EINVAL);
        assert_se(parse_permyriad("0") == -EINVAL);
        assert_se(parse_permyriad("50") == -EINVAL);
        assert_se(parse_permyriad("100") == -EINVAL);
        assert_se(parse_permyriad("-1") == -EINVAL);

        assert_se(parse_permyriad("0‱") == 0);
        assert_se(parse_permyriad("555‱") == 555);
        assert_se(parse_permyriad("1000‱") == 1000);
        assert_se(parse_permyriad("-7‱") == -ERANGE);
        assert_se(parse_permyriad("10007‱") == -ERANGE);
        assert_se(parse_permyriad("‱") == -EINVAL);
        assert_se(parse_permyriad("‱‱") == -EINVAL);
        assert_se(parse_permyriad("‱1") == -EINVAL);
        assert_se(parse_permyriad("1‱‱") == -EINVAL);
        assert_se(parse_permyriad("3.2‱") == -EINVAL);

        assert_se(parse_permyriad("0‰") == 0);
        assert_se(parse_permyriad("555.5‰") == 5555);
        assert_se(parse_permyriad("1000.0‰") == 10000);
        assert_se(parse_permyriad("-7‰") == -ERANGE);
        assert_se(parse_permyriad("1007‰") == -ERANGE);
        assert_se(parse_permyriad("‰") == -EINVAL);
        assert_se(parse_permyriad("‰‰") == -EINVAL);
        assert_se(parse_permyriad("‰1") == -EINVAL);
        assert_se(parse_permyriad("1‰‰") == -EINVAL);
        assert_se(parse_permyriad("3.22‰") == -EINVAL);

        assert_se(parse_permyriad("0%") == 0);
        assert_se(parse_permyriad("55%") == 5500);
        assert_se(parse_permyriad("55.5%") == 5550);
        assert_se(parse_permyriad("55.50%") == 5550);
        assert_se(parse_permyriad("55.53%") == 5553);
        assert_se(parse_permyriad("100%") == 10000);
        assert_se(parse_permyriad("-7%") == -ERANGE);
        assert_se(parse_permyriad("107%") == -ERANGE);
        assert_se(parse_permyriad("%") == -EINVAL);
        assert_se(parse_permyriad("%%") == -EINVAL);
        assert_se(parse_permyriad("%1") == -EINVAL);
        assert_se(parse_permyriad("1%%") == -EINVAL);
        assert_se(parse_permyriad("3.212%") == -EINVAL);
}

static void test_parse_permyriad_unbounded(void) {
        assert_se(parse_permyriad_unbounded("1001‱") == 1001);
        assert_se(parse_permyriad_unbounded("4000‱") == 4000);
        assert_se(parse_permyriad_unbounded("2147483647‱") == 2147483647);
        assert_se(parse_permyriad_unbounded("2147483648‱") == -ERANGE);
        assert_se(parse_permyriad_unbounded("4294967295‱") == -ERANGE);
        assert_se(parse_permyriad_unbounded("4294967296‱") == -ERANGE);

        assert_se(parse_permyriad_unbounded("101‰") == 1010);
        assert_se(parse_permyriad_unbounded("400‰") == 4000);
        assert_se(parse_permyriad_unbounded("214748364.7‰") == 2147483647);
        assert_se(parse_permyriad_unbounded("214748364.8‰") == -ERANGE);
        assert_se(parse_permyriad_unbounded("429496729.5‰") == -ERANGE);
        assert_se(parse_permyriad_unbounded("429496729.6‰") == -ERANGE);

        assert_se(parse_permyriad_unbounded("99%") == 9900);
        assert_se(parse_permyriad_unbounded("40%") == 4000);
        assert_se(parse_permyriad_unbounded("21474836.47%") == 2147483647);
        assert_se(parse_permyriad_unbounded("21474836.48%") == -ERANGE);
        assert_se(parse_permyriad_unbounded("42949672.95%") == -ERANGE);
        assert_se(parse_permyriad_unbounded("42949672.96%") == -ERANGE);
}

static void test_scale(void) {
        /* Check some fixed values */
        assert_se(UINT32_SCALE_FROM_PERCENT(0) == 0);
        assert_se(UINT32_SCALE_FROM_PERCENT(50) == UINT32_MAX/2+1);
        assert_se(UINT32_SCALE_FROM_PERCENT(100) == UINT32_MAX);

        assert_se(UINT32_SCALE_FROM_PERMILLE(0) == 0);
        assert_se(UINT32_SCALE_FROM_PERMILLE(500) == UINT32_MAX/2+1);
        assert_se(UINT32_SCALE_FROM_PERMILLE(1000) == UINT32_MAX);

        assert_se(UINT32_SCALE_FROM_PERMYRIAD(0) == 0);
        assert_se(UINT32_SCALE_FROM_PERMYRIAD(5000) == UINT32_MAX/2+1);
        assert_se(UINT32_SCALE_FROM_PERMYRIAD(10000) == UINT32_MAX);

        /* Make sure there's no numeric noise on the 0%…100% scale when converting from percent and back. */
        for (int percent = 0; percent <= 100; percent++) {
                log_debug("%i%% → %" PRIu32 " → %i%%",
                          percent,
                          UINT32_SCALE_FROM_PERCENT(percent),
                          UINT32_SCALE_TO_PERCENT(UINT32_SCALE_FROM_PERCENT(percent)));

                assert_se(UINT32_SCALE_TO_PERCENT(UINT32_SCALE_FROM_PERCENT(percent)) == percent);
        }

        /* Make sure there's no numeric noise on the 0‰…1000‰ scale when converting from permille and back. */
        for (int permille = 0; permille <= 1000; permille++) {
                log_debug("%i‰ → %" PRIu32 " → %i‰",
                          permille,
                          UINT32_SCALE_FROM_PERMILLE(permille),
                          UINT32_SCALE_TO_PERMILLE(UINT32_SCALE_FROM_PERMILLE(permille)));

                assert_se(UINT32_SCALE_TO_PERMILLE(UINT32_SCALE_FROM_PERMILLE(permille)) == permille);
        }

        /* Make sure there's no numeric noise on the 0‱…10000‱ scale when converting from permyriad and back. */
        for (int permyriad = 0; permyriad <= 10000; permyriad++) {
                log_debug("%i‱ → %" PRIu32 " → %i‱",
                          permyriad,
                          UINT32_SCALE_FROM_PERMYRIAD(permyriad),
                          UINT32_SCALE_TO_PERMYRIAD(UINT32_SCALE_FROM_PERMYRIAD(permyriad)));

                assert_se(UINT32_SCALE_TO_PERMYRIAD(UINT32_SCALE_FROM_PERMYRIAD(permyriad)) == permyriad);
        }
}

int main(int argc, char *argv[]) {
        test_setup_logging(LOG_DEBUG);

        test_parse_percent();
        test_parse_percent_unbounded();
        test_parse_permille();
        test_parse_permille_unbounded();
        test_parse_permyriad();
        test_parse_permyriad_unbounded();
        test_scale();

        return 0;
}
