207 lines
5.7 KiB
C++
207 lines
5.7 KiB
C++
#ifndef __PROGTEST__
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cctype>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <list>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
using namespace std;
|
|
#endif /* __PROGTEST__ */
|
|
|
|
// https://fit-wiki.cz/%C5%A1kola/p%C5%99edm%C4%9Bty/bi-pa2/pa2_zkou%C5%A1ka_2023-06-01
|
|
|
|
template <typename T_>
|
|
class CSparseMatrix {
|
|
public:
|
|
// constructor
|
|
static constexpr size_t DEFAULT_TABLE_SIZE = 128;
|
|
static constexpr size_t DEFAULT_BUCKET_SIZE = 16;
|
|
CSparseMatrix( size_t x, size_t y )
|
|
: _x(x), _y(y), _m(DEFAULT_TABLE_SIZE), _l(0), _table(DEFAULT_TABLE_SIZE) {
|
|
}
|
|
|
|
// destructor, operator =, copy constructor if needed
|
|
CSparseMatrix( const CSparseMatrix& oth ) = default;
|
|
CSparseMatrix& operator=( const CSparseMatrix& oth ) = default;
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// operator ( row, col )
|
|
T_& operator()( size_t x, size_t y ) {
|
|
if (x >= _x || y >= _y) throw std::out_of_range("Index error");
|
|
|
|
if (_l > DEFAULT_BUCKET_SIZE) rehash();
|
|
|
|
std::vector< Record >& bucket = _table[ hash(x,y,_m)];
|
|
|
|
auto it = std::find_if(bucket.begin(), bucket.end(), [x, y](const Record& r){ return r._x == x && r._y == y; });
|
|
if (it == bucket.end()) {
|
|
bucket.emplace_back(x,y);
|
|
_l = std::max(_l, bucket.size());
|
|
|
|
return bucket.back()._data;
|
|
}
|
|
|
|
return it->_data;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// contains ( row, col )
|
|
bool contains( size_t x, size_t y ) const {
|
|
if (x >= _x || y >= _y) return false;
|
|
|
|
const std::vector< Record >& bucket = _table[ hash(x,y,_m)];
|
|
|
|
auto it = std::find_if(bucket.begin(), bucket.end(), [x, y](const Record& r){ return r._x == x && r._y == y; });
|
|
return it != bucket.end();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
private:
|
|
struct Record {
|
|
size_t _x, _y;
|
|
T_ _data;
|
|
|
|
Record( size_t x, size_t y )
|
|
: _x(x), _y(y) {}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
static size_t hash( size_t x, size_t y, size_t m ) {
|
|
size_t tmp1 = (x * 4013) % 850973;
|
|
size_t tmp2 = (y * 7079) % 889271;
|
|
return (tmp1 ^ tmp2) % m;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void rehash() {
|
|
std::vector< std::vector< Record > > newData(_m *= 2);
|
|
_l = 0;
|
|
|
|
for (auto& bucket : _table) {
|
|
for ( Record& r : bucket ) {
|
|
size_t newPos = hash(r._x, r._y, _m);
|
|
newData[ newPos ].emplace_back( r._x, r._y );
|
|
newData[ newPos ].back()._data = std::move(r._data);
|
|
|
|
_l = std::max( _l, newData[ newPos ].size() );
|
|
}
|
|
}
|
|
|
|
std::swap(_table, newData);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
size_t _x, _y, _m, _l;
|
|
std::vector< std::vector< Record > > _table;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef __PROGTEST__
|
|
int main ( void )
|
|
{
|
|
CSparseMatrix<int> m1 ( 300, 700 );
|
|
assert ( !m1 . contains ( 0, 0 ) );
|
|
m1 ( 0, 0 ) = 123;
|
|
m1 ( 1, 1 ) = 321;
|
|
m1 ( 2, 2 ) = m1 ( 1, 1 ) + m1 ( 0, 0 );
|
|
assert ( m1 . contains ( 0, 0 ) && m1 ( 0, 0 ) == 123 );
|
|
assert ( m1 . contains ( 1, 1 ) && m1 ( 1, 1 ) == 321 );
|
|
assert ( m1 . contains ( 2, 2 ) && m1 ( 2, 2 ) == 444 );
|
|
|
|
for ( int i = 0; i < 300; ++i )
|
|
for ( int j = 0; j < 300; ++j )
|
|
m1 ( i, j ) = i * j;
|
|
|
|
for ( int i = 0; i < 300; ++i )
|
|
for ( int j = 0; j < 300; ++j )
|
|
assert ( m1 . contains ( i, j ) && m1 ( i, j ) == i * j );
|
|
|
|
try
|
|
{
|
|
m1 ( 300, 300 ) = 666;
|
|
assert ( false );
|
|
}
|
|
catch ( out_of_range & o )
|
|
{
|
|
assert ( o . what() == "Index error"s );
|
|
}
|
|
catch ( ... )
|
|
{
|
|
assert ( false );
|
|
}
|
|
|
|
try
|
|
{
|
|
m1 ( 299, 300 ) = 322;
|
|
}
|
|
catch ( ... )
|
|
{
|
|
assert ( false );
|
|
}
|
|
|
|
CSparseMatrix m2 = m1;
|
|
|
|
for ( int i = 0; i < 300; ++i )
|
|
for ( int j = 0; j < 300; ++j )
|
|
assert ( m2 . contains( i, j ) && m2 ( i, j ) == i * j );
|
|
m1 ( 0, 0 ) = 15;
|
|
|
|
assert ( m2 ( 0, 0 ) == 0 );
|
|
assert ( m1 ( 0, 0 ) == 15 );
|
|
|
|
CSparseMatrix <int> m3 ( 1000, 1000 );
|
|
assert ( !m3 . contains ( 666, 666 ) );
|
|
m3 ( 666, 666 ) = 666;
|
|
assert ( m3 . contains ( 666, 666 ) && m3 ( 666, 666 ) == 666 );
|
|
m3 = m1;
|
|
assert ( !m3 . contains ( 666, 666 ) );
|
|
assert ( m3 . contains ( 0, 0 ) && m3 ( 0, 0 ) == 15 );
|
|
|
|
for ( int i = 1; i < 300; ++i )
|
|
for ( int j = 1; j < 300; ++j )
|
|
assert ( m3 . contains( i, j ) && m3 ( i, j ) == i * j );
|
|
|
|
CSparseMatrix <string> m4 ( 1000000, 1000000 );
|
|
assert ( !m4 . contains ( 0, 0 ) );
|
|
assert ( !m4 . contains ( 999999, 999999 ) );
|
|
m4 ( 0, 0 ) = "P";
|
|
m4 ( 10, 40 ) = "r";
|
|
m4 ( 55, 50000 ) = "o";
|
|
m4 ( 400000, 11 ) = "g";
|
|
m4 ( 999999, 1221 ) = "t";
|
|
m4 ( 121, 169 ) = "e";
|
|
m4 ( 66, 6 ) = "s";
|
|
m4 ( 6, 66 ) = "t";
|
|
assert ( m4 . contains ( 0, 0 ) && m4 ( 0, 0 ) == "P"s );
|
|
assert ( m4 . contains ( 10, 40 ) && m4 ( 10, 40 ) == "r"s );
|
|
assert ( m4 . contains ( 55, 50000 ) && m4 ( 55, 50000 ) == "o"s );
|
|
assert ( m4 . contains ( 400000, 11 ) && m4 ( 400000, 11 ) == "g"s );
|
|
assert ( m4 . contains ( 999999, 1221 ) && m4 ( 999999, 1221 ) == "t"s );
|
|
assert ( m4 . contains ( 121, 169 ) && m4 ( 121, 169 ) == "e"s );
|
|
assert ( m4 . contains ( 66, 6 ) && m4 ( 66, 6 ) == "s"s );
|
|
assert ( m4 . contains ( 6, 66 ) && m4 ( 6, 66 ) == "t"s );
|
|
|
|
std::cout << "success!\n";
|
|
return EXIT_SUCCESS;
|
|
}
|
|
#endif /* __PROGTEST__ */ |