/***************************************************************************
 *
 * specialized.cpp - test exercising 20.4.4 [lib.specialized.algorithms]
 *
 * $Id: 20.specialized.cpp 590052 2007-10-30 12:44:14Z faridz $
 *
 ***************************************************************************
 *
 * Licensed to the Apache Software  Foundation (ASF) under one or more
 * contributor  license agreements.  See  the NOTICE  file distributed
 * with  this  work  for  additional information  regarding  copyright
 * ownership.   The ASF  licenses this  file to  you under  the Apache
 * License, Version  2.0 (the  "License"); you may  not use  this file
 * except in  compliance with the License.   You may obtain  a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the  License is distributed on an  "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY  KIND, either  express or
 * implied.   See  the License  for  the  specific language  governing
 * permissions and limitations under the License.
 *
 * Copyright 2001-2006 Rogue Wave Software.
 * 
 **************************************************************************/

#include <memory>
#include <alg_test.h>
#include <driver.h>

/**************************************************************************/

#ifndef _RWSTD_NO_EXCEPTIONS

struct Y
{
    static int count_;
    static int ctor_;
    static int dtor_;
    static int throw_at_;

    int val_;

    Y (const Y &rhs) : val_ (rhs.val_) {
        ++ctor_;
        if (count_ + 1 == throw_at_)
            throw 0;
        ++count_;
    }

    Y (int val) : val_ (val) {
        ++ctor_;
        if (count_ + 1 == throw_at_)
            throw 0;
        ++count_;
    }

    ~Y () {
        ++dtor_;
        val_ = -2;
        --count_;
    }

private:
    void operator= (const Y&);   // not Assignable
};


int Y::count_;
int Y::ctor_;
int Y::dtor_;
int Y::throw_at_;

/**************************************************************************/

template <class T>
const char* type_name (volatile T*, T*) { return "volatile T*"; }

template <class T>
const char* type_name (const volatile T*, T*) { return "const volatile T*"; }

// defined as ordinary functions (as opposed to templates)
// to avoid tripping up broken compilers on argument deduction
inline const volatile int*
make_iter (const int *beg, const int*, const int*, const volatile int*)
{
    return beg;
}

inline volatile Y*
make_iter (Y *beg, Y*, Y*, volatile Y*)
{
    return beg;
}

/**************************************************************************/

// exercises uninitialized_copy [lib.uninitialized.copy]
// with emphasis on exception safety - the function is to have no effect
// if an exception is thrown
template <class InputIterator, class ForwardIterator>
void test_uninitialized_copy (const InputIterator &dummy,
                              const ForwardIterator*)
{
    const char* const i1name = type_name (dummy, (int*)0);
    const char* const i2name = type_name (ForwardIterator (), (Y*)0);

    rw_info (0, 0, __LINE__, "std::uninitialized_copy(%s, %1$s, %s)",
             i1name, i2name);

    static const int a[] = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
    };

    const unsigned N = sizeof a / sizeof *a;

    // allocate an uninitialized buffer
    Y *buf = _RWSTD_STATIC_CAST (Y*, ::operator new (N * sizeof (Y)));

    for (unsigned i = 0; i != N; ++i) {

        Y::count_ = Y::ctor_ = Y::dtor_ = 0;

        // throw during the copy construction of the i-th elem
        Y::throw_at_ = i;

        try {
            // constructs i elements, the last ctor throws
            const InputIterator first =
                make_iter (a, a, a + i, dummy);
            const InputIterator last =
                make_iter (a + i, a, a + i, first);

            const ForwardIterator result =
                make_iter (buf, buf, buf + i, ForwardIterator ());

            std::uninitialized_copy (first, last, result);

            rw_assert (i == 0, 0, __LINE__,
                       "%zu. expected exception not thrown", i);
        }
        catch (int) {
            // ctor throws an int, this is what's expected
        }
        catch (...) {
            rw_assert (0, 0, __LINE__,
                       "%zu. exception of an unexpected type thrown", i);
        }

        // verify that all constructed elements were destroyed
        rw_assert (Y::count_ == 0, 0, __LINE__,
                   "%zu. %d elements not destroyed", i, Y::count_);

        // verify that the expected number of ctor and dtor calls were made
        rw_assert (Y::ctor_ == Y::throw_at_, 0, __LINE__,
                   "%zu. %d ctor calls expected, got %d",
                   i, Y::throw_at_, Y::ctor_);
        rw_assert (i ? Y::dtor_ + 1 == Y::throw_at_ : true, 0, __LINE__,
                   "%zu. %d dtor calls expected, got %d",
                   i, Y::throw_at_ - 1, Y::dtor_);
    }

    ::operator delete (buf);
}

