test_multi_integer.cpp

Back to Fixed-size integers

test/pastel/sys/

// Description: Testing for MultiInteger
// DocumentationOf: multi_integer.h

#include "test/test_init.h"

#include "pastel/sys/integer/multi_integer.h"

#include <iomanip>

TEST_CASE("Infinity (MultiInteger)")
{
    {
        using F = Unsigned_Integer<32, uint8>;
        CHECK((F)Infinity() == 0xFFFFFFFF);
        CHECK(positive((F)Infinity()));
        CHECK(zero(++(F)Infinity()));
    }
    {
        using F = Signed_Integer<32, uint8>;
        CHECK((F)Infinity() == 0x7FFFFFFF);
        CHECK(positive((F)Infinity()));
        CHECK(negative(++(F)Infinity()));
    }
    {
        using F = Unsigned_Integer<8, uint8>;
        CHECK((F)Infinity() == 255);
        CHECK(positive((F)Infinity()));
        CHECK(zero(++(F)Infinity()));
    }
    {
        using F = Signed_Integer<8, uint8>;
        CHECK((F)Infinity() == 127);
        CHECK(positive((F)Infinity()));
        CHECK(negative(++(F)Infinity()));
    }
    {
        using F = Unsigned_Integer<7, uint8>;
        CHECK((F)Infinity() == 127);
        CHECK(positive((F)Infinity()));
        CHECK(zero(++(F)Infinity()));
    }
    {
        using F = Signed_Integer<7, uint8>;
        CHECK((F)Infinity() == 63);
        CHECK(positive((F)Infinity()));
        CHECK(negative(++(F)Infinity()));
    }
}

TEST_CASE("AsString (MultiInteger)")
{
    {
        using F = Unsigned_Integer<1, uint8>;
        CHECK(F(0).asString(2) == "0");
        CHECK(F(1).asString(2) == "1");
        CHECK(F(2).asString(2) == "0");
        CHECK(F(3).asString(2) == "1");
    }
    {
        using F = Signed_Integer<1, uint8>;

        CHECK(F(-2).asString(2) == "0");
        CHECK(F(-1).asString(2) == "-1");
        CHECK(F(0).asString(2) == "0");
        CHECK(F(1).asString(2) == "-1");
        CHECK(F(2).asString(2) == "0");
        CHECK(F(3).asString(2) == "-1");
    }
    {
        using F = Signed_Integer<20, uint8>;
        CHECK(F(-0x80000).asString(2) == "-10000000000000000000");
        CHECK(F(0x80000).asString(2) == "-10000000000000000000");
        CHECK(F(-0x80000).asString() == "-524288");
        CHECK(F(0x80000).asString() == "-524288");
    }
    {
        using F = Unsigned_Integer<32, uint8>;

        CHECK(F(0x12345678).asString(16) == "12345678");
        CHECK(F(132).asString(10) == "132");
        CHECK(F(432874).asString(10) == "432874");

        CHECK(F(0x0).asString(16) == "0");
        CHECK(F(0xFFFFFFFF).asString(16) == "ffffffff");

        CHECK(F(0x9ABCDEF0).asString(2) ==
            "10011010101111001101111011110000");
    }
    {
        using F = Signed_Integer<32, uint8>;

        /*
       std::cout << std::hex << F(-0x123) << std::endl;
       std::cout << std::dec << F(-123) << std::endl;
       std::cout << std::oct << F(-0123) << std::endl;

       std::cout << std::hex << std::showbase << F(-0x123) << std::endl;
       std::cout << std::dec << std::showbase << F(-123) << std::endl;
       std::cout << std::oct << std::showbase << F(-0123) << std::endl;
       */

        CHECK(F(-132).asString(10) == "-132");
        CHECK(F(-0x12345678).asString(16) == "-12345678");
        CHECK(F(-1).asString(2) == "-1");
        CHECK(F(-2).asString(2) == "-10");
        CHECK(F(-3).asString(2) == "-11");
        CHECK(F(-4).asString(2) == "-100");
    }
    {
        using F = Unsigned_Integer<32, uint8>;
        CHECK(F(0x12345678).asString(16) == "12345678");

        CHECK(F(0x9ABCDEF0).asString(2) ==
            "10011010101111001101111011110000");
    }
}

