/* 
   Copyright (c) Marshall Clow 2013.

   Distributed under the Boost Software License, Version 1.0. (See accompanying
   file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

    For more information, see http://www.boost.org
*/

#include <boost/config.hpp>
#include <boost/algorithm/cxx14/equal.hpp>

#include "iterator_test.hpp"

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

template <typename T>
BOOST_CXX14_CONSTEXPR bool eq ( const T& a, const T& b ) { return a == b; }

template <typename T>
bool never_eq ( const T&, const T& ) { return false; }

int comparison_count = 0;
template <typename T>
bool counting_equals ( const T &a, const T &b ) {
    ++comparison_count;
    return a == b;
    }

namespace ba = boost::algorithm;

void test_equal ()
{
//  Note: The literal values here are tested against directly, careful if you change them:
    int num[] = { 1, 1, 2, 3, 5 };
    const int sz = sizeof (num)/sizeof(num[0]);
    
    
//  Empty sequences are equal to each other, but not to non-empty sequences
    BOOST_CHECK ( ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num), 
                              input_iterator<int *>(num),     input_iterator<int *>(num)));
    BOOST_CHECK ( ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num), 
                              input_iterator<int *>(num),     input_iterator<int *>(num),
                              never_eq<int> ));
    BOOST_CHECK ( ba::equal ( random_access_iterator<int *>(num),     random_access_iterator<int *>(num), 
                              random_access_iterator<int *>(num),     random_access_iterator<int *>(num),
                              never_eq<int> ));
                              
    BOOST_CHECK (!ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num), 
                              input_iterator<int *>(num),     input_iterator<int *>(num + 1)));
    BOOST_CHECK (!ba::equal ( input_iterator<int *>(num + 1), input_iterator<int *>(num + 2), 
                              input_iterator<int *>(num),     input_iterator<int *>(num)));
    BOOST_CHECK (!ba::equal ( random_access_iterator<int *>(num + 1), random_access_iterator<int *>(num + 2), 
                              random_access_iterator<int *>(num),     random_access_iterator<int *>(num)));

//  Single element sequences are equal if they contain the same value
    BOOST_CHECK ( ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num + 1),
                              input_iterator<int *>(num),     input_iterator<int *>(num + 1)));
    BOOST_CHECK ( ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num + 1),
                              input_iterator<int *>(num),     input_iterator<int *>(num + 1),
                              eq<int> ));
    BOOST_CHECK ( ba::equal ( random_access_iterator<int *>(num),     random_access_iterator<int *>(num + 1),
                              random_access_iterator<int *>(num),     random_access_iterator<int *>(num + 1),
                              eq<int> ));
    BOOST_CHECK (!ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num + 1),
                              input_iterator<int *>(num),     input_iterator<int *>(num + 1),
                              never_eq<int> ));
    BOOST_CHECK (!ba::equal ( random_access_iterator<int *>(num),     random_access_iterator<int *>(num + 1),
                              random_access_iterator<int *>(num),     random_access_iterator<int *>(num + 1),
                              never_eq<int> ));

    BOOST_CHECK ( ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num + 1),
                              input_iterator<int *>(num + 1), input_iterator<int *>(num + 2)));
    BOOST_CHECK ( ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num + 1),
                              input_iterator<int *>(num + 1), input_iterator<int *>(num + 2),
                              eq<int> ));

    BOOST_CHECK (!ba::equal ( input_iterator<int *>(num + 2), input_iterator<int *>(num + 3), 
                              input_iterator<int *>(num),     input_iterator<int *>(num + 1)));
    BOOST_CHECK (!ba::equal ( input_iterator<int *>(num + 2), input_iterator<int *>(num + 3),
                              input_iterator<int *>(num),     input_iterator<int *>(num + 1),
                              eq<int> ));
                              
