/**************************************************************************
 *
 * alg3.cpp - Sample programs for STL generic algorihtms that modify 
 *            their arguments in place.
 *
 * $Id: alg3.cpp 550991 2007-06-26 23:58:07Z sebor $
 *
 ***************************************************************************
 *
 * 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 1994-2006 Rogue Wave Software.
 * 
 **************************************************************************/

#include <algorithm>    // for copy(), find(), generate(), ...
#include <functional>   // for greater
#include <iostream>     // for cout
#include <iterator>     // for ostream_iterator
#include <list>         // for list
#include <string>       // for string
#include <vector>       // for vector

#include <ctype.h>
#include <cstring>

#include <examples.h>


typedef std::ostream_iterator<int, char, std::char_traits<char> >
ostrm_iter_type;


struct iotaGenerator
{
    iotaGenerator (int iv): current (iv) { }

    int operator () () {
        return current++;
    }

private:
    int current;
};


struct RandomInteger
{
    static long seq [16];

    long operator () (long n) const {
        std::random_shuffle (seq, seq + sizeof seq / sizeof *seq);

        const long rnd =
            (seq [0] << 11) | (seq [1] << 8) | (seq [2] << 4) + seq [3];

        return rnd % n;
    }
};

long RandomInteger::seq [16] = {
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf
};


bool isEven (int n)
{
    return 0 == (n % 2);
}

// Illustrate the use of the reverse function.
void reverse_example ()
{
    std::cout << "Illustrate std::reverse() algorithm\n";
    
    // Example 1, reversing a std::string.
    char text[] = "<Rats live on no evil star>";
    
    std::cout << text << '\n';

    std::reverse (text, text + sizeof text - 1);
    
    std::cout << text << '\n';
    
    // Example 2, reversing a std::list.
    std::list<int, std::allocator<int> > iList;
    
    std::generate_n (std::inserter (iList, iList.begin ()),
                     10, iotaGenerator (2));

    std::copy (iList.begin (), iList.end (),
               ostrm_iter_type (std::cout, " "));

    std::cout << '\n';

    std::reverse (iList.begin (), iList.end ());
    std::copy (iList.begin (), iList.end (),
               ostrm_iter_type (std::cout, " "));
    
    std::cout << '\n';
}


// Illustrate the use of the replace function.
void replace_example ()
{
    std::cout << "Illustrate std::replace() algorithm\n";
    
    // Make std::vector 0 1 2 3 4.
    std::vector<int, std::allocator<int> > numbers (11);    
    for (std::size_t i = 0; i < numbers.size (); i++)
        numbers [i] = int (i < 5U ? i : 10U - i);

    std::copy (numbers.begin (), numbers.end (),
               ostrm_iter_type (std::cout, " "));
    
    std::cout << '\n';
    
    // Replace 0 by 2.
    std::replace (numbers.begin (), numbers.end (), 3, 7);
    std::copy (numbers.begin (), numbers.end (),
               ostrm_iter_type (std::cout, " "));
    
    std::cout << '\n';
    
    // Replace even numbers by 9.
    std::replace_if (numbers.begin (),
                     numbers.end (), isEven, 9);
    
    std::copy (numbers.begin (), numbers.end (),
               ostrm_iter_type (std::cout, " "));
    
    std::cout << '\n';
    
    // Copy into a std::list, replastd::cing 9 by 3.
    int aList[] = { 2, 1, 4, 3, 2, 5 };
    int bList[6];
    int cList[6];
    
    std::replace_copy (aList, aList+6, &bList[0], 2, 7);
    std::replace_copy_if (bList, bList+6, &cList[0],
                          std::bind2nd (std::greater<int> (), 3), 8);
    
    std::copy (bList, bList + 6, ostrm_iter_type (std::cout, " "));
    std::cout << '\n';
    
    std::copy (cList, cList + 6, ostrm_iter_type (std::cout, " "));
    std::cout << '\n';
}


// Illustrate the use of the rotate function.
void rotate_example ()
{
    std::cout << "Illustrate std::rotate() algorithm\n";
    
    // Create the std::list 1 2 3 ... 10
    std::list<int, std::allocator<int> > iList;
    std::generate_n (std::inserter (iList, iList.begin ()),
                     10, iotaGenerator (1));
    
    // Find the location of the seven.
    std::list<int, std::allocator<int> >::iterator
        middle = std::find (iList.begin (), iList.end (), 7);
    
    // Now rotate around that location.
    std::rotate (iList.begin (), middle, iList.end ());
    std::copy (iList.begin (), iList.end (),
               ostrm_iter_type (std::cout, " "));
    
    std::cout << '\n';
    
    // Rotate again around the same location.
    std::list<int, std::allocator<int> > cList;
    
    std::rotate_copy (iList.begin (), middle, iList.end (),
                      std::inserter (cList, cList.begin ()));
    std::copy (cList.begin (), cList.end (),
               ostrm_iter_type (std::cout, " "));
    
    std::cout << '\n';
}