TEST_CASE("AsNative (MultiInteger)")
{
    {
        using F = Unsigned_Integer<32, uint8>;
        CHECK(static_cast<uint64>(F(true)) == 1);
    }
    {
        using F = Unsigned_Integer<32, uint8>;
        CHECK(static_cast<uint64>(F(false)) == 0);
    }
    {
        using F = Unsigned_Integer<32, uint8>;
        CHECK(static_cast<uint64>(F(0x12345678)) == 0x12345678);
    }
    {
        using F = Unsigned_Integer<31, uint8>;
        CHECK((uint64)F(0x12345678) == 0x12345678);
    }
    {
        using F = Unsigned_Integer<30, uint8>;
        CHECK((uint64)F(0x12345678) == 0x12345678);
    }
    {
        using F = Unsigned_Integer<29, uint8>;
        CHECK((uint64)F(0x12345678) == 0x12345678);
    }
    {
        using F = Unsigned_Integer<28, uint8>;
        CHECK((uint64)F(0x12345678) == 0x02345678);
    }
    {
        using F = Unsigned_Integer<20, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00045678);
    }
    {
        using F = Unsigned_Integer<19, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00045678);
    }
    {
        using F = Unsigned_Integer<18, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00005678);
    }
    {
        using F = Unsigned_Integer<17, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00005678);
    }
    {
        using F = Unsigned_Integer<16, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00005678);
    }
    {
        using F = Unsigned_Integer<15, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00005678);
    }
    {
        using F = Unsigned_Integer<14, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00001678);
    }
    {
        using F = Unsigned_Integer<13, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00001678);
    }
    {
        using F = Unsigned_Integer<12, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00000678);
    }
    {
        using F = Unsigned_Integer<11, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00000678);
    }
    {
        using F = Unsigned_Integer<10, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00000278);
    }
    {
        using F = Unsigned_Integer<9, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00000078);
    }
    {
        using F = Unsigned_Integer<8, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00000078);
    }
    {
        using F = Unsigned_Integer<7, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00000078);
    }
    {
        using F = Unsigned_Integer<6, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00000038);
    }
    {
        using F = Unsigned_Integer<5, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00000018);
    }
    {
        using F = Unsigned_Integer<4, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00000008);
    }
    {
        using F = Unsigned_Integer<3, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00000000);
    }
    {
        using F = Unsigned_Integer<2, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00000000);
    }
    {
        using F = Unsigned_Integer<1, uint8>;
        CHECK((uint64)F(0x12345678) == 0x00000000);
    }
    {
        using F = Unsigned_Integer<32, uint8>;
        CHECK((uint64)F(0x87654321) == 0x87654321);
    }
    {
        using F = Signed_Integer<32, uint8>;
        CHECK(static_cast<int64>(F(-0x12345678)) == -0x12345678);
    }
    {
        using F = Signed_Integer<16, uint8>;
        for (integer i = -40000; i < 40000; i += 13)
        {
            CHECK(static_cast<int64>(F(i)) == mod(i + 32768, (integer)65536) - 32768);
        }
    }
    {
        using F = Signed_Integer<15, uint8>;
        for (integer i = -40000; i < 40000; i += 13)
        {
            CHECK(static_cast<int64>(F(i)) == mod(i + 16384, (integer)32768) - 16384);
        }
    }
    {
        using F = Signed_Integer<6, uint8>;
        for (integer i = -100; i < 100; ++i)
        {
            CHECK(static_cast<int64>(F(i)) == mod(i + 32, (integer)64) - 32);
        }
    }
    {
        using F = Signed_Integer<6, uint16>;
        for (integer i = -100; i < 100; ++i)
        {
            CHECK(static_cast<int64>(F(i)) == mod(i + 32, (integer)64) - 32);
        }
    }
}

