/************************************************************************************//*! \file SparseSet.h \author Tng Kah Wei, kahwei.tng, 390009620 \par email: kahwei.tng\@digipen.edu \date Jul 30, 2021 \brief Contains the declaration and template implementation of a generic SparseSet. Copyright (C) 2021 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ #pragma once // Primary Header #include "SparseSet.h" namespace SHADE { /*---------------------------------------------------------------------------------*/ /* SparseSet: Constructor */ /*---------------------------------------------------------------------------------*/ template SparseSet::SparseSet() { static_assert(std::is_move_assignable_v, "Objects stored by the SparseSet must be move-assignable."); static_assert(std::is_move_constructible_v, "Objects stored by the SparseSet must be move-constructible."); } /*---------------------------------------------------------------------------------*/ /* SparseSet: Usage Functions */ /*---------------------------------------------------------------------------------*/ template T& SparseSet::get(index_type idx) { return at(idx); } template const T& SparseSet::get(index_type idx) const { return at(idx); } template bool SparseSet::contains(index_type idx) const { if (idx >= sparseArray.size()) return false; return sparseArray[idx] != INVALID; } template void SparseSet::erase(index_type idx) { if (idx >= sparseArray.size() || !contains(idx)) throw std::invalid_argument("An element at this index does not exist!"); // Swap with the last element const int BACK_IDX = denseArray.size() - 1; std::swap(denseArray[sparseArray[idx]], denseArray.back()); denseArray.pop_back(); // Update the sparse set by swapping the indices sparseArray[inverseSparseArray[BACK_IDX]] = sparseArray[idx]; inverseSparseArray[sparseArray[idx]] = inverseSparseArray[BACK_IDX]; // Mark the "removed" values as invalid sparseArray[idx] = INVALID; inverseSparseArray[BACK_IDX] = INVALID; // Reset the dense array value } template SparseSet::reference SparseSet::at(index_type idx) { return const_cast(static_cast&>(*this).at(idx)); } template SparseSet::const_reference SparseSet::at(index_type idx) const { // Range Check if (idx >= sparseArray.size() || !contains(idx)) throw std::out_of_range("An element at this index does not exist!"); return denseArray[sparseArray[idx]]; } template SparseSet::size_type SparseSet::size() const { return denseArray.size(); } template bool SparseSet::empty() const { return denseArray.empty(); } template void SparseSet::clear() { // Default initialize denseArray.clear(); // Invalidate the sparse array std::fill(sparseArray.begin(), sparseArray.end(), INVALID); std::fill(inverseSparseArray.begin(), inverseSparseArray.end(), INVALID); } template template SparseSet::reference SparseSet::insert(index_type idx, Args && ...args) { // We need to resize the array if (idx >= sparseArray.size()) { const int NEW_SIZE = idx + 1; sparseArray.resize(NEW_SIZE, INVALID); inverseSparseArray.resize(NEW_SIZE, INVALID); } else if (contains(idx)) { throw std::invalid_argument("An element at this index already exists!"); } // Insert to the back auto& insertedElem = denseArray.emplace_back(std::forward(args) ...); // Update sparse and inverse sparse arrays const index_type DENSE_IDX = denseArray.size() - 1; sparseArray[idx] = DENSE_IDX; inverseSparseArray[DENSE_IDX] = idx; return insertedElem; } /*---------------------------------------------------------------------------------*/ /* Convenience Operator Functions */ /*---------------------------------------------------------------------------------*/ template inline typename SparseSet::reference SparseSet::operator[](index_type idx) { return at(idx); } template inline typename SparseSet::const_reference SparseSet::operator[](index_type idx) const { return at(idx); } }