//  Identical long sequences are equal. 
    BOOST_CHECK ( ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num + sz),
                              input_iterator<int *>(num),     input_iterator<int *>(num + sz)));
    BOOST_CHECK ( ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num + sz),
                              input_iterator<int *>(num),     input_iterator<int *>(num + sz),
                              eq<int> ));
    BOOST_CHECK (!ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num + sz),
                              input_iterator<int *>(num),     input_iterator<int *>(num + sz),
                              never_eq<int> ));
    BOOST_CHECK ( ba::equal ( input_iterator<int *>(num),             input_iterator<int *>(num + sz),
                              random_access_iterator<int *>(num),     random_access_iterator<int *>(num + sz),
                              eq<int> ));

//  different sequences are different
    BOOST_CHECK (!ba::equal ( input_iterator<int *>(num + 1), input_iterator<int *>(num + sz),
                              input_iterator<int *>(num),     input_iterator<int *>(num + sz)));
    BOOST_CHECK (!ba::equal ( input_iterator<int *>(num + 1), input_iterator<int *>(num + sz),
                              input_iterator<int *>(num),     input_iterator<int *>(num + sz),
                              eq<int> ));
    BOOST_CHECK (!ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num + sz),
                              input_iterator<int *>(num),     input_iterator<int *>(num + sz - 1)));
    BOOST_CHECK (!ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num + sz),
                              input_iterator<int *>(num),     input_iterator<int *>(num + sz - 1),
                              eq<int> ));

//  When there's a cheap check, bail early
    comparison_count = 0;
    BOOST_CHECK (!ba::equal ( random_access_iterator<int *>(num),     random_access_iterator<int *>(num + sz),
                              random_access_iterator<int *>(num),     random_access_iterator<int *>(num + sz - 1),
                              counting_equals<int> ));
    BOOST_CHECK ( comparison_count == 0 );
//  And when there's not, we can't
    comparison_count = 0;
    BOOST_CHECK (!ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num + sz),
                              input_iterator<int *>(num),     input_iterator<int *>(num + sz - 1),
                              counting_equals<int> ));
    BOOST_CHECK ( comparison_count > 0 );
    
}


BOOST_CXX14_CONSTEXPR bool test_constexpr_equal() {
    int num[] = { 1, 1, 2, 3, 5};
    const int sz = sizeof (num)/sizeof(num[0]);
    bool res = true;
//  Empty sequences are equal to each other
    res = (   ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num), 
                          input_iterator<int *>(num),     input_iterator<int *>(num))
//  Identical long sequences are equal                         
           && ba::equal ( input_iterator<int *>(num),     input_iterator<int *>(num + sz),
                          input_iterator<int *>(num),     input_iterator<int *>(num + sz),
                          eq<int> )
//  Different sequences are different
           && !ba::equal ( input_iterator<int *>(num + 1), input_iterator<int *>(num + sz),
                           input_iterator<int *>(num),     input_iterator<int *>(num + sz))
          );
#ifdef __cpp_lib_array_constexpr // or cpp17 compiler
//  Turn on tests for random_access_iterator, because std functions used in equal are marked constexpr_res
    res = ( res 
//  Empty sequences are equal to each other
           && ba::equal ( random_access_iterator<int *>(num),     random_access_iterator<int *>(num), 
                          random_access_iterator<int *>(num),     random_access_iterator<int *>(num))
//  Identical long sequences are equal                         
           && ba::equal ( random_access_iterator<int *>(num),     random_access_iterator<int *>(num + sz),
                          random_access_iterator<int *>(num),     random_access_iterator<int *>(num + sz),
                          eq<int> )
//  Different sequences are different
           && !ba::equal ( random_access_iterator<int *>(num + 1), random_access_iterator<int *>(num + sz),
                           random_access_iterator<int *>(num),     random_access_iterator<int *>(num + sz))
          );
#endif
    return res;
  }


BOOST_AUTO_TEST_CASE( test_main )
{
  test_equal ();
  BOOST_CXX14_CONSTEXPR bool constexpr_res = test_constexpr_equal ();
  BOOST_CHECK (constexpr_res);
}