TEST_CASE("Construction (MultiInteger)")
{
    using F = Unsigned_Integer<20, uint8>;
    PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

    CHECK(F(0x12345678) == F({ 0x12, 0x34, 0x56, 0x78 }));
    CHECK(F(0x12345678) == F({ 0x34, 0x56, 0x78 }));
    CHECK(F(0x12345678) == F({ 0x04, 0x56, 0x78 }));

    {
        CHECK(F((int8)-1) == F({0xFF, 0xFF, 0xFF}));
        CHECK(F((int8)-2) == F({0xFF, 0xFF, 0xFE}));
        CHECK(F((int8)-16) == F({0xFF, 0xFF, 0xF0}));
    }
    {
        F a({ 0x04, 0x56, 0x78 });
        CHECK(a.word(0) == 0x78);
        CHECK(a.word(1) == 0x56);
        CHECK(a.word(2) == 0x04);
    }

    {
        F a(0x12345678);
        CHECK(a.word(0) == 0x78);
        CHECK(a.word(1) == 0x56);
        CHECK(a.word(2) == 0x04);
    }

    CHECK(F(0) == F()); 

    {
        F a(-1);
        CHECK(a.word(0) == 0xFF);
        CHECK(a.word(1) == 0xFF);
        CHECK(a.word(2) == 0x0F);
    }

    {
        F a(-16);
        CHECK(a.word(0) == 0xF0);
        CHECK(a.word(1) == 0xFF);
        CHECK(a.word(2) == 0x0F);
    }

    {
        using F = Unsigned_Integer<20, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        F a(0x12345678);

        CHECK(F(a, 0, 0) == F(0x00000000));
        CHECK(F(a, 0, 1) == F(0x00000000));
        CHECK(F(a, 0, 2) == F(0x00000000));
        CHECK(F(a, 0, 3) == F(0x00000000));
        CHECK(F(a, 0, 4) == F(0x00000008));
        CHECK(F(a, 0, 5) == F(0x00000018));
        CHECK(F(a, 0, 6) == F(0x00000038));
        CHECK(F(a, 0, 7) == F(0x00000078));

        CHECK(F(a, 0, 8) == F(0x00000078));
        CHECK(F(a, 0, 9) == F(0x00000078));
        CHECK(F(a, 0, 10) == F(0x00000278));
        CHECK(F(a, 0, 11) == F(0x00000678));
        CHECK(F(a, 0, 12) == F(0x00000678));
        CHECK(F(a, 0, 13) == F(0x00001678));
        CHECK(F(a, 0, 14) == F(0x00001678));
        CHECK(F(a, 0, 15) == F(0x00005678));

        CHECK(F(a, 0, 16) == F(0x00005678));
        CHECK(F(a, 1, 16) == F(0x00005678));
        CHECK(F(a, 2, 16) == F(0x00005678));
        CHECK(F(a, 3, 16) == F(0x00005678));
        CHECK(F(a, 4, 16) == F(0x00005670));
        CHECK(F(a, 5, 16) == F(0x00005660));
        CHECK(F(a, 6, 16) == F(0x00005640));
        CHECK(F(a, 7, 16) == F(0x00005600));

        CHECK(F(a, 8, 16) == F(0x00005600));
        CHECK(F(a, 9, 16) == F(0x00005600));
        CHECK(F(a, 10, 16) == F(0x00005400));
        CHECK(F(a, 11, 16) == F(0x00005000));
        CHECK(F(a, 12, 16) == F(0x00005000));
        CHECK(F(a, 13, 16) == F(0x00004000));
        CHECK(F(a, 14, 16) == F(0x00004000));
        CHECK(F(a, 15, 16) == F(0x00000000));

        CHECK(F(a, 16, 16) == F(0x00000000));
    }
}

TEST_CASE("Negation (MultiInteger)")
{
    {
        using F = Signed_Integer<16, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        for (integer i = -512; i <= 512; ++i)
        {
            CHECK(-F(i) == F(-i));
            CHECK(F(i) + (-F(i)) == 0);
        } 
    }

    {
        using F = Unsigned_Integer<16, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        for (integer i = -512; i < 512; ++i)
        {
            CHECK(-F(i) == F(-i));
            CHECK(F(i) + (-F(i)) == 0);
        }
    }
}

TEST_CASE("IncrementDecrement (MultiInteger)")
{
    using F = Signed_Integer<20, uint8>;
    PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

    for (integer i = -512; i <= 512; ++i)
    {
        CHECK(--F(i) == i - 1);
        CHECK(++F(i) == i + 1);
    }

    CHECK(++F(0x7FFFF) == -0x80000);
    CHECK(--F(0x80000) == +0x7FFFF);
    CHECK(--F(0x10000) == +0x0FFFF);
    CHECK(++F(0x0FFFF) == +0x10000);
}

