146 lines
5.2 KiB
C++
146 lines
5.2 KiB
C++
|
/************************************************************************************//*!
|
||
|
\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<typename T>
|
||
|
SparseSet<T>::SparseSet()
|
||
|
{
|
||
|
static_assert(std::is_move_assignable_v<T>, "Objects stored by the SparseSet must be move-assignable.");
|
||
|
static_assert(std::is_move_constructible_v<T>, "Objects stored by the SparseSet must be move-constructible.");
|
||
|
}
|
||
|
/*---------------------------------------------------------------------------------*/
|
||
|
/* SparseSet: Usage Functions */
|
||
|
/*---------------------------------------------------------------------------------*/
|
||
|
template<typename T>
|
||
|
T& SparseSet<T>::get(index_type idx)
|
||
|
{
|
||
|
return at(idx);
|
||
|
}
|
||
|
|
||
|
template<typename T>
|
||
|
const T& SparseSet<T>::get(index_type idx) const
|
||
|
{
|
||
|
return at(idx);
|
||
|
}
|
||
|
|
||
|
template<typename T>
|
||
|
bool SparseSet<T>::contains(index_type idx) const
|
||
|
{
|
||
|
if (idx >= sparseArray.size())
|
||
|
return false;
|
||
|
return sparseArray[idx] != INVALID;
|
||
|
}
|
||
|
|
||
|
template<typename T>
|
||
|
void SparseSet<T>::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<typename T>
|
||
|
SparseSet<T>::reference SparseSet<T>::at(index_type idx)
|
||
|
{
|
||
|
return const_cast<reference>(static_cast<const SparseSet<T>&>(*this).at(idx));
|
||
|
}
|
||
|
|
||
|
template<typename T>
|
||
|
SparseSet<T>::const_reference SparseSet<T>::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<typename T>
|
||
|
SparseSet<T>::size_type SparseSet<T>::size() const
|
||
|
{
|
||
|
return denseArray.size();
|
||
|
}
|
||
|
template<typename T>
|
||
|
bool SparseSet<T>::empty() const
|
||
|
{
|
||
|
return denseArray.empty();
|
||
|
}
|
||
|
template<typename T>
|
||
|
void SparseSet<T>::clear()
|
||
|
{
|
||
|
// Default initialize
|
||
|
denseArray.clear();
|
||
|
|
||
|
// Invalidate the sparse array
|
||
|
std::fill(sparseArray.begin(), sparseArray.end(), INVALID);
|
||
|
std::fill(inverseSparseArray.begin(), inverseSparseArray.end(), INVALID);
|
||
|
}
|
||
|
template<typename T>
|
||
|
template<typename ...Args>
|
||
|
SparseSet<T>::reference SparseSet<T>::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>(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<typename T>
|
||
|
inline typename SparseSet<T>::reference SparseSet<T>::operator[](index_type idx)
|
||
|
{
|
||
|
return at(idx);
|
||
|
}
|
||
|
template<typename T>
|
||
|
inline typename SparseSet<T>::const_reference SparseSet<T>::operator[](index_type idx) const
|
||
|
{
|
||
|
return at(idx);
|
||
|
}
|
||
|
}
|