/**************************************************************************/

// exercises 20.4.4.2 and 3 - uninitialized_fill [lib.uninitialized.fill]
// and uninitialized_fill_n [lib.uninitialized.fill_n] with emphasis on
// exception safety - the function is to have no effect if an exception
// is thrown
template <class ForwardIterator>
void test_uninitialized_fill (const ForwardIterator*, bool test_count)
{
    const char* const itname = type_name (ForwardIterator (), (Y*)0);

    rw_info (0, 0, __LINE__,
             "std::uninitialized_fill%{?}_n%{;}(%s, %{?}size_t%{:}%2$s%{;}, "
             "const int&)", test_count, itname, test_count);

    const unsigned N = 32;

    // allocate an uninitialized buffer
    Y *buf = _RWSTD_STATIC_CAST (Y*, ::operator new (N * sizeof (Y)));

    for (unsigned i = 0; i != N; ++i) {

        // prevent ctor below from throwing
        Y::throw_at_ = -1;

        // create a temporary and reset counters (in that order)
        Y val (i);

        Y::count_ = Y::ctor_ = Y::dtor_ = 0;

        // throw during the copy construction of the i-th elem
        Y::throw_at_ = i;

        try {
            // copy-constructs i elements, the last copy ctor throws
            const ForwardIterator first =
                make_iter (buf, buf, buf + i, ForwardIterator ());

            if (test_count) {
                std::uninitialized_fill_n (first, i, val);
            }
            else {
                const ForwardIterator last =
                    make_iter (buf + i, buf, buf + i, first);

                std::uninitialized_fill (first, last, val);
            }
                
            rw_assert (i == 0, 0, __LINE__,
                       "%zu. expected exception not thrown", i);
        }
        catch (int) {
            // ctor throws an int, this is what's expected
        }
        catch (...) {
            rw_assert (0, 0, __LINE__,
                       "%zu. exception of an unexpected type thrown", i);
        }

        // verify that all constructed elements were destroyed
        rw_assert (Y::count_ == 0, 0, __LINE__,
                   "%zu. %d elements not destroyed", i, Y::count_);

        // verify that the expected number of ctor and dtor calls were made
        rw_assert (Y::ctor_ == Y::throw_at_, 0, __LINE__,
                   "%zu. %d ctor calls expected, got %d",
                   i, Y::throw_at_, Y::ctor_);
        rw_assert (i ? Y::dtor_ + 1 == Y::throw_at_ : true, 0, __LINE__,
                   "%d. %d dtor calls expected, got %d",
                   i, Y::throw_at_ - 1, Y::dtor_);
    }

    ::operator delete (buf);
}

/**************************************************************************/

int opt_copy;
int opt_fill;
int opt_fill_n;

int opt_input_iter;
int opt_fwd_iter;
int opt_bidir_iter;
int opt_rnd_iter;
int opt_volatile_ptr;


template <class InputIterator>
void test_uninitialized_copy (const InputIterator &dummy)
{
    if (-1 < opt_fwd_iter)
        test_uninitialized_copy (dummy, (FwdIter<Y>*)0);
    else
        rw_note (-1 > opt_fwd_iter--, 0, __LINE__,
                 "ForwardIterator tests disabled");

    if (-1 < opt_bidir_iter)
        test_uninitialized_copy (dummy, (BidirIter<Y>*)0);
    else
        rw_note (-1 > opt_bidir_iter--, 0, __LINE__,
                 "BidirectionalIterator tests disabled");

    if (-1 < opt_rnd_iter)
        test_uninitialized_copy (dummy, (RandomAccessIter<Y>*)0);
    else
        rw_note (-1 > opt_rnd_iter--, 0, __LINE__,
                 "RandomAccessIterator tests disabled");

    if (-1 < opt_volatile_ptr) {
        typedef volatile Y* VolatilePointer;
        test_uninitialized_copy (dummy, (VolatilePointer*)0);
    }
    else
        rw_note (-1 > opt_volatile_ptr--, 0, __LINE__,
                 "volatile T* tests disabled");
}

/**************************************************************************/