TEST_CASE("Comparison (MultiInteger)")
{
    using F = Signed_Integer<20, uint8>;
    PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

    CHECK(negative(F(-0x00001)));
    CHECK(negative(F(-0x23456)));
    CHECK(negative(F(+0x83456u)));
    CHECK(negative(F(+0x80000u)));
    CHECK(positive(F(+0x00001)));
    CHECK(positive(F(+0x23456)));
    CHECK(positive(F(+0x7FFFF)));
    CHECK(positive(F(+0x7FFFFu)));
    CHECK(zero(F(0x000000)));
    CHECK(zero(F(0x000000u)));

    CHECK(F(-0x7FFFF) < F(0x12354));
    CHECK(F(-0x20000) < F(0x10000));
    CHECK(F(-0x10000) < F(0x00001));
    CHECK(F(0x00000) < F(0x00001));
    CHECK(F(0x00000) < F(0x04323));
    CHECK(F(0x04323) < F(0x10000));
    CHECK(F(0x100000) < F(0x20000));
    CHECK(F(0x23456) < F(0x34567));
}

TEST_CASE("ClearBits (MultiInteger)")
{
        using F = Unsigned_Integer<16, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        auto f = [&](integer begin, integer end) -> F
        {
            return F(0xFFFF).clearBits(begin, end);
        };

        // Clear the front for multiple words.

        CHECK(f(0, 0) == F(0xFFFF));
        CHECK(f(0, 1) == F(0xFFFE));
        CHECK(f(0, 2) == F(0xFFFC));
        CHECK(f(0, 3) == F(0xFFF8));
        CHECK(f(0, 4) == F(0xFFF0));
        CHECK(f(0, 5) == F(0xFFE0));
        CHECK(f(0, 6) == F(0xFFC0));
        CHECK(f(0, 7) == F(0xFF80));
        CHECK(f(0, 8) == F(0xFF00));
        CHECK(f(0, 9) == F(0xFE00));
        CHECK(f(0, 10) == F(0xFC00));
        CHECK(f(0, 11) == F(0xF800));
        CHECK(f(0, 12) == F(0xF000));
        CHECK(f(0, 13) == F(0xE000));
        CHECK(f(0, 14) == F(0xC000));
        CHECK(f(0, 15) == F(0x8000));
        CHECK(f(0, 16) == F(0x0000));

        // Clear the tail for multiple words.

        CHECK(f(16, 16) == F(0xFFFF));
        CHECK(f(15, 16) == F(0x7FFF));
        CHECK(f(14, 16) == F(0x3FFF));
        CHECK(f(13, 16) == F(0x1FFF));
        CHECK(f(12, 16) == F(0x0FFF));
        CHECK(f(11, 16) == F(0x07FF));
        CHECK(f(10, 16) == F(0x03FF));
        CHECK(f(9, 16) == F(0x01FF));
        CHECK(f(8, 16) == F(0x00FF));
        CHECK(f(7, 16) == F(0x007F));
        CHECK(f(6, 16) == F(0x003F));
        CHECK(f(5, 16) == F(0x001F));
        CHECK(f(4, 16) == F(0x000F));
        CHECK(f(3, 16) == F(0x0007));
        CHECK(f(2, 16) == F(0x0003));
        CHECK(f(1, 16) == F(0x0001));
        CHECK(f(0, 16) == F(0x0000));

        // Clear the front inside a single word.

        CHECK(f(0, 0) == F(0xFFFF));
        CHECK(f(0, 1) == F(0xFFFE));
        CHECK(f(0, 2) == F(0xFFFC));
        CHECK(f(0, 3) == F(0xFFF8));
        CHECK(f(0, 4) == F(0xFFF0));
        CHECK(f(0, 5) == F(0xFFE0));
        CHECK(f(0, 6) == F(0xFFC0));
        CHECK(f(0, 7) == F(0xFF80));
        CHECK(f(0, 8) == F(0xFF00));

        // Clear the tail inside a single word.

        CHECK(f(0, 8) == F(0xFF00));
        CHECK(f(1, 8) == F(0xFF01));
        CHECK(f(2, 8) == F(0xFF03));
        CHECK(f(3, 8) == F(0xFF07));
        CHECK(f(4, 8) == F(0xFF0F));
        CHECK(f(5, 8) == F(0xFF1F));
        CHECK(f(6, 8) == F(0xFF3F));
        CHECK(f(7, 8) == F(0xFF7F));
        CHECK(f(8, 8) == F(0xFFFF));

        // Clear zero bits.

        CHECK(f(0, 0) == F(0xFFFF));
        CHECK(f(1, 1) == F(0xFFFF));
        CHECK(f(2, 2) == F(0xFFFF));
        CHECK(f(3, 3) == F(0xFFFF));
        CHECK(f(4, 4) == F(0xFFFF));
        CHECK(f(5, 5) == F(0xFFFF));
        CHECK(f(6, 6) == F(0xFFFF));
        CHECK(f(7, 7) == F(0xFFFF));
        CHECK(f(8, 8) == F(0xFFFF));
        CHECK(f(9, 9) == F(0xFFFF));
        CHECK(f(10, 10) == F(0xFFFF));
        CHECK(f(11, 11) == F(0xFFFF));
        CHECK(f(12, 12) == F(0xFFFF));
        CHECK(f(13, 13) == F(0xFFFF));
        CHECK(f(14, 14) == F(0xFFFF));
        CHECK(f(15, 15) == F(0xFFFF));
        CHECK(f(16, 16) == F(0xFFFF));

        // Clear one bit.

        CHECK(f(0, 1) == F(0xFFFE));
        CHECK(f(1, 2) == F(0xFFFD));
        CHECK(f(2, 3) == F(0xFFFB));
        CHECK(f(3, 4) == F(0xFFF7));
        CHECK(f(4, 5) == F(0xFFEF));
        CHECK(f(5, 6) == F(0xFFDF));
        CHECK(f(6, 7) == F(0xFFBF));
        CHECK(f(7, 8) == F(0xFF7F));
        CHECK(f(8, 9) == F(0xFEFF));
        CHECK(f(9, 10) == F(0xFDFF));
        CHECK(f(10, 11) == F(0xFBFF));
        CHECK(f(11, 12) == F(0xF7FF));
        CHECK(f(12, 13) == F(0xEFFF));
        CHECK(f(13, 14) == F(0xDFFF));
        CHECK(f(14, 15) == F(0xBFFF));
        CHECK(f(15, 16) == F(0x7FFF));

        // Clear two bits.

        CHECK(f(0, 2) == F(0xFFFC));
        CHECK(f(1, 3) == F(0xFFF9));
        CHECK(f(2, 4) == F(0xFFF3));
        CHECK(f(3, 5) == F(0xFFE7));
        CHECK(f(4, 6) == F(0xFFCF));
        CHECK(f(5, 7) == F(0xFF9F));
        CHECK(f(6, 8) == F(0xFF3F));
        CHECK(f(7, 9) == F(0xFE7F));
        CHECK(f(8, 10) == F(0xFCFF));
        CHECK(f(9, 11) == F(0xF9FF));
        CHECK(f(10, 12) == F(0xF3FF));
        CHECK(f(11, 13) == F(0xE7FF));
        CHECK(f(12, 14) == F(0xCFFF));
        CHECK(f(13, 15) == F(0x9FFF));
        CHECK(f(14, 16) == F(0x3FFF));
}

