#include "util/len_abs.h"
#include "util/debug.h"
#include <iostream>
namespace {
static bool admits(len_abs const& a, unsigned n) {
return n >= a.lo() && n <= a.hi() && (a.residues() & (1ull << (n % a.period())));
}
static void check_sound_binary(len_abs const& x, len_abs const& y, len_abs const& r,
bool sum, unsigned bound) {
for (unsigned i = 0; i <= bound; ++i) {
if (!admits(x, i))
continue;
if (!sum) {
ENSURE(admits(r, i)); continue;
}
for (unsigned j = 0; j <= bound; ++j)
if (admits(y, j))
ENSURE(admits(r, i + j)); }
}
static void tst_basics() {
len_abs top;
ENSURE(top.is_trivial() && !top.is_empty());
ENSURE(top.lo() == 0 && top.hi() == UINT_MAX);
ENSURE(len_abs::empty().is_empty());
ENSURE(len_abs::exact(7).lo() == 7 && len_abs::exact(7).hi() == 7);
ENSURE(len_abs::exact(7).eff_period() == 0); ENSURE(len_abs::exact(0).gcd() == 0);
}
static void tst_progression() {
len_abs p = len_abs::progression(3, 4);
ENSURE(p.lo() == 3 && p.period() == 4 && p.num_residues() == 1);
ENSURE(admits(p, 3) && admits(p, 7) && admits(p, 11));
ENSURE(!admits(p, 4) && !admits(p, 5) && !admits(p, 6));
len_abs e = len_abs::progression(5, 0);
ENSURE(e.lo() == 5 && e.hi() == 5);
ENSURE(len_abs::progression(1, 1000).is_trivial());
}
static void tst_star_and_plus() {
len_abs four = len_abs::exact(4);
len_abs star4 = four.star();
ENSURE(star4.period() == 4 && star4.num_residues() == 1);
ENSURE(admits(star4, 0) && admits(star4, 4) && admits(star4, 8));
ENSURE(!admits(star4, 1) && !admits(star4, 6));
len_abs star6 = len_abs::exact(6).star();
ENSURE(star6.period() == 6 && star6.num_residues() == 1);
len_abs odd = len_abs::exact(1).concat(len_abs::exact(2).star());
ENSURE(odd.period() == 2 && odd.residues() == 0x2);
ENSURE(admits(odd, 1) && admits(odd, 3) && !admits(odd, 2));
ENSURE(odd.star().is_trivial());
ENSURE(four.plus().lo() == 4 && four.star().lo() == 0);
}
static void tst_concat() {
len_abs star2 = len_abs::exact(2).star(); len_abs star3 = len_abs::exact(3).star(); ENSURE(star2.concat(star3).is_trivial());
len_abs ab_star_a = star2.concat(len_abs::exact(1));
ENSURE(ab_star_a.period() == 2 && ab_star_a.residues() == 0x2);
check_sound_binary(star2, star3, star2.concat(star3), true, 24);
check_sound_binary(star2, len_abs::exact(1), ab_star_a, true, 24);
}
static void tst_unite_beats_single_progression() {
len_abs u = len_abs::exact(2).star().unite(len_abs::exact(3).star());
ENSURE(u.period() == 6);
ENSURE(u.residues() == ((1ull << 0) | (1ull << 2) | (1ull << 3) | (1ull << 4)));
ENSURE(!admits(u, 1) && !admits(u, 5)); ENSURE(admits(u, 6) && admits(u, 8) && admits(u, 9));
check_sound_binary(len_abs::exact(2).star(), len_abs(), u, false, 30);
check_sound_binary(len_abs::exact(3).star(), len_abs(), u, false, 30);
}
static void tst_meet() {
len_abs ab_star = len_abs::exact(2).star(); len_abs a_ba_star = len_abs::exact(1).concat(ab_star); len_abs m = ab_star.meet(a_ba_star);
ENSURE(m.residues() == 0 && m.is_empty());
len_abs sharpened = ab_star.meet(len_abs::progression(4, 6));
for (unsigned n = 0; n <= 60; ++n)
ENSURE(!admits(sharpened, n) || (admits(ab_star, n) && admits(len_abs::progression(4, 6), n)));
ENSURE(admits(sharpened, 4) && admits(sharpened, 10));
ENSURE(ab_star.meet(len_abs()) == ab_star);
}
static void tst_loop_and_opt() {
len_abs two = len_abs::exact(2);
ENSURE(two.opt().residues() == 0x1 && two.opt().lo() == 0);
len_abs odd = len_abs::exact(1).concat(two.star());
ENSURE(odd.opt().period() == 2 && odd.opt().residues() == 0x3);
len_abs l12 = odd.loop(1, 2);
ENSURE(l12.period() == 2 && l12.residues() == 0x3);
ENSURE(admits(l12, 1) && admits(l12, 2));
len_abs f12 = two.loop(1, 2);
ENSURE(f12.period() == 2 && f12.residues() == 0x1);
ENSURE(admits(f12, 2) && admits(f12, 4) && !admits(f12, 3));
len_abs f13 = len_abs::exact(4).loop(1, 3);
ENSURE(f13.period() == 4 && f13.residues() == 0x1);
ENSURE(admits(f13, 4) && admits(f13, 12) && !admits(f13, 6));
len_abs lu = two.loop(1, UINT_MAX);
ENSURE(lu.period() == 2 && lu.residues() == 0x1);
ENSURE(admits(lu, 2) && admits(lu, 4) && !admits(lu, 3));
}
static void tst_emptiness_needs_a_full_window() {
len_abs a(2, 2, 2, 0x2);
ENSURE(a.is_empty());
len_abs b(2, 3, 2, 0x2);
ENSURE(!b.is_empty());
ENSURE(len_abs(0, UINT_MAX, 4, 0).is_empty());
ENSURE(a.gcd() == 0 && a.num_residues() == 0);
}
static void tst_residues_mod() {
len_abs star4 = len_abs::exact(4).star(); ENSURE(star4.residues_mod(2) == 0x1); ENSURE(star4.residues_mod(8) == ((1ull << 0) | (1ull << 4))); ENSURE(star4.residues_mod(3) == 0x7);
ENSURE(star4.residues_mod(0) == 0);
}
}
void tst_len_abs() {
tst_basics();
tst_progression();
tst_star_and_plus();
tst_concat();
tst_unite_beats_single_progression();
tst_meet();
tst_loop_and_opt();
tst_emptiness_needs_a_full_window();
tst_residues_mod();
std::cout << "tst_len_abs: all tests passed\n";
}