// Copyright 2014, ARM Limited
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of ARM Limited nor the names of its contributors may be
//     used to endorse or promote products derived from this software without
//     specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "test-runner.h"

#include "vixl/invalset.h"

namespace vixl {

// This file contains tests for the `InvalSet` and `InvalSetIterator` classes.

#define TEST(name)  TEST_(INVALSET_##name)

typedef ptrdiff_t KeyType;
typedef ptrdiff_t ValType;

// We test with an object for which the key and the value are distinct.
class Obj {
 public:
  Obj() {}
  Obj(KeyType key, ValType val) : key_(key), val_(val) {}
  KeyType key_;
  ValType val_;

  bool operator==(const Obj& other) const {
    return (key_ == other.key_) && (val_ == other.val_);
  }
  bool operator<(const Obj& other) const {
    return (key_ < other.key_) ||
        ((key_ == other.key_) && (val_ < other.val_));
  }
  bool operator<=(const Obj& other) const {
    return (key_ <= other.key_) ||
        ((key_ == other.key_) && (val_ <= other.val_));
  }
  bool operator>(const Obj& other) const {
    return (key_ > other.key_) ||
        ((key_ == other.key_) && (val_ > other.val_));
  }
};

static const unsigned kNPreallocatedElements = 8;
static const KeyType kInvalidKey = PTRDIFF_MAX;
static const size_t kReclaimFrom = 1000;
static const unsigned kReclaimFactor = 10;

typedef InvalSet<Obj,
                 kNPreallocatedElements,
                 KeyType,
                 kInvalidKey,
                 kReclaimFrom,
                 kReclaimFactor> TestSet;

template<>
inline KeyType InvalSet<Obj,
                        kNPreallocatedElements,
                        KeyType,
                        kInvalidKey,
                        kReclaimFrom,
                        kReclaimFactor>::Key(const Obj& obj) {
  return obj.key_;
}
template<>
inline void InvalSet<Obj,
                     kNPreallocatedElements,
                     KeyType,
                     kInvalidKey,
                     kReclaimFrom,
                     kReclaimFactor>::SetKey(Obj* obj, KeyType key) {
  obj->key_ = key;
}


TEST(basic_test) {
  TestSet set;
  VIXL_CHECK(set.empty() && (set.size() == 0));

  for (unsigned i = 0; i < kNPreallocatedElements; i++) {
    set.insert(Obj(i, i));
  }
  VIXL_CHECK(set.size() == kNPreallocatedElements);

  set.insert(Obj(-123, 456));
  set.insert(Obj(2718, 2871828));
  VIXL_CHECK(set.size() == kNPreallocatedElements + 2);
  VIXL_CHECK(set.min_element() == Obj(-123, 456));

  set.erase(Obj(-123, 456));
  VIXL_CHECK(set.min_element_key() == 0);

  set.clear();
  VIXL_CHECK(set.empty() && (set.size() == 0));
}


TEST(valid_element) {
  VIXL_CHECK(TestSet::IsValid(Obj(0, 0)));
  VIXL_CHECK(TestSet::IsValid(Obj(-1, 0)));
  VIXL_CHECK(TestSet::IsValid(Obj(kInvalidKey - 1, 0)));
  VIXL_CHECK(!TestSet::IsValid(Obj(kInvalidKey, 0)));
}


TEST(insert) {
  TestSet set;
  VIXL_CHECK(set.empty() && (set.size() == 0));

  for (unsigned i = 0; i < kNPreallocatedElements; i++) {
    set.insert(Obj(i, i));
  }
  VIXL_CHECK(set.size() == kNPreallocatedElements);
  set.insert(Obj(-123, 1));
  VIXL_CHECK(set.size() == kNPreallocatedElements + 1);
  set.insert(Obj(-123, 2));
  set.insert(Obj(-123, 3));
  VIXL_CHECK(set.size() == kNPreallocatedElements + 3);

  set.clear();
  VIXL_CHECK(set.empty() && (set.size() == 0));
}


TEST(erase) {
  TestSet set;
  VIXL_CHECK(set.empty() && (set.size() == 0));

  // Test with only preallocated elements in the set.
  VIXL_STATIC_ASSERT(kNPreallocatedElements >= 2);
  set.insert(Obj(2718, 0));
  set.erase(Obj(2718, 0));
  VIXL_CHECK(set.empty() && (set.size() == 0));
  set.insert(Obj(2718, 0));
  VIXL_CHECK(set.size() == 1);
  set.insert(Obj(2718, 1));
  VIXL_CHECK(set.size() == 2);
  set.erase(Obj(2718, 0));
  VIXL_CHECK(set.size() == 1);

  // Test with more elements.
  for (unsigned i = 0; i < 100 * kNPreallocatedElements; i++) {
    set.insert(Obj(i * i, i % 30));
    set.insert(Obj(i, -1));
  }
  size_t max_size = set.size();
  set.erase(Obj(100, -1));
  VIXL_CHECK(set.size() == max_size - 1);
  for (size_t i = 2; i <= max_size; i++) {
    set.erase(set.min_element());
    VIXL_CHECK(set.size() == max_size - i);
  }

  VIXL_CHECK(set.empty() && (set.size() == 0));
}


TEST(min) {
  TestSet set;
  VIXL_CHECK(set.empty() && (set.size() == 0));

  // Test with only preallocated elements in the set.
  VIXL_STATIC_ASSERT(kNPreallocatedElements >= 4);
  set.insert(Obj(-1, -1));
  set.insert(Obj(-1, 0));
  set.insert(Obj(0, 0));
  set.insert(Obj(1, 0));
  VIXL_CHECK(set.min_element() == Obj(-1, -1));
  VIXL_CHECK(set.min_element_key() == -1);
  VIXL_CHECK(set.min_element().key_ == set.min_element_key());

  // Test with more elements.
  set.clear();
  int max_index = 100 * kNPreallocatedElements;
  for (int i = 0; i <= max_index; i++) {
    // Insert elements out of order.
    int sign = ((i % 2) == 0) ? -1 : 1;
    set.insert(Obj(sign * i, i));
  }
  VIXL_CHECK(set.min_element() == Obj(-max_index, max_index));
  VIXL_CHECK(set.min_element().key_ == set.min_element_key());

  set.erase(Obj(0, 0));
  VIXL_CHECK(set.min_element() == Obj(-max_index, max_index));
  set.erase(set.min_element());
  VIXL_CHECK(set.min_element() == Obj(-(max_index - 2), max_index - 2));

  set.clear();
  VIXL_CHECK(set.empty() && (set.size() == 0));
}


TEST(iterator) {
  TestSet set;
  VIXL_CHECK(set.empty() && (set.size() == 0));

  // Test with only preallocated elements in the set.
  for (unsigned i = 0; i < kNPreallocatedElements; i++) {
    set.insert(Obj(i, i));
  }

  size_t size = 0;
  for (InvalSetIterator<TestSet> it(&set); !it.Done(); it.Advance()) {
    size++;
  }
  VIXL_CHECK(size == set.size());

  // Test with more elements.
  for (unsigned i = kNPreallocatedElements;
       i < 4 * kNPreallocatedElements;
       i++) {
    set.insert(Obj(i, i));
  }

  size = 0;
  for (InvalSetIterator<TestSet> it(&set); !it.Done(); it.Advance()) {
    size++;
  }
  VIXL_CHECK(size == set.size());

  // Test after an element has been deleted.
  size = 0;
  set.erase(Obj(0, 0));
  for (InvalSetIterator<TestSet> it(&set); !it.Done(); it.Advance()) {
    size++;
  }
  VIXL_CHECK(size == set.size());
}

}  // namespace vixl