TEST_CASE("Addition (MultiInteger)")
{
    {
        using F = Signed_Integer<64, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        CHECK(F(-10) + F(-15) == F(-25));
    }
}

TEST_CASE("Arithmetic (MultiInteger)")
{
    {
        using F = Unsigned_Integer<1, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        CHECK(F(0) + F(0) == F(0));
        CHECK(F(0) + F(1) == F(1));
        CHECK(F(1) + F(0) == F(1));
        CHECK(F(1) + F(1) == F(0));

        CHECK(F(0) - F(0) == F(0));
        CHECK(F(0) - F(1) == F(-1));
        CHECK(F(1) - F(0) == F(1));
        CHECK(F(1) - F(1) == F(0));
    }

    {
        using F = Unsigned_Integer<2, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        CHECK(F(0) + F(0) == F(0));
        CHECK(F(0) + F(1) == F(1));
        CHECK(F(0) + F(2) == F(2));
        CHECK(F(0) + F(3) == F(3));
        CHECK(F(1) + F(0) == F(1));
        CHECK(F(1) + F(1) == F(2));
        CHECK(F(1) + F(2) == F(3));
        CHECK(F(1) + F(3) == F(0));
        CHECK(F(2) + F(0) == F(2));
        CHECK(F(2) + F(1) == F(3));
        CHECK(F(2) + F(2) == F(0));
        CHECK(F(2) + F(3) == F(1));
        CHECK(F(3) + F(0) == F(3));
        CHECK(F(3) + F(1) == F(0));
        CHECK(F(3) + F(2) == F(1));
        CHECK(F(3) + F(3) == F(2));

        CHECK(F(0) - F(0) == F(0));
        CHECK(F(0) - F(1) == F(-1));
        CHECK(F(0) - F(2) == F(-2));
        CHECK(F(0) - F(3) == F(-3));
        CHECK(F(1) - F(0) == F(1));
        CHECK(F(1) - F(1) == F(0));
        CHECK(F(1) - F(2) == F(-1));
        CHECK(F(1) - F(3) == F(-2));
        CHECK(F(2) - F(0) == F(2));
        CHECK(F(2) - F(1) == F(1));
        CHECK(F(2) - F(2) == F(0));
        CHECK(F(2) - F(3) == F(-1));
        CHECK(F(3) - F(0) == F(3));
        CHECK(F(3) - F(1) == F(2));
        CHECK(F(3) - F(2) == F(1));
        CHECK(F(3) - F(3) == F(0));
    }

    {
        using F = Unsigned_Integer<20, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        auto f = [](integer a, integer b) -> bool
        {
            return (F(a) + F(b)) == (a + b);
        };

        CHECK(f(0x13413, 0x32432));
        CHECK(f(0x35486, 0x72234));
        CHECK(f(0x91847, 0xF4824));
    }

    {
        using F = Unsigned_Integer<20, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        auto f = [](integer a, integer b) -> bool
        {
            return (F(a) - F(b)) == (a - b);
        };

        CHECK(f(0x13413, 0x32432));
        CHECK(f(0x35486, 0x72234));
        CHECK(f(0x91847, 0xF4824));
    }

    {
        using F = Unsigned_Integer<32, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        uint32 a = 0x12345678;
        for (integer i = 0;i < 32;++i)
        {
            // Interesting fact:
            // if i ranges up to 33, then the following
            // tests fail for i == 32. This probably has 
            // to do with the shifts (a << i) and (a >> i) 
            // being implementation-defined in this case.

            CHECK(
                (F(a) << i) == F(a << i));

            CHECK(
                (F(a) >> i) == F(a >> i));
        }

        // We define the out-of-range shift anyway.
        CHECK((F(a) << 33) == F(0));
        CHECK((F(a) >> 33) == F(0));
    }
}

