Skip to content

Commit

Permalink
Add Bimap, a basic bi-directional map
Browse files Browse the repository at this point in the history
  • Loading branch information
oblivioncth committed Oct 23, 2024
1 parent 3ab133c commit d3c0d87
Show file tree
Hide file tree
Showing 3 changed files with 381 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ qx_add_component("Core"
qx-algorithm.h
qx-array.h
qx-base85.h
qx-bimap.h
qx-bitarray.h
qx-bytearray.h
qx-char.h
Expand Down Expand Up @@ -74,6 +75,7 @@ qx_add_component("Core"
__private/qx-processwaiter_linux.h
__private/qx-processwaiter_linux.cpp
DOC_ONLY
qx-bimap.dox
qx-regularexpression.dox
qx-bytearray.dox
qx-exclusiveaccess.dox
Expand Down
134 changes: 134 additions & 0 deletions lib/core/include/qx/core/qx-bimap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#ifndef QX_BIMAP_H
#define QX_BIMAP_H

// Qt Includes
#include <QHash>

namespace Qx
{

template<typename Left, typename Right>
class Bimap
{
//-Instance Variables-------------------------------------------------------------------------------------------
private:
QHash<Left, Right*> mL2R;
QHash<Right, Left*> mR2L;

//-Constructor--------------------------------------------------------------------------------------------------
public:
Bimap() {}

Bimap(std::initializer_list<std::pair<Left, Right>> list)
{
reserve(list.size());
for(auto it = list.cbegin(); it != list.cend(); ++it)
insert(it->first, it->second);
}

//-Class Functions-------------------------------------------------------------------------------------------
private:
template<typename AMap, typename BMap, typename V>
void removeCrossReference(AMap& am, BMap& bm, const V& v)
{
Q_ASSERT(&am != &bm);
if(am.conains(v))
bm.remove(*am[v]);
}

template<typename AMap, typename BMap, typename V>
bool remove(AMap& am, BMap& bm, const V& v)
{
if(!am.contains(v))
return false;

bm.remove(*am[v]);
am.remove(v);
return true;
}

//-Instance Functions-------------------------------------------------------------------------------------------
private:
bool existingRelation(const Left& l, const Right& r) { return mL2R.contains(l) && *mL2R[l] == r; }

void removeCrossReferences(const Left& l, const Right& r)
{
// Remove to-be stale relations
removeCrossReference(mL2R, mR2L, l);
removeCrossReference(mR2L, mL2R, r);
}

void addOrUpdateRelation(const Left& l, const Right& r)
{
auto lItr = mL2R.insert(l, nullptr);
auto rItr = mR2L.insert(r, nullptr);
lItr.value() = &rItr.key();
rItr.value() = &lItr.key();
}

public:
void insert(const Left& l, const Right& r)
{
if(existingRelation(l, r))
return;

removeCrossReferences(l, r);
addOrUpdateRelation(l, r);
}

bool containsLeft(const Left& l) { return mL2R.contains(l); }
bool containsRight(const Right& r) { return mR2L.contains(r); }

Right fromLeft(const Left& l)
{
return mL2R.contains(l) ? *mL2R[l] : Right();
}

Right fromLeft(const Left& l, const Right& defaultValue)
{
return mL2R.contains(l) ? *mL2R[l] : defaultValue;
}

Left fromRight(const Right& l)
{
return mR2L.contains(l) ? *mR2L[l] : Left();
}

Left fromRight(const Right& l, const Left& defaultValue)
{
return mR2L.contains(l) ? *mR2L[l] : defaultValue;
}

bool removeLeft(const Left& l) { return remove(mL2R, mR2L, l); }
bool removeRight(const Right& r) { return remove(mR2L, mL2R, r); }

qsizetype size() const { return mL2R.size(); }
qsizetype count() const { return size(); }
bool isEmpty() const { return size() == 0; }
bool empty() const { return isEmpty(); }

qsizetype capacity() const { return mL2R.capacity(); }
void clear() { mL2R.clear(); mR2L.clear(); }
void reserve(qsizetype size) { mL2R.reserve(size); mR2L.reserve(size); }
void squeeze() { mL2R.squeeze(); mR2L.squeeze(); }

QList<Left> lefts() const { return mL2R.keys(); }
QList<Right> rights() const { return mR2L.keys(); }

bool operator==(const Bimap& other) const
{
const auto& oL2R = other.mL2R;
for (auto [l, rp] : mL2R.asKeyValueRange())
if(!oL2R.contains(l) || *rp != *oL2R[l])
return false;

return true;
}

bool operator!=(const Bimap& other) const = default;
};


}

#endif // QX_BIMAP_H
245 changes: 245 additions & 0 deletions lib/core/src/qx-bimap.dox
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
namespace Qx
{
//===============================================================================================================
// Bimap
//===============================================================================================================

/*!
* @class Bimap qx/core/qx-bimap.h
* @ingroup qx-core
*
* @brief The Bimap template class offers a rudimentary bi-directional associative map.
*
* Qx::Bimap<Left, Right> is like QHash<Key, T>, except that instead of Key and Value there is
* Left and Right, meaning that no neither type in any specialization of the container is more
* significant than the other. Lookup of one of the "side's" values using the other is possible
* via fromLeft() and fromRight().
*
* @warning This class is somewhat incomplete as it currently does not have iterators, nor
* a copy constructor/assignment operator implemented.
*/

//-Constructor----------------------------------------------------------------------------------------------
//Public:
/*!
* @fn Bimap<Left, Right>::Bimap()
*
* Creates an empty bimap.
*
* @sa clear().
*/

/*!
* @fn Bimap<Left, Right>::Bimap(std::initializer_list<std::pair<Left, Right>> list)
*
* Creates a bimap with a copy of each of the elements in the initializer list @a list.
*/

//-Class Functions----------------------------------------------------------------------------------------------
//Public:
/*!
* @fn void Bimap<Left, Right>::insert(const Left& l, const Right& r)
*
* Inserts a new relation between the Left value @a l and Right value @a r.
*
* If there is already a relationship for either value, that relationship is
* removed.
*/

/*!
* @fn bool Bimap<Left, Right>::containsLeft(const Left& l)
*
* Returns @c true if the bimap contains a relationship with the Left value @a l; otherwise,
* returns @c false.
*
* @sa containsRight() and count().
*/

/*!
* @fn bool Bimap<Left, Right>::containsRight(const Right& r)
*
* Returns @c true if the bimap contains a relationship with the Right value @a r; otherwise,
* returns @c false.
*
* @sa containsLeft() and count().
*/

/*!
* @fn Right Bimap<Left, Right>::fromLeft(const Left& l)
*
* Returns the Right value associated with Left value @a l.
*
* If the bimap does not contain a relationship with @a l, a default constructed Right
* value is returned.
*
* @sa fromRight().
*/

/*!
* @fn Right Bimap<Left, Right>::fromLeft(const Left& l, const Right& defaultValue)
*
* @overload
*
* Returns the Right value associated with Left value @a l.
*
* If the bimap does not contain a relationship with @a l, @a defaultValue is returned.
*/

/*!
* @fn Left Bimap<Left, Right>::fromRight(const Right& r)
*
* Returns the Left value associated with Right value @a r.
*
* If the bimap does not contain a relationship with @a r, a default constructed Left
* value is returned.
*
* @sa fromLeft().
*/

/*!
* @fn Left Bimap<Left, Right>::fromRight(const Right& r, const Left& defaultValue)
*
* @overload
*
* Returns the Left value associated with Right value @a r.
*
* If the bimap does not contain a relationship with @a r, @a defaultValue is returned.
*/

/*!
* @fn bool Bimap<Left, Right>::removeLeft(const Left& l)
*
* Removes the relationship containing the Left value @a l from the bimap if present and
* returns @c true; otherwise, returns @c false.
*
* @sa removeRight() and clear().
*/

/*!
* @fn bool Bimap<Left, Right>::removeRight(const Right& r)
*
* Removes the relationship containing the Right value @a r from the bimap if present and
* returns @c true; otherwise, returns @c false.
*
* @sa removeLeft() and clear().
*/

/*!
* @fn qsizetype Bimap<Left, Right>::size() const
*
* Returns the number of relations in the bimap.
*
* @sa isEmpty() and count().
*/

/*!
* @fn qsizetype Bimap<Left, Right>::count() const
*
* Same as size().
*/

/*!
* @fn bool Bimap<Left, Right>::isEmpty() const
*
* Returns @c true if the bimap contains no relations; otherwise, returns @c false.
*
* @sa size().
*/

/*!
* @fn bool Bimap<Left, Right>::empty() const
*
* Same as isEmpty().
*/

/*!
* @fn qsizetype Bimap<Left, Right>::capacity() const
*
* Returns the number of buckets in the bimap's internal table.
*
* The sole purpose of this function is to provide a means of fine tuning Bimap's memory
* usage. In general, you will rarely ever need to call this function. If you want to know
* how many items are in the bimap, call size().
*
* @sa reserve() and squeeze().
*/

/*!
* @fn void Bimap<Left, Right>::clear()
*
* Removes all relations from the bimap and frees up all memory used by it.
*
* @sa remove().
*/

/*!
* @fn void Bimap<Left, Right>::reserve()
*
* Ensures that the bimap's internal table has space to store at least @a size items without
* having to grow the table.
*
* This function is useful for code that needs to build a huge bimap and wants to avoid repeated
* reallocation.
*
* In general, you will rarely ever need to call this function. Bimap's internal table
* automatically grows to provide good performance without wasting too much memory.
*
* @sa squeeze() and capacity().
*/

/*!
* @fn void Bimap<Left, Right>::squeeze()
*
* Reduces the size of the Bimap's internal table to save memory.
*
* The sole purpose of this function is to provide a means of fine tuning Bimap's memory usage.
* In general, you will rarely ever need to call this function.
*
* @sa reserve() and capacity().
*/

/*!
* @fn QList<Left> Bimap<Left, Right>::lefts()
*
* Returns a list containing all of the Left values in the bimap, in an arbitrary order.
*
* This function creates a new list, in linear time.
*
* @sa rights() and fromRight().
*/

/*!
* @fn QList<Right> Bimap<Left, Right>::rights()
*
* Returns a list containing all of the Right values in the bimap, in an arbitrary order.
*
* This function creates a new list, in linear time.
*
* @sa lefts() and fromLeft().
*/

/*!
* @fn bool Bimap<Left, Right>::operator==(const Bimap& other) const
*
* Returns @c true if @a other is equal to this bimap; otherwise, returns @c false.
*
* Two bimap's are considered equal if they contain the same (right, left) relationships.
*
* This function requires the Right and Left types to implement operator==().
*
* @sa operator!=().
*/

/*!
* @fn bool Bimap<Left, Right>::operator!=(const Bimap& other) const
*
* Returns @c true if @a other is not equal to this bimap; otherwise, returns @c false.
*
* Two bimap's are considered equal if they contain the same (right, left) relationships.
*
* This function requires the Right and Left types to implement operator==().
*
* @sa operator==().
*/

}

0 comments on commit d3c0d87

Please sign in to comment.