// Illustrate the use of the paration function.
void partition_example ()
{
    std::cout << "Illustrate std::partition() algorithm\n";
    
    // First make the std::vector 1 2 3 ... 10.
    std::vector<int, std::allocator<int> > numbers (10);
    
    std::generate (numbers.begin (),
                   numbers.end (), iotaGenerator (1));
    
    // Now put the odd values low, even values high.
    std::vector<int, std::allocator<int> >::iterator
        result = std::partition (numbers.begin (), numbers.end (),
                                 isEven);
    
    std::copy (numbers.begin (), numbers.end (),
               ostrm_iter_type (std::cout, " "));
    
    std::cout << "\nmiddle location " << result - numbers.begin () << '\n';
    
    // Now do a stable partition.
    std::generate (numbers.begin (),
                   numbers.end (), iotaGenerator (1));
    
    std::copy (numbers.begin (), numbers.end (),
               ostrm_iter_type (std::cout, " "));
    
    std::cout << '\n';
}


bool nameCompare (const char *a, const char *b)
{
    return std::strcmp (a, b) <= 0;
}


// Illustrate the use of the next_permutation function.
void permutation_example ()
{
    std::cout << "Illustrate std::next_permutation() algorithm\n";

    // Start with the values 1 2 3 in sequence.
    int start [] = { 1, 2, 3 };
    
    do {
        std::copy  (start, start + 3, ostrm_iter_type (std::cout, " "));
        std::cout << '\n';
    } while  (std::next_permutation (start, start + 3));
    
    const char alpha[] = "Alpha";
    const char beta[]  = "Beta";
    const char gamma[] = "Gamma";
    
    const char* names[] = { alpha, beta, gamma };
    
    typedef std::ostream_iterator<const char*, char, std::char_traits<char> >
        Iter;
    
    do {
        std::copy (names, names + 3, Iter (std::cout, " "));
        std::cout << '\n';
    } while (std::next_permutation (names, names + 3, nameCompare));
    
    std::cout << "Illustrate std::prev_permutation() algorithm\n";

    char word[] = "bela";
    
    do
        std::cout << word << ' ';
    while (std::prev_permutation (word,  &word[4]));
    
    std::cout << '\n';
}


// Illustrate the use of the inplace_merge function.
void inplace_merge_example ()
{
    std::cout << "Illustrate std::inplace_merge() algorithm\n";
    
    // First generate the numbers 0 2 4 6 8 1 3 5 7 9.
    std::vector<int, std::allocator<int> > numbers (10);
    
    for (std::size_t i = 0; i < numbers.size (); i++)
        numbers [i] = int (i < 5U ? 2U * i : 2U * (i - 5U) + 1U);

    std::copy (numbers.begin (), numbers.end (),
               ostrm_iter_type (std::cout, " "));
    std::cout << '\n';
    
    std::vector<int, std::allocator<int> >::iterator
        midvec = std::find (numbers.begin (), numbers.end (), 1);
    
    // Copy them into a std::list.
    std::list<int, std::allocator<int> > numList;
    std::copy (numbers.begin (), numbers.end (),
               std::inserter (numList, numList.begin ()));
    
    std::list<int, std::allocator<int> >::iterator
        midList = std::find (numList.begin (), numList.end (), 1);
    std::copy (numList.begin (), numList.end (),
               ostrm_iter_type (std::cout, " "));
    
    std::cout << '\n';
    
    // Now put them back together.
    std::inplace_merge (numbers.begin (), midvec, numbers.end ());
    std::inplace_merge (numList.begin (), midList, numList.end ());
    std::copy (numList.begin (), numList.end (),
               ostrm_iter_type (std::cout, " "));
    
    std::cout << '\n';
}


// Illustrate the use of the random_shuffle function.
void random_shuffle_example ()
{
    std::cout << "Illustrate std::generate() algorithm\n";

    // First make the std::vector 1 2 3 ... 10.
    std::vector<int, std::allocator<int> > numbers (10);
    
    std::generate (numbers.begin (),
                   numbers.end (), iotaGenerator (1));
    std::copy (numbers.begin (), numbers.end (),
               ostrm_iter_type (std::cout, " "));

    std::cout << "\nIllustrate std::random_shuffle() algorithm\n";

    // Randomly shuffle the elements.
    RandomInteger rnd;
    std::random_shuffle (numbers.begin (), numbers.end (), rnd);
    std::copy (numbers.begin (), numbers.end (),
               ostrm_iter_type (std::cout, " "));
    
    std::cout << '\n';
    
    // Do it again.
    std::random_shuffle (numbers.begin (), numbers.end (), rnd);
    std::copy (numbers.begin (), numbers.end (),
               ostrm_iter_type (std::cout, " "));
    
    std::cout << '\n';
}


int main ()
{
    std::cout << "STL generic algorithms -- in-place algorithms\n";
    
    reverse_example ();
    replace_example ();
    rotate_example ();
    partition_example ();
    permutation_example ();
    inplace_merge_example ();
    random_shuffle_example ();
    
    std::cout << "End of in-place algorithm sample program\n";
    
    return 0;
}