TEST_CASE("BitOperators (MultiInteger)")
{
    using F = Unsigned_Integer<20, uint8>;
    PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

    CHECK((F(0x12345678u) ^ F(0x45635603u)) == (0x12345678u ^ 0x45635603u));
    CHECK((F(0x12345678u) | F(0x45635603u)) == (0x12345678u | 0x45635603u));
    CHECK((F(0x12345678u) & F(0x45635603u)) == (0x12345678u & 0x45635603u));
    CHECK((~F(0x12345678u)) == (~0x12345678u));
}

TEST_CASE("Counting (MultiInteger)")
{
    {
        using F = Unsigned_Integer<1, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        CHECK(F(0).oneBits() == 0);
        CHECK(F(1).oneBits() == 1);
    }

    {
        using F = Unsigned_Integer<32, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        CHECK(F(0x12345678).oneBits() == 1 + 1 + 2 + 1 + 2 + 2 + 3 + 1);

        CHECK(F(0x9ABCDEF0).oneBits() == 2 + 2 + 3 + 2 + 3 + 3 + 4 + 0);
    }
}

TEST_CASE("SetBits (MultiInteger)")
{
    using F = Unsigned_Integer<20, uint8>;
    PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

    {
        CHECK(!F(0).bit(0));
        CHECK(F(1).bit(0));

        CHECK(F(0).setBit(0).bit(0));
        CHECK(F(0).setBit(0, true).bit(0));
        CHECK(!F(1).setBit(0, false).bit(0));
        CHECK(!F(1).clearBit(0).bit(0));
    }

    F a;

    CHECK(!a.bit(20));
    CHECK(!a.bit(21));
    CHECK(!a.bit(22));
    CHECK(!a.bit(32));
    CHECK(!a.bit(64));
    CHECK(!a.bit(128));

    a.setBit(0);
    CHECK(a == (1 << 0));

    a.setBit(1);
    CHECK(a == (1 << 0) + (1 << 1));

    a.setBit(2);
    CHECK(a == (1 << 0) + (1 << 1) + (1 << 2));

    a.setBit(7);
    CHECK(a == (1 << 0) + (1 << 1) + (1 << 2) + (1 << 7));

    a.setBit(12);
    CHECK(a == (1 << 0) + (1 << 1) + (1 << 2) + (1 << 7) + (1 << 12));

    a.clearBit(2);
    CHECK(a == (1 << 0) + (1 << 1) + (1 << 7) + (1 << 12));

    a.clearBit(7);
    CHECK(a == (1 << 0) + (1 << 1) + (1 << 12));

    a.clearBit(12);
    CHECK(a == (1 << 0) + (1 << 1));

    a.clearBit(1);
    CHECK(a == (1 << 0));

    a.clearBit(0);
    CHECK(a == 0);

    a.setBit(19);
    CHECK(a == (1 << 19));

    a.clearBit(19);
    CHECK(a == 0);

    a = 0x12345678;
    a.clearBits();
    CHECK(a == 0);

    a = 0x12345678;
    a.setBits();
    CHECK(a == 0x000FFFFF);

    {
        Signed_Integer<20, uint8> b(-1);
        CHECK(b.bit(20));
        CHECK(b.bit(21));
        CHECK(b.bit(22));
        CHECK(b.bit(32));
        CHECK(b.bit(64));
        CHECK(b.bit(128));
    }
    {
        Signed_Integer<20, uint8> b(0);
        CHECK(!b.bit(20));
        CHECK(!b.bit(21));
        CHECK(!b.bit(22));
        CHECK(!b.bit(32));
        CHECK(!b.bit(64));
        CHECK(!b.bit(128));
    }
}

