#include "../tap.hpp"

#include "../iterator/counting.hpp"
#include "../iterator/dereference.hpp"
#include "../iterator/iota.hpp"
#include "../iterator/zip.hpp"

#include <vector>
#include <array>


///////////////////////////////////////////////////////////////////////////////
static void
test_counting_output_iterator (cruft::TAP::logger &tap)
{
    cruft::iterator::counting_output_iterator out;
    *out = 0; ++out;
    *out = 1; ++out;
    *out = 2; ++out;

    tap.expect_eq (out.count (), 3u, "counting_output_iterator simple manual assignment");
}


//-----------------------------------------------------------------------------
static void
test_dereference_iterator (cruft::TAP::logger &tap)
{
    int values[4] { 0, 1, 2, 3 };
    int const* pointers[4] = { values + 0, values + 1, values + 2, values + 3 };

    auto const sum = std::accumulate (
        cruft::iterator::dereference_adapter (std::cbegin (pointers)),
        cruft::iterator::dereference_adapter (std::cend   (pointers)),
        0
    );

    tap.expect_eq (sum, 6, "dereference_adapter used in accumulate");
}


//-----------------------------------------------------------------------------
static void
test_iota (cruft::TAP::logger &tap)
{
    cruft::iterator::iota seq (5);
    auto const sum = std::accumulate (std::begin (seq), std::end (seq), 0u);
    tap.expect_eq (sum, 4u+3u+2u+1u+0u, "iota summation %!", sum);
}


///////////////////////////////////////////////////////////////////////////////
int
main (int, char**)
{
    cruft::TAP::logger tap;

    // test that iteration across disparate types works as expected.
    //
    // the char array is an important element because it tends to decay to a
    // pointer type unless we're paying careful attention.
    {
        std::vector<int> v_int { 1, 2, 3 };
        std::array<float,3> a_float { 1.1f, 2.2f, 3.3f };
        char c_char[] = { '\0', 'b', 'c' };

        bool success = true;
        for (auto const [i, v, a, c]: cruft::iterator::izip (v_int, a_float, c_char)) {
            success = success &&
                      v_int[i] == v &&
                      cruft::equal (a_float[i], a) &&
                      c_char[i] == c;
        }

        tap.expect (success, "izip containers of int, float, and char and an initialiser_list");
    }


    // test that the obvious structured binding syntax gives references to
    // the underlying values rather than silently supplying lvalues.
    //
    // we deliberately do not use any type of references in the range loop so
    // that we check the syntax a user is likely to employ actually works.
    // references may not be an obvious consideration.
    {
        const std::array<unsigned,3> src { 0, 1, 2 };
        std::array<unsigned,3> dst { 2, 0, 1 };

        for (auto [a,b]: cruft::iterator::zip (src, dst))
            b = a;

        tap.expect_eq (src, dst, "copy using structured bindings");
    }

    test_counting_output_iterator (tap);
    test_dereference_iterator (tap);
    test_iota (tap);

    return tap.status ();
}