<commit_before>/*  This file is part of the Vc library.

    Copyright (C) 2009-2012 Matthias Kretz <kretz@kde.org>

    Vc is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation, either version 3 of
    the License, or (at your option) any later version.

    Vc is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Vc.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <Vc/Vc>
#include "unittest.h"
#include <iostream>
#include "vectormemoryhelper.h"

using namespace Vc;

template<typename Vec> void testSort()
{
    typedef typename Vec::EntryType EntryType;
    typedef typename Vec::IndexType IndexType;

    typename Vec::Memory _a;
    const IndexType _ref(IndexesFromZero);
    Vec ref(_ref);
    Vec a;
    int maxPerm = 1;
    for (int x = Vec::Size; x > 0; --x) {
        maxPerm *= x;
    }
    for (int perm = 0; perm < maxPerm; ++perm) {
        int rest = perm;
        for (int i = 0; i < Vec::Size; ++i) {
            _a[i] = 0;
            for (int j = 0; j < i; ++j) {
                if (_a[i] == _a[j]) {
                    ++_a[i];
                    j = -1;
                }
            }
            _a[i] += rest % (Vec::Size - i);
            rest /= (Vec::Size - i);
            for (int j = 0; j < i; ++j) {
                if (_a[i] == _a[j]) {
                    ++_a[i];
                    j = -1;
                }
            }
        }
        a.load(_a);
        //std::cout << a << a.sorted() << std::endl;
        COMPARE(ref, a.sorted()) << ", a: " << a;
    }
}

template<typename T, typename Mem> struct Foo
{
    Foo() : i(0) {}
    void reset() { i = 0; }
    void operator()(T v) { d[i++] = v; }
    Mem d;
    int i;
};

template<typename V> void testCall()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    typedef typename V::Mask M;
    typedef typename I::Mask MI;
    const I _indexes(IndexesFromZero);
    const MI _odd = (_indexes & I(One)) > 0;
    const M odd(_odd);
    V a(_indexes);
    Foo<T, typename V::Memory> f;
    a.callWithValuesSorted(f);
    V b(f.d);
    COMPARE(b, a);

    f.reset();
    a(odd) -= 1;
    a.callWithValuesSorted(f);
    V c(f.d);
    for (int i = 0; i < V::Size / 2; ++i) {
        COMPARE(a[i * 2], c[i]);
    }
    for (int i = V::Size / 2; i < V::Size; ++i) {
        COMPARE(b[i], c[i]);
    }
}

template<typename V> void testForeachBit()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    typedef typename V::Mask M;
    typedef typename I::Mask MI;
    const I indexes(IndexesFromZero);
    for_all_masks(V, mask) {
        V tmp = V::Zero();
        foreach_bit(int j, mask) {
            tmp[j] = T(1);
        }
        COMPARE(tmp == V::One(), mask);
    }
}

template<typename V> void copySign()
{
    typedef typename V::EntryType T;
    V v(One);
    V positive(One);
    V negative = -positive;
    COMPARE(v, v.copySign(positive));
    COMPARE(-v, v.copySign(negative));
}

#ifdef VC_MSVC
void bzero(void *p, size_t n) { memset(p, 0, n); }
#else
#include <strings.h>
#endif

template<typename V> void Random()
{
    typedef typename V::EntryType T;
    enum {
        NBits = 3,
        NBins = 1 << NBits,                        // short int
        TotalBits = sizeof(T) * 8,                 //    16  32
        RightShift = TotalBits - NBits,            //    13  29
        NHistograms = TotalBits - NBits + 1,       //    14  30
        LeftShift = (RightShift + 1) / NHistograms,//     1   1
        Mean = 135791,
        MinGood = Mean - Mean/10,
        MaxGood = Mean + Mean/10
    };
    const V mask((1 << NBits) - 1);
    int histogram[NHistograms][NBins];
    bzero(&histogram[0][0], sizeof(histogram));
    for (size_t i = 0; i < NBins * Mean / V::Size; ++i) {
        const V rand = V::Random();
        for (size_t hist = 0; hist < NHistograms; ++hist) {
            const V bin = ((rand << (hist * LeftShift)) >> RightShift) & mask;
            for (size_t k = 0; k < V::Size; ++k) {
                ++histogram[hist][bin[k]];
            }
        }
    }
//#define PRINT_RANDOM_HISTOGRAM
#ifdef PRINT_RANDOM_HISTOGRAM
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        std::cout << "histogram[" << std::setw(2) << hist << "]: ";
        for (size_t bin = 0; bin < NBins; ++bin) {
            std::cout << std::setw(3) << (histogram[hist][bin] - Mean) * 1000 / Mean << "|";
        }
        std::cout << std::endl;
    }
#endif
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        for (size_t bin = 0; bin < NBins; ++bin) {
            VERIFY(histogram[hist][bin] > MinGood)
                << " bin = " << bin << " is " << histogram[0][bin];
            VERIFY(histogram[hist][bin] < MaxGood)
                << " bin = " << bin << " is " << histogram[0][bin];
        }
    }
}

template<typename V, typename I> void FloatRandom()
{
    typedef typename V::EntryType T;
    enum {
        NBins = 64,
        NHistograms = 1,
        Mean = 135791,
        MinGood = Mean - Mean/10,
        MaxGood = Mean + Mean/10
    };
    int histogram[NHistograms][NBins];
    bzero(&histogram[0][0], sizeof(histogram));
    for (size_t i = 0; i < NBins * Mean / V::Size; ++i) {
        const V rand = V::Random();
        const I bin = static_cast<I>(rand * T(NBins));
        for (size_t k = 0; k < V::Size; ++k) {
            ++histogram[0][bin[k]];
        }
    }
#ifdef PRINT_RANDOM_HISTOGRAM
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        std::cout << "histogram[" << std::setw(2) << hist << "]: ";
        for (size_t bin = 0; bin < NBins; ++bin) {
            std::cout << std::setw(3) << (histogram[hist][bin] - Mean) * 1000 / Mean << "|";
        }
        std::cout << std::endl;
    }
#endif
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        for (size_t bin = 0; bin < NBins; ++bin) {
            VERIFY(histogram[hist][bin] > MinGood)
                << " bin = " << bin << " is " << histogram[0][bin];
            VERIFY(histogram[hist][bin] < MaxGood)
                << " bin = " << bin << " is " << histogram[0][bin];
        }
    }
}

template<> void Random<float_v>() { FloatRandom<float_v, int_v>(); }
template<> void Random<double_v>() { FloatRandom<double_v, int_v>(); }
template<> void Random<sfloat_v>() { FloatRandom<sfloat_v, short_v>(); }

template<typename T> T add2(T x) { return x + T(2); }

template<typename T, typename V>
class CallTester
{
    public:
        CallTester() : v(Vc::Zero), i(0) {}

        void operator()(T x) {
            v[i] = x;
            ++i;
        }

        void reset() { v.setZero(); i = 0; }

        int callCount() const { return i; }
        V callValues() const { return v; }

    private:
        V v;
        int i;
};

#if __cplusplus >= 201103 && (!defined(VC_CLANG) || VC_CLANG > 0x30000)
#define DO_LAMBDA_TESTS 1
#endif

template<typename V>
void applyAndCall()
{
    typedef typename V::EntryType T;

    const V two(T(2));
    for (int i = 0; i < 1000; ++i) {
        const V rand = V::Random();
        COMPARE(rand.apply(add2<T>), rand + two);
#ifdef DO_LAMBDA_TESTS
        COMPARE(rand.apply([](T x) { return x + T(2); }), rand + two);
#endif

        CallTester<T, V> callTester;
        rand.call(callTester);
        COMPARE(callTester.callCount(), int(V::Size));
        COMPARE(callTester.callValues(), rand);

        for_all_masks(V, mask) {
            V copy1 = rand;
            V copy2 = rand;
            copy1(mask) += two;

            COMPARE(copy2(mask).apply(add2<T>), copy1) << mask;
            COMPARE(rand.apply(add2<T>, mask), copy1) << mask;
#ifdef DO_LAMBDA_TESTS
            COMPARE(copy2(mask).apply([](T x) { return x + T(2); }), copy1) << mask;
            COMPARE(rand.apply([](T x) { return x + T(2); }, mask), copy1) << mask;
#endif

            callTester.reset();
            copy2(mask).call(callTester);
            COMPARE(callTester.callCount(), mask.count());

            callTester.reset();
            rand.call(callTester, mask);
            COMPARE(callTester.callCount(), mask.count());
        }
    }
}

template<typename T, int value> T returnConstant() { return T(value); }
template<typename T, int value> T returnConstantOffset(int i) { return T(value) + T(i); }
template<typename T, int value> T returnConstantOffset2(unsigned short i) { return T(value) + T(i); }

template<typename V> void fill()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    V test = V::Random();
    test.fill(returnConstant<T, 2>);
    COMPARE(test, V(T(2)));

    test = V::Random();
    test.fill(returnConstantOffset<T, 0>);
    COMPARE(test, static_cast<V>(I::IndexesFromZero()));

    test = V::Random();
    test.fill(returnConstantOffset2<T, 0>);
    COMPARE(test, static_cast<V>(I::IndexesFromZero()));
}

int main()
{
    runTest(testCall<int_v>);
    runTest(testCall<uint_v>);
    runTest(testCall<short_v>);
    runTest(testCall<ushort_v>);
    runTest(testCall<float_v>);
    runTest(testCall<sfloat_v>);
    runTest(testCall<double_v>);

    runTest(testForeachBit<int_v>);
    runTest(testForeachBit<uint_v>);
    runTest(testForeachBit<short_v>);
    runTest(testForeachBit<ushort_v>);
    runTest(testForeachBit<float_v>);
    runTest(testForeachBit<sfloat_v>);
    runTest(testForeachBit<double_v>);

    runTest(testSort<int_v>);
    runTest(testSort<uint_v>);
    runTest(testSort<float_v>);
    runTest(testSort<double_v>);
    runTest(testSort<sfloat_v>);
    runTest(testSort<short_v>);
    runTest(testSort<ushort_v>);

    runTest(copySign<float_v>);
    runTest(copySign<sfloat_v>);
    runTest(copySign<double_v>);

    testAllTypes(Random);

    testAllTypes(applyAndCall);
    testAllTypes(fill);

    return 0;
}
<commit_msg>test continue and break in foreach_bit<commit_after>/*  This file is part of the Vc library.

    Copyright (C) 2009-2012 Matthias Kretz <kretz@kde.org>

    Vc is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation, either version 3 of
    the License, or (at your option) any later version.

    Vc is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Vc.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <Vc/Vc>
#include "unittest.h"
#include <iostream>
#include "vectormemoryhelper.h"

using namespace Vc;

template<typename Vec> void testSort()
{
    typedef typename Vec::EntryType EntryType;
    typedef typename Vec::IndexType IndexType;

    typename Vec::Memory _a;
    const IndexType _ref(IndexesFromZero);
    Vec ref(_ref);
    Vec a;
    int maxPerm = 1;
    for (int x = Vec::Size; x > 0; --x) {
        maxPerm *= x;
    }
    for (int perm = 0; perm < maxPerm; ++perm) {
        int rest = perm;
        for (int i = 0; i < Vec::Size; ++i) {
            _a[i] = 0;
            for (int j = 0; j < i; ++j) {
                if (_a[i] == _a[j]) {
                    ++_a[i];
                    j = -1;
                }
            }
            _a[i] += rest % (Vec::Size - i);
            rest /= (Vec::Size - i);
            for (int j = 0; j < i; ++j) {
                if (_a[i] == _a[j]) {
                    ++_a[i];
                    j = -1;
                }
            }
        }
        a.load(_a);
        //std::cout << a << a.sorted() << std::endl;
        COMPARE(ref, a.sorted()) << ", a: " << a;
    }
}

template<typename T, typename Mem> struct Foo
{
    Foo() : i(0) {}
    void reset() { i = 0; }
    void operator()(T v) { d[i++] = v; }
    Mem d;
    int i;
};

template<typename V> void testCall()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    typedef typename V::Mask M;
    typedef typename I::Mask MI;
    const I _indexes(IndexesFromZero);
    const MI _odd = (_indexes & I(One)) > 0;
    const M odd(_odd);
    V a(_indexes);
    Foo<T, typename V::Memory> f;
    a.callWithValuesSorted(f);
    V b(f.d);
    COMPARE(b, a);

    f.reset();
    a(odd) -= 1;
    a.callWithValuesSorted(f);
    V c(f.d);
    for (int i = 0; i < V::Size / 2; ++i) {
        COMPARE(a[i * 2], c[i]);
    }
    for (int i = V::Size / 2; i < V::Size; ++i) {
        COMPARE(b[i], c[i]);
    }
}

template<typename V> void testForeachBit()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    typedef typename V::Mask M;
    typedef typename I::Mask MI;
    const I indexes(IndexesFromZero);
    for_all_masks(V, mask) {
        V tmp = V::Zero();
        foreach_bit(int j, mask) {
            tmp[j] = T(1);
        }
        COMPARE(tmp == V::One(), mask);

        int count = 0;
        foreach_bit(int j, mask) {
            ++count;
            if (j >= 0) {
                continue;
            }
        }
        COMPARE(count, mask.count());

        count = 0;
        foreach_bit(int j, mask) {
            if (j >= 0) {
                break;
            }
            ++count;
        }
        COMPARE(count, 0);
    }
}

template<typename V> void copySign()
{
    typedef typename V::EntryType T;
    V v(One);
    V positive(One);
    V negative = -positive;
    COMPARE(v, v.copySign(positive));
    COMPARE(-v, v.copySign(negative));
}

#ifdef VC_MSVC
void bzero(void *p, size_t n) { memset(p, 0, n); }
#else
#include <strings.h>
#endif

template<typename V> void Random()
{
    typedef typename V::EntryType T;
    enum {
        NBits = 3,
        NBins = 1 << NBits,                        // short int
        TotalBits = sizeof(T) * 8,                 //    16  32
        RightShift = TotalBits - NBits,            //    13  29
        NHistograms = TotalBits - NBits + 1,       //    14  30
        LeftShift = (RightShift + 1) / NHistograms,//     1   1
        Mean = 135791,
        MinGood = Mean - Mean/10,
        MaxGood = Mean + Mean/10
    };
    const V mask((1 << NBits) - 1);
    int histogram[NHistograms][NBins];
    bzero(&histogram[0][0], sizeof(histogram));
    for (size_t i = 0; i < NBins * Mean / V::Size; ++i) {
        const V rand = V::Random();
        for (size_t hist = 0; hist < NHistograms; ++hist) {
            const V bin = ((rand << (hist * LeftShift)) >> RightShift) & mask;
            for (size_t k = 0; k < V::Size; ++k) {
                ++histogram[hist][bin[k]];
            }
        }
    }
//#define PRINT_RANDOM_HISTOGRAM
#ifdef PRINT_RANDOM_HISTOGRAM
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        std::cout << "histogram[" << std::setw(2) << hist << "]: ";
        for (size_t bin = 0; bin < NBins; ++bin) {
            std::cout << std::setw(3) << (histogram[hist][bin] - Mean) * 1000 / Mean << "|";
        }
        std::cout << std::endl;
    }
#endif
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        for (size_t bin = 0; bin < NBins; ++bin) {
            VERIFY(histogram[hist][bin] > MinGood)
                << " bin = " << bin << " is " << histogram[0][bin];
            VERIFY(histogram[hist][bin] < MaxGood)
                << " bin = " << bin << " is " << histogram[0][bin];
        }
    }
}

template<typename V, typename I> void FloatRandom()
{
    typedef typename V::EntryType T;
    enum {
        NBins = 64,
        NHistograms = 1,
        Mean = 135791,
        MinGood = Mean - Mean/10,
        MaxGood = Mean + Mean/10
    };
    int histogram[NHistograms][NBins];
    bzero(&histogram[0][0], sizeof(histogram));
    for (size_t i = 0; i < NBins * Mean / V::Size; ++i) {
        const V rand = V::Random();
        const I bin = static_cast<I>(rand * T(NBins));
        for (size_t k = 0; k < V::Size; ++k) {
            ++histogram[0][bin[k]];
        }
    }
#ifdef PRINT_RANDOM_HISTOGRAM
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        std::cout << "histogram[" << std::setw(2) << hist << "]: ";
        for (size_t bin = 0; bin < NBins; ++bin) {
            std::cout << std::setw(3) << (histogram[hist][bin] - Mean) * 1000 / Mean << "|";
        }
        std::cout << std::endl;
    }
#endif
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        for (size_t bin = 0; bin < NBins; ++bin) {
            VERIFY(histogram[hist][bin] > MinGood)
                << " bin = " << bin << " is " << histogram[0][bin];
            VERIFY(histogram[hist][bin] < MaxGood)
                << " bin = " << bin << " is " << histogram[0][bin];
        }
    }
}

template<> void Random<float_v>() { FloatRandom<float_v, int_v>(); }
template<> void Random<double_v>() { FloatRandom<double_v, int_v>(); }
template<> void Random<sfloat_v>() { FloatRandom<sfloat_v, short_v>(); }

template<typename T> T add2(T x) { return x + T(2); }

template<typename T, typename V>
class CallTester
{
    public:
        CallTester() : v(Vc::Zero), i(0) {}

        void operator()(T x) {
            v[i] = x;
            ++i;
        }

        void reset() { v.setZero(); i = 0; }

        int callCount() const { return i; }
        V callValues() const { return v; }

    private:
        V v;
        int i;
};

#if __cplusplus >= 201103 && (!defined(VC_CLANG) || VC_CLANG > 0x30000)
#define DO_LAMBDA_TESTS 1
#endif

template<typename V>
void applyAndCall()
{
    typedef typename V::EntryType T;

    const V two(T(2));
    for (int i = 0; i < 1000; ++i) {
        const V rand = V::Random();
        COMPARE(rand.apply(add2<T>), rand + two);
#ifdef DO_LAMBDA_TESTS
        COMPARE(rand.apply([](T x) { return x + T(2); }), rand + two);
#endif

        CallTester<T, V> callTester;
        rand.call(callTester);
        COMPARE(callTester.callCount(), int(V::Size));
        COMPARE(callTester.callValues(), rand);

        for_all_masks(V, mask) {
            V copy1 = rand;
            V copy2 = rand;
            copy1(mask) += two;

            COMPARE(copy2(mask).apply(add2<T>), copy1) << mask;
            COMPARE(rand.apply(add2<T>, mask), copy1) << mask;
#ifdef DO_LAMBDA_TESTS
            COMPARE(copy2(mask).apply([](T x) { return x + T(2); }), copy1) << mask;
            COMPARE(rand.apply([](T x) { return x + T(2); }, mask), copy1) << mask;
#endif

            callTester.reset();
            copy2(mask).call(callTester);
            COMPARE(callTester.callCount(), mask.count());

            callTester.reset();
            rand.call(callTester, mask);
            COMPARE(callTester.callCount(), mask.count());
        }
    }
}

template<typename T, int value> T returnConstant() { return T(value); }
template<typename T, int value> T returnConstantOffset(int i) { return T(value) + T(i); }
template<typename T, int value> T returnConstantOffset2(unsigned short i) { return T(value) + T(i); }

template<typename V> void fill()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    V test = V::Random();
    test.fill(returnConstant<T, 2>);
    COMPARE(test, V(T(2)));

    test = V::Random();
    test.fill(returnConstantOffset<T, 0>);
    COMPARE(test, static_cast<V>(I::IndexesFromZero()));

    test = V::Random();
    test.fill(returnConstantOffset2<T, 0>);
    COMPARE(test, static_cast<V>(I::IndexesFromZero()));
}

int main()
{
    runTest(testCall<int_v>);
    runTest(testCall<uint_v>);
    runTest(testCall<short_v>);
    runTest(testCall<ushort_v>);
    runTest(testCall<float_v>);
    runTest(testCall<sfloat_v>);
    runTest(testCall<double_v>);

    runTest(testForeachBit<int_v>);
    runTest(testForeachBit<uint_v>);
    runTest(testForeachBit<short_v>);
    runTest(testForeachBit<ushort_v>);
    runTest(testForeachBit<float_v>);
    runTest(testForeachBit<sfloat_v>);
    runTest(testForeachBit<double_v>);

    runTest(testSort<int_v>);
    runTest(testSort<uint_v>);
    runTest(testSort<float_v>);
    runTest(testSort<double_v>);
    runTest(testSort<sfloat_v>);
    runTest(testSort<short_v>);
    runTest(testSort<ushort_v>);

    runTest(copySign<float_v>);
    runTest(copySign<sfloat_v>);
    runTest(copySign<double_v>);

    testAllTypes(Random);

    testAllTypes(applyAndCall);
    testAllTypes(fill);

    return 0;
}
<|endoftext|>


<commit_before>/*  This file is part of the Vc library.

    Copyright (C) 2009-2012 Matthias Kretz <kretz@kde.org>

    Vc is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation, either version 3 of
    the License, or (at your option) any later version.

    Vc is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Vc.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "unittest.h"
#include <iostream>
#include "vectormemoryhelper.h"
#include <Vc/cpuid.h>

using namespace Vc;

template<typename Vec> void testSort()
{
    typedef typename Vec::EntryType EntryType;
    typedef typename Vec::IndexType IndexType;

    typename Vec::Memory _a;
    const IndexType _ref(IndexesFromZero);
    Vec ref(_ref);
    Vec a;
    int maxPerm = 1;
    for (int x = Vec::Size; x > 0; --x) {
        maxPerm *= x;
    }
    for (int perm = 0; perm < maxPerm; ++perm) {
        int rest = perm;
        for (int i = 0; i < Vec::Size; ++i) {
            _a[i] = 0;
            for (int j = 0; j < i; ++j) {
                if (_a[i] == _a[j]) {
                    ++_a[i];
                    j = -1;
                }
            }
            _a[i] += rest % (Vec::Size - i);
            rest /= (Vec::Size - i);
            for (int j = 0; j < i; ++j) {
                if (_a[i] == _a[j]) {
                    ++_a[i];
                    j = -1;
                }
            }
        }
        a.load(_a);
        //std::cout << a << a.sorted() << std::endl;
        COMPARE(ref, a.sorted()) << ", a: " << a;
    }
}

template<typename T, typename Mem> struct Foo
{
    Foo() : i(0) {}
    void reset() { i = 0; }
    void operator()(T v) { d[i++] = v; }
    Mem d;
    int i;
};

template<typename V> void testCall()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    typedef typename V::Mask M;
    typedef typename I::Mask MI;
    const I _indexes(IndexesFromZero);
    const MI _odd = (_indexes & I(One)) > 0;
    const M odd(_odd);
    V a(_indexes);
    Foo<T, typename V::Memory> f;
    a.callWithValuesSorted(f);
    V b(f.d);
    COMPARE(b, a);

    f.reset();
    a(odd) -= 1;
    a.callWithValuesSorted(f);
    V c(f.d);
    for (int i = 0; i < V::Size / 2; ++i) {
        COMPARE(a[i * 2], c[i]);
    }
    for (int i = V::Size / 2; i < V::Size; ++i) {
        COMPARE(b[i], c[i]);
    }
}

template<typename V> void testForeachBit()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    typedef typename V::Mask M;
    typedef typename I::Mask MI;
    const I indexes(IndexesFromZero);
    for_all_masks(V, mask) {
        V tmp = V::Zero();
        foreach_bit(int j, mask) {
            tmp[j] = T(1);
        }
        COMPARE(tmp == V::One(), mask);

        unsigned int count = 0;
        foreach_bit(int j, mask) {
            ++count;
            if (j >= 0) {
                continue;
            }
        }
        COMPARE(count, mask.count());

        count = 0;
        foreach_bit(int j, mask) {
            if (j >= 0) {
                break;
            }
            ++count;
        }
        COMPARE(count, 0U);
    }
}

template<typename V> void copySign()
{
    typedef typename V::EntryType T;
    V v(One);
    V positive(One);
    V negative = -positive;
    COMPARE(v, v.copySign(positive));
    COMPARE(-v, v.copySign(negative));
}

#ifdef _WIN32
void bzero(void *p, size_t n) { memset(p, 0, n); }
#else
#include <strings.h>
#endif

template<typename V> void Random()
{
    typedef typename V::EntryType T;
    enum {
        NBits = 3,
        NBins = 1 << NBits,                        // short int
        TotalBits = sizeof(T) * 8,                 //    16  32
        RightShift = TotalBits - NBits,            //    13  29
        NHistograms = TotalBits - NBits + 1,       //    14  30
        LeftShift = (RightShift + 1) / NHistograms,//     1   1
        Mean = 135791,
        MinGood = Mean - Mean/10,
        MaxGood = Mean + Mean/10
    };
    const V mask((1 << NBits) - 1);
    int histogram[NHistograms][NBins];
    bzero(&histogram[0][0], sizeof(histogram));
    for (size_t i = 0; i < NBins * Mean / V::Size; ++i) {
        const V rand = V::Random();
        for (size_t hist = 0; hist < NHistograms; ++hist) {
            const V bin = ((rand << (hist * LeftShift)) >> RightShift) & mask;
            for (size_t k = 0; k < V::Size; ++k) {
                ++histogram[hist][bin[k]];
            }
        }
    }
//#define PRINT_RANDOM_HISTOGRAM
#ifdef PRINT_RANDOM_HISTOGRAM
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        std::cout << "histogram[" << std::setw(2) << hist << "]: ";
        for (size_t bin = 0; bin < NBins; ++bin) {
            std::cout << std::setw(3) << (histogram[hist][bin] - Mean) * 1000 / Mean << "|";
        }
        std::cout << std::endl;
    }
#endif
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        for (size_t bin = 0; bin < NBins; ++bin) {
            VERIFY(histogram[hist][bin] > MinGood)
                << " bin = " << bin << " is " << histogram[0][bin];
            VERIFY(histogram[hist][bin] < MaxGood)
                << " bin = " << bin << " is " << histogram[0][bin];
        }
    }
}

template<typename V, typename I> void FloatRandom()
{
    typedef typename V::EntryType T;
    enum {
        NBins = 64,
        NHistograms = 1,
        Mean = 135791,
        MinGood = Mean - Mean/10,
        MaxGood = Mean + Mean/10
    };
    int histogram[NHistograms][NBins];
    bzero(&histogram[0][0], sizeof(histogram));
    for (size_t i = 0; i < NBins * Mean / V::Size; ++i) {
        const V rand = V::Random();
        const I bin = static_cast<I>(rand * T(NBins));
        for (size_t k = 0; k < V::Size; ++k) {
            ++histogram[0][bin[k]];
        }
    }
#ifdef PRINT_RANDOM_HISTOGRAM
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        std::cout << "histogram[" << std::setw(2) << hist << "]: ";
        for (size_t bin = 0; bin < NBins; ++bin) {
            std::cout << std::setw(3) << (histogram[hist][bin] - Mean) * 1000 / Mean << "|";
        }
        std::cout << std::endl;
    }
#endif
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        for (size_t bin = 0; bin < NBins; ++bin) {
            VERIFY(histogram[hist][bin] > MinGood)
                << " bin = " << bin << " is " << histogram[0][bin];
            VERIFY(histogram[hist][bin] < MaxGood)
                << " bin = " << bin << " is " << histogram[0][bin];
        }
    }
}

template<> void Random<float_v>() { FloatRandom<float_v, int_v>(); }
template<> void Random<double_v>() { FloatRandom<double_v, int_v>(); }
template<> void Random<sfloat_v>() { FloatRandom<sfloat_v, short_v>(); }

template<typename T> T add2(T x) { return x + T(2); }

template<typename T, typename V>
class CallTester
{
    public:
        CallTester() : v(Vc::Zero), i(0) {}

        void operator()(T x) {
            v[i] = x;
            ++i;
        }

        void reset() { v.setZero(); i = 0; }

        unsigned int callCount() const { return i; }
        V callValues() const { return v; }

    private:
        V v;
        unsigned int i;
};

template<typename V>
void applyAndCall()
{
    typedef typename V::EntryType T;

    const V two(T(2));
    for (int i = 0; i < 1000; ++i) {
        const V rand = V::Random();
        COMPARE(rand.apply(add2<T>), rand + two);
        COMPARE(rand.apply([](T x) { return x + T(2); }), rand + two);

        CallTester<T, V> callTester;
        rand.call(callTester);
        COMPARE(callTester.callCount(), static_cast<unsigned int>(V::Size));
        COMPARE(callTester.callValues(), rand);

        for_all_masks(V, mask) {
            V copy1 = rand;
            V copy2 = rand;
            copy1(mask) += two;

            COMPARE(copy2(mask).apply(add2<T>), copy1) << mask;
            COMPARE(rand.apply(add2<T>, mask), copy1) << mask;
            COMPARE(copy2(mask).apply([](T x) { return x + T(2); }), copy1) << mask;
            COMPARE(rand.apply([](T x) { return x + T(2); }, mask), copy1) << mask;

            callTester.reset();
            copy2(mask).call(callTester);
            COMPARE(callTester.callCount(), mask.count());

            callTester.reset();
            rand.call(callTester, mask);
            COMPARE(callTester.callCount(), mask.count());
        }
    }
}

template<typename T, int value> T returnConstant() { return T(value); }
template<typename T, int value> T returnConstantOffset(int i) { return T(value) + T(i); }
template<typename T, int value> T returnConstantOffset2(unsigned short i) { return T(value) + T(i); }

template<typename V> void fill()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    V test = V::Random();
    test.fill(returnConstant<T, 2>);
    COMPARE(test, V(T(2)));

    test = V::Random();
    test.fill(returnConstantOffset<T, 0>);
    COMPARE(test, static_cast<V>(I::IndexesFromZero()));

    test = V::Random();
    test.fill(returnConstantOffset2<T, 0>);
    COMPARE(test, static_cast<V>(I::IndexesFromZero()));
}

template<typename V> void shifted()
{
    typedef typename V::EntryType T;
    for (int shift = -2 * V::Size; shift <= 2 * V::Size; ++shift) {
        const V reference = V::Random();
        const V test = reference.shifted(shift);
        for (int i = 0; i < V::Size; ++i) {
            if (i + shift >= 0 && i + shift < V::Size) {
                COMPARE(test[i], reference[i + shift]) << "shift: " << shift << ", i: " << i << ", test: " << test << ", reference: " << reference;
            } else {
                COMPARE(test[i], T(0)) << "shift: " << shift << ", i: " << i << ", test: " << test << ", reference: " << reference;
            }
        }
    }
}

template<typename V> void rotated()
{
    for (int shift = -2 * V::Size; shift <= 2 * V::Size; ++shift) {
        //std::cout << "amount = " << shift % V::Size << std::endl;
        const V reference = V::Random();
        const V test = reference.rotated(shift);
        for (int i = 0; i < V::Size; ++i) {
            unsigned int refShift = i + shift;
            COMPARE(test[i], reference[refShift % V::Size]) << "shift: " << shift << ", i: " << i << ", test: " << test << ", reference: " << reference;
        }
    }
}

void testMallocAlignment()
{
    int_v *a = Vc::malloc<int_v, Vc::AlignOnVector>(10);

    unsigned long mask = VectorAlignment - 1;
    for (int i = 0; i < 10; ++i) {
        VERIFY((reinterpret_cast<unsigned long>(&a[i]) & mask) == 0);
    }
    const char *data = reinterpret_cast<const char *>(&a[0]);
    for (int i = 0; i < 10; ++i) {
        VERIFY(&data[i * int_v::Size * sizeof(int_v::EntryType)] == reinterpret_cast<const char *>(&a[i]));
    }

    a = Vc::malloc<int_v, Vc::AlignOnCacheline>(10);
    mask = CpuId::cacheLineSize() - 1;
    COMPARE((reinterpret_cast<unsigned long>(&a[0]) & mask), 0ul);

    // I don't know how to properly check page alignment. So we check for 4 KiB alignment as this is
    // the minimum page size on x86
    a = Vc::malloc<int_v, Vc::AlignOnPage>(10);
    mask = 4096 - 1;
    COMPARE((reinterpret_cast<unsigned long>(&a[0]) & mask), 0ul);
}

template<typename V> void testIif()
{
    typedef typename V::EntryType T;
    const T one = T(1);
    const T two = T(2);

    for (int i = 0; i < 10000; ++i) {
        const V x = V::Random();
        const V y = V::Random();
        V reference = y;
        V reference2 = two;
        for (size_t j = 0; j < V::Size; ++j) {
            if (x[j] > y[j]) {
                reference[j] = x[j];
                reference2[j] = one;
            }
        }
        COMPARE(iif (x > y, x, y), reference);
        COMPARE(iif (x > y, V(one), V(two)), reference2);
    }
}

int main()
{
    testAllTypes(testCall);
    testAllTypes(testForeachBit);
    testAllTypes(testSort);
    testRealTypes(copySign);

    testAllTypes(shifted);
    testAllTypes(rotated);
    testAllTypes(Random);

    testAllTypes(applyAndCall);
    testAllTypes(fill);

    runTest(testMallocAlignment);
    testAllTypes(testIif);

    return 0;
}
<commit_msg>simplify sort test<commit_after>/*  This file is part of the Vc library.

    Copyright (C) 2009-2012 Matthias Kretz <kretz@kde.org>

    Vc is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation, either version 3 of
    the License, or (at your option) any later version.

    Vc is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Vc.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "unittest.h"
#include <iostream>
#include "vectormemoryhelper.h"
#include <Vc/cpuid.h>

using namespace Vc;

template<typename Vec> void testSort()
{
    typedef typename Vec::EntryType EntryType;
    typedef typename Vec::IndexType IndexType;

    const IndexType _ref(IndexesFromZero);
    Vec ref(_ref);
    Vec a;
    int maxPerm = 1;
    for (int x = Vec::Size; x > 0; --x) {
        maxPerm *= x;
    }
    for (int perm = 0; perm < maxPerm; ++perm) {
        int rest = perm;
        for (int i = 0; i < Vec::Size; ++i) {
            a[i] = 0;
            for (int j = 0; j < i; ++j) {
                if (a[i] == a[j]) {
                    ++(a[i]);
                    j = -1;
                }
            }
            a[i] += rest % (Vec::Size - i);
            rest /= (Vec::Size - i);
            for (int j = 0; j < i; ++j) {
                if (a[i] == a[j]) {
                    ++(a[i]);
                    j = -1;
                }
            }
        }
        //std::cout << a << a.sorted() << std::endl;
        COMPARE(ref, a.sorted()) << ", a: " << a;
    }
}

template<typename T, typename Mem> struct Foo
{
    Foo() : i(0) {}
    void reset() { i = 0; }
    void operator()(T v) { d[i++] = v; }
    Mem d;
    int i;
};

template<typename V> void testCall()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    typedef typename V::Mask M;
    typedef typename I::Mask MI;
    const I _indexes(IndexesFromZero);
    const MI _odd = (_indexes & I(One)) > 0;
    const M odd(_odd);
    V a(_indexes);
    Foo<T, typename V::Memory> f;
    a.callWithValuesSorted(f);
    V b(f.d);
    COMPARE(b, a);

    f.reset();
    a(odd) -= 1;
    a.callWithValuesSorted(f);
    V c(f.d);
    for (int i = 0; i < V::Size / 2; ++i) {
        COMPARE(a[i * 2], c[i]);
    }
    for (int i = V::Size / 2; i < V::Size; ++i) {
        COMPARE(b[i], c[i]);
    }
}

template<typename V> void testForeachBit()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    typedef typename V::Mask M;
    typedef typename I::Mask MI;
    const I indexes(IndexesFromZero);
    for_all_masks(V, mask) {
        V tmp = V::Zero();
        foreach_bit(int j, mask) {
            tmp[j] = T(1);
        }
        COMPARE(tmp == V::One(), mask);

        unsigned int count = 0;
        foreach_bit(int j, mask) {
            ++count;
            if (j >= 0) {
                continue;
            }
        }
        COMPARE(count, mask.count());

        count = 0;
        foreach_bit(int j, mask) {
            if (j >= 0) {
                break;
            }
            ++count;
        }
        COMPARE(count, 0U);
    }
}

template<typename V> void copySign()
{
    typedef typename V::EntryType T;
    V v(One);
    V positive(One);
    V negative = -positive;
    COMPARE(v, v.copySign(positive));
    COMPARE(-v, v.copySign(negative));
}

#ifdef _WIN32
void bzero(void *p, size_t n) { memset(p, 0, n); }
#else
#include <strings.h>
#endif

template<typename V> void Random()
{
    typedef typename V::EntryType T;
    enum {
        NBits = 3,
        NBins = 1 << NBits,                        // short int
        TotalBits = sizeof(T) * 8,                 //    16  32
        RightShift = TotalBits - NBits,            //    13  29
        NHistograms = TotalBits - NBits + 1,       //    14  30
        LeftShift = (RightShift + 1) / NHistograms,//     1   1
        Mean = 135791,
        MinGood = Mean - Mean/10,
        MaxGood = Mean + Mean/10
    };
    const V mask((1 << NBits) - 1);
    int histogram[NHistograms][NBins];
    bzero(&histogram[0][0], sizeof(histogram));
    for (size_t i = 0; i < NBins * Mean / V::Size; ++i) {
        const V rand = V::Random();
        for (size_t hist = 0; hist < NHistograms; ++hist) {
            const V bin = ((rand << (hist * LeftShift)) >> RightShift) & mask;
            for (size_t k = 0; k < V::Size; ++k) {
                ++histogram[hist][bin[k]];
            }
        }
    }
//#define PRINT_RANDOM_HISTOGRAM
#ifdef PRINT_RANDOM_HISTOGRAM
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        std::cout << "histogram[" << std::setw(2) << hist << "]: ";
        for (size_t bin = 0; bin < NBins; ++bin) {
            std::cout << std::setw(3) << (histogram[hist][bin] - Mean) * 1000 / Mean << "|";
        }
        std::cout << std::endl;
    }
#endif
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        for (size_t bin = 0; bin < NBins; ++bin) {
            VERIFY(histogram[hist][bin] > MinGood)
                << " bin = " << bin << " is " << histogram[0][bin];
            VERIFY(histogram[hist][bin] < MaxGood)
                << " bin = " << bin << " is " << histogram[0][bin];
        }
    }
}

template<typename V, typename I> void FloatRandom()
{
    typedef typename V::EntryType T;
    enum {
        NBins = 64,
        NHistograms = 1,
        Mean = 135791,
        MinGood = Mean - Mean/10,
        MaxGood = Mean + Mean/10
    };
    int histogram[NHistograms][NBins];
    bzero(&histogram[0][0], sizeof(histogram));
    for (size_t i = 0; i < NBins * Mean / V::Size; ++i) {
        const V rand = V::Random();
        const I bin = static_cast<I>(rand * T(NBins));
        for (size_t k = 0; k < V::Size; ++k) {
            ++histogram[0][bin[k]];
        }
    }
#ifdef PRINT_RANDOM_HISTOGRAM
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        std::cout << "histogram[" << std::setw(2) << hist << "]: ";
        for (size_t bin = 0; bin < NBins; ++bin) {
            std::cout << std::setw(3) << (histogram[hist][bin] - Mean) * 1000 / Mean << "|";
        }
        std::cout << std::endl;
    }
#endif
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        for (size_t bin = 0; bin < NBins; ++bin) {
            VERIFY(histogram[hist][bin] > MinGood)
                << " bin = " << bin << " is " << histogram[0][bin];
            VERIFY(histogram[hist][bin] < MaxGood)
                << " bin = " << bin << " is " << histogram[0][bin];
        }
    }
}

template<> void Random<float_v>() { FloatRandom<float_v, int_v>(); }
template<> void Random<double_v>() { FloatRandom<double_v, int_v>(); }
template<> void Random<sfloat_v>() { FloatRandom<sfloat_v, short_v>(); }

template<typename T> T add2(T x) { return x + T(2); }

template<typename T, typename V>
class CallTester
{
    public:
        CallTester() : v(Vc::Zero), i(0) {}

        void operator()(T x) {
            v[i] = x;
            ++i;
        }

        void reset() { v.setZero(); i = 0; }

        unsigned int callCount() const { return i; }
        V callValues() const { return v; }

    private:
        V v;
        unsigned int i;
};

template<typename V>
void applyAndCall()
{
    typedef typename V::EntryType T;

    const V two(T(2));
    for (int i = 0; i < 1000; ++i) {
        const V rand = V::Random();
        COMPARE(rand.apply(add2<T>), rand + two);
        COMPARE(rand.apply([](T x) { return x + T(2); }), rand + two);

        CallTester<T, V> callTester;
        rand.call(callTester);
        COMPARE(callTester.callCount(), static_cast<unsigned int>(V::Size));
        COMPARE(callTester.callValues(), rand);

        for_all_masks(V, mask) {
            V copy1 = rand;
            V copy2 = rand;
            copy1(mask) += two;

            COMPARE(copy2(mask).apply(add2<T>), copy1) << mask;
            COMPARE(rand.apply(add2<T>, mask), copy1) << mask;
            COMPARE(copy2(mask).apply([](T x) { return x + T(2); }), copy1) << mask;
            COMPARE(rand.apply([](T x) { return x + T(2); }, mask), copy1) << mask;

            callTester.reset();
            copy2(mask).call(callTester);
            COMPARE(callTester.callCount(), mask.count());

            callTester.reset();
            rand.call(callTester, mask);
            COMPARE(callTester.callCount(), mask.count());
        }
    }
}

template<typename T, int value> T returnConstant() { return T(value); }
template<typename T, int value> T returnConstantOffset(int i) { return T(value) + T(i); }
template<typename T, int value> T returnConstantOffset2(unsigned short i) { return T(value) + T(i); }

template<typename V> void fill()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    V test = V::Random();
    test.fill(returnConstant<T, 2>);
    COMPARE(test, V(T(2)));

    test = V::Random();
    test.fill(returnConstantOffset<T, 0>);
    COMPARE(test, static_cast<V>(I::IndexesFromZero()));

    test = V::Random();
    test.fill(returnConstantOffset2<T, 0>);
    COMPARE(test, static_cast<V>(I::IndexesFromZero()));
}

template<typename V> void shifted()
{
    typedef typename V::EntryType T;
    for (int shift = -2 * V::Size; shift <= 2 * V::Size; ++shift) {
        const V reference = V::Random();
        const V test = reference.shifted(shift);
        for (int i = 0; i < V::Size; ++i) {
            if (i + shift >= 0 && i + shift < V::Size) {
                COMPARE(test[i], reference[i + shift]) << "shift: " << shift << ", i: " << i << ", test: " << test << ", reference: " << reference;
            } else {
                COMPARE(test[i], T(0)) << "shift: " << shift << ", i: " << i << ", test: " << test << ", reference: " << reference;
            }
        }
    }
}

template<typename V> void rotated()
{
    for (int shift = -2 * V::Size; shift <= 2 * V::Size; ++shift) {
        //std::cout << "amount = " << shift % V::Size << std::endl;
        const V reference = V::Random();
        const V test = reference.rotated(shift);
        for (int i = 0; i < V::Size; ++i) {
            unsigned int refShift = i + shift;
            COMPARE(test[i], reference[refShift % V::Size]) << "shift: " << shift << ", i: " << i << ", test: " << test << ", reference: " << reference;
        }
    }
}

void testMallocAlignment()
{
    int_v *a = Vc::malloc<int_v, Vc::AlignOnVector>(10);

    unsigned long mask = VectorAlignment - 1;
    for (int i = 0; i < 10; ++i) {
        VERIFY((reinterpret_cast<unsigned long>(&a[i]) & mask) == 0);
    }
    const char *data = reinterpret_cast<const char *>(&a[0]);
    for (int i = 0; i < 10; ++i) {
        VERIFY(&data[i * int_v::Size * sizeof(int_v::EntryType)] == reinterpret_cast<const char *>(&a[i]));
    }

    a = Vc::malloc<int_v, Vc::AlignOnCacheline>(10);
    mask = CpuId::cacheLineSize() - 1;
    COMPARE((reinterpret_cast<unsigned long>(&a[0]) & mask), 0ul);

    // I don't know how to properly check page alignment. So we check for 4 KiB alignment as this is
    // the minimum page size on x86
    a = Vc::malloc<int_v, Vc::AlignOnPage>(10);
    mask = 4096 - 1;
    COMPARE((reinterpret_cast<unsigned long>(&a[0]) & mask), 0ul);
}

template<typename V> void testIif()
{
    typedef typename V::EntryType T;
    const T one = T(1);
    const T two = T(2);

    for (int i = 0; i < 10000; ++i) {
        const V x = V::Random();
        const V y = V::Random();
        V reference = y;
        V reference2 = two;
        for (size_t j = 0; j < V::Size; ++j) {
            if (x[j] > y[j]) {
                reference[j] = x[j];
                reference2[j] = one;
            }
        }
        COMPARE(iif (x > y, x, y), reference);
        COMPARE(iif (x > y, V(one), V(two)), reference2);
    }
}

int main()
{
    testAllTypes(testCall);
    testAllTypes(testForeachBit);
    testAllTypes(testSort);
    testRealTypes(copySign);

    testAllTypes(shifted);
    testAllTypes(rotated);
    testAllTypes(Random);

    testAllTypes(applyAndCall);
    testAllTypes(fill);

    runTest(testMallocAlignment);
    testAllTypes(testIif);

    return 0;
}
<|endoftext|>


<commit_before>#ifndef TOML11_REGION_H
#define TOML11_REGION_H
#include <memory>
#include <algorithm>
#include <iterator>
#include <string>

namespace toml
{
namespace detail
{

// location in a container, normally in a file content.
// shared_ptr points the resource that the iter points.
// it can be used not only for resource handling, but also error message.
template<typename Container>
struct location
{
    using const_iterator = typename Container::const_iterator;

    location(Container cont)
      : source(std::make_shared<Container>(std::move(cont))),
        begin(source->cbegin()), iter(source->cbegin()), end(source->cend())
    {}
    location(const location&) = default;
    location(location&&)      = default;
    location& operator=(const location&) = default;
    location& operator=(location&&)      = default;
   ~location() = default;

    std::shared_ptr<const Container> source;
    const_iterator begin, iter, end;
};

// region in a container, normally in a file content.
// shared_ptr points the resource that the iter points.
// combinators returns this.
// it can be used not only for resource handling, but also error message.
template<typename Container>
struct region
{
    using const_iterator = typename Container::const_iterator;

    region(const location<Container>& loc)
      : begin(loc.begin), first(loc.iter), last(loc.iter), end(loc.end),
        source(loc.source)
    {}
    region(location<Container>&& loc)
      : begin(loc.begin), first(loc.iter), last(loc.iter), end(loc.end),
        source(std::move(loc.source))
    {}

    region(const region&) = default;
    region(region&&)      = default;
    region& operator=(const region&) = default;
    region& operator=(region&&)      = default;
    ~region() = default;

    const_iterator begin, first, last, end;
    std::shared_ptr<const Container> source;
};

// to show a better error message.
template<typename Container>
std::string
format_underline(const region<Container>& reg, const std::string& msg)
{
    using const_iterator = typename region<Container>::const_iterator;
    using reverse_iterator = std::reverse_iterator<const_iterator>;
    const auto line_begin = std::find(
            reverse_iterator(reg.first),
            reverse_iterator(reg.begin),
            '\n').base();
    const auto line_end = std::find(reg.last, reg.end, '\n');

    const auto line_number = std::to_string(
            1 + std::count(reg.begin, reg.first, '\n'));

    std::string retval;
    retval += ' ';
    retval += line_number;
    retval += " | ";
    retval += std::string(line_begin, line_end);
    retval += '\n';
    retval += std::string(line_number.size() + 1, ' ');
    retval += " | ";
    retval += std::string(std::distance(line_begin, reg.first), ' ');
    retval += std::string(std::distance(reg.first, reg.last), '~');
    retval += ' ';
    retval += msg;
    return retval;
}

// to show a better error message.
template<typename Container>
std::string format_underline(const region<Container>& reg,
                             typename Container::const_iterator pos,
                             const std::string& msg)
{
    using const_iterator = typename region<Container>::const_iterator;
    using reverse_iterator = std::reverse_iterator<const_iterator>;
    const auto line_begin = std::find(
            reverse_iterator(reg.first),
            reverse_iterator(reg.begin),
            '\n').base();
    const auto line_end = std::find(reg.last, reg.end, '\n');

    const auto line_number = std::to_string(
            1 + std::count(reg.begin, reg.first, '\n'));

    std::string retval;
    retval += ' ';
    retval += line_number;
    retval += " | ";
    retval += std::string(line_begin, line_end);
    retval += '\n';
    retval += std::string(line_number.size() + 1, ' ');
    retval += " | ";
    retval += std::string(std::distance(line_begin, reg.first), ' ');

    if(std::distance(reg.first, std::prev(pos)) > 0)
    {
        retval += std::string(std::distance(reg.first, std::prev(pos)), '-');
    }
    retval += '^';
    if(std::distance(pos, reg.last) > 0)
    {
        retval += std::string(std::distance(std::next(pos), reg.last), '-');
    }

    retval += ' ';
    retval += msg;
    return retval;
}

} // detail
} // toml
#endif// TOML11_REGION_H
<commit_msg>add constructors that receive range to region<commit_after>#ifndef TOML11_REGION_H
#define TOML11_REGION_H
#include <memory>
#include <algorithm>
#include <iterator>
#include <string>

namespace toml
{
namespace detail
{

// location in a container, normally in a file content.
// shared_ptr points the resource that the iter points.
// it can be used not only for resource handling, but also error message.
template<typename Container>
struct location
{
    using const_iterator = typename Container::const_iterator;

    location(Container cont)
      : source(std::make_shared<Container>(std::move(cont))),
        begin(source->cbegin()), iter(source->cbegin()), end(source->cend())
    {}
    location(const location&) = default;
    location(location&&)      = default;
    location& operator=(const location&) = default;
    location& operator=(location&&)      = default;
   ~location() = default;

    std::shared_ptr<const Container> source;
    const_iterator begin, iter, end;
};

// region in a container, normally in a file content.
// shared_ptr points the resource that the iter points.
// combinators returns this.
// it can be used not only for resource handling, but also error message.
template<typename Container>
struct region
{
    using const_iterator = typename Container::const_iterator;

    region(const location<Container>& loc)
      : begin(loc.begin), first(loc.iter), last(loc.iter), end(loc.end),
        source(loc.source)
    {}
    region(location<Container>&& loc)
      : begin(loc.begin), first(loc.iter), last(loc.iter), end(loc.end),
        source(std::move(loc.source))
    {}

    region(const location<Container>& loc, const_iterator f, const_iterator l)
      : begin(loc.begin), first(f), last(l), end(loc.end),
        source(loc.source)
    {}
    region(location<Container>&& loc, const_iterator f, const_iterator l)
      : begin(loc.begin), first(f), last(l), end(loc.end),
        source(std::move(loc.source))
    {}

    region(const region&) = default;
    region(region&&)      = default;
    region& operator=(const region&) = default;
    region& operator=(region&&)      = default;
    ~region() = default;

    const_iterator begin, first, last, end;
    std::shared_ptr<const Container> source;
};

// to show a better error message.
template<typename Container>
std::string
format_underline(const region<Container>& reg, const std::string& msg)
{
    using const_iterator = typename region<Container>::const_iterator;
    using reverse_iterator = std::reverse_iterator<const_iterator>;
    const auto line_begin = std::find(
            reverse_iterator(reg.first),
            reverse_iterator(reg.begin),
            '\n').base();
    const auto line_end = std::find(reg.last, reg.end, '\n');

    const auto line_number = std::to_string(
            1 + std::count(reg.begin, reg.first, '\n'));

    std::string retval;
    retval += ' ';
    retval += line_number;
    retval += " | ";
    retval += std::string(line_begin, line_end);
    retval += '\n';
    retval += std::string(line_number.size() + 1, ' ');
    retval += " | ";
    retval += std::string(std::distance(line_begin, reg.first), ' ');
    retval += std::string(std::distance(reg.first, reg.last), '~');
    retval += ' ';
    retval += msg;
    return retval;
}

// to show a better error message.
template<typename Container>
std::string format_underline(const region<Container>& reg,
                             typename Container::const_iterator pos,
                             const std::string& msg)
{
    using const_iterator = typename region<Container>::const_iterator;
    using reverse_iterator = std::reverse_iterator<const_iterator>;
    const auto line_begin = std::find(
            reverse_iterator(reg.first),
            reverse_iterator(reg.begin),
            '\n').base();
    const auto line_end = std::find(reg.last, reg.end, '\n');

    const auto line_number = std::to_string(
            1 + std::count(reg.begin, reg.first, '\n'));

    std::string retval;
    retval += ' ';
    retval += line_number;
    retval += " | ";
    retval += std::string(line_begin, line_end);
    retval += '\n';
    retval += std::string(line_number.size() + 1, ' ');
    retval += " | ";
    retval += std::string(std::distance(line_begin, reg.first), ' ');

    if(std::distance(reg.first, std::prev(pos)) > 0)
    {
        retval += std::string(std::distance(reg.first, std::prev(pos)), '-');
    }
    retval += '^';
    if(std::distance(pos, reg.last) > 0)
    {
        retval += std::string(std::distance(std::next(pos), reg.last), '-');
    }

    retval += ' ';
    retval += msg;
    return retval;
}

} // detail
} // toml
#endif// TOML11_REGION_H
<|endoftext|>


<commit_before>//     Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_REGION_HPP
#define TOML11_REGION_HPP
#include "exception.hpp"
#include <memory>
#include <vector>
#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <iomanip>
#include <cassert>

namespace toml
{
namespace detail
{

// helper function to avoid std::string(0, 'c') or std::string(iter, iter)
template<typename Iterator>
std::string make_string(Iterator first, Iterator last)
{
    if(first == last) {return "";}
    return std::string(first, last);
}
inline std::string make_string(std::size_t len, char c)
{
    if(len == 0) {return "";}
    return std::string(len, c);
}

// region_base is a base class of location and region that are defined below.
// it will be used to generate better error messages.
struct region_base
{
    region_base() = default;
    virtual ~region_base() = default;
    region_base(const region_base&) = default;
    region_base(region_base&&     ) = default;
    region_base& operator=(const region_base&) = default;
    region_base& operator=(region_base&&     ) = default;

    virtual bool is_ok()           const noexcept {return false;}

    virtual std::string str()      const {return std::string("unknown region");}
    virtual std::string name()     const {return std::string("unknown file");}
    virtual std::string line()     const {return std::string("unknown line");}
    virtual std::string line_num() const {return std::string("?");}

    // length of the region
    virtual std::size_t size()     const noexcept {return 0;}
    // number of characters in the line before the region
    virtual std::size_t before()   const noexcept {return 0;}
    // number of characters in the line after the region
    virtual std::size_t after()    const noexcept {return 0;}

    virtual std::vector<std::string> comments()const {return {};}
    // ```toml
    // # comment_before
    // key = "value" # comment_inline
    // ```
};

// location represents a position in a container, which contains a file content.
// it can be considered as a region that contains only one character.
//
// it contains pointer to the file content and iterator that points the current
// location.
template<typename Container>
struct location final : public region_base
{
    using const_iterator = typename Container::const_iterator;
    using source_ptr     = std::shared_ptr<const Container>;

    static_assert(std::is_same<char, typename Container::value_type>::value,"");
    static_assert(std::is_same<std::random_access_iterator_tag,
        typename std::iterator_traits<const_iterator>::iterator_category>::value,
        "container should be randomly accessible");

    location(std::string name, Container cont)
      : source_(std::make_shared<Container>(std::move(cont))), line_number_(1),
        source_name_(std::move(name)), iter_(source_->cbegin())
    {}
    location(const location&) = default;
    location(location&&)      = default;
    location& operator=(const location&) = default;
    location& operator=(location&&)      = default;
    ~location() = default;

    bool is_ok() const noexcept override {return static_cast<bool>(source_);}

    // this const prohibits codes like `++(loc.iter())`.
    const const_iterator iter()  const noexcept {return iter_;}

    const_iterator begin() const noexcept {return source_->cbegin();}
    const_iterator end()   const noexcept {return source_->cend();}

    // XXX `location::line_num()` used to be implemented using `std::count` to
    // count a number of '\n'. But with a long toml file (typically, 10k lines),
    // it becomes intolerably slow because each time it generates error messages,
    // it counts '\n' from thousands of characters. To workaround it, I decided
    // to introduce `location::line_number_` member variable and synchronize it
    // to the location changes the point to look. So an overload of `iter()`
    // which returns mutable reference is removed and `advance()`, `retrace()`
    // and `reset()` is added.
    void advance(std::size_t n = 1) noexcept
    {
        this->line_number_ += std::count(this->iter_, this->iter_ + n, '\n');
        this->iter_ += n;
        return;
    }
    void retrace(std::size_t n = 1) noexcept
    {
        this->line_number_ -= std::count(this->iter_ - n, this->iter_, '\n');
        this->iter_ -= n;
        return;
    }
    void reset(const_iterator rollback) noexcept
    {
        // since c++11, std::distance works in both ways for random-access
        // iterators and returns a negative value if `first > last`.
        if(0 <= std::distance(rollback, this->iter_)) // rollback < iter
        {
            this->line_number_ -= std::count(rollback, this->iter_, '\n');
        }
        else // iter < rollback [[unlikely]]
        {
            this->line_number_ += std::count(this->iter_, rollback, '\n');
        }
        this->iter_ = rollback;
        return;
    }

    std::string str()  const override {return make_string(1, *this->iter());}
    std::string name() const override {return source_name_;}

    std::string line_num() const override
    {
        return std::to_string(this->line_number_);
    }

    std::string line() const override
    {
        return make_string(this->line_begin(), this->line_end());
    }

    const_iterator line_begin() const noexcept
    {
        using reverse_iterator = std::reverse_iterator<const_iterator>;
        return std::find(reverse_iterator(this->iter()),
                         reverse_iterator(this->begin()), '\n').base();
    }
    const_iterator line_end() const noexcept
    {
        return std::find(this->iter(), this->end(), '\n');
    }

    // location is always points a character. so the size is 1.
    std::size_t size() const noexcept override
    {
        return 1u;
    }
    std::size_t before() const noexcept override
    {
        return std::distance(this->line_begin(), this->iter());
    }
    std::size_t after() const noexcept override
    {
        return std::distance(this->iter(), this->line_end());
    }

    source_ptr const& source() const& noexcept {return source_;}
    source_ptr&&      source() &&     noexcept {return std::move(source_);}

  private:

    source_ptr     source_;
    std::size_t    line_number_;
    std::string    source_name_;
    const_iterator iter_;
};

// region represents a range in a container, which contains a file content.
//
// it contains pointer to the file content and iterator that points the first
// and last location.
template<typename Container>
struct region final : public region_base
{
    using const_iterator = typename Container::const_iterator;
    using source_ptr     = std::shared_ptr<const Container>;

    static_assert(std::is_same<char, typename Container::value_type>::value,"");
    static_assert(std::is_same<std::random_access_iterator_tag,
        typename std::iterator_traits<const_iterator>::iterator_category>::value,
        "container should be randomly accessible");

    // delete default constructor. source_ never be null.
    region() = delete;

    region(const location<Container>& loc)
      : source_(loc.source()), source_name_(loc.name()),
        first_(loc.iter()), last_(loc.iter())
    {}
    region(location<Container>&& loc)
      : source_(loc.source()), source_name_(loc.name()),
        first_(loc.iter()), last_(loc.iter())
    {}

    region(const location<Container>& loc, const_iterator f, const_iterator l)
      : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
    {}
    region(location<Container>&& loc, const_iterator f, const_iterator l)
      : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
    {}

    region(const region&) = default;
    region(region&&)      = default;
    region& operator=(const region&) = default;
    region& operator=(region&&)      = default;
    ~region() = default;

    region& operator+=(const region& other)
    {
        if(this->begin() != other.begin() || this->end() != other.end() ||
           this->last_  != other.first_)
        {
            throw internal_error("invalid region concatenation");
        }
        this->last_ = other.last_;
        return *this;
    }

    bool is_ok() const noexcept override {return static_cast<bool>(source_);}

    std::string str()  const override {return make_string(first_, last_);}
    std::string line() const override
    {
        if(this->contain_newline())
        {
            return make_string(this->line_begin(),
                    std::find(this->line_begin(), this->last(), '\n'));
        }
        return make_string(this->line_begin(), this->line_end());
    }
    std::string line_num() const override
    {
        return std::to_string(1 + std::count(this->begin(), this->first(), '\n'));
    }

    std::size_t size() const noexcept override
    {
        return std::distance(first_, last_);
    }
    std::size_t before() const noexcept override
    {
        return std::distance(this->line_begin(), this->first());
    }
    std::size_t after() const noexcept override
    {
        return std::distance(this->last(), this->line_end());
    }

    bool contain_newline() const noexcept
    {
        return std::find(this->first(), this->last(), '\n') != this->last();
    }

    const_iterator line_begin() const noexcept
    {
        using reverse_iterator = std::reverse_iterator<const_iterator>;
        return std::find(reverse_iterator(this->first()),
                         reverse_iterator(this->begin()), '\n').base();
    }
    const_iterator line_end() const noexcept
    {
        return std::find(this->last(), this->end(), '\n');
    }

    const_iterator begin() const noexcept {return source_->cbegin();}
    const_iterator end()   const noexcept {return source_->cend();}
    const_iterator first() const noexcept {return first_;}
    const_iterator last()  const noexcept {return last_;}

    source_ptr const& source() const& noexcept {return source_;}
    source_ptr&&      source() &&     noexcept {return std::move(source_);}

    std::string name() const override {return source_name_;}

    std::vector<std::string> comments() const override
    {
        // assuming the current region (`*this`) points a value.
        // ```toml
        // a = "value"
        //     ^^^^^^^- this region
        // ```
        using rev_iter = std::reverse_iterator<const_iterator>;

        std::vector<std::string> com{};
        {
            // find comments just before the current region.
            // ```toml
            // # this should be collected.
            // # this also.
            // a = value # not this.
            // ```
            auto iter = this->line_begin(); // points the first character
            while(iter != this->begin())
            {
                iter = std::prev(iter);

                // range [line_start, iter) represents the previous line
                const auto line_start   = std::find(
                        rev_iter(iter), rev_iter(this->begin()), '\n').base();
                const auto comment_found = std::find(line_start, iter, '#');
                if(comment_found == iter)
                {
                    break; // comment not found.
                }

                // exclude the following case.
                // > a = "foo" # comment // <-- this is not a comment for b but a.
                // > b = "current value"
                if(std::all_of(line_start, comment_found,
                        [](const char c) noexcept -> bool {
                            return c == ' ' || c == '\t';
                        }))
                {
                    // unwrap the first '#' by std::next.
                    com.push_back(make_string(std::next(comment_found), iter));
                }
                else
                {
                    break;
                }
                iter = line_start;
            }
        }
        {
            // find comments just after the current region.
            // ```toml
            // # not this.
            // a = value # this one.
            // a = [ # not this (technically difficult)
            //
            // ] # and this.
            // ```
            // The reason why it's difficult is that it requires parsing in the
            // following case.
            // ```toml
            // a = [ 10 # this comment is for `10`. not for `a` but `a[0]`.
            // # ...
            // ] # this is apparently a comment for a.
            //
            // b = [
            // 3.14 ] # there is no way to add a comment to `3.14` currently.
            //
            // c = [
            //   3.14 # do this if you need a comment here.
            // ]
            // ```
            const auto last_comment_found =
                std::find(this->last(), this->line_end(), '#');
            if(last_comment_found != this->line_end()) // '#' found
            {
                com.push_back(make_string(last_comment_found, this->line_end()));
            }
        }
        return com;
    }

  private:

    source_ptr     source_;
    std::string    source_name_;
    const_iterator first_, last_;
};

// to show a better error message.
inline std::string format_underline(const std::string& message,
        const std::vector<std::pair<region_base const*, std::string>>& reg_com,
        const std::vector<std::string>& helps = {})
{
    assert(!reg_com.empty());

    const auto line_num_width = std::max_element(reg_com.begin(), reg_com.end(),
        [](std::pair<region_base const*, std::string> const& lhs,
           std::pair<region_base const*, std::string> const& rhs)
        {
            return lhs.first->line_num().size() < rhs.first->line_num().size();
        }
    )->first->line_num().size();

    std::ostringstream retval;
    retval << message << '\n';

    for(auto iter = reg_com.begin(); iter != reg_com.end(); ++iter)
    {
        // if the filenames are the same, print "..."
        if(iter != reg_com.begin() &&
           std::prev(iter)->first->name() == iter->first->name())
        {
            retval << "\n ...\n";
        }
        else // if filename differs, print " --> filename.toml"
        {
            if(iter != reg_com.begin()) {retval << '\n';}
            retval << " --> " << iter->first->name() << '\n';
        }
        const region_base* const reg = iter->first;
        const std::string&   comment = iter->second;

        retval << ' ' << std::setw(line_num_width) << reg->line_num();
        retval << " | " << reg->line() << '\n';
        retval << make_string(line_num_width + 1, ' ');
        retval << " | " << make_string(reg->before(), ' ');

        if(reg->size() == 1)
        {
            // invalid
            // ^------
            retval << '^';
            retval << make_string(reg->after(), '-');
        }
        else
        {
            // invalid
            // ~~~~~~~
            const auto underline_len = std::min(reg->size(), reg->line().size());
            retval << make_string(underline_len, '~');
        }
        retval << ' ';
        retval << comment;
    }

    if(!helps.empty())
    {
        retval << '\n';
        retval << make_string(line_num_width + 1, ' ');
        retval << " | ";
        for(const auto help : helps)
        {
            retval << "\nHint: ";
            retval << help;
        }
    }
    return retval.str();
}

} // detail
} // toml
#endif// TOML11_REGION_H
<commit_msg>fix: correct the order and remove last CR<commit_after>//     Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_REGION_HPP
#define TOML11_REGION_HPP
#include "exception.hpp"
#include <memory>
#include <vector>
#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <iomanip>
#include <cassert>

namespace toml
{
namespace detail
{

// helper function to avoid std::string(0, 'c') or std::string(iter, iter)
template<typename Iterator>
std::string make_string(Iterator first, Iterator last)
{
    if(first == last) {return "";}
    return std::string(first, last);
}
inline std::string make_string(std::size_t len, char c)
{
    if(len == 0) {return "";}
    return std::string(len, c);
}

// region_base is a base class of location and region that are defined below.
// it will be used to generate better error messages.
struct region_base
{
    region_base() = default;
    virtual ~region_base() = default;
    region_base(const region_base&) = default;
    region_base(region_base&&     ) = default;
    region_base& operator=(const region_base&) = default;
    region_base& operator=(region_base&&     ) = default;

    virtual bool is_ok()           const noexcept {return false;}

    virtual std::string str()      const {return std::string("unknown region");}
    virtual std::string name()     const {return std::string("unknown file");}
    virtual std::string line()     const {return std::string("unknown line");}
    virtual std::string line_num() const {return std::string("?");}

    // length of the region
    virtual std::size_t size()     const noexcept {return 0;}
    // number of characters in the line before the region
    virtual std::size_t before()   const noexcept {return 0;}
    // number of characters in the line after the region
    virtual std::size_t after()    const noexcept {return 0;}

    virtual std::vector<std::string> comments()const {return {};}
    // ```toml
    // # comment_before
    // key = "value" # comment_inline
    // ```
};

// location represents a position in a container, which contains a file content.
// it can be considered as a region that contains only one character.
//
// it contains pointer to the file content and iterator that points the current
// location.
template<typename Container>
struct location final : public region_base
{
    using const_iterator = typename Container::const_iterator;
    using source_ptr     = std::shared_ptr<const Container>;

    static_assert(std::is_same<char, typename Container::value_type>::value,"");
    static_assert(std::is_same<std::random_access_iterator_tag,
        typename std::iterator_traits<const_iterator>::iterator_category>::value,
        "container should be randomly accessible");

    location(std::string name, Container cont)
      : source_(std::make_shared<Container>(std::move(cont))), line_number_(1),
        source_name_(std::move(name)), iter_(source_->cbegin())
    {}
    location(const location&) = default;
    location(location&&)      = default;
    location& operator=(const location&) = default;
    location& operator=(location&&)      = default;
    ~location() = default;

    bool is_ok() const noexcept override {return static_cast<bool>(source_);}

    // this const prohibits codes like `++(loc.iter())`.
    const const_iterator iter()  const noexcept {return iter_;}

    const_iterator begin() const noexcept {return source_->cbegin();}
    const_iterator end()   const noexcept {return source_->cend();}

    // XXX `location::line_num()` used to be implemented using `std::count` to
    // count a number of '\n'. But with a long toml file (typically, 10k lines),
    // it becomes intolerably slow because each time it generates error messages,
    // it counts '\n' from thousands of characters. To workaround it, I decided
    // to introduce `location::line_number_` member variable and synchronize it
    // to the location changes the point to look. So an overload of `iter()`
    // which returns mutable reference is removed and `advance()`, `retrace()`
    // and `reset()` is added.
    void advance(std::size_t n = 1) noexcept
    {
        this->line_number_ += std::count(this->iter_, this->iter_ + n, '\n');
        this->iter_ += n;
        return;
    }
    void retrace(std::size_t n = 1) noexcept
    {
        this->line_number_ -= std::count(this->iter_ - n, this->iter_, '\n');
        this->iter_ -= n;
        return;
    }
    void reset(const_iterator rollback) noexcept
    {
        // since c++11, std::distance works in both ways for random-access
        // iterators and returns a negative value if `first > last`.
        if(0 <= std::distance(rollback, this->iter_)) // rollback < iter
        {
            this->line_number_ -= std::count(rollback, this->iter_, '\n');
        }
        else // iter < rollback [[unlikely]]
        {
            this->line_number_ += std::count(this->iter_, rollback, '\n');
        }
        this->iter_ = rollback;
        return;
    }

    std::string str()  const override {return make_string(1, *this->iter());}
    std::string name() const override {return source_name_;}

    std::string line_num() const override
    {
        return std::to_string(this->line_number_);
    }

    std::string line() const override
    {
        return make_string(this->line_begin(), this->line_end());
    }

    const_iterator line_begin() const noexcept
    {
        using reverse_iterator = std::reverse_iterator<const_iterator>;
        return std::find(reverse_iterator(this->iter()),
                         reverse_iterator(this->begin()), '\n').base();
    }
    const_iterator line_end() const noexcept
    {
        return std::find(this->iter(), this->end(), '\n');
    }

    // location is always points a character. so the size is 1.
    std::size_t size() const noexcept override
    {
        return 1u;
    }
    std::size_t before() const noexcept override
    {
        return std::distance(this->line_begin(), this->iter());
    }
    std::size_t after() const noexcept override
    {
        return std::distance(this->iter(), this->line_end());
    }

    source_ptr const& source() const& noexcept {return source_;}
    source_ptr&&      source() &&     noexcept {return std::move(source_);}

  private:

    source_ptr     source_;
    std::size_t    line_number_;
    std::string    source_name_;
    const_iterator iter_;
};

// region represents a range in a container, which contains a file content.
//
// it contains pointer to the file content and iterator that points the first
// and last location.
template<typename Container>
struct region final : public region_base
{
    using const_iterator = typename Container::const_iterator;
    using source_ptr     = std::shared_ptr<const Container>;

    static_assert(std::is_same<char, typename Container::value_type>::value,"");
    static_assert(std::is_same<std::random_access_iterator_tag,
        typename std::iterator_traits<const_iterator>::iterator_category>::value,
        "container should be randomly accessible");

    // delete default constructor. source_ never be null.
    region() = delete;

    region(const location<Container>& loc)
      : source_(loc.source()), source_name_(loc.name()),
        first_(loc.iter()), last_(loc.iter())
    {}
    region(location<Container>&& loc)
      : source_(loc.source()), source_name_(loc.name()),
        first_(loc.iter()), last_(loc.iter())
    {}

    region(const location<Container>& loc, const_iterator f, const_iterator l)
      : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
    {}
    region(location<Container>&& loc, const_iterator f, const_iterator l)
      : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
    {}

    region(const region&) = default;
    region(region&&)      = default;
    region& operator=(const region&) = default;
    region& operator=(region&&)      = default;
    ~region() = default;

    region& operator+=(const region& other)
    {
        if(this->begin() != other.begin() || this->end() != other.end() ||
           this->last_  != other.first_)
        {
            throw internal_error("invalid region concatenation");
        }
        this->last_ = other.last_;
        return *this;
    }

    bool is_ok() const noexcept override {return static_cast<bool>(source_);}

    std::string str()  const override {return make_string(first_, last_);}
    std::string line() const override
    {
        if(this->contain_newline())
        {
            return make_string(this->line_begin(),
                    std::find(this->line_begin(), this->last(), '\n'));
        }
        return make_string(this->line_begin(), this->line_end());
    }
    std::string line_num() const override
    {
        return std::to_string(1 + std::count(this->begin(), this->first(), '\n'));
    }

    std::size_t size() const noexcept override
    {
        return std::distance(first_, last_);
    }
    std::size_t before() const noexcept override
    {
        return std::distance(this->line_begin(), this->first());
    }
    std::size_t after() const noexcept override
    {
        return std::distance(this->last(), this->line_end());
    }

    bool contain_newline() const noexcept
    {
        return std::find(this->first(), this->last(), '\n') != this->last();
    }

    const_iterator line_begin() const noexcept
    {
        using reverse_iterator = std::reverse_iterator<const_iterator>;
        return std::find(reverse_iterator(this->first()),
                         reverse_iterator(this->begin()), '\n').base();
    }
    const_iterator line_end() const noexcept
    {
        return std::find(this->last(), this->end(), '\n');
    }

    const_iterator begin() const noexcept {return source_->cbegin();}
    const_iterator end()   const noexcept {return source_->cend();}
    const_iterator first() const noexcept {return first_;}
    const_iterator last()  const noexcept {return last_;}

    source_ptr const& source() const& noexcept {return source_;}
    source_ptr&&      source() &&     noexcept {return std::move(source_);}

    std::string name() const override {return source_name_;}

    std::vector<std::string> comments() const override
    {
        // assuming the current region (`*this`) points a value.
        // ```toml
        // a = "value"
        //     ^^^^^^^- this region
        // ```
        using rev_iter = std::reverse_iterator<const_iterator>;

        std::vector<std::string> com{};
        {
            // find comments just before the current region.
            // ```toml
            // # this should be collected.
            // # this also.
            // a = value # not this.
            // ```
            auto iter = this->line_begin(); // points the first character
            while(iter != this->begin())
            {
                iter = std::prev(iter);

                // range [line_start, iter) represents the previous line
                const auto line_start   = std::find(
                        rev_iter(iter), rev_iter(this->begin()), '\n').base();
                const auto comment_found = std::find(line_start, iter, '#');
                if(comment_found == iter)
                {
                    break; // comment not found.
                }

                // exclude the following case.
                // > a = "foo" # comment // <-- this is not a comment for b but a.
                // > b = "current value"
                if(std::all_of(line_start, comment_found,
                        [](const char c) noexcept -> bool {
                            return c == ' ' || c == '\t';
                        }))
                {
                    // unwrap the first '#' by std::next.
                    auto str = make_string(std::next(comment_found), iter);
                    if(str.back() == '\r') {str.pop_back();}
                    com.push_back(std::move(str));
                }
                else
                {
                    break;
                }
                iter = line_start;
            }
        }

        if(com.size() > 1)
        {
            std::reverse(com.begin(), com.end());
        }

        {
            // find comments just after the current region.
            // ```toml
            // # not this.
            // a = value # this one.
            // a = [ # not this (technically difficult)
            //
            // ] # and this.
            // ```
            // The reason why it's difficult is that it requires parsing in the
            // following case.
            // ```toml
            // a = [ 10 # this comment is for `10`. not for `a` but `a[0]`.
            // # ...
            // ] # this is apparently a comment for a.
            //
            // b = [
            // 3.14 ] # there is no way to add a comment to `3.14` currently.
            //
            // c = [
            //   3.14 # do this if you need a comment here.
            // ]
            // ```
            const auto comment_found =
                std::find(this->last(), this->line_end(), '#');
            if(comment_found != this->line_end()) // '#' found
            {
                // unwrap the first '#' by std::next.
                auto str = make_string(std::next(comment_found), this->line_end());
                if(str.back() == '\r') {str.pop_back();}
                com.push_back(std::move(str));
            }
        }
        return com;
    }

  private:

    source_ptr     source_;
    std::string    source_name_;
    const_iterator first_, last_;
};

// to show a better error message.
inline std::string format_underline(const std::string& message,
        const std::vector<std::pair<region_base const*, std::string>>& reg_com,
        const std::vector<std::string>& helps = {})
{
    assert(!reg_com.empty());

    const auto line_num_width = std::max_element(reg_com.begin(), reg_com.end(),
        [](std::pair<region_base const*, std::string> const& lhs,
           std::pair<region_base const*, std::string> const& rhs)
        {
            return lhs.first->line_num().size() < rhs.first->line_num().size();
        }
    )->first->line_num().size();

    std::ostringstream retval;
    retval << message << '\n';

    for(auto iter = reg_com.begin(); iter != reg_com.end(); ++iter)
    {
        // if the filenames are the same, print "..."
        if(iter != reg_com.begin() &&
           std::prev(iter)->first->name() == iter->first->name())
        {
            retval << "\n ...\n";
        }
        else // if filename differs, print " --> filename.toml"
        {
            if(iter != reg_com.begin()) {retval << '\n';}
            retval << " --> " << iter->first->name() << '\n';
        }
        const region_base* const reg = iter->first;
        const std::string&   comment = iter->second;

        retval << ' ' << std::setw(line_num_width) << reg->line_num();
        retval << " | " << reg->line() << '\n';
        retval << make_string(line_num_width + 1, ' ');
        retval << " | " << make_string(reg->before(), ' ');

        if(reg->size() == 1)
        {
            // invalid
            // ^------
            retval << '^';
            retval << make_string(reg->after(), '-');
        }
        else
        {
            // invalid
            // ~~~~~~~
            const auto underline_len = std::min(reg->size(), reg->line().size());
            retval << make_string(underline_len, '~');
        }
        retval << ' ';
        retval << comment;
    }

    if(!helps.empty())
    {
        retval << '\n';
        retval << make_string(line_num_width + 1, ' ');
        retval << " | ";
        for(const auto help : helps)
        {
            retval << "\nHint: ";
            retval << help;
        }
    }
    return retval.str();
}

} // detail
} // toml
#endif// TOML11_REGION_H
<|endoftext|>