TEST_CASE("Multiplication (MultiInteger)")
{
    {
        using F = Unsigned_Integer<20, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        for (integer i = 0; i < 256; ++i)
        {
            for (integer j = 0; j < 256; ++j)
            {
                CHECK(F(i) * F(j) == F(i * j));
            }
        }
    }
    {
        using F = Signed_Integer<20, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        for (integer i = -127; i <= 128; ++i)
        {
            for (integer j = -127; j <= 128; ++j)
            {
                CHECK(F(i) * F(j) == F(i * j));
            }
        }
    }
}

TEST_CASE("Division (MultiInteger)")
{
    {
        using F = Unsigned_Integer<23, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        for (integer i = 0; i < 300; i += 7)
        {
            for (integer j = 1; j < 300; j += 5)
            {
                CHECK(F(i * 213) / F(j) == F((i * 213) / j));
            }
        }
    }
    {
        using F = Signed_Integer<23, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        for (integer i = -300; i < 300; i += 3)
        {
            for (integer j = -300; j < 300; j += 11)
            {
                if (j != 0)
                {
                    CHECK(F(i * 213) / F(j) == F((i * 213) / j));
                }
            }
        }
    }

    {
        using F = Signed_Integer<1, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        CHECK(F(0) / -2 == F(0));
        CHECK(F(0) / -1 == F(0));
        CHECK(F(0) / 1 == F(0));
        CHECK(F(0) / 2 == F(0));

        CHECK(F(-1) / -2 == F(0));
        CHECK(F(-1) / -1 == F(1));
        CHECK(F(-1) / 1 == F(1));
        CHECK(F(-1) / 2 == F(0));
        CHECK(F(-1) / 3 == F(0));
    }

    {
        using F = Signed_Integer<2, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        CHECK(F(0) / 1 == F(0));
        CHECK(F(1) / 1 == F(1));
        CHECK(F(-1) / 1 == F(-1));
        F(-2) / 2;
        CHECK(F(-2) / 1 == F(-2));

        CHECK(F(0) / 2 == F(0));
        CHECK(F(1) / 2 == F(0));
        CHECK(F(-1) / 2 == F(0));
        CHECK(F(-2) / 2 == F(-1));

        CHECK(F(0) / 3 == F(0));
        CHECK(F(1) / 3 == F(0));
        CHECK(F(-1) / 3 == F(0));
        CHECK(F(-2) / 3 == F(0));

        CHECK(F(0) / 4 == F(0));
        CHECK(F(1) / 4 == F(0));
        CHECK(F(-1) / 4 == F(0));
        CHECK(F(-2) / 4 == F(0));
    }
    {
        using F = Unsigned_Integer<2, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        CHECK(F(0) / 1 == F(0));
        CHECK(F(1) / 1 == F(1));
        CHECK(F(2) / 1 == F(2));
        CHECK(F(3) / 1 == F(3));

        CHECK(F(0) / 2 == F(0));
        CHECK(F(1) / 2 == F(0));
        CHECK(F(2) / 2 == F(1));
        CHECK(F(3) / 2 == F(1));

        CHECK(F(0) / 3 == F(0));
        CHECK(F(1) / 3 == F(0));
        CHECK(F(2) / 3 == F(0));
        CHECK(F(3) / 3 == F(1));

        CHECK(F(0) / 4 == F(0));
        CHECK(F(1) / 4 == F(0));
        CHECK(F(2) / 4 == F(0));
        CHECK(F(3) / 4 == F(0));
    }
}