static int
run_test (int, char**)
{
    typedef const volatile int* ConstVolatilePointer;
    typedef /* */ volatile Y*   VolatilePointer;

    if (-1 < opt_copy) {
        if (-1 < opt_input_iter)
            test_uninitialized_copy (InputIter<int>(0, 0, 0));
        else
            rw_note (-1 > opt_input_iter--, 0, __LINE__,
                     "InputIterator tests disabled");

        if (-1 < opt_fwd_iter)
            test_uninitialized_copy (ConstFwdIter<int>());
        else
            rw_note (-1 > opt_fwd_iter--, 0, __LINE__,
                     "ForwardIterator tests disabled");
    
        if (-1 < opt_bidir_iter)
            test_uninitialized_copy (ConstBidirIter<int>());
        else
            rw_note (-1 > opt_bidir_iter--, 0, __LINE__,
                     "BidirectionalIterator tests disabled");

        if (-1 < opt_rnd_iter)
            test_uninitialized_copy (ConstRandomAccessIter<int>());
        else
            rw_note (-1 > opt_rnd_iter--, 0, __LINE__,
                     "RandomAccessIterator tests disabled");

        if (-1 < opt_volatile_ptr)
            test_uninitialized_copy (ConstVolatilePointer ());
        else
            rw_note (-1 > opt_volatile_ptr--, 0, __LINE__,
                     "volatile T* tests disabled");
    }
    else
        rw_note (0, 0, 0, "tests of std::uninitialized_copy disabled");

    //////////////////////////////////////////////////////////////////

    if (-1 < opt_fill) {
        if (-1 < opt_fwd_iter)
            test_uninitialized_fill ((FwdIter<Y>*)0, false);
        else
            rw_note (-1 > opt_fwd_iter--, 0, __LINE__,
                     "ForwardIterator tests disabled");

        if (-1 < opt_bidir_iter)
            test_uninitialized_fill ((BidirIter<Y>*)0, false);
        else
            rw_note (-1 > opt_bidir_iter--, 0, __LINE__,
                     "BidirectionalIterator tests disabled");

        if (-1 < opt_bidir_iter)
            test_uninitialized_fill ((RandomAccessIter<Y>*)0, false);
        else
            rw_note (-1 > opt_bidir_iter--, 0, __LINE__,
                     "RandomAccessIterator tests disabled");

        if (-1 < opt_volatile_ptr) {
            test_uninitialized_fill ((VolatilePointer*)0, false);
        }
        else
            rw_note (-1 > opt_volatile_ptr++,
                     0, 0, "volatile T* tests disabled");
    }
    else
        rw_note (0, 0, 0, "tests of std::uninitialized_fill disabled");

    //////////////////////////////////////////////////////////////////
    if (-1 < opt_fill) {
        if (-1 < opt_fwd_iter)
            test_uninitialized_fill ((FwdIter<Y>*)0, true);
        else
            rw_note (-1 > opt_fwd_iter--, 0, __LINE__,
                     "ForwardIterator tests disabled");

        if (-1 < opt_bidir_iter)
            test_uninitialized_fill ((BidirIter<Y>*)0, true);
        else
            rw_note (-1 > opt_bidir_iter--, 0, __LINE__,
                     "BidirectionalIterator tests disabled");

        if (-1 < opt_bidir_iter)
            test_uninitialized_fill ((RandomAccessIter<Y>*)0, true);
        else
            rw_note (-1 > opt_bidir_iter--, 0, __LINE__,
                     "RandomAccessIterator tests disabled");

        if (-1 < opt_volatile_ptr) {
            test_uninitialized_fill ((VolatilePointer*)0, true);
        }
        else
            rw_note (-1 > opt_volatile_ptr--, 0, __LINE__,
                     "volatile T* tests disabled");
    }
    else
        rw_note (0, 0, 0, "tests of std::uninitialized_fill disabled");

    return 0;
}

#else   // _RWSTD_NO_EXCEPTIONS

static int
run_test (int, char**)
{
    rw_note (0, 0, 0,
             "exceptions disabled (_RWSTD_NO_EXCEPTIONS #defined), "
             "cannot test");

    return 0;
}

#endif   // _RWSTD_NO_EXCEPTIONS

/**************************************************************************/

int main (int argc, char *argv[])
{
    return rw_test (argc, argv, __FILE__,
                    "lib.specialized.algorithms",
                    0 /* no comment */,
                    run_test,
                    "|-uninitialized_copy~ "
                    "|-uninitialized_fill~ "
                    "|-uninitialized_fill_n~ "
                    "|-InputIterator~ "
                    "|-ForwardIterator~ "
                    "|-BidirectionalIterator~ "
                    "|-RandomAccessIterator~ "
                    "|-volatile-pointer",
                    &opt_copy,
                    &opt_fill,
                    &opt_fill_n,
                    &opt_input_iter,
                    &opt_fwd_iter,
                    &opt_bidir_iter,
                    &opt_rnd_iter,
                    &opt_volatile_ptr);
}