TEST_CASE("Modulo (MultiInteger)")
{
    {
        using F = Unsigned_Integer<23, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        for (integer i = 0; i < 300; i += 7)
        {
            for (integer j = 1; j < 300; j += 5)
            {
                CHECK(F(i * 213) % F(j) == F((i * 213) % j));
            }
        }
    }
    {
        using F = Signed_Integer<23, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        for (integer i = -300; i < 300; i += 3)
        {
            for (integer j = -300; j < 300; j += 11)
            {
                if (j != 0)
                {
                    CHECK(F(i * 213) % F(j) == F((i * 213) % j));
                }
            }
        }
    }

    {
        using F = Signed_Integer<1, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        CHECK(F(0) % -2 == F(0));
        CHECK(F(0) % -1 == F(0));
        CHECK(F(0) % 1 == F(0));
        CHECK(F(0) % 2 == F(0));

        CHECK(F(-1) % -2 == F(-1));
        CHECK(F(-1) % -1 == F(0));
        CHECK(F(-1) % 1 == F(0));
        CHECK(F(-1) % 2 == F(-1));
        CHECK(F(-1) % 3 == F(-1));
    }

    {
        using F = Signed_Integer<2, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        CHECK(F(-2) % -4 == F(-2));
        CHECK(F(-1) % -4 == F(-1));
        CHECK(F(0) % -4 == F(0));
        CHECK(F(1) % -4 == F(1));

        CHECK(F(-2) % -3 == F(-2));
        CHECK(F(-1) % -3 == F(-1));
        CHECK(F(0) % -3 == F(0));
        CHECK(F(1) % -3 == F(1));

        CHECK(F(-2) % -2 == F(0));
        CHECK(F(-1) % -2 == F(-1));
        CHECK(F(0) % -2 == F(0));
        CHECK(F(1) % -2 == F(1));

        CHECK(F(-2) % -1 == F(0));
        CHECK(F(-1) % -1 == F(0));
        CHECK(F(0) % -1 == F(0));
        CHECK(F(1) % -1 == F(0));

        CHECK(F(-2) % 1 == F(0));
        CHECK(F(-1) % 1 == F(0));
        CHECK(F(0) % 1 == F(0));
        CHECK(F(1) % 1 == F(0));

        CHECK(F(-2) % 2 == F(0));
        CHECK(F(-1) % 2 == F(-1));
        CHECK(F(0) % 2 == F(0));
        CHECK(F(1) % 2 == F(1));

        CHECK(F(-2) % 3 == F(-2));
        CHECK(F(-1) % 3 == F(-1));
        CHECK(F(0) % 3 == F(0));
        CHECK(F(1) % 3 == F(1));

        CHECK(F(-2) % 4 == F(-2));
        CHECK(F(-1) % 4 == F(-1));
        CHECK(F(0) % 4 == F(0));
        CHECK(F(1) % 4 == F(1));
    }
    {
        using F = Unsigned_Integer<2, uint8>;
        PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

        CHECK(F(0) % 1 == F(0));
        CHECK(F(1) % 1 == F(0));
        CHECK(F(2) % 1 == F(0));
        CHECK(F(3) % 1 == F(0));

        CHECK(F(0) % 2 == F(0));
        CHECK(F(1) % 2 == F(1));
        CHECK(F(2) % 2 == F(0));
        CHECK(F(3) % 2 == F(1));

        CHECK(F(0) % 3 == F(0));
        CHECK(F(1) % 3 == F(1));
        CHECK(F(2) % 3 == F(2));
        CHECK(F(3) % 3 == F(0));

        CHECK(F(0) % 4 == F(0));
        CHECK(F(1) % 4 == F(1));
        CHECK(F(2) % 4 == F(2));
        CHECK(F(3) % 4 == F(3));
    }
}

TEST_CASE("NumberOfOneBits (MultiInteger)")
{
    using F = Unsigned_Integer<20, uint8>;
    PASTEL_CONCEPT_CHECK(F, Finite_Integer_Concept);

    auto test = [&](F that, integer correct)
    {
        return numberOfOneBits(that) == correct;
    };

    CHECK(test(0xFFF34567ul, 10));
    CHECK(test(0x00034567ul, 10));
}