* speed up hop-to-hop calculations

* better and faster trip clustering: trip tries
* add --write-colors to extract line colors from OSM data
* refactor config parameter names, update default pfaedle.cfg
* add --stats for writing a stats.json file
* add --no-fast-hops, --no-a-star, --no-trie for debugging
* general refactoring
This commit is contained in:
Patrick Brosi 2022-01-03 22:27:59 +01:00
parent f1822868c5
commit 4c29892658
126 changed files with 14576 additions and 12196 deletions

335
src/util/3rdparty/MurmurHash3.cpp vendored Normal file
View file

@ -0,0 +1,335 @@
//-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
// Note - The x86 and x64 versions do _not_ produce the same results, as the
// algorithms are optimized for their respective platforms. You can still
// compile and run any of them on any platform, but your performance with the
// non-native version will be less than optimal.
#include "MurmurHash3.h"
//-----------------------------------------------------------------------------
// Platform-specific functions and macros
// Microsoft Visual Studio
#if defined(_MSC_VER)
#define FORCE_INLINE __forceinline
#include <stdlib.h>
#define ROTL32(x,y) _rotl(x,y)
#define ROTL64(x,y) _rotl64(x,y)
#define BIG_CONSTANT(x) (x)
// Other compilers
#else // defined(_MSC_VER)
#define FORCE_INLINE inline __attribute__((always_inline))
inline uint32_t rotl32 ( uint32_t x, int8_t r )
{
return (x << r) | (x >> (32 - r));
}
inline uint64_t rotl64 ( uint64_t x, int8_t r )
{
return (x << r) | (x >> (64 - r));
}
#define ROTL32(x,y) rotl32(x,y)
#define ROTL64(x,y) rotl64(x,y)
#define BIG_CONSTANT(x) (x##LLU)
#endif // !defined(_MSC_VER)
//-----------------------------------------------------------------------------
// Block read - if your platform needs to do endian-swapping or can only
// handle aligned reads, do the conversion here
FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i )
{
return p[i];
}
FORCE_INLINE uint64_t getblock64 ( const uint64_t * p, int i )
{
return p[i];
}
//-----------------------------------------------------------------------------
// Finalization mix - force all bits of a hash block to avalanche
FORCE_INLINE uint32_t fmix32 ( uint32_t h )
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
//----------
FORCE_INLINE uint64_t fmix64 ( uint64_t k )
{
k ^= k >> 33;
k *= BIG_CONSTANT(0xff51afd7ed558ccd);
k ^= k >> 33;
k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
k ^= k >> 33;
return k;
}
//-----------------------------------------------------------------------------
void MurmurHash3_x86_32 ( const void * key, int len,
uint32_t seed, void * out )
{
const uint8_t * data = (const uint8_t*)key;
const int nblocks = len / 4;
uint32_t h1 = seed;
const uint32_t c1 = 0xcc9e2d51;
const uint32_t c2 = 0x1b873593;
//----------
// body
const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
for(int i = -nblocks; i; i++)
{
uint32_t k1 = getblock32(blocks,i);
k1 *= c1;
k1 = ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1,13);
h1 = h1*5+0xe6546b64;
}
//----------
// tail
const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
uint32_t k1 = 0;
switch(len & 3)
{
case 3: k1 ^= tail[2] << 16;
case 2: k1 ^= tail[1] << 8;
case 1: k1 ^= tail[0];
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
};
//----------
// finalization
h1 ^= len;
h1 = fmix32(h1);
*(uint32_t*)out = h1;
}
//-----------------------------------------------------------------------------
void MurmurHash3_x86_128 ( const void * key, const int len,
uint32_t seed, void * out )
{
const uint8_t * data = (const uint8_t*)key;
const int nblocks = len / 16;
uint32_t h1 = seed;
uint32_t h2 = seed;
uint32_t h3 = seed;
uint32_t h4 = seed;
const uint32_t c1 = 0x239b961b;
const uint32_t c2 = 0xab0e9789;
const uint32_t c3 = 0x38b34ae5;
const uint32_t c4 = 0xa1e38b93;
//----------
// body
const uint32_t * blocks = (const uint32_t *)(data + nblocks*16);
for(int i = -nblocks; i; i++)
{
uint32_t k1 = getblock32(blocks,i*4+0);
uint32_t k2 = getblock32(blocks,i*4+1);
uint32_t k3 = getblock32(blocks,i*4+2);
uint32_t k4 = getblock32(blocks,i*4+3);
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
}
//----------
// tail
const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
uint32_t k1 = 0;
uint32_t k2 = 0;
uint32_t k3 = 0;
uint32_t k4 = 0;
switch(len & 15)
{
case 15: k4 ^= tail[14] << 16;
case 14: k4 ^= tail[13] << 8;
case 13: k4 ^= tail[12] << 0;
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
case 12: k3 ^= tail[11] << 24;
case 11: k3 ^= tail[10] << 16;
case 10: k3 ^= tail[ 9] << 8;
case 9: k3 ^= tail[ 8] << 0;
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
case 8: k2 ^= tail[ 7] << 24;
case 7: k2 ^= tail[ 6] << 16;
case 6: k2 ^= tail[ 5] << 8;
case 5: k2 ^= tail[ 4] << 0;
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
case 4: k1 ^= tail[ 3] << 24;
case 3: k1 ^= tail[ 2] << 16;
case 2: k1 ^= tail[ 1] << 8;
case 1: k1 ^= tail[ 0] << 0;
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
};
//----------
// finalization
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
h1 += h2; h1 += h3; h1 += h4;
h2 += h1; h3 += h1; h4 += h1;
h1 = fmix32(h1);
h2 = fmix32(h2);
h3 = fmix32(h3);
h4 = fmix32(h4);
h1 += h2; h1 += h3; h1 += h4;
h2 += h1; h3 += h1; h4 += h1;
((uint32_t*)out)[0] = h1;
((uint32_t*)out)[1] = h2;
((uint32_t*)out)[2] = h3;
((uint32_t*)out)[3] = h4;
}
//-----------------------------------------------------------------------------
void MurmurHash3_x64_128 ( const void * key, const int len,
const uint32_t seed, void * out )
{
const uint8_t * data = (const uint8_t*)key;
const int nblocks = len / 16;
uint64_t h1 = seed;
uint64_t h2 = seed;
const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);
//----------
// body
const uint64_t * blocks = (const uint64_t *)(data);
for(int i = 0; i < nblocks; i++)
{
uint64_t k1 = getblock64(blocks,i*2+0);
uint64_t k2 = getblock64(blocks,i*2+1);
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
}
//----------
// tail
const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
uint64_t k1 = 0;
uint64_t k2 = 0;
switch(len & 15)
{
case 15: k2 ^= ((uint64_t)tail[14]) << 48;
case 14: k2 ^= ((uint64_t)tail[13]) << 40;
case 13: k2 ^= ((uint64_t)tail[12]) << 32;
case 12: k2 ^= ((uint64_t)tail[11]) << 24;
case 11: k2 ^= ((uint64_t)tail[10]) << 16;
case 10: k2 ^= ((uint64_t)tail[ 9]) << 8;
case 9: k2 ^= ((uint64_t)tail[ 8]) << 0;
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
case 8: k1 ^= ((uint64_t)tail[ 7]) << 56;
case 7: k1 ^= ((uint64_t)tail[ 6]) << 48;
case 6: k1 ^= ((uint64_t)tail[ 5]) << 40;
case 5: k1 ^= ((uint64_t)tail[ 4]) << 32;
case 4: k1 ^= ((uint64_t)tail[ 3]) << 24;
case 3: k1 ^= ((uint64_t)tail[ 2]) << 16;
case 2: k1 ^= ((uint64_t)tail[ 1]) << 8;
case 1: k1 ^= ((uint64_t)tail[ 0]) << 0;
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
};
//----------
// finalization
h1 ^= len; h2 ^= len;
h1 += h2;
h2 += h1;
h1 = fmix64(h1);
h2 = fmix64(h2);
h1 += h2;
h2 += h1;
((uint64_t*)out)[0] = h1;
((uint64_t*)out)[1] = h2;
}
//-----------------------------------------------------------------------------

37
src/util/3rdparty/MurmurHash3.h vendored Normal file
View file

@ -0,0 +1,37 @@
//-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
#ifndef _MURMURHASH3_H_
#define _MURMURHASH3_H_
//-----------------------------------------------------------------------------
// Platform-specific functions and macros
// Microsoft Visual Studio
#if defined(_MSC_VER) && (_MSC_VER < 1600)
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned __int64 uint64_t;
// Other compilers
#else // defined(_MSC_VER)
#include <stdint.h>
#endif // !defined(_MSC_VER)
//-----------------------------------------------------------------------------
void MurmurHash3_x86_32 ( const void * key, int len, uint32_t seed, void * out );
void MurmurHash3_x86_128 ( const void * key, int len, uint32_t seed, void * out );
void MurmurHash3_x64_128 ( const void * key, int len, uint32_t seed, void * out );
//-----------------------------------------------------------------------------
#endif // _MURMURHASH3_H_

454
src/util/3rdparty/dtoa_milo.h vendored Normal file
View file

@ -0,0 +1,454 @@
// based on
// https://github.com/miloyip/dtoa-benchmark/blob/master/src/milo/dtoa_milo.h
#pragma once
#include <assert.h>
#include <math.h>
#include <cmath>
#if defined(_MSC_VER)
#include <intrin.h>
#include "msinttypes/stdint.h"
#else
#include <stdint.h>
#endif
namespace gcc_ints {
__extension__ typedef __int128 int128;
__extension__ typedef unsigned __int128 uint128;
} // namespace gcc_ints
#define UINT64_C2(h, l) \
((static_cast<uint64_t>(h) << 32) | static_cast<uint64_t>(l))
namespace util {
struct DiyFp {
DiyFp() {}
DiyFp(uint64_t f, int e) : f(f), e(e) {}
DiyFp(double d) {
union {
double d;
uint64_t u64;
} u = {d};
int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize;
uint64_t significand = (u.u64 & kDpSignificandMask);
if (biased_e != 0) {
f = significand + kDpHiddenBit;
e = biased_e - kDpExponentBias;
} else {
f = significand;
e = kDpMinExponent + 1;
}
}
DiyFp operator-(const DiyFp& rhs) const {
assert(e == rhs.e);
assert(f >= rhs.f);
return DiyFp(f - rhs.f, e);
}
DiyFp operator*(const DiyFp& rhs) const {
#if defined(_MSC_VER) && defined(_M_AMD64)
uint64_t h;
uint64_t l = _umul128(f, rhs.f, &h);
if (l & (uint64_t(1) << 63)) // rounding
h++;
return DiyFp(h, e + rhs.e + 64);
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && \
defined(__x86_64__)
gcc_ints::uint128 p = static_cast<gcc_ints::uint128>(f) *
static_cast<gcc_ints::uint128>(rhs.f);
uint64_t h = p >> 64;
uint64_t l = static_cast<uint64_t>(p);
if (l & (uint64_t(1) << 63)) // rounding
h++;
return DiyFp(h, e + rhs.e + 64);
#else
const uint64_t M32 = 0xFFFFFFFF;
const uint64_t a = f >> 32;
const uint64_t b = f & M32;
const uint64_t c = rhs.f >> 32;
const uint64_t d = rhs.f & M32;
const uint64_t ac = a * c;
const uint64_t bc = b * c;
const uint64_t ad = a * d;
const uint64_t bd = b * d;
uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
tmp += 1U << 31; /// mult_round
return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
#endif
}
DiyFp Normalize() const {
#if defined(_MSC_VER) && defined(_M_AMD64)
unsigned long index;
_BitScanReverse64(&index, f);
return DiyFp(f << (63 - index), e - (63 - index));
#elif defined(__GNUC__)
int s = __builtin_clzll(f);
return DiyFp(f << s, e - s);
#else
DiyFp res = *this;
while (!(res.f & kDpHiddenBit)) {
res.f <<= 1;
res.e--;
}
res.f <<= (kDiySignificandSize - kDpSignificandSize - 1);
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 1);
return res;
#endif
}
DiyFp NormalizeBoundary() const {
#if defined(_MSC_VER) && defined(_M_AMD64)
unsigned long index;
_BitScanReverse64(&index, f);
return DiyFp(f << (63 - index), e - (63 - index));
#else
DiyFp res = *this;
while (!(res.f & (kDpHiddenBit << 1))) {
res.f <<= 1;
res.e--;
}
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
return res;
#endif
}
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2)
: DiyFp((f << 1) - 1, e - 1);
mi.f <<= mi.e - pl.e;
mi.e = pl.e;
*plus = pl;
*minus = mi;
}
static const int kDiySignificandSize = 64;
static const int kDpSignificandSize = 52;
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
static const int kDpMinExponent = -kDpExponentBias;
static const uint64_t kDpExponentMask = UINT64_C2(0x7FF00000, 0x00000000);
static const uint64_t kDpSignificandMask = UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
static const uint64_t kDpHiddenBit = UINT64_C2(0x00100000, 0x00000000);
uint64_t f;
int e;
};
inline DiyFp GetCachedPower(int e, int* K) {
// 10^-348, 10^-340, ..., 10^340
static const uint64_t kCachedPowers_F[] = {
UINT64_C2(0xfa8fd5a0, 0x081c0288), UINT64_C2(0xbaaee17f, 0xa23ebf76),
UINT64_C2(0x8b16fb20, 0x3055ac76), UINT64_C2(0xcf42894a, 0x5dce35ea),
UINT64_C2(0x9a6bb0aa, 0x55653b2d), UINT64_C2(0xe61acf03, 0x3d1a45df),
UINT64_C2(0xab70fe17, 0xc79ac6ca), UINT64_C2(0xff77b1fc, 0xbebcdc4f),
UINT64_C2(0xbe5691ef, 0x416bd60c), UINT64_C2(0x8dd01fad, 0x907ffc3c),
UINT64_C2(0xd3515c28, 0x31559a83), UINT64_C2(0x9d71ac8f, 0xada6c9b5),
UINT64_C2(0xea9c2277, 0x23ee8bcb), UINT64_C2(0xaecc4991, 0x4078536d),
UINT64_C2(0x823c1279, 0x5db6ce57), UINT64_C2(0xc2109436, 0x4dfb5637),
UINT64_C2(0x9096ea6f, 0x3848984f), UINT64_C2(0xd77485cb, 0x25823ac7),
UINT64_C2(0xa086cfcd, 0x97bf97f4), UINT64_C2(0xef340a98, 0x172aace5),
UINT64_C2(0xb23867fb, 0x2a35b28e), UINT64_C2(0x84c8d4df, 0xd2c63f3b),
UINT64_C2(0xc5dd4427, 0x1ad3cdba), UINT64_C2(0x936b9fce, 0xbb25c996),
UINT64_C2(0xdbac6c24, 0x7d62a584), UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
UINT64_C2(0xf3e2f893, 0xdec3f126), UINT64_C2(0xb5b5ada8, 0xaaff80b8),
UINT64_C2(0x87625f05, 0x6c7c4a8b), UINT64_C2(0xc9bcff60, 0x34c13053),
UINT64_C2(0x964e858c, 0x91ba2655), UINT64_C2(0xdff97724, 0x70297ebd),
UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), UINT64_C2(0xf8a95fcf, 0x88747d94),
UINT64_C2(0xb9447093, 0x8fa89bcf), UINT64_C2(0x8a08f0f8, 0xbf0f156b),
UINT64_C2(0xcdb02555, 0x653131b6), UINT64_C2(0x993fe2c6, 0xd07b7fac),
UINT64_C2(0xe45c10c4, 0x2a2b3b06), UINT64_C2(0xaa242499, 0x697392d3),
UINT64_C2(0xfd87b5f2, 0x8300ca0e), UINT64_C2(0xbce50864, 0x92111aeb),
UINT64_C2(0x8cbccc09, 0x6f5088cc), UINT64_C2(0xd1b71758, 0xe219652c),
UINT64_C2(0x9c400000, 0x00000000), UINT64_C2(0xe8d4a510, 0x00000000),
UINT64_C2(0xad78ebc5, 0xac620000), UINT64_C2(0x813f3978, 0xf8940984),
UINT64_C2(0xc097ce7b, 0xc90715b3), UINT64_C2(0x8f7e32ce, 0x7bea5c70),
UINT64_C2(0xd5d238a4, 0xabe98068), UINT64_C2(0x9f4f2726, 0x179a2245),
UINT64_C2(0xed63a231, 0xd4c4fb27), UINT64_C2(0xb0de6538, 0x8cc8ada8),
UINT64_C2(0x83c7088e, 0x1aab65db), UINT64_C2(0xc45d1df9, 0x42711d9a),
UINT64_C2(0x924d692c, 0xa61be758), UINT64_C2(0xda01ee64, 0x1a708dea),
UINT64_C2(0xa26da399, 0x9aef774a), UINT64_C2(0xf209787b, 0xb47d6b85),
UINT64_C2(0xb454e4a1, 0x79dd1877), UINT64_C2(0x865b8692, 0x5b9bc5c2),
UINT64_C2(0xc83553c5, 0xc8965d3d), UINT64_C2(0x952ab45c, 0xfa97a0b3),
UINT64_C2(0xde469fbd, 0x99a05fe3), UINT64_C2(0xa59bc234, 0xdb398c25),
UINT64_C2(0xf6c69a72, 0xa3989f5c), UINT64_C2(0xb7dcbf53, 0x54e9bece),
UINT64_C2(0x88fcf317, 0xf22241e2), UINT64_C2(0xcc20ce9b, 0xd35c78a5),
UINT64_C2(0x98165af3, 0x7b2153df), UINT64_C2(0xe2a0b5dc, 0x971f303a),
UINT64_C2(0xa8d9d153, 0x5ce3b396), UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
UINT64_C2(0xbb764c4c, 0xa7a44410), UINT64_C2(0x8bab8eef, 0xb6409c1a),
UINT64_C2(0xd01fef10, 0xa657842c), UINT64_C2(0x9b10a4e5, 0xe9913129),
UINT64_C2(0xe7109bfb, 0xa19c0c9d), UINT64_C2(0xac2820d9, 0x623bf429),
UINT64_C2(0x80444b5e, 0x7aa7cf85), UINT64_C2(0xbf21e440, 0x03acdd2d),
UINT64_C2(0x8e679c2f, 0x5e44ff8f), UINT64_C2(0xd433179d, 0x9c8cb841),
UINT64_C2(0x9e19db92, 0xb4e31ba9), UINT64_C2(0xeb96bf6e, 0xbadf77d9),
UINT64_C2(0xaf87023b, 0x9bf0ee6b)};
static const int16_t kCachedPowers_E[] = {
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
-343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77,
-50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216,
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
// int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
double dk = (-61 - e) * 0.30102999566398114 +
347; // dk must be positive, so can do ceiling in positive
int k = static_cast<int>(dk);
if (dk - k > 0.0) k++;
unsigned index = static_cast<unsigned>((k >> 3) + 1);
*K = -(-348 + static_cast<int>(
index << 3)); // decimal exponent no need lookup table
assert(index < sizeof(kCachedPowers_F) / sizeof(kCachedPowers_F[0]));
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
}
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest,
uint64_t ten_kappa, uint64_t wp_w) {
while (rest < wp_w && delta - rest >= ten_kappa &&
(rest + ten_kappa < wp_w || /// closer
wp_w - rest > rest + ten_kappa - wp_w)) {
buffer[len - 1]--;
rest += ten_kappa;
}
}
inline unsigned CountDecimalDigit32(uint32_t n) {
// Simple pure C++ implementation was faster than __builtin_clz version in
// this situation.
if (n < 10) return 1;
if (n < 100) return 2;
if (n < 1000) return 3;
if (n < 10000) return 4;
if (n < 100000) return 5;
if (n < 1000000) return 6;
if (n < 10000000) return 7;
if (n < 100000000) return 8;
if (n < 1000000000) return 9;
return 10;
}
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta,
char* buffer, int* len, int* K) {
static const uint64_t kPow10[] = {1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000};
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
const DiyFp wp_w = Mp - W;
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
uint64_t p2 = Mp.f & (one.f - 1);
int kappa = static_cast<int>(CountDecimalDigit32(p1));
*len = 0;
while (kappa > 0) {
uint32_t d;
switch (kappa) {
case 10:
d = p1 / 1000000000;
p1 %= 1000000000;
break;
case 9:
d = p1 / 100000000;
p1 %= 100000000;
break;
case 8:
d = p1 / 10000000;
p1 %= 10000000;
break;
case 7:
d = p1 / 1000000;
p1 %= 1000000;
break;
case 6:
d = p1 / 100000;
p1 %= 100000;
break;
case 5:
d = p1 / 10000;
p1 %= 10000;
break;
case 4:
d = p1 / 1000;
p1 %= 1000;
break;
case 3:
d = p1 / 100;
p1 %= 100;
break;
case 2:
d = p1 / 10;
p1 %= 10;
break;
case 1:
d = p1;
p1 = 0;
break;
default:
#if defined(_MSC_VER)
__assume(0);
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
__builtin_unreachable();
#else
d = 0;
#endif
}
if (d || *len) buffer[(*len)++] = '0' + static_cast<char>(d);
kappa--;
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
if (tmp <= delta) {
*K += kappa;
GrisuRound(buffer, *len, delta, tmp,
static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
return;
}
}
// kappa = 0
for (;;) {
p2 *= 10;
delta *= 10;
char d = static_cast<char>(p2 >> -one.e);
if (d || *len) buffer[(*len)++] = '0' + d;
p2 &= one.f - 1;
kappa--;
if (p2 < delta) {
*K += kappa;
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]);
return;
}
}
}
inline void Grisu2(double value, char* buffer, int* length, int* K) {
const DiyFp v(value);
DiyFp w_m, w_p;
v.NormalizedBoundaries(&w_m, &w_p);
const DiyFp c_mk = GetCachedPower(w_p.e, K);
const DiyFp W = v.Normalize() * c_mk;
DiyFp Wp = w_p * c_mk;
DiyFp Wm = w_m * c_mk;
Wm.f++;
Wp.f--;
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
}
inline const char* GetDigitsLut() {
static const char cDigitsLut[200] = {
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0',
'7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4',
'1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2',
'2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9',
'3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3',
'7', '3', '8', '3', '9', '4', '0', '4', '1', '4', '2', '4', '3', '4', '4',
'4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '5', '0', '5', '1', '5',
'2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
'6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6',
'7', '6', '8', '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4',
'7', '5', '7', '6', '7', '7', '7', '8', '7', '9', '8', '0', '8', '1', '8',
'2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9',
'9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9',
'7', '9', '8', '9', '9'};
return cDigitsLut;
}
inline void WriteExponent(int K, char* buffer) {
if (K < 0) {
*buffer++ = '-';
K = -K;
}
if (K >= 100) {
*buffer++ = '0' + static_cast<char>(K / 100);
K %= 100;
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
*buffer++ = d[1];
} else if (K >= 10) {
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
*buffer++ = d[1];
} else
*buffer++ = '0' + static_cast<char>(K);
*buffer = '\0';
}
inline void Prettify(char* buffer, int length, int k) {
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
if (length <= kk && kk <= 21) {
// 1234e7 -> 12340000000
for (int i = length; i < kk; i++) buffer[i] = '0';
buffer[kk] = '.';
buffer[kk + 1] = '0';
buffer[kk + 2] = '\0';
} else if (0 < kk && kk <= 21) {
// 1234e-2 -> 12.34
memmove(&buffer[kk + 1], &buffer[kk], length - kk);
buffer[kk] = '.';
buffer[length + 1] = '\0';
} else if (-6 < kk && kk <= 0) {
// 1234e-6 -> 0.001234
const int offset = 2 - kk;
memmove(&buffer[offset], &buffer[0], length);
buffer[0] = '0';
buffer[1] = '.';
for (int i = 2; i < offset; i++) buffer[i] = '0';
buffer[length + offset] = '\0';
} else if (length == 1) {
// 1e30
buffer[1] = 'e';
WriteExponent(kk - 1, &buffer[2]);
} else {
// 1234e30 -> 1.234e33
memmove(&buffer[2], &buffer[1], length - 1);
buffer[1] = '.';
buffer[length + 1] = 'e';
WriteExponent(kk - 1, &buffer[0 + length + 2]);
}
}
inline void dtoa_milo(double value, char* buffer) {
// Not handling NaN and inf
assert(!std::isnan(value));
assert(!std::isinf(value));
if (value == 0) {
buffer[0] = '0';
buffer[1] = '.';
buffer[2] = '0';
buffer[3] = '\0';
} else {
if (value < 0) {
*buffer++ = '-';
value = -value;
}
int length, K;
Grisu2(value, buffer, &length, &K);
Prettify(buffer, length, K);
}
}
} // namespace util

View file

@ -9,18 +9,81 @@
#include <cstring>
#include <chrono>
#include <sstream>
#include <immintrin.h>
#include <iostream>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <map>
#include <thread>
#include "3rdparty/dtoa_milo.h"
#define UNUSED(expr) do { (void)(expr); } while (0)
#define TIME() std::chrono::high_resolution_clock::now()
#define TOOK(t1, t2) (std::chrono::duration_cast<microseconds>(t2 - t1).count() / 1000.0)
#define TOOK(t1, t2) (std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() / 1000.0)
#define T_START(n) auto _tstart_##n = std::chrono::high_resolution_clock::now()
#define T_STOP(n) (std::chrono::duration_cast<microseconds>(std::chrono::high_resolution_clock::now() - _tstart_##n).count() / 1000.0)
#define T_STOP(n) (std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - _tstart_##n).count() / 1000.0)
#define _TEST3(s, o, e) if (!(s o e)) { std::cerr << "\n" << __FILE__ << ":" << __LINE__ << ": Test failed!\n Expected " << #s << " " << #o " " << (e) << ", got " << (s) << std::endl; exit(1);}
#define _TEST2(s, e) _TEST3(s, ==, o)
#define _TEST1(s) _TEST3(static_cast<bool>(s), ==, true)
#define _GET_TEST_MACRO(_1,_2,_3,NAME,...) NAME
#define TEST(...) _GET_TEST_MACRO(__VA_ARGS__, _TEST3, _TEST2, _TEST1, UNUSED)(__VA_ARGS__)
#define TODO(msg) std::cerr << "\n" __FILE__ << ":" << __LINE__ << ": TODO: " << #msg << std::endl;
#if defined(_WIN32)
#include <windows.h>
#include <psapi.h>
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#include <sys/resource.h>
#if defined(__APPLE__) && defined(__MACH__)
#include <mach/mach.h>
#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
#include <fcntl.h>
#include <procfs.h>
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
#include <stdio.h>
#endif
#else
#error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS."
#endif
namespace util {
struct hashPair {
template <class T1, class T2>
size_t operator()(const std::pair<T1, T2>& p) const {
auto h1 = std::hash<T1>{}(p.first);
auto h2 = std::hash<T2>{}(p.second);
return h1 ^ h2;
}
};
template<typename Key, typename Val, Val Def>
class SparseMatrix {
public:
Val get(const Key& x, const Key& y) const {
auto a = _m.find(std::pair<Key, Key>(x, y));
if (a == _m.end()) return Def;
return a->second;
}
void set(Key x, Key y, Val v) {
_m[std::pair<Key, Key>(x, y)] = v;
}
const std::map<std::pair<Key, Key>, Val>& vals() const {
return _m;
}
private:
std::map<std::pair<Key, Key>, Val> _m;
};
// cached first 10 powers of 10
static int pow10[10] = {
1, 10, 100, 1000, 10000,
@ -28,7 +91,7 @@ static int pow10[10] = {
// _____________________________________________________________________________
inline uint64_t factorial(uint64_t n) {
if (n == 1) return n;
if (n < 2) return 1;
return n * factorial(n - 1);
}
@ -89,6 +152,82 @@ inline double atof(const char* p, uint8_t mn) {
// _____________________________________________________________________________
inline double atof(const char* p) { return atof(p, 38); }
// _____________________________________________________________________________
template <typename V>
int merge(V* lst, V* tmpLst, size_t l, size_t m, size_t r) {
size_t ret = 0;
size_t lp = l;
size_t rp = m;
size_t outp = l;
while (lp < m && rp < r + 1) {
if (lst[lp] <= lst[rp]) {
// if left element is smaller or equal, add it to return list,
// increase left pointer
tmpLst[outp] = lst[lp];
lp++;
} else {
// if left element is bigger, add the right element, add it to ret,
// increase right pointer
tmpLst[outp] = lst[rp];
rp++;
// if the left element was bigger, everything to the right in the
// left list is also bigger, and all these m - i elements were
// initially in the wrong order! Count these inversions.
ret += m - lp;
}
outp++;
}
// fill in remaining values
if (lp < m) std::memcpy(tmpLst + outp, lst + lp, (m - lp) * sizeof(V));
if (rp <= r) std::memcpy(tmpLst + outp, lst + rp, ((r + 1) - rp) * sizeof(V));
// copy to output
std::memcpy(lst + l, tmpLst + l, ((r + 1) - l) * sizeof(V));
return ret;
}
// _____________________________________________________________________________
template <typename V>
size_t mergeInvCount(V* lst, V* tmpLst, size_t l, size_t r) {
size_t ret = 0;
if (l < r) {
size_t m = (r + l) / 2;
ret += mergeInvCount(lst, tmpLst, l, m);
ret += mergeInvCount(lst, tmpLst, m + 1, r);
ret += merge(lst, tmpLst, l, m + 1, r);
}
return ret;
}
// _____________________________________________________________________________
template <typename V>
size_t inversions(const std::vector<V>& v) {
if (v.size() < 2) return 0; // no inversions possible
// unroll some simple cases
if (v.size() == 2) return v[1] < v[0];
if (v.size() == 3) return (v[0] > v[1]) + (v[0] > v[2]) + (v[1] > v[2]);
auto tmpLst = new V[v.size()];
auto lst = new V[v.size()];
for (size_t i = 0; i < v.size(); i++) lst[i] = v[i];
size_t ret = mergeInvCount<V>(lst, tmpLst, 0, v.size() - 1);
delete[] tmpLst;
delete[] lst;
return ret;
}
// _____________________________________________________________________________
inline std::string getHomeDir() {
// parse implicit paths
@ -115,6 +254,29 @@ inline std::string getHomeDir() {
return ret;
}
// _____________________________________________________________________________
inline char* readableSize(double size, char* buf) {
int i = 0;
const char* units[] = {"B", "kB", "MB", "GB", "TB", "PB"};
while (size > 1024 && i < 5) {
size /= 1024;
i++;
}
sprintf(buf, "%.*f %s", i, size, units[i]);
return buf;
}
// _____________________________________________________________________________
inline std::string readableSize(double size) {
char buffer[30];
return readableSize(size, buffer);
}
// _____________________________________________________________________________
inline float f_rsqrt(float x) {
return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x)));
}
// _____________________________________________________________________________
inline std::string getTmpDir() {
// first, check if an env variable is set
@ -131,6 +293,150 @@ inline std::string getTmpDir() {
return getHomeDir();
}
// _____________________________________________________________________________
class approx {
public:
explicit approx(double magnitude)
: _epsilon{std::numeric_limits<float>::epsilon() * 100},
_magnitude{magnitude} {}
friend bool operator==(double lhs, approx const& rhs) {
return std::abs(lhs - rhs._magnitude) < rhs._epsilon;
}
friend bool operator==(approx const& lhs, double rhs) {
return operator==(rhs, lhs);
}
friend bool operator!=(double lhs, approx const& rhs) {
return !operator==(lhs, rhs);
}
friend bool operator!=(approx const& lhs, double rhs) {
return !operator==(rhs, lhs);
}
friend bool operator<=(double lhs, approx const& rhs) {
return lhs < rhs._magnitude || lhs == rhs;
}
friend bool operator<=(approx const& lhs, double rhs) {
return lhs._magnitude < rhs || lhs == rhs;
}
friend bool operator>=(double lhs, approx const& rhs) {
return lhs > rhs._magnitude || lhs == rhs;
}
friend bool operator>=(approx const& lhs, double rhs) {
return lhs._magnitude > rhs || lhs == rhs;
}
friend std::ostream& operator<< (std::ostream &out, const approx &a) {
out << "~" << a._magnitude;
return out;
}
private:
double _epsilon;
double _magnitude;
};
/*
* Author: David Robert Nadeau
* Site: http://NadeauSoftware.com/
* License: Creative Commons Attribution 3.0 Unported License
* http://creativecommons.org/licenses/by/3.0/deed.en_US
*/
/**
* Returns the peak (maximum so far) resident set size (physical
* memory use) measured in bytes, or zero if the value cannot be
* determined on this OS.
*/
// _____________________________________________________________________________
inline size_t getPeakRSS() {
#if defined(_WIN32)
/* Windows -------------------------------------------------- */
PROCESS_MEMORY_COUNTERS info;
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
return (size_t)info.PeakWorkingSetSize;
#elif (defined(_AIX) || defined(__TOS__AIX__)) || \
(defined(__sun__) || defined(__sun) || \
defined(sun) && (defined(__SVR4) || defined(__svr4__)))
/* AIX and Solaris ------------------------------------------ */
struct psinfo psinfo;
int fd = -1;
if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1)
return (size_t)0L; /* Can't open? */
if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) {
close(fd);
return (size_t)0L; /* Can't read? */
}
close(fd);
return (size_t)(psinfo.pr_rssize * 1024L);
#elif defined(__unix__) || defined(__unix) || defined(unix) || \
(defined(__APPLE__) && defined(__MACH__))
/* BSD, Linux, and OSX -------------------------------------- */
struct rusage rusage;
getrusage(RUSAGE_SELF, &rusage);
#if defined(__APPLE__) && defined(__MACH__)
return (size_t)rusage.ru_maxrss;
#else
return (size_t)(rusage.ru_maxrss * 1024L);
#endif
#else
/* Unknown OS ----------------------------------------------- */
return (size_t)0L; /* Unsupported. */
#endif
}
/*
* Returns the current resident set size (physical memory use) measured
* in bytes, or zero if the value cannot be determined on this OS.
*/
// _____________________________________________________________________________
inline size_t getCurrentRSS() {
#if defined(_WIN32)
/* Windows -------------------------------------------------- */
PROCESS_MEMORY_COUNTERS info;
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
return (size_t)info.WorkingSetSize;
#elif defined(__APPLE__) && defined(__MACH__)
/* OSX ------------------------------------------------------ */
struct mach_task_basic_info info;
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info,
&infoCount) != KERN_SUCCESS)
return (size_t)0L; /* Can't access? */
return (size_t)info.resident_size;
#elif defined(__linux__) || defined(__linux) || defined(linux) || \
defined(__gnu_linux__)
/* Linux ---------------------------------------------------- */
long rss = 0L;
FILE* fp = NULL;
if ((fp = fopen("/proc/self/statm", "r")) == NULL)
return (size_t)0L; /* Can't open? */
if (fscanf(fp, "%*s%ld", &rss) != 1) {
fclose(fp);
return (size_t)0L; /* Can't read? */
}
fclose(fp);
return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE);
#else
/* AIX, BSD, Solaris, and Unknown OS ------------------------ */
return (size_t)0L; /* Unsupported. */
#endif
}
} // namespace util
#endif // UTIL_MISC_H_

39
src/util/PriorityQueue.h Normal file
View file

@ -0,0 +1,39 @@
// Copyright 2019, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_PRIORITYQUEUE_H_
#define UTIL_PRIORITYQUEUE_H_
#include<iomanip>
#include<queue>
#include<iostream>
namespace util {
template <typename K, typename V>
class PriorityQueue {
struct _ByFirst {
bool operator()(const std::pair<K, V>& a, const std::pair<K, V>& b) {
return a.first > b.first;
}
};
public:
PriorityQueue() : _last(std::numeric_limits<K>::lowest()) {}
void push(K k, const V& v);
const K topKey() ;
const V& topVal() ;
void pop();
bool empty() const;
private:
K _last;
std::priority_queue<std::pair<K, V>, std::vector<std::pair<K, V>>, _ByFirst>
_pq;
};
#include "util/PriorityQueue.tpp"
} // namespace util
#endif

View file

@ -0,0 +1,41 @@
// Copyright 2019, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
// _____________________________________________________________________________
template <typename K, typename V>
void PriorityQueue<K, V>::push(K key, const V& val) {
// if (_last - key > 0) key = _last;
if (key < _last) {
std::cout << std::setprecision(10) << _last << " vs " << key << std::endl;
key = _last;
}
// assert(key >= _last);
_pq.emplace(std::pair<K, V>(key, val));
}
// _____________________________________________________________________________
template <typename K, typename V>
const K PriorityQueue<K, V>::topKey() {
_last = _pq.top().first;
return _pq.top().first;
}
// _____________________________________________________________________________
template <typename K, typename V>
const V& PriorityQueue<K, V>::topVal() {
_last = _pq.top().first;
return _pq.top().second;
}
// _____________________________________________________________________________
template <typename K, typename V>
bool PriorityQueue<K, V>::empty() const {
return _pq.empty();
}
// _____________________________________________________________________________
template <typename K, typename V>
void PriorityQueue<K, V>::pop() {
_pq.pop();
}

View file

@ -6,11 +6,17 @@
#define UTIL_STRING_H_
#include <algorithm>
#include <cassert>
#include <codecvt>
#include <cstring>
#include <exception>
#include <iomanip>
#include <iostream>
#include <locale>
#include <sstream>
#include <string>
#include <vector>
#include <set>
namespace util {
@ -169,7 +175,8 @@ inline size_t editDist(const std::string& s1, const std::string& s2) {
}
// _____________________________________________________________________________
inline size_t prefixEditDist(const std::string& prefix, const std::string& s,
template <class String>
inline size_t prefixEditDist(const String& prefix, const String& s,
size_t deltaMax) {
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++
size_t len1 = prefix.size();
@ -200,10 +207,45 @@ inline size_t prefixEditDist(const std::string& prefix, const std::string& s,
}
// _____________________________________________________________________________
inline size_t prefixEditDist(const std::string& prefix, const std::string& s) {
template <class String>
inline size_t prefixEditDist(const String& prefix, const String& s) {
return prefixEditDist(prefix, s, s.size());
}
// _____________________________________________________________________________
inline size_t prefixEditDist(const char* prefix, const char* s) {
return prefixEditDist<std::string>(std::string(prefix), std::string(s));
}
// _____________________________________________________________________________
inline size_t prefixEditDist(const char* prefix, const char* s,
size_t deltaMax) {
return prefixEditDist<std::string>(std::string(prefix), std::string(s),
deltaMax);
}
// _____________________________________________________________________________
inline size_t prefixEditDist(const char* prefix, const std::string& s) {
return prefixEditDist<std::string>(std::string(prefix), s);
}
// _____________________________________________________________________________
inline size_t prefixEditDist(const char* prefix, const std::string& s,
size_t deltaMax) {
return prefixEditDist<std::string>(std::string(prefix), s, deltaMax);
}
// _____________________________________________________________________________
inline size_t prefixEditDist(const std::string& prefix, const char* s) {
return prefixEditDist<std::string>(prefix, std::string(s));
}
// _____________________________________________________________________________
inline size_t prefixEditDist(const std::string& prefix, const char* s,
size_t deltaMax) {
return prefixEditDist<std::string>(prefix, std::string(s), deltaMax);
}
// _____________________________________________________________________________
inline std::string toUpper(std::string str) {
std::transform(str.begin(), str.end(), str.begin(), toupper);
@ -251,10 +293,66 @@ inline std::string normalizeWhiteSpace(const std::string& input) {
}
// _____________________________________________________________________________
template <typename T>
inline std::wstring toWStr(const std::string& str) {
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(str);
}
// _____________________________________________________________________________
inline std::string toNStr(const std::wstring& wstr) {
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.to_bytes(wstr);
}
// _____________________________________________________________________________
inline std::vector<std::string> tokenize(const std::string& str) {
std::vector<std::string> ret;
std::wstring wStr = toWStr(str);
std::wstring cur;
for (size_t i = 0; i < wStr.size(); ++i) {
if (!std::iswalnum(wStr[i])) {
if (cur.size()) ret.push_back(toNStr(cur));
cur = L"";
continue;
}
cur += wStr[i];
}
if (cur.size()) ret.push_back(toNStr(cur));
return ret;
}
// _____________________________________________________________________________
inline double jaccardSimi(const std::string& a,
const std::string& b) {
std::set<std::string> sa, sb;
auto toksA = tokenize(a);
auto toksB = tokenize(b);
// 0 if both are empty
if (toksA.size() == 0 && toksB.size() == 0) return 0;
sa.insert(toksA.begin(), toksA.end());
sb.insert(toksB.begin(), toksB.end());
std::set<std::string> isect;
std::set_intersection(sa.begin(), sa.end(), sb.begin(), sb.end(),
std::inserter(isect, isect.begin()));
double sInter = isect.size();
double s1 = sa.size();
double s2 = sb.size();
return sInter / (s1 + s2 - sInter);
}
// _____________________________________________________________________________
template <class T>
inline std::string implode(const std::vector<T>& vec, const char* del) {
return implode(vec.begin(), vec.end(), del);
}
}
} // namespace util
#endif // UTIL_STRING_H_

View file

@ -64,7 +64,7 @@ const PolyLine<T>& BezierCurve<T>::render(double d) {
}
// _____________________________________________________________________________
double CubicPolynom::valueAt(double atx) const {
inline double CubicPolynom::valueAt(double atx) const {
double dx = atx - x;
return a + b * dx + c * dx * dx + d * dx * dx * dx;
}

View file

@ -0,0 +1,41 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_GEO_CIRCULARSEGMENT_H_
#define UTIL_GEO_CIRCULARSEGMENT_H_
#include <vector>
#include "util/geo/Geo.h"
#include "util/geo/PolyLine.h"
namespace util {
namespace geo {
/**
* Circular segment
*/
template <typename T>
class CircularSegment {
public:
CircularSegment(const Point<T>& a, double ang, const Point<T>& c);
const PolyLine<T>& render(double d);
private:
// store the rendered polyline for quicker access
PolyLine<T> _rendered;
const Point<T>& _a, _c;
double _renderD;
double _ang, _rad, _s, _initAng;
Point<T> valueAt(double t) const;
};
#include "util/geo/CircularSegment.tpp"
}
}
#endif // UTIL_GEO_BEZIERCURVE_H_

View file

@ -0,0 +1,51 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
// _____________________________________________________________________________
template <typename T>
CircularSegment<T>::CircularSegment(const Point<T>& a, double ang,
const Point<T>& c) : _a(a), _c(c), _renderD(0), _ang(ang)
{
_rad = dist(a, c);
_s = fabs(_ang * _rad);
_initAng = angBetween(c, a);
}
// _____________________________________________________________________________
template <typename T>
Point<T> CircularSegment<T>::valueAt(double ang) const {
double xPos = _c.getX() + _rad * cos(ang);
double yPos = _c.getY() + _rad * sin(ang);
return Point<T>(xPos, yPos);
}
// _____________________________________________________________________________
template <typename T>
const PolyLine<T>& CircularSegment<T>::render(double d) {
assert(d > 0);
if (fabs(d - _renderD) < 0.001) return _rendered;
_renderD = d;
if (_s == 0) {
_rendered << _a << _a;
return _rendered;
}
_rendered.empty();
double n = _s / d, dt = 1 / n, t = 0;
bool cancel = false;
while (true) {
_rendered << valueAt(_initAng + t * _ang);
t += dt;
if (cancel) break;
if (t > 1) {
t = 1;
cancel = true;
}
}
return _rendered;
}

View file

@ -51,6 +51,11 @@ typedef Polygon<int> IPolygon;
const static double EPSILON = 0.00001;
const static double RAD = 0.017453292519943295; // PI/180
const static double IRAD = 180.0 / M_PI; // 180 / PI
const static double AVERAGING_STEP = 20;
const static double M_PER_DEG = 111319.4;
// _____________________________________________________________________________
template <typename T>
@ -203,6 +208,14 @@ inline Polygon<T> move(Polygon<T> geo, double x, double y) {
return geo;
}
// _____________________________________________________________________________
template <typename T>
inline Box<T> move(Box<T> geo, double x, double y) {
geo.setLowerLeft(move(geo.getLowerLeft(), x, y));
geo.setUpperRight(move(geo.getUpperRight(), x, y));
return geo;
}
// _____________________________________________________________________________
template <template <typename> class Geometry, typename T>
inline std::vector<Geometry<T>> move(std::vector<Geometry<T>> multigeo,
@ -356,8 +369,9 @@ inline bool contains(const Polygon<T>& polyC, const Polygon<T>& poly) {
}
// also check the last hop
if (!contains(LineSegment<T>(polyC.getOuter().back(), polyC.getOuter().front()),
poly))
if (!contains(
LineSegment<T>(polyC.getOuter().back(), polyC.getOuter().front()),
poly))
return false;
return true;
@ -627,6 +641,34 @@ inline bool intersects(const Box<T>& b, const Point<T>& p) {
return intersects(p, b);
}
// _____________________________________________________________________________
template <template <typename> class GeometryA,
template <typename> class GeometryB, typename T>
inline bool intersects(const std::vector<GeometryA<T>>& multigeom,
const GeometryB<T>& b) {
for (const auto& geom : multigeom)
if (intersects(geom, b)) return true;
return false;
}
// _____________________________________________________________________________
template <template <typename> class GeometryA,
template <typename> class GeometryB, typename T>
inline bool intersects(const GeometryB<T>& b,
const std::vector<GeometryA<T>>& multigeom) {
return intersects(multigeom, b);
}
// _____________________________________________________________________________
template <template <typename> class GeometryA,
template <typename> class GeometryB, typename T>
inline bool intersects(const std::vector<GeometryA<T>>& multigeomA,
const std::vector<GeometryA<T>>& multigeomB) {
for (const auto& geom : multigeomA)
if (intersects(geom, multigeomB)) return true;
return false;
}
// _____________________________________________________________________________
template <typename T>
inline Point<T> intersection(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x,
@ -777,6 +819,36 @@ inline double dist(const Line<T>& la, const Line<T>& lb) {
return d;
}
// _____________________________________________________________________________
template <template <typename> class GeometryA,
template <typename> class GeometryB, typename T>
inline double dist(const std::vector<GeometryA<T>>& multigeom,
const GeometryB<T>& b) {
double d = std::numeric_limits<double>::infinity();
for (const auto& geom : multigeom)
if (dist(geom, b) < d) d = dist(geom, b);
return d;
}
// _____________________________________________________________________________
template <template <typename> class GeometryA,
template <typename> class GeometryB, typename T>
inline double dist(const GeometryB<T>& b,
const std::vector<GeometryA<T>>& multigeom) {
return dist(multigeom, b);
}
// _____________________________________________________________________________
template <template <typename> class GeometryA,
template <typename> class GeometryB, typename T>
inline double dist(const std::vector<GeometryA<T>>& multigeomA,
const std::vector<GeometryB<T>>& multigeomB) {
double d = std::numeric_limits<double>::infinity();
for (const auto& geom : multigeomB)
if (dist(geom, multigeomA) < d) d = dist(geom, multigeomA);
return d;
}
// _____________________________________________________________________________
inline double innerProd(double x1, double y1, double x2, double y2, double x3,
double y3) {
@ -788,7 +860,7 @@ inline double innerProd(double x1, double y1, double x2, double y2, double x3,
double m13 = sqrt(dx31 * dx31 + dy31 * dy31);
double theta = acos(std::min((dx21 * dx31 + dy21 * dy31) / (m12 * m13), 1.0));
return theta * (180 / M_PI);
return theta * IRAD;
}
// _____________________________________________________________________________
@ -1240,6 +1312,56 @@ inline Box<T> getBoundingBox(const std::vector<Geometry<T>>& multigeo) {
return b;
}
// _____________________________________________________________________________
template <typename T>
inline Box<T> getBoundingRect(const Box<T>& b) {
auto box = Box<T>();
auto centroid = util::geo::centroid(b);
box = extendBox(b, box);
box = extendBox(rotate(convexHull(b), 180, centroid), box);
return box;
}
// _____________________________________________________________________________
template <template <typename> class Geometry, typename T>
inline Box<T> getBoundingRect(const Geometry<T> geom) {
return getBoundingRect<T>(getBoundingBox<T>(geom));
}
// _____________________________________________________________________________
template <typename T>
inline double getEnclosingRadius(const Point<T>& p, const Point<T>& pp) {
return dist(p, pp);
}
// _____________________________________________________________________________
template <typename T>
inline double getEnclosingRadius(const Point<T>& p, const Line<T>& l) {
double ret = 0;
for (const auto& pp : l)
if (getEnclosingRadius(p, pp) > ret) ret = getEnclosingRadius(p, pp);
return ret;
}
// _____________________________________________________________________________
template <typename T>
inline double getEnclosingRadius(const Point<T>& p, const Polygon<T>& pg) {
double ret = 0;
for (const auto& pp : pg.getOuter())
if (getEnclosingRadius(p, pp) > ret) ret = getEnclosingRadius(p, pp);
return ret;
}
// _____________________________________________________________________________
template <template <typename> class Geometry, typename T>
inline double getEnclosingRadius(const Point<T>& p,
const std::vector<Geometry<T>>& multigeom) {
double ret = 0;
for (const auto& pp : multigeom)
if (getEnclosingRadius(p, pp) > ret) ret = getEnclosingRadius(p, pp);
return ret;
}
// _____________________________________________________________________________
template <typename T>
inline Polygon<T> convexHull(const Point<T>& p) {
@ -1308,7 +1430,7 @@ inline Polygon<T> convexHull(const MultiPoint<T>& l) {
convexHullImpl(l, 0, 1, &hull);
hull.push_back(hull.front());
convexHullImpl(l, hull.size() - 2, hull.size() - 1, &hull);
hull.pop_back();
hull.pop_back();
return Polygon<T>(hull);
}
@ -1355,6 +1477,181 @@ inline Box<T> extendBox(const std::vector<Geometry<T>>& multigeom, Box<T> b) {
return b;
}
// _____________________________________________________________________________
template <typename T>
Point<T> pointAt(const Line<T> l, double at) {
return pointAtDist(l, at * len(l));
}
// _____________________________________________________________________________
template <typename T>
Point<T> pointAt(const Line<T> l, double at, size_t* lastI, double* totPos) {
return pointAtDist(l, at * len(l), lastI, totPos);
}
// _____________________________________________________________________________
template <typename T>
Point<T> pointAtDist(const Line<T> l, double atDist) {
return pointAtDist(l, atDist, 0, 0);
}
// _____________________________________________________________________________
template <typename T>
Point<T> pointAtDist(const Line<T> l, double atDist, size_t* lastI,
double* totPos) {
if (l.size() == 1) {
if (lastI) *lastI = 0;
if (totPos) *totPos = 0;
return l[1];
}
if (atDist > geo::len(l)) atDist = geo::len(l);
if (atDist < 0) atDist = 0;
double dist = 0;
const Point<T>* last = &l[0];
for (size_t i = 1; i < l.size(); i++) {
const Point<T>& cur = l[i];
double d = geo::dist(*last, cur);
dist += d;
if (dist > atDist) {
double p = (d - (dist - atDist));
if (lastI) *lastI = i - 1;
if (totPos) *totPos = atDist / util::geo::len(l);
return interpolate(*last, cur, p / dist);
}
last = &l[i];
}
if (lastI) *lastI = l.size() - 1;
if (totPos) *totPos = 1;
return l.back();
}
// _____________________________________________________________________________
template <typename T>
Point<T> interpolate(const Point<T>& a, const Point<T>& b, double d) {
double n1 = b.getX() - a.getX();
double n2 = b.getY() - a.getY();
return Point<T>(a.getX() + (n1 * d), a.getY() + (n2 * d));
}
// _____________________________________________________________________________
template <typename T>
Line<T> orthoLineAtDist(const Line<T>& l, double d, double length) {
Point<T> avgP = pointAtDist(l, d);
double angle = angBetween(pointAtDist(l, d - 5), pointAtDist(l, d + 5));
double angleX1 = avgP.getX() + cos(angle + M_PI / 2) * length / 2;
double angleY1 = avgP.getY() + sin(angle + M_PI / 2) * length / 2;
double angleX2 = avgP.getX() + cos(angle + M_PI / 2) * -length / 2;
double angleY2 = avgP.getY() + sin(angle + M_PI / 2) * -length / 2;
return Line<T>{Point<T>(angleX1, angleY1), Point<T>(angleX2, angleY2)};
}
// _____________________________________________________________________________
template <typename T>
Line<T> segment(const Line<T>& line, double a, double b) {
if (a > b) {
double c = a;
a = b;
b = c;
}
size_t startI, endI;
auto start = pointAt(line, a, &startI, 0);
auto end = pointAt(line, b, &endI, 0);
return segment(line, start, startI, end, endI);
}
// _____________________________________________________________________________
template <typename T>
Line<T> segment(const Line<T>& line, const Point<T>& start, size_t startI,
const Point<T>& end, size_t endI) {
Line<T> ret;
ret.push_back(start);
if (startI + 1 <= endI) {
ret.insert(ret.end(), line.begin() + startI + 1, line.begin() + endI + 1);
}
ret.push_back(end);
// find a more performant way to clear the result of above
ret = util::geo::simplify(ret, 0);
assert(ret.size());
return ret;
}
// _____________________________________________________________________________
template <typename T>
Line<T> average(const std::vector<const Line<T>*>& lines) {
return average(lines, std::vector<double>());
}
// _____________________________________________________________________________
template <typename T>
Line<T> average(const std::vector<const Line<T>*>& lines,
const std::vector<double>& weights) {
bool weighted = lines.size() == weights.size();
double stepSize;
double longestLength =
std::numeric_limits<double>::min(); // avoid recalc of length on each
// comparision
for (auto p : lines) {
if (len(*p) > longestLength) {
longestLength = len(*p);
}
}
Line<T> ret;
double total = 0;
for (size_t i = 0; i < lines.size(); ++i) {
if (weighted) {
total += weights[i];
} else {
total += 1;
}
}
stepSize = AVERAGING_STEP / longestLength;
bool end = false;
for (double a = 0; !end; a += stepSize) {
if (a > 1) {
a = 1;
end = true;
}
double x = 0, y = 0;
for (size_t i = 0; i < lines.size(); ++i) {
auto pl = lines[i];
Point<T> p = pointAt(*pl, a);
if (weighted) {
x += p.getX() * weights[i];
y += p.getY() * weights[i];
} else {
x += p.getX();
y += p.getY();
}
}
ret.push_back(Point<T>(x / total, y / total));
}
simplify(ret, 0);
return ret;
}
// _____________________________________________________________________________
template <typename T>
inline double area(const Point<T>& b) {
@ -1510,28 +1807,30 @@ inline Line<T> densify(const Line<T>& l, double d) {
// _____________________________________________________________________________
template <typename T>
inline double frechetDistC(size_t i, size_t j, const Line<T>& p,
const Line<T>& q,
std::vector<std::vector<double>>& ca) {
const Line<T>& q, std::vector<float>& ca) {
// based on Eiter / Mannila
// http://www.kr.tuwien.ac.at/staff/eiter/et-archive/cdtr9464.pdf
if (ca[i][j] > -1)
return ca[i][j];
if (ca[i * q.size() + j] > -1)
return ca[i * q.size() + j];
else if (i == 0 && j == 0)
ca[i][j] = dist(p[0], q[0]);
ca[i * q.size() + j] = dist(p[0], q[0]);
else if (i > 0 && j == 0)
ca[i][j] = std::max(frechetDistC(i - 1, 0, p, q, ca), dist(p[i], q[0]));
ca[i * q.size() + j] =
std::max(frechetDistC(i - 1, 0, p, q, ca), dist(p[i], q[0]));
else if (i == 0 && j > 0)
ca[i][j] = std::max(frechetDistC(0, j - 1, p, q, ca), dist(p[0], q[j]));
ca[i * q.size() + j] =
std::max(frechetDistC(0, j - 1, p, q, ca), dist(p[0], q[j]));
else if (i > 0 && j > 0)
ca[i][j] = std::max(std::min(std::min(frechetDistC(i - 1, j, p, q, ca),
frechetDistC(i - 1, j - 1, p, q, ca)),
frechetDistC(i, j - 1, p, q, ca)),
dist(p[i], q[j]));
ca[i * q.size() + j] =
std::max(std::min(std::min(frechetDistC(i - 1, j, p, q, ca),
frechetDistC(i - 1, j - 1, p, q, ca)),
frechetDistC(i, j - 1, p, q, ca)),
dist(p[i], q[j]));
else
ca[i][j] = std::numeric_limits<double>::infinity();
ca[i * q.size() + j] = std::numeric_limits<float>::infinity();
return ca[i][j];
return ca[i * q.size() + j];
}
// _____________________________________________________________________________
@ -1543,8 +1842,7 @@ inline double frechetDist(const Line<T>& a, const Line<T>& b, double d) {
auto p = densify(a, d);
auto q = densify(b, d);
std::vector<std::vector<double>> ca(p.size(),
std::vector<double>(q.size(), -1.0));
std::vector<float> ca(p.size() * q.size(), -1.0);
double fd = frechetDistC(p.size() - 1, q.size() - 1, p, q, ca);
return fd;
@ -1556,24 +1854,28 @@ inline double accFrechetDistC(const Line<T>& a, const Line<T>& b, double d) {
auto p = densify(a, d);
auto q = densify(b, d);
std::vector<std::vector<double>> ca(p.size(),
std::vector<double>(q.size(), 0));
assert(p.size());
assert(q.size());
std::vector<float> ca(p.size() * q.size(), 0);
for (size_t i = 0; i < p.size(); i++)
ca[i][0] = std::numeric_limits<double>::infinity();
ca[i * q.size() + 0] = std::numeric_limits<float>::infinity();
for (size_t j = 0; j < q.size(); j++)
ca[0][j] = std::numeric_limits<double>::infinity();
ca[0][0] = 0;
ca[j] = std::numeric_limits<float>::infinity();
ca[0] = 0;
for (size_t i = 1; i < p.size(); i++) {
for (size_t j = 1; j < q.size(); j++) {
double d = util::geo::dist(p[i], q[j]) * util::geo::dist(p[i], p[i - 1]);
ca[i][j] =
d + std::min(ca[i - 1][j], std::min(ca[i][j - 1], ca[i - 1][j - 1]));
float d = util::geo::dist(p[i], q[j]) * util::geo::dist(p[i], p[i - 1]);
ca[i * q.size() + j] =
d + std::min(ca[(i - 1) * q.size() + j],
std::min(ca[i * q.size() + (j - 1)],
ca[(i - 1) * q.size() + (j - 1)]));
}
}
return ca[p.size() - 1][q.size() - 1];
return ca[p.size() * q.size() - 1];
}
// _____________________________________________________________________________
@ -1586,23 +1888,65 @@ inline Point<T> latLngToWebMerc(double lat, double lng) {
return Point<T>(x, y);
}
// _____________________________________________________________________________
template <typename T>
//TODO: rename to lngLat
inline Point<T> latLngToWebMerc(Point<T> lngLat) {
return latLngToWebMerc<T>(lngLat.getY(), lngLat.getX());
}
// _____________________________________________________________________________
template <typename T>
inline Point<T> webMercToLatLng(double x, double y) {
double lat =
(1.5707963267948966 - (2.0 * atan(exp(-y / 6378137.0)))) * (180.0 / M_PI);
double lon = x / 111319.4907932735677;
const double lat =
(1.5707963267948966 - (2.0 * atan(exp(-y / 6378137.0)))) * IRAD;
const double lon = x / 111319.4907932735677;
return Point<T>(lon, lat);
}
// _____________________________________________________________________________
template <typename T>
inline double haversine(T lat1, T lon1, T lat2, T lon2) {
lat1 *= RAD;
lat2 *= RAD;
const double dLat = lat2 - lat1;
const double dLon = (lon2 - lon1) * RAD;
const double sDLat = sin(dLat / 2);
const double sDLon = sin(dLon / 2);
const double a = (sDLat * sDLat) + (sDLon * sDLon) * cos(lat1) * cos(lat2);
return 6378137.0 * 2.0 * asin(sqrt(a));
}
// _____________________________________________________________________________
template <typename T>
inline double haversine(const Point<T>& a, const Point<T>& b) {
return haversine(a.getY(), a.getX(), b.getY(), b.getX());
}
// _____________________________________________________________________________
template <typename T>
inline double webMercMeterDist(const Point<T>& a, const Point<T>& b) {
const auto llA = webMercToLatLng<T>(a.getX(), a.getY());
const auto llB = webMercToLatLng<T>(b.getX(), b.getY());
return haversine(llA.getY(), llA.getX(), llB.getY(), llB.getX());
}
// _____________________________________________________________________________
template <typename G1, typename G2>
inline double webMercMeterDist(const G1& a, const G2& b) {
// euclidean distance on web mercator is in meters on equator,
// and proportional to cos(lat) in both y directions
double latA = 2 * atan(exp(a.getY() / 6378137.0)) - 1.5707965;
double latB = 2 * atan(exp(b.getY() / 6378137.0)) - 1.5707965;
// this is just an approximation
auto pa = centroid(a);
auto pb = centroid(b);
double latA = 2 * atan(exp(pa.getY() / 6378137.0)) - 1.5707965;
double latB = 2 * atan(exp(pb.getY() / 6378137.0)) - 1.5707965;
return util::geo::dist(a, b) * cos((latA + latB) / 2.0);
}
@ -1624,7 +1968,17 @@ inline double webMercDistFactor(const G& a) {
double lat = 2 * atan(exp(a.getY() / 6378137.0)) - 1.5707965;
return cos(lat);
}
}
// _____________________________________________________________________________
template <typename G>
inline double latLngDistFactor(const G& a) {
// euclidean distance on web mercator is in meters on equator,
// and proportional to cos(lat) in both y directions
return cos(a.getY() * RAD);
}
} // namespace geo
} // namespace util
#endif // UTIL_GEO_GEO_H_

View file

@ -5,9 +5,9 @@
#ifndef UTIL_GEO_GRID_H_
#define UTIL_GEO_GRID_H_
#include <map>
#include <set>
#include <vector>
#include <map>
#include "util/geo/Geo.h"
namespace util {
@ -21,6 +21,39 @@ class GridException : public std::runtime_error {
template <typename V, template <typename> class G, typename T>
class Grid {
public:
Grid(const Grid<V, G, T>&) = delete;
Grid(Grid<V, G, T>&& o)
: _width(o._width),
_height(o._height),
_cellWidth(o._cellWidth),
_cellHeight(o._cellHeight),
_bb(o._bb),
_xWidth(o._xWidth),
_yHeight(o._yHeight),
_hasValIdx(o._hasValIdx),
_grid(o._grid),
_index(o._index),
_removed(o._removed) {
o._grid = 0;
}
Grid<V, G, T>& operator=(Grid<V, G, T>&& o) {
_width = o._width;
_height = o._height;
_cellWidth = o._cellWidth;
_cellHeight = o._cellHeight;
_bb = o._bb;
_xWidth = o._xWidth;
_yHeight = o._yHeight;
_hasValIdx = o._hasValIdx;
_grid = o._grid;
_index = std::move(o._index);
_removed = std::move(o._removed);
o._grid = 0;
return *this;
};
// initialization of a point grid with cell width w and cell height h
// that covers the area of bounding box bbox
Grid(double w, double h, const Box<T>& bbox);
@ -36,6 +69,14 @@ class Grid {
// the empty grid
Grid(bool buildValIdx);
~Grid() {
if (!_grid) return;
for (size_t i = 0; i < _xWidth; i++) {
delete[] _grid[i];
}
delete[] _grid;
}
// add object t to this grid
void add(G<T> geom, V val);
void add(size_t x, size_t y, V val);
@ -55,6 +96,9 @@ class Grid {
size_t getXWidth() const;
size_t getYHeight() const;
size_t getCellXFromX(double lon) const;
size_t getCellYFromY(double lat) const;
private:
double _width;
double _height;
@ -64,21 +108,17 @@ class Grid {
Box<T> _bb;
size_t _counter;
size_t _xWidth;
size_t _yHeight;
bool _hasValIdx;
std::vector<std::vector<std::set<V> > > _grid;
// raw 2d array, less memory overhead
std::set<V>** _grid;
std::map<V, std::set<std::pair<size_t, size_t> > > _index;
std::set<V> _removed;
Box<T> getBox(size_t x, size_t y) const;
size_t getCellXFromX(double lon) const;
size_t getCellYFromY(double lat) const;
};
#include "util/geo/Grid.tpp"

View file

@ -11,7 +11,8 @@ Grid<V, G, T>::Grid(bool bldIdx)
_cellHeight(0),
_xWidth(0),
_yHeight(0),
_hasValIdx(bldIdx) {}
_hasValIdx(bldIdx),
_grid(0) {}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
@ -28,11 +29,10 @@ Grid<V, G, T>::Grid(double w, double h, const Box<T>& bbox, bool bValIdx)
: _cellWidth(fabs(w)),
_cellHeight(fabs(h)),
_bb(bbox),
_hasValIdx(bValIdx) {
_width =
bbox.getUpperRight().getX() - bbox.getLowerLeft().getX();
_height =
bbox.getUpperRight().getY() - bbox.getLowerLeft().getY();
_hasValIdx(bValIdx),
_grid(0) {
_width = bbox.getUpperRight().getX() - bbox.getLowerLeft().getX();
_height = bbox.getUpperRight().getY() - bbox.getLowerLeft().getY();
if (_width < 0 || _height < 0) {
_width = 0;
@ -46,11 +46,11 @@ Grid<V, G, T>::Grid(double w, double h, const Box<T>& bbox, bool bValIdx)
_yHeight = ceil(_height / _cellHeight);
// resize rows
_grid.resize(_xWidth);
_grid = new std::set<V>*[_xWidth];
// resize columns
for (size_t i = 0; i < _xWidth; i++) {
_grid[i].resize(_yHeight);
for (size_t x = 0; x < _xWidth; x++) {
_grid[x] = new std::set<V>[_yHeight];
}
}
@ -64,8 +64,8 @@ void Grid<V, G, T>::add(G<T> geom, V val) {
size_t neX = getCellXFromX(box.getUpperRight().getX());
size_t neY = getCellYFromY(box.getUpperRight().getY());
for (size_t x = swX; x <= neX && x < _grid.size(); x++) {
for (size_t y = swY; y <= neY && y < _grid[x].size(); y++) {
for (size_t x = swX; x <= neX && x < _xWidth; x++) {
for (size_t y = swY; y <= neY && y < _yHeight; y++) {
if (intersects(geom, getBox(x, y))) {
add(x, y, val);
}
@ -97,24 +97,23 @@ void Grid<V, G, T>::get(const Box<T>& box, std::set<V>* s) const {
template <typename V, template <typename> class G, typename T>
void Grid<V, G, T>::get(const G<T>& geom, double d, std::set<V>* s) const {
Box<T> a = getBoundingBox(geom);
Box<T> b(Point<T>(a.getLowerLeft().getX() - d,
a.getLowerLeft().getY() - d),
Point<T>(a.getUpperRight().getX() + d,
a.getUpperRight().getY() + d));
Box<T> b(
Point<T>(a.getLowerLeft().getX() - d, a.getLowerLeft().getY() - d),
Point<T>(a.getUpperRight().getX() + d, a.getUpperRight().getY() + d));
return get(b, s);
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
void Grid<V, G, T>::get(size_t x, size_t y, std::set<V>* s) const {
if (_hasValIdx) {
if (_hasValIdx || _removed.size() == 0) {
s->insert(_grid[x][y].begin(), _grid[x][y].end());
} else {
// if we dont have a value index, we have a set of deleted nodes.
// in this case, only insert if not deleted
std::copy_if(_grid[x][y].begin(), _grid[x][y].end(),
std::inserter(*s, s->end()),
[&](const V& v) { return Grid<V, G, T>::_removed.count(v) == 0; });
std::copy_if(
_grid[x][y].begin(), _grid[x][y].end(), std::inserter(*s, s->end()),
[&](const V& v) { return Grid<V, G, T>::_removed.count(v) == 0; });
}
}

View file

@ -17,7 +17,6 @@ namespace util {
namespace geo {
static const double MAX_EQ_DISTANCE = 15;
static const double AVERAGING_STEP = 20;
// legacy code, will be removed in the future
@ -61,11 +60,11 @@ class PolyLine {
PolyLine& operator>>(const Point<T>& p);
void reverse();
PolyLine getReversed() const;
PolyLine reversed() const;
void offsetPerp(double units);
PolyLine getPerpOffsetted(double units) const;
PolyLine offsetted(double units) const;
const Line<T>& getLine() const;

View file

@ -38,7 +38,7 @@ void PolyLine<T>::reverse() {
// _____________________________________________________________________________
template <typename T>
PolyLine<T> PolyLine<T>::getReversed() const {
PolyLine<T> PolyLine<T>::reversed() const {
PolyLine ret = *this;
ret.reverse();
return ret;
@ -52,7 +52,7 @@ const Line<T>& PolyLine<T>::getLine() const {
// _____________________________________________________________________________
template <typename T>
PolyLine<T> PolyLine<T>::getPerpOffsetted(double units) const {
PolyLine<T> PolyLine<T>::offsetted(double units) const {
PolyLine p = *this;
p.offsetPerp(units);
return p;
@ -70,8 +70,6 @@ void PolyLine<T>::offsetPerp(double units) {
if (fabs(units) < 0.001) return;
assert(getLength() > 0);
if (_line.size() < 2) return;
Line<T> ret;
@ -180,6 +178,7 @@ template <typename T>
PolyLine<T> PolyLine<T>::getSegment(const LinePoint<T>& start,
const LinePoint<T>& end) const {
PolyLine ret;
ret << start.p;
if (start.lastIndex + 1 <= end.lastIndex) {
@ -189,7 +188,9 @@ PolyLine<T> PolyLine<T>::getSegment(const LinePoint<T>& start,
ret << end.p;
// find a more performant way to clear the result of above
ret.simplify(0);
// ret.simplify(0);
// assert(ret.getLine().size());
return ret;
}
@ -197,9 +198,19 @@ PolyLine<T> PolyLine<T>::getSegment(const LinePoint<T>& start,
// _____________________________________________________________________________
template <typename T>
LinePoint<T> PolyLine<T>::getPointAtDist(double atDist) const {
if (atDist > getLength()) atDist = getLength();
double l = getLength();
if (atDist > l) atDist = l;
if (atDist < 0) atDist = 0;
// shortcuts
if (atDist == 0) {
return LinePoint<T>(0, 0, _line.front());
}
if (atDist == l) {
return LinePoint<T>(_line.size() - 1, 1, _line.back());
}
double dist = 0;
if (_line.size() == 1) return LinePoint<T>(0, 0, _line[0]);
@ -213,8 +224,7 @@ LinePoint<T> PolyLine<T>::getPointAtDist(double atDist) const {
if (dist > atDist) {
double p = (d - (dist - atDist));
return LinePoint<T>(i - 1, atDist / getLength(),
interpolate(*last, cur, p));
return LinePoint<T>(i - 1, atDist / l, interpolate(*last, cur, p));
}
last = &_line[i];
@ -270,8 +280,9 @@ PolyLine<T> PolyLine<T>::average(const std::vector<const PolyLine<T>*>& lines,
double longestLength = DBL_MIN; // avoid recalc of length on each comparision
for (const PolyLine* p : lines) {
if (p->getLength() > longestLength) {
longestLength = p->getLength();
double l = p->getLength();
if (l > longestLength) {
longestLength = l;
}
}
@ -377,11 +388,13 @@ LinePoint<T> PolyLine<T>::projectOnAfter(const Point<T>& p, size_t a) const {
assert(a < _line.size());
std::pair<size_t, double> bc = nearestSegmentAfter(p, a);
Point<T> ret = geo::projectOn(_line[bc.first], p, _line[bc.first + 1]);
size_t next = bc.first + 1;
if (next >= _line.size()) next = bc.first;
if (getLength() > 0) {
bc.second += dist(_line[bc.first], ret) / getLength();
}
Point<T> ret = geo::projectOn(_line[bc.first], p, _line[next]);
double l = getLength();
if (l > 0) bc.second += dist(_line[bc.first], ret) / l;
return LinePoint<T>(bc.first, bc.second, ret);
}
@ -475,7 +488,7 @@ SharedSegments<T> PolyLine<T>::getSharedSegments(const PolyLine<T>& pl,
*/
double STEP_SIZE = 2;
double MAX_SKIPS = 4;
double MIN_SEG_LENGTH = 1; // dmax / 2; // make this configurable!
double MIN_SEG_LENGTH = 0.1; // dmax / 2; // make this configurable!
SharedSegments<T> ret;
@ -505,7 +518,6 @@ SharedSegments<T> PolyLine<T>::getSharedSegments(const PolyLine<T>& pl,
LinePoint<T> curCmpPointer = pl.projectOn(curPointer);
LinePoint<T> curBackProjectedPointer = projectOn(curCmpPointer.p);
skips = 0;
if (in) {
@ -536,7 +548,6 @@ SharedSegments<T> PolyLine<T>::getSharedSegments(const PolyLine<T>& pl,
curEndCand.totalPos * length) > MIN_SEG_LENGTH &&
fabs(curStartCandCmp.totalPos * plLength -
curEndCandCmp.totalPos * plLength) > MIN_SEG_LENGTH)) {
assert(curStartCand.totalPos < curEndCand.totalPos);
ret.segments.push_back(
SharedSegment<T>(std::pair<LinePoint<T>, LinePoint<T>>(
curStartCand, curStartCandCmp),
@ -573,7 +584,6 @@ SharedSegments<T> PolyLine<T>::getSharedSegments(const PolyLine<T>& pl,
MIN_SEG_LENGTH &&
fabs(curStartCandCmp.totalPos * plLength -
curEndCandCmp.totalPos * plLength) > MIN_SEG_LENGTH)) {
assert(curStartCand.totalPos < curEndCand.totalPos);
ret.segments.push_back(SharedSegment<T>(
std::pair<LinePoint<T>, LinePoint<T>>(curStartCand, curStartCandCmp),
std::pair<LinePoint<T>, LinePoint<T>>(curEndCand, curEndCandCmp)));
@ -661,7 +671,8 @@ std::pair<double, double> PolyLine<T>::getSlopeBetween(double ad,
template <typename T>
std::pair<double, double> PolyLine<T>::getSlopeBetweenDists(double ad,
double bd) const {
return getSlopeBetween(ad / getLength(), bd / getLength());
double l = getLength();
return getSlopeBetween(ad / l, bd / l);
}
// _____________________________________________________________________________

View file

@ -21,9 +21,10 @@ class Polygon {
Polygon(const Line<T>& l) : _outer(l) {}
Polygon(const Box<T>& b)
: _outer({b.getLowerLeft(),
Point<T>(b.getUpperRight().getX(), b.getLowerLeft().getY()),
Point<T>(b.getLowerLeft().getX(), b.getUpperRight().getY()),
b.getUpperRight(),
Point<T>(b.getLowerLeft().getX(), b.getUpperRight().getY())}) {}
Point<T>(b.getUpperRight().getX(), b.getLowerLeft().getY()),
b.getLowerLeft()}) {}
const Line<T>& getOuter() const { return _outer; }
Line<T>& getOuter() { return _outer; }

94
src/util/geo/QuadTree.h Normal file
View file

@ -0,0 +1,94 @@
// Copyright 2020, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_GEO_QUADTREE_H_
#define UTIL_GEO_QUADTREE_H_
#include <map>
#include <set>
#include <vector>
#include "util/geo/Geo.h"
#include "util/geo/output/GeoJsonOutput.h"
namespace util {
namespace geo {
template <typename V, typename T>
struct QuadValue {
V val; // the actual value of this entry
Point<T> point; // the value's position
int64_t nextValue; // index of the next quad value, -1 means no next value
};
template <typename T>
struct QuadNode {
int64_t numEls; // number of elements, -1 if this is not a leaf node
int64_t childs; // for leafs, points to the first value contained. for
// other nodes, points to the array block containing the
// 4 childs
Box<T> bbox;
};
template <typename V, typename T>
struct SplitFunc {
virtual ~SplitFunc() = default;
virtual bool operator()(const QuadNode<T>& nd,
const QuadValue<V, T>& newVal) const = 0;
};
template <typename V, typename T>
struct CapaSplitFunc : SplitFunc<V, T> {
CapaSplitFunc(size_t c) : _c(c) {}
virtual bool operator()(const QuadNode<T>& nd,
const QuadValue<V, T>& newVal) const {
UNUSED(newVal);
return static_cast<size_t>(nd.numEls) + 1 > _c;
}
size_t _c;
};
// QuadTree for point data (and only point data)
template <typename V, typename T>
class QuadTree {
public:
// initialization of a quad tree with maximum depth d and maximum node // capacity c
QuadTree(size_t d, size_t c, const Box<T>& bbox);
QuadTree(size_t d, const SplitFunc<V, T>& splitF, const Box<T>& bbox);
// insert into the tree
void insert(const V& val, const Point<T>& point);
// insert into a specific node
void insert(int64_t vid, int64_t nid, size_t d);
size_t size() const;
const std::vector<QuadNode<T>>& getNds() const;
const QuadNode<T>& getNd(size_t nid) const;
// GeoJSON output
void print(std::ostream& o) const;
private:
size_t _maxDepth;
std::vector<QuadValue<V, T>> _vals;
std::vector<QuadNode<T>> _nds;
CapaSplitFunc<V, T> _capaFunc;
const SplitFunc<V, T>& _splFunc;
// split a node
void split(size_t nid, size_t d);
};
#include "util/geo/QuadTree.tpp"
} // namespace geo
} // namespace util
#endif // UTIL_GEO_QUADTREE_H_

117
src/util/geo/QuadTree.tpp Normal file
View file

@ -0,0 +1,117 @@
// _____________________________________________________________________________
template <typename V, typename T>
QuadTree<V, T>::QuadTree(size_t d, size_t c, const Box<T>& bbox)
: _maxDepth(d), _capaFunc(c), _splFunc(_capaFunc) {
_nds.push_back(QuadNode<T>{0, 0, bbox});
}
// _____________________________________________________________________________
template <typename V, typename T>
QuadTree<V, T>::QuadTree(size_t d, const SplitFunc<V, T>& splitF,
const Box<T>& bbox)
: _maxDepth(d), _capaFunc(0), _splFunc(splitF) {
_nds.push_back(QuadNode<T>{0, 0, bbox});
}
// _____________________________________________________________________________
template <typename V, typename T>
void QuadTree<V, T>::insert(const V& val, const Point<T>& pos) {
if (!intersects(pos, _nds[0].bbox)) return;
int64_t valId = _vals.size();
_vals.push_back(QuadValue<V, T>{val, pos, -1});
_vals[valId].nextValue = -1;
insert(valId, 0, 0);
}
// _____________________________________________________________________________
template <typename V, typename T>
void QuadTree<V, T>::insert(int64_t vid, int64_t nid, size_t d) {
if (!intersects(_vals[vid].point, _nds[nid].bbox)) return;
if (d < _maxDepth && _nds[nid].numEls > -1 &&
_splFunc(_nds[nid], _vals[vid])) {
split(nid, d);
}
if (_nds[nid].numEls == -1) {
// insert into fitting subtree
for (size_t i = 0; i < 4; i++) insert(vid, _nds[nid].childs + i, d + 1);
} else {
if (_nds[nid].numEls == 0) {
_nds[nid].childs = vid;
} else {
_vals[vid].nextValue = _nds[nid].childs;
_nds[nid].childs = vid;
}
_nds[nid].numEls++;
}
}
// _____________________________________________________________________________
template <typename V, typename T>
void QuadTree<V, T>::split(size_t nid, size_t d) {
const auto& box = _nds[nid].bbox;
T w = (box.getUpperRight().getX() - box.getLowerLeft().getX()) / T(2);
int64_t curEl = _nds[nid].numEls > 0 ? _nds[nid].childs : -1;
_nds[nid].numEls = -1; // the node is now a leaf node
_nds[nid].childs = _nds.size(); // the nodes quadrant block starts here
// box at 0, 0
_nds.push_back(QuadNode<T>{
0, 0,
Box<T>(box.getLowerLeft(), Point<T>(box.getLowerLeft().getX() + w,
box.getLowerLeft().getY() + w))});
// box at 0, 1
_nds.push_back(QuadNode<T>{
0, 0,
Box<T>(Point<T>(box.getLowerLeft().getX() + w, box.getLowerLeft().getY()),
Point<T>(box.getUpperRight().getX(),
box.getLowerLeft().getY() + w))});
// box at 1,0
_nds.push_back(QuadNode<T>{
0, 0,
Box<T>(Point<T>(box.getLowerLeft().getX(), box.getLowerLeft().getY() + w),
Point<T>(box.getLowerLeft().getX() + w,
box.getUpperRight().getY()))});
// box at 1,1
_nds.push_back(QuadNode<T>{0, 0,
Box<T>(Point<T>(box.getLowerLeft().getX() + w,
box.getLowerLeft().getY() + w),
box.getUpperRight())});
while (curEl > -1) {
_vals[curEl].nextValue = -1;
insert(curEl, nid, d + 1);
curEl = _vals[curEl].nextValue;
}
}
// _____________________________________________________________________________
template <typename V, typename T>
size_t QuadTree<V, T>::size() const {
return _vals.size();
}
// _____________________________________________________________________________
template <typename V, typename T>
const QuadNode<T>& QuadTree<V, T>::getNd(size_t nid) const {
return _nds[nid];
}
// _____________________________________________________________________________
template <typename V, typename T>
const std::vector<QuadNode<T>>& QuadTree<V, T>::getNds() const {
return _nds;
}
// _____________________________________________________________________________
template <typename V, typename T>
void QuadTree<V, T>::print(std::ostream& o) const {
util::geo::output::GeoJsonOutput out(o);
for (const auto& nd : _nds) {
if (nd.numEls == -1) continue; // don't print non-leaf nodes
out.print(util::geo::convexHull(nd.bbox), json::Dict{{"elements", json::Int(nd.numEls)}});
}
}

View file

@ -19,13 +19,22 @@ class GeoGraphJsonOutput {
public:
inline GeoGraphJsonOutput(){};
// print a graph to the provided path
// print a graph to the provided path, with optional JSON attributes
// written on the graph-level
template <typename N, typename E>
void print(const util::graph::Graph<N, E>& outG, std::ostream& str);
template <typename N, typename E>
void print(const util::graph::Graph<N, E>& outG, std::ostream& str,
json::Val attrs);
// print a graph to the provided path, but treat coordinates as Web Mercator coordinates and reproject to WGS84
// print a graph to the provided path, but treat coordinates as Web Mercator
// coordinates and reproject to WGS84, with optional JSON attributes
// written on the graph-level
template <typename N, typename E>
void printLatLng(const util::graph::Graph<N, E>& outG, std::ostream& str);
template <typename N, typename E>
void printLatLng(const util::graph::Graph<N, E>& outG, std::ostream& str,
json::Val attrs);
private:
template <typename T>
@ -34,12 +43,13 @@ class GeoGraphJsonOutput {
// print a graph to the provided path
template <typename N, typename E>
void printImpl(const util::graph::Graph<N, E>& outG, std::ostream& str, bool proj);
void printImpl(const util::graph::Graph<N, E>& outG, std::ostream& str,
bool proj, json::Val attrs);
};
#include "util/geo/output/GeoGraphJsonOutput.tpp"
}
}
}
} // namespace output
} // namespace geo
} // namespace util
#endif // UTIL_GEO_OUTPUT_GEOGRAPHJSONOUTPUT_H_

View file

@ -16,21 +16,36 @@ Line<T> GeoGraphJsonOutput::createLine(const util::geo::Point<T>& a,
template <typename N, typename E>
void GeoGraphJsonOutput::print(const util::graph::Graph<N, E>& outG,
std::ostream& str) {
printImpl(outG, str, false);
printImpl(outG, str, false, {});
}
// _____________________________________________________________________________
template <typename N, typename E>
void GeoGraphJsonOutput::printLatLng(const util::graph::Graph<N, E>& outG,
std::ostream& str) {
printImpl(outG, str, true);
printImpl(outG, str, true, {});
}
// _____________________________________________________________________________
template <typename N, typename E>
void GeoGraphJsonOutput::print(const util::graph::Graph<N, E>& outG,
std::ostream& str, json::Val attrs) {
printImpl(outG, str, false, attrs);
}
// _____________________________________________________________________________
template <typename N, typename E>
void GeoGraphJsonOutput::printLatLng(const util::graph::Graph<N, E>& outG,
std::ostream& str, json::Val attrs) {
printImpl(outG, str, true, attrs);
}
// _____________________________________________________________________________
template <typename N, typename E>
void GeoGraphJsonOutput::printImpl(const util::graph::Graph<N, E>& outG,
std::ostream& str, bool proj) {
GeoJsonOutput _out(str);
std::ostream& str, bool proj, json::Val attrs) {
GeoJsonOutput _out(str, attrs);
// first pass, nodes
for (util::graph::Node<N, E>* n : outG.getNds()) {

View file

@ -28,12 +28,30 @@ class GeoJsonOutput {
template <typename T>
void print(const Line<T>& l, json::Val attrs);
template <typename T>
void print(const MultiLine<T>& l, json::Val attrs);
template <typename T>
void print(const Polygon<T>& l, json::Val attrs);
template <typename T>
void print(const MultiPolygon<T>& l, json::Val attrs);
template <typename T>
void printLatLng(const Point<T>& p, json::Val attrs);
template <typename T>
void printLatLng(const Line<T>& l, json::Val attrs);
template <typename T>
void printLatLng(const MultiLine<T>& l, json::Val attrs);
template <typename T>
void printLatLng(const Polygon<T>& l, json::Val attrs);
template <typename T>
void printLatLng(const MultiPolygon<T>& l, json::Val attrs);
void flush();
private:
@ -41,8 +59,8 @@ class GeoJsonOutput {
};
#include "util/geo/output/GeoJsonOutput.tpp"
}
}
}
} // namespace output
} // namespace geo
} // namespace util
#endif // UTIL_GEO_OUTPUT_GEOJSONOUTPUT_H_

View file

@ -47,6 +47,46 @@ void GeoJsonOutput::print(const Line<T>& line, json::Val attrs) {
_wr.close();
}
// _____________________________________________________________________________
template <typename T>
void GeoJsonOutput::print(const MultiLine<T>& line, json::Val attrs) {
for (const auto& l : line) print(l, attrs);
}
// _____________________________________________________________________________
template <typename T>
void GeoJsonOutput::print(const Polygon<T>& poly, json::Val attrs) {
if (!poly.getOuter().size()) return;
_wr.obj();
_wr.keyVal("type", "Feature");
_wr.key("geometry");
_wr.obj();
_wr.keyVal("type", "Polygon");
_wr.key("coordinates");
_wr.arr();
_wr.arr();
for (auto p : poly.getOuter()) {
_wr.arr();
_wr.val(p.getX());
_wr.val(p.getY());
_wr.close();
}
_wr.close();
_wr.close();
_wr.close();
_wr.key("properties");
_wr.val(attrs);
_wr.close();
}
// _____________________________________________________________________________
template <typename T>
void GeoJsonOutput::print(const MultiPolygon<T>& mpoly, json::Val attrs) {
for (const auto& p : mpoly) print(p, attrs);
}
// _____________________________________________________________________________
template <typename T>
void GeoJsonOutput::printLatLng(const Point<T>& p, json::Val attrs) {
@ -58,7 +98,30 @@ void GeoJsonOutput::printLatLng(const Point<T>& p, json::Val attrs) {
template <typename T>
void GeoJsonOutput::printLatLng(const Line<T>& line, json::Val attrs) {
Line<T> projL;
for (auto p : line) projL.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
for (auto p : line)
projL.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
print(projL, attrs);
}
// _____________________________________________________________________________
template <typename T>
void GeoJsonOutput::printLatLng(const MultiLine<T>& mline, json::Val attrs) {
for (const auto& l : mline) printLatLng(l, attrs);
}
// _____________________________________________________________________________
template <typename T>
void GeoJsonOutput::printLatLng(const Polygon<T>& poly, json::Val attrs) {
Polygon<T> projP;
for (auto p : poly)
projP.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
print(projP, attrs);
}
// _____________________________________________________________________________
template <typename T>
void GeoJsonOutput::printLatLng(const MultiPolygon<T>& mpoly, json::Val attrs) {
for (const auto& p : mpoly) printLatLng(p, attrs);
}

View file

@ -7,8 +7,8 @@
#include <stack>
#include "util/graph/Edge.h"
#include "util/graph/UndirGraph.h"
#include "util/graph/Node.h"
#include "util/graph/UndirGraph.h"
namespace util {
namespace graph {
@ -20,13 +20,26 @@ using util::graph::Edge;
// collection of general graph algorithms
class Algorithm {
public:
template <typename N, typename E>
struct EdgeCheckFunc {
virtual bool operator()(const Node<N, E>* frNd,
const Edge<N, E>* edge) const {
UNUSED(frNd);
UNUSED(edge);
return true;
};
};
template <typename N, typename E>
static std::vector<std::set<Node<N, E>*> > connectedComponents(
const UndirGraph<N, E>& g);
template <typename N, typename E>
static std::vector<std::set<Node<N, E>*> > connectedComponents(
const UndirGraph<N, E>& g, const EdgeCheckFunc<N, E>& checkFunc);
};
#include "util/graph/Algorithm.tpp"
}
}

View file

@ -1,11 +1,18 @@
// Copyright 2017, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
//
// _____________________________________________________________________________
template <typename N, typename E>
std::vector<std::set<Node<N, E>*>> Algorithm::connectedComponents(
const UndirGraph<N, E>& g) {
return connectedComponents(g, EdgeCheckFunc<N, E>());
}
// _____________________________________________________________________________
template <typename N, typename E>
std::vector<std::set<Node<N, E>*> > Algorithm::connectedComponents(
const UndirGraph<N, E>& g) {
std::vector<std::set<Node<N, E>*>> Algorithm::connectedComponents(
const UndirGraph<N, E>& g, const EdgeCheckFunc<N, E>& checkFunc) {
std::vector<std::set<Node<N, E>*>> ret;
std::set<Node<N, E>*> visited;
@ -22,6 +29,7 @@ std::vector<std::set<Node<N, E>*> > Algorithm::connectedComponents(
visited.insert(cur);
for (auto* e : cur->getAdjList()) {
if (!checkFunc(cur, e)) continue;
if (!visited.count(e->getOtherNd(cur))) q.push(e->getOtherNd(cur));
}
}

View file

@ -0,0 +1,7 @@
// Copyright 2017, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include "util/graph/BiDijkstra.h"
size_t util::graph::BiDijkstra::ITERS = 0;

129
src/util/graph/BiDijkstra.h Normal file
View file

@ -0,0 +1,129 @@
// Copyright 2017, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_GRAPH_BIDIJKSTRA_H_
#define UTIL_GRAPH_BIDIJKSTRA_H_
#include <limits>
#include <list>
#include <queue>
#include <set>
#include <algorithm>
#include <unordered_map>
#include "util/graph/Edge.h"
#include "util/graph/Graph.h"
#include "util/graph/Node.h"
#include "util/graph/ShortestPath.h"
namespace util {
namespace graph {
using util::graph::Edge;
using util::graph::Graph;
using util::graph::Node;
// bidirectional dijkstras algorithm for util graph
class BiDijkstra : public ShortestPath<BiDijkstra> {
public:
template <typename N, typename E, typename C>
struct RouteNode {
RouteNode() : n(0), parent(0), d(), h() {}
RouteNode(Node<N, E>* n) : n(n), parent(0), d(), h() {}
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d)
: n(n), parent(parent), d(d), h() {}
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d, C h)
: n(n), parent(parent), d(d), h(h) {}
Node<N, E>* n;
Node<N, E>* parent;
// the cost so far
C d;
// the heuristical remaining cost + the cost so far
C h;
bool operator<(const RouteNode<N, E, C>& p) const { return h > p.h; }
};
template <typename N, typename E, typename C>
using Settled = std::unordered_map<Node<N, E>*, RouteNode<N, E, C> >;
template <typename N, typename E, typename C>
using PQ = std::priority_queue<RouteNode<N, E, C> >;
template <typename N, typename E, typename C>
struct CostFunc : public util::graph::CostFunc<N, E, C> {
virtual ~CostFunc() = default; C operator()(const Edge<N, E>* from, const Node<N, E>* n,
const Edge<N, E>* to) const {
UNUSED(from);
UNUSED(n);
UNUSED(to);
return C();
};
};
template <typename N, typename E, typename C>
struct HeurFunc : public util::graph::HeurFunc<N, E, C> {
virtual ~HeurFunc() = default;
C operator()(const Edge<N, E>* from,
const std::set<Edge<N, E>*>& to) const {
UNUSED(from);
UNUSED(to);
return C();
};
};
template <typename N, typename E, typename C>
static std::unordered_map<Node<N, E>*, C> shortestPathImpl(
Node<N, E>* from, const std::set<Node<N, E>*>& to,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>&,
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
std::unordered_map<Node<N, E>*, NList<N, E>*> resNode);
template <typename N, typename E, typename C>
static C shortestPathImpl(const std::set<Node<N, E>*> from,
const std::set<Node<N, E>*>& to,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes);
template <typename N, typename E, typename C>
static C shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes);
template <typename N, typename E, typename C>
static void relax(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQ<N, E, C>& pq);
template <typename N, typename E, typename C>
static C relaxFwd(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQ<N, E, C>& pq, const Settled<N, E, C>& settledBwd);
template <typename N, typename E, typename C>
static C relaxBwd(const std::set<Node<N, E>*>& froms, RouteNode<N, E, C>& cur,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQ<N, E, C>& pq, const Settled<N, E, C>& settledFwd);
template <typename N, typename E, typename C>
static void buildPath(Node<N, E>* curN, Settled<N, E, C>& settledFwd,
Settled<N, E, C>& settledBwd, NList<N, E>* resNodes,
EList<N, E>* resEdges);
static size_t ITERS;
};
#include "util/graph/BiDijkstra.tpp"
} // namespace graph
} // namespace util
#endif // UTIL_GRAPH_BIDIJKSTRA_H_

View file

@ -0,0 +1,293 @@
// Copyright 2017, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
// _____________________________________________________________________________
template <typename N, typename E, typename C>
C BiDijkstra::shortestPathImpl(Node<N, E>* from,
const std::set<Node<N, E>*>& to,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes) {
if (from->getOutDeg() == 0) return costFunc.inf();
std::set<Node<N, E>*> froms;
froms.insert(from);
return shortestPathImpl(froms, to, costFunc, heurFunc, resEdges, resNodes);
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
C BiDijkstra::shortestPathImpl(const std::set<Node<N, E>*> from,
const std::set<Node<N, E>*>& to,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes) {
Settled<N, E, C> settledFwd, settledBwd;
PQ<N, E, C> pqFwd, pqBwd;
bool found = false;
// starter for forward search
for (auto n : from) pqFwd.emplace(n);
auto l = costFunc.inf();
// starter for backward search
for (auto n : to) pqBwd.emplace(n);
RouteNode<N, E, C> cur;
while (!pqFwd.empty() && !pqBwd.empty()) {
if (costFunc.inf() <= pqFwd.top().h && costFunc.inf() <= pqBwd.top().h)
return costFunc.inf();
if (pqFwd.top() < pqBwd.top()) {
auto se = settledBwd.find(pqBwd.top().n);
if (se != settledBwd.end()) {
// to allow non-consistent heuristics
if (se->second.d <= pqBwd.top().d) {
pqBwd.pop();
continue;
}
}
} else {
auto se = settledFwd.find(pqFwd.top().n);
if (se != settledFwd.end()) {
// to allow non-consistent heuristics
if (se->second.d <= pqFwd.top().d) {
pqFwd.pop();
continue;
}
}
}
BiDijkstra::ITERS++;
if (pqFwd.top() < pqBwd.top()) {
cur = pqBwd.top();
pqBwd.pop();
settledBwd[cur.n] = cur;
if (settledFwd.find(cur.n) != settledFwd.end()) {
auto newL = cur.d + settledFwd.find(cur.n)->second.d;
if (!(newL > l)) {
l = newL;
found = true;
break;
}
}
C bestCost = relaxBwd(from, cur, costFunc, heurFunc, pqBwd, settledFwd);
if (bestCost < l) l = bestCost;
} else {
cur = pqFwd.top();
pqFwd.pop();
settledFwd[cur.n] = cur;
if (settledBwd.find(cur.n) != settledBwd.end()) {
auto newL = cur.d + settledBwd.find(cur.n)->second.d;
if (!(newL > l)) {
l = newL;
found = true;
break;
}
}
C bestCost = relaxFwd(cur, to, costFunc, heurFunc, pqFwd, settledBwd);
if (bestCost < l) l = bestCost;
}
}
if (!found) return costFunc.inf();
buildPath(cur.n, settledFwd, settledBwd, resNodes, resEdges);
return l;
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
std::unordered_map<Node<N, E>*, C> BiDijkstra::shortestPathImpl(
Node<N, E>* from, const std::set<Node<N, E>*>& to,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
std::unordered_map<Node<N, E>*, NList<N, E>*> resNodes) {
UNUSED(from);
UNUSED(to);
UNUSED(costFunc);
UNUSED(heurFunc);
UNUSED(resEdges);
UNUSED(resNodes);
assert(false);
// std::unordered_map<Node<N, E>*, C> costs;
// if (to.size() == 0) return costs;
// // init costs with inf
// for (auto n : to) costs[n] = costFunc.inf();
// if (from->getOutDeg() == 0) return costs;
// Settled<N, E, C> settled;
// PQ<N, E, C> pq;
// size_t found = 0;
// pq.emplace(from);
// RouteNode<N, E, C> cur;
// while (!pq.empty()) {
// if (costFunc.inf() <= pq.top().h) return costs;
// if (settled.find(pq.top().n) != settled.end()) {
// pq.pop();
// continue;
// }
// BiDijkstra::ITERS++;
// cur = pq.top();
// pq.pop();
// settled[cur.n] = cur;
// if (to.find(cur.n) != to.end()) {
// found++;
// }
// if (found == to.size()) break;
// relax(cur, to, costFunc, heurFunc, pq);
// }
// for (auto nto : to) {
// if (!settled.count(nto)) continue;
// Node<N, E>* curN = nto;
// costs[nto] = settled[curN].d;
// buildPath(nto, settled, resNodes[nto], resEdges[nto]);
// }
// return costs;
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
void BiDijkstra::relax(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQ<N, E, C>& pq) {
for (auto edge : cur.n->getAdjListOut()) {
C newC = costFunc(cur.n, edge, edge->getOtherNd(cur.n));
newC = cur.d + newC;
if (costFunc.inf() <= newC) continue;
// addition done here to avoid it in the PQ
const C& newH = newC + heurFunc(edge->getOtherNd(cur.n), to);
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH);
}
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
C BiDijkstra::relaxFwd(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQ<N, E, C>& pq, const Settled<N, E, C>& settledBwd) {
UNUSED(to);
UNUSED(heurFunc);
C ret = costFunc.inf();
for (auto edge : cur.n->getAdjListOut()) {
C newC = costFunc(cur.n, edge, edge->getOtherNd(cur.n));
newC = cur.d + newC;
if (costFunc.inf() <= newC) continue;
// addition done here to avoid it in the PQ
// const C& newH = newC + heurFunc(froms, edge->getOtherNd(cur.n));
// TODO:
const C& newH = newC + 0;
// update new best found cost
if (settledBwd.find(edge->getOtherNd(cur.n)) != settledBwd.end()) {
C bwdCost = settledBwd.find(edge->getOtherNd(cur.n))->second.d + newC;
if (bwdCost < ret) ret = bwdCost;
}
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH);
}
return ret;
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
C BiDijkstra::relaxBwd(const std::set<Node<N, E>*>& froms,
RouteNode<N, E, C>& cur,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQ<N, E, C>& pq, const Settled<N, E, C>& settledFwd) {
UNUSED(froms);
UNUSED(heurFunc);
C ret = costFunc.inf();
for (auto edge : cur.n->getAdjListIn()) {
C newC = costFunc(edge->getOtherNd(cur.n), edge, cur.n);
newC = cur.d + newC;
if (costFunc.inf() <= newC) continue;
// addition done here to avoid it in the PQ
// const C& newH = newC + heurFunc(froms, edge->getOtherNd(cur.n));
// TODO:
const C& newH = newC + 0;
// update new best found cost
if (settledFwd.find(edge->getOtherNd(cur.n)) != settledFwd.end()) {
C fwdCost = settledFwd.find(edge->getOtherNd(cur.n))->second.d + newC;
if (fwdCost < ret) ret = fwdCost;
}
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH);
}
return ret;
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
void BiDijkstra::buildPath(Node<N, E>* curN, Settled<N, E, C>& settledFwd,
Settled<N, E, C>& settledBwd, NList<N, E>* resNodes,
EList<N, E>* resEdges) {
Node<N, E>* curNFwd = curN;
Node<N, E>* curNBwd = curN;
// the forward part
while (resNodes || resEdges) {
const RouteNode<N, E, C>& curNode = settledFwd[curNFwd];
if (resNodes) resNodes->push_back(curNode.n);
if (!curNode.parent) break;
if (resEdges) {
for (auto e : curNode.n->getAdjListIn()) {
if (e->getOtherNd(curNode.n) == curNode.parent) resEdges->push_back(e);
}
}
curNFwd = curNode.parent;
}
if (resNodes) std::reverse(resNodes->begin(), resNodes->end());
if (resEdges) std::reverse(resEdges->begin(), resEdges->end());
// the backward part
while (resNodes || resEdges) {
const RouteNode<N, E, C>& curNode = settledBwd[curNBwd];
if (resNodes && curNode.n != curN) resNodes->push_back(curNode.n);
if (!curNode.parent) break;
if (resEdges) {
for (auto e : curNode.n->getAdjListOut()) {
if (e->getOtherNd(curNode.n) == curNode.parent) resEdges->push_back(e);
}
}
curNBwd = curNode.parent;
}
if (resNodes) std::reverse(resNodes->begin(), resNodes->end());
if (resEdges) std::reverse(resEdges->begin(), resEdges->end());
}

View file

@ -9,7 +9,6 @@
#include <list>
#include <queue>
#include <set>
#include <set>
#include <unordered_map>
#include "util/graph/Edge.h"
#include "util/graph/Graph.h"
@ -19,33 +18,32 @@
namespace util {
namespace graph {
using util::graph::Edge;
using util::graph::Graph;
using util::graph::Node;
using util::graph::Edge;
// dijkstras algorithm for util graph
class Dijkstra : public ShortestPath<Dijkstra> {
public:
template <typename N, typename E, typename C>
struct RouteNode {
RouteNode() : n(0), parent(0), d(), h(), e(0) {}
RouteNode(Node<N, E>* n) : n(n), parent(0), d(), h(), e(0) {}
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d, Edge<N, E>* e)
: n(n), parent(parent), d(d), h(), e(e) {}
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d, C h, Edge<N, E>* e)
: n(n), parent(parent), d(d), h(h), e(e) {}
RouteNode() : n(0), parent(0), d(), h() {}
RouteNode(Node<N, E>* n) : n(n), parent(0), d(), h() {}
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d)
: n(n), parent(parent), d(d), h() {}
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d, C h)
: n(n), parent(parent), d(d), h(h) {}
Node<N, E>* n;
Node<N, E>* parent;
// the cost so far
C d;
// the heuristical remaining cost + the cost so far
C h;
Edge<N, E>* e;
bool operator<(const RouteNode<N, E, C>& p) const {
return h > p.h || (h == p.h && d > p.d);
}
bool operator<(const RouteNode<N, E, C>& p) const { return h > p.h; }
};
template <typename N, typename E, typename C>
@ -55,7 +53,8 @@ class Dijkstra : public ShortestPath<Dijkstra> {
using PQ = std::priority_queue<RouteNode<N, E, C> >;
template <typename N, typename E, typename C>
struct CostFunc : public ShortestPath::CostFunc<N, E, C> {
struct CostFunc : public util::graph::CostFunc<N, E, C> {
virtual ~CostFunc() = default;
C operator()(const Edge<N, E>* from, const Node<N, E>* n,
const Edge<N, E>* to) const {
UNUSED(from);
@ -66,7 +65,8 @@ class Dijkstra : public ShortestPath<Dijkstra> {
};
template <typename N, typename E, typename C>
struct HeurFunc : public ShortestPath::HeurFunc<N, E, C> {
struct HeurFunc : public util::graph::HeurFunc<N, E, C> {
virtual ~HeurFunc() = default;
C operator()(const Edge<N, E>* from,
const std::set<Edge<N, E>*>& to) const {
UNUSED(from);
@ -78,26 +78,29 @@ class Dijkstra : public ShortestPath<Dijkstra> {
template <typename N, typename E, typename C>
static std::unordered_map<Node<N, E>*, C> shortestPathImpl(
Node<N, E>* from, const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc, const ShortestPath::HeurFunc<N, E, C>&,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>&,
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
std::unordered_map<Node<N, E>*, NList<N, E>*> resNode);
template <typename N, typename E, typename C>
static C shortestPathImpl(const std::set<Node<N, E>*> from, const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
static C shortestPathImpl(const std::set<Node<N, E>*> from,
const std::set<Node<N, E>*>& to,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes);
template <typename N, typename E, typename C>
static C shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes);
template <typename N, typename E, typename C>
static void relax(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc, PQ<N, E, C>& pq);
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQ<N, E, C>& pq);
template <typename N, typename E, typename C>
static void buildPath(Node<N, E>* curN, Settled<N, E, C>& settled,
@ -107,7 +110,7 @@ class Dijkstra : public ShortestPath<Dijkstra> {
};
#include "util/graph/Dijkstra.tpp"
}
}
} // namespace graph
} // namespace util
#endif // UTIL_GRAPH_DIJKSTRA_H_

View file

@ -5,8 +5,8 @@
// _____________________________________________________________________________
template <typename N, typename E, typename C>
C Dijkstra::shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes) {
if (from->getOutDeg() == 0) return costFunc.inf();
@ -18,12 +18,12 @@ C Dijkstra::shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
RouteNode<N, E, C> cur;
while (!pq.empty()) {
Dijkstra::ITERS++;
if (costFunc.inf() <= pq.top().h) return costFunc.inf();
if (settled.find(pq.top().n) != settled.end()) {
pq.pop();
continue;
}
Dijkstra::ITERS++;
cur = pq.top();
pq.pop();
@ -49,8 +49,8 @@ C Dijkstra::shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
template <typename N, typename E, typename C>
C Dijkstra::shortestPathImpl(const std::set<Node<N, E>*> from,
const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes) {
Settled<N, E, C> settled;
PQ<N, E, C> pq;
@ -61,12 +61,16 @@ C Dijkstra::shortestPathImpl(const std::set<Node<N, E>*> from,
RouteNode<N, E, C> cur;
while (!pq.empty()) {
Dijkstra::ITERS++;
if (settled.find(pq.top().n) != settled.end()) {
pq.pop();
continue;
if (costFunc.inf() <= pq.top().h) return costFunc.inf();
auto se = settled.find(pq.top().n);
if (se != settled.end()) {
// to allow non-consistent heuristics
if (se->second.d <= pq.top().d) {
pq.pop();
continue;
}
}
Dijkstra::ITERS++;
cur = pq.top();
pq.pop();
@ -92,8 +96,8 @@ C Dijkstra::shortestPathImpl(const std::set<Node<N, E>*> from,
template <typename N, typename E, typename C>
std::unordered_map<Node<N, E>*, C> Dijkstra::shortestPathImpl(
Node<N, E>* from, const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
std::unordered_map<Node<N, E>*, NList<N, E>*> resNodes) {
std::unordered_map<Node<N, E>*, C> costs;
@ -112,12 +116,16 @@ std::unordered_map<Node<N, E>*, C> Dijkstra::shortestPathImpl(
RouteNode<N, E, C> cur;
while (!pq.empty()) {
Dijkstra::ITERS++;
if (settled.find(pq.top().n) != settled.end()) {
pq.pop();
continue;
if (costFunc.inf() <= pq.top().h) return costs;
auto se = settled.find(pq.top().n);
if (se != settled.end()) {
// to allow non-consistent heuristics
if (se->second.d <= pq.top().d) {
pq.pop();
continue;
}
}
Dijkstra::ITERS++;
cur = pq.top();
pq.pop();
@ -147,29 +155,41 @@ std::unordered_map<Node<N, E>*, C> Dijkstra::shortestPathImpl(
// _____________________________________________________________________________
template <typename N, typename E, typename C>
void Dijkstra::relax(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc, PQ<N, E, C>& pq) {
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQ<N, E, C>& pq) {
for (auto edge : cur.n->getAdjListOut()) {
C newC = costFunc(cur.n, edge, edge->getOtherNd(cur.n));
newC = cur.d + newC;
if (newC < cur.d) continue; // cost overflow!
if (costFunc.inf() <= newC) continue;
const C& newH = newC + heurFunc(edge->getOtherNd(cur.n), to);
// addition done here to avoid it in the PQ
auto h = heurFunc(edge->getOtherNd(cur.n), to);
if (costFunc.inf() <= h) continue;
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH, &(*edge));
const C& newH = newC + h;
if (newH < newC) continue; // cost overflow!
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH);
}
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
void Dijkstra::buildPath(Node<N, E>* curN,
Settled<N, E, C>& settled, NList<N, E>* resNodes,
EList<N, E>* resEdges) {
while (true) {
void Dijkstra::buildPath(Node<N, E>* curN, Settled<N, E, C>& settled,
NList<N, E>* resNodes, EList<N, E>* resEdges) {
while (resNodes || resEdges) {
const RouteNode<N, E, C>& curNode = settled[curN];
if (resNodes) resNodes->push_back(curNode.n);
if (!curNode.parent) break;
if (resEdges) resEdges->push_back(curNode.e);
if (resEdges) {
for (auto e : curNode.n->getAdjListIn()) {
if (e->getOtherNd(curNode.n) == curNode.parent) resEdges->push_back(e);
}
}
curN = curNode.parent;
}
}

View file

@ -21,8 +21,6 @@ using UndirEdge = Edge<N, E>;
template <typename N, typename E>
class DirGraph : public Graph<N, E> {
public:
explicit DirGraph();
using Graph<N, E>::addEdg;
Node<N, E>* addNd();

View file

@ -2,10 +2,6 @@
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
// _____________________________________________________________________________
template <typename N, typename E>
DirGraph<N, E>::DirGraph() {}
// _____________________________________________________________________________
template <typename N, typename E>
Node<N, E>* DirGraph<N, E>::addNd(const N& pl) {
@ -21,7 +17,7 @@ Node<N, E>* DirGraph<N, E>::addNd() {
// _____________________________________________________________________________
template <typename N, typename E>
Node<N, E>* DirGraph<N, E>::addNd(DirNode<N, E>* n) {
auto ins = Graph<N, E>::getNds()->insert(n);
auto ins = Graph<N, E>::_nodes.insert(n);
return *ins.first;
}

View file

@ -69,7 +69,7 @@ void DirNode<N, E>::removeEdge(Edge<N, E>* e) {
if (p != _adjListIn.end()) _adjListIn.erase(p);
}
}
//
// _____________________________________________________________________________
template <typename N, typename E>
bool DirNode<N, E>::hasEdgeIn(const Edge<N, E>* e) const {
@ -139,6 +139,8 @@ const N& DirNode<N, E>::pl() const {
// _____________________________________________________________________________
template <typename N, typename E>
bool DirNode<N, E>::adjInContains(const Edge<N, E>* e) const {
// this is faster than a binary search as the adjacency lists are typically
// very small for our use cases
for (size_t i = 0; i < _adjListIn.size(); i++)
if (_adjListIn[i] == e) return true;
return false;
@ -147,6 +149,8 @@ bool DirNode<N, E>::adjInContains(const Edge<N, E>* e) const {
// _____________________________________________________________________________
template <typename N, typename E>
bool DirNode<N, E>::adjOutContains(const Edge<N, E>* e) const {
// this is faster than a binary search as the adjacency lists are typically
// very small for our use cases
for (size_t i = 0; i < _adjListOut.size(); i++)
if (_adjListOut[i] == e) return true;
return false;

View file

@ -7,48 +7,84 @@
#include <limits>
#include <list>
#include <queue>
#include <set>
#include <unordered_map>
#include "util/PriorityQueue.h"
#include "util/graph/Edge.h"
#include "util/graph/Graph.h"
#include "util/graph/Node.h"
#include "util/graph/ShortestPath.h"
#include "util/graph/radix_heap.h"
#include "util/graph/robin/robin_map.h"
namespace util {
namespace graph {
using util::graph::Edge;
using util::graph::Graph;
using util::graph::Node;
using util::graph::Edge;
// edge-based dijkstra - settles edges instead of nodes
class EDijkstra : public ShortestPath<EDijkstra> {
public:
template <typename N, typename E, typename C>
struct RouteEdge {
RouteEdge() : e(0), parent(0), d(), h(), n(0) {}
RouteEdge(Edge<N, E>* e) : e(e), parent(0), d(), h(), n(0) {}
RouteEdge() : e(0), parent(0), d(), n(0) {}
RouteEdge(Edge<N, E>* e) : e(e), parent(0), d(), n(0) {}
RouteEdge(Edge<N, E>* e, Edge<N, E>* parent, Node<N, E>* n, C d)
: e(e), parent(parent), d(d), h(), n(n) {}
RouteEdge(Edge<N, E>* e, Edge<N, E>* parent, Node<N, E>* n, C d, C h)
: e(e), parent(parent), d(d), h(h), n(n) {}
: e(e), parent(parent), d(d), n(n) {}
Edge<N, E>* e;
Edge<N, E>* parent;
// the cost so far
C d;
C h;
Node<N, E>* n;
bool operator<(const RouteEdge<N, E, C>& p) const {
return h > p.h || (h == p.h && d > p.d);
}
};
template <typename N, typename E, typename C>
struct CostFunc : public ShortestPath::CostFunc<N, E, C> {
struct RouteEdgeInit {
RouteEdgeInit() : e(0), parent(0), d(), dwi(), n(0) {}
RouteEdgeInit(Edge<N, E>* e) : e(e), parent(0), d(), dwi(), n(0) {}
RouteEdgeInit(Edge<N, E>* e, Edge<N, E>* parent, Node<N, E>* n, C d)
: e(e), parent(parent), d(d), dwi(), n(n) {}
RouteEdgeInit(Edge<N, E>* e, Edge<N, E>* parent, Node<N, E>* n, C d, C dwi)
: e(e), parent(parent), d(d), dwi(dwi), n(n) {}
Edge<N, E>* e;
Edge<N, E>* parent;
// the cost so far
C d;
// the cost without the initial costs
C dwi;
Node<N, E>* n;
};
template <typename N, typename E, typename C>
struct RouteEdgeInitNoRes {
RouteEdgeInitNoRes() : e(0), parent(0), d(), dwi() {}
RouteEdgeInitNoRes(Edge<N, E>* e) : e(e), parent(0), d(), dwi() {}
RouteEdgeInitNoRes(Edge<N, E>* e, Edge<N, E>* parent, C d)
: e(e), parent(parent), d(d), dwi() {}
RouteEdgeInitNoRes(Edge<N, E>* e, Edge<N, E>* parent, C d, C dwi)
: e(e), parent(parent), d(d), dwi(dwi) {}
Edge<N, E>* e;
Edge<N, E>* parent;
// the cost so far
C d;
// the cost without the initial costs
C dwi;
};
template <typename N, typename E, typename C>
struct CostFunc : public util::graph::CostFunc<N, E, C> {
C operator()(const Node<N, E>* from, const Edge<N, E>* e,
const Node<N, E>* to) const {
UNUSED(from);
@ -59,7 +95,7 @@ class EDijkstra : public ShortestPath<EDijkstra> {
};
template <typename N, typename E, typename C>
struct HeurFunc : public ShortestPath::HeurFunc<N, E, C> {
struct HeurFunc : public util::graph::HeurFunc<N, E, C> {
C operator()(const Node<N, E>* from,
const std::set<Node<N, E>*>& to) const {
UNUSED(from);
@ -69,71 +105,119 @@ class EDijkstra : public ShortestPath<EDijkstra> {
};
template <typename N, typename E, typename C>
using Settled = std::unordered_map<Edge<N, E>*, RouteEdge<N, E, C> >;
using Settled = tsl::robin_map<Edge<N, E>*, RouteEdge<N, E, C>>;
template <typename N, typename E, typename C>
using PQ = std::priority_queue<RouteEdge<N, E, C> >;
using PQ = radix_heap::pair_radix_heap<C, RouteEdge<N, E, C>>;
template <typename N, typename E, typename C>
using SettledInit = tsl::robin_map<Edge<N, E>*, RouteEdgeInit<N, E, C>>;
template <typename N, typename E, typename C>
using SettledInitNoRes = tsl::robin_map<Edge<N, E>*, RouteEdgeInitNoRes<N, E, C>>;
template <typename N, typename E, typename C>
using PQInit = radix_heap::pair_radix_heap<C, RouteEdgeInit<N, E, C>>;
template <typename N, typename E, typename C>
using PQInitNoRes = radix_heap::pair_radix_heap<C, RouteEdgeInitNoRes<N, E, C>>;
template <typename N, typename E, typename C>
static C shortestPathImpl(const std::set<Edge<N, E>*> from,
const std::set<Edge<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes);
template <typename N, typename E, typename C>
static C shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes);
template <typename N, typename E, typename C>
static C shortestPathImpl(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes);
template <typename N, typename E, typename C>
static C shortestPathImpl(const std::set<Edge<N, E>*>& from,
const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes);
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPathImpl(
const std::set<Edge<N, E>*>& from,
const ShortestPath::CostFunc<N, E, C>& costFunc, bool rev);
const util::graph::CostFunc<N, E, C>& costFunc, bool rev);
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPathImpl(
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes);
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPathImpl(
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
const std::unordered_map<Edge<N, E>*, C>& initCosts, C stall,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes);
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, std::pair<Edge<N, E>*, C>>
shortestPathImpl(const std::set<Edge<N, E>*>& from,
const std::set<Edge<N, E>*>& to,
const std::unordered_map<Edge<N, E>*, C>& initCosts, C stall,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc);
template <typename N, typename E, typename C>
static void buildPath(Edge<N, E>* curE, const Settled<N, E, C>& settled,
NList<N, E>* resNodes, EList<N, E>* resEdges);
template <typename N, typename E, typename C>
static void buildPathInit(Edge<N, E>* curE,
const SettledInit<N, E, C>& settled,
NList<N, E>* resNodes, EList<N, E>* resEdges);
template <typename N, typename E, typename C>
static inline void relax(RouteEdge<N, E, C>& cur,
const std::set<Edge<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQ<N, E, C>& pq);
template <typename N, typename E, typename C>
static inline void relaxInit(RouteEdgeInit<N, E, C>& cur,
const std::set<Edge<N, E>*>& to, C stall,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQInit<N, E, C>& pq);
template <typename N, typename E, typename C>
static inline void relaxInitNoResEdgs(RouteEdgeInitNoRes<N, E, C>& cur,
const std::set<Edge<N, E>*>& to, C stall,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQInitNoRes<N, E, C>& pq);
template <typename N, typename E, typename C>
static void relaxInv(RouteEdge<N, E, C>& cur,
const ShortestPath::CostFunc<N, E, C>& costFunc,
PQ<N, E, C>& pq);
const util::graph::CostFunc<N, E, C>& costFunc,
PQ<N, E, C>& pq);
static size_t ITERS;
};
#include "util/graph/EDijkstra.tpp"
}
}
} // namespace graph
} // namespace util
#endif // UTIL_GRAPH_DIJKSTRA_H_

View file

@ -5,8 +5,8 @@
// _____________________________________________________________________________
template <typename N, typename E, typename C>
C EDijkstra::shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes) {
std::set<Edge<N, E>*> frEs;
std::set<Edge<N, E>*> toEs;
@ -28,8 +28,8 @@ C EDijkstra::shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
// _____________________________________________________________________________
template <typename N, typename E, typename C>
C EDijkstra::shortestPathImpl(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes) {
std::set<Edge<N, E>*> frEs;
std::set<Edge<N, E>*> toEs;
@ -49,8 +49,8 @@ C EDijkstra::shortestPathImpl(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
template <typename N, typename E, typename C>
C EDijkstra::shortestPathImpl(const std::set<Edge<N, E>*> from,
const std::set<Edge<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes) {
if (from.size() == 0 || to.size() == 0) return costFunc.inf();
@ -63,20 +63,24 @@ C EDijkstra::shortestPathImpl(const std::set<Edge<N, E>*> from,
for (auto e : from) {
C c = costFunc(0, 0, e);
C h = heurFunc(e, to);
pq.emplace(e, (Edge<N, E>*)0, (Node<N, E>*)0, c, c + h);
pq.push(c + h, {e, (Edge<N, E>*)0, (Node<N, E>*)0, c});
}
RouteEdge<N, E, C> cur;
while (!pq.empty()) {
if (costFunc.inf() <= pq.topKey()) return costFunc.inf();
auto se = settled.find(pq.topVal().e);
if (se != settled.end()) {
// to allow non-consistent heuristics
if (se->second.d <= pq.topVal().d) {
pq.pop();
continue;
}
}
EDijkstra::ITERS++;
if (settled.find(pq.top().e) != settled.end()) {
pq.pop();
continue;
}
cur = pq.top();
cur = pq.topVal();
pq.pop();
settled[cur.e] = cur;
@ -99,8 +103,8 @@ C EDijkstra::shortestPathImpl(const std::set<Edge<N, E>*> from,
// _____________________________________________________________________________
template <typename N, typename E, typename C>
std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
const std::set<Edge<N, E>*>& from, const ShortestPath::CostFunc<N, E, C>& costFunc,
bool rev) {
const std::set<Edge<N, E>*>& from,
const util::graph::CostFunc<N, E, C>& costFunc, bool rev) {
std::unordered_map<Edge<N, E>*, C> costs;
Settled<N, E, C> settled;
@ -109,20 +113,23 @@ std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
std::set<Edge<N, E>*> to;
for (auto e : from) {
pq.emplace(e, (Edge<N, E>*)0, (Node<N, E>*)0, costFunc(0, 0, e), C());
pq.push(C(), {e, (Edge<N, E>*)0, (Node<N, E>*)0, costFunc(0, 0, e)});
}
RouteEdge<N, E, C> cur;
while (!pq.empty()) {
auto se = settled.find(pq.topVal().e);
if (se != settled.end()) {
// to allow non-consistent heuristics
if (se->second.d <= pq.topVal().d) {
pq.pop();
continue;
}
}
EDijkstra::ITERS++;
if (settled.find(pq.top().e) != settled.end()) {
pq.pop();
continue;
}
cur = pq.top();
cur = pq.topVal();
pq.pop();
settled[cur.e] = cur;
@ -143,8 +150,8 @@ std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
template <typename N, typename E, typename C>
std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
std::unordered_map<Edge<N, E>*, C> costs;
@ -160,19 +167,23 @@ std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
C c = costFunc(0, 0, from);
C h = heurFunc(from, to);
pq.emplace(from, (Edge<N, E>*)0, (Node<N, E>*)0, c, c + h);
pq.push(c + h, {from, (Edge<N, E>*)0, (Node<N, E>*)0, c});
RouteEdge<N, E, C> cur;
while (!pq.empty()) {
if (costFunc.inf() <= pq.topKey()) return costs;
auto se = settled.find(pq.topVal().e);
if (se != settled.end()) {
// to allow non-consistent heuristics
if (se->second.d <= pq.topVal().d) {
pq.pop();
continue;
}
}
EDijkstra::ITERS++;
if (settled.find(pq.top().e) != settled.end()) {
pq.pop();
continue;
}
cur = pq.top();
cur = pq.topVal();
pq.pop();
settled[cur.e] = cur;
@ -193,10 +204,148 @@ std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
// _____________________________________________________________________________
template <typename N, typename E, typename C>
void EDijkstra::relaxInv(RouteEdge<N, E, C>& cur,
const ShortestPath::CostFunc<N, E, C>& costFunc,
PQ<N, E, C>& pq) {
std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
const std::unordered_map<Edge<N, E>*, C>& initCosts, C stall,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
/**
* Shortest paths from the set <from> to ALL nodes in <TO>, but
* init <from> nodes with costs (this is equivalent to adding an auxiliary
* node S, connecting it with directed edges to all <from>, setting the
* costs of these edges to the initial costs and run a 1->N Dijkstra from S
**/
std::unordered_map<Edge<N, E>*, C> costs;
if (to.size() == 0) return costs;
// init costs with inf
for (auto e : to) costs[e] = costFunc.inf();
SettledInit<N, E, C> settled;
PQInit<N, E, C> pq;
size_t found = 0;
// put all nodes in from onto the PQ with their initial costs, also set
// the initial cost as a heuristic starting point!
for (auto e : from) {
C iCost = initCosts.find(e)->second;
assert(iCost + heurFunc(e, to) >= iCost);
pq.push(iCost + heurFunc(e, to),
{e, (Edge<N, E>*)0, (Node<N, E>*)0, iCost});
}
RouteEdgeInit<N, E, C> cur;
while (!pq.empty()) {
if (costFunc.inf() <= pq.topKey()) return costs;
if (stall <= pq.topVal().dwi) return costs;
auto se = settled.find(pq.topVal().e);
if (se != settled.end()) {
// to allow non-consistent heuristics
if (se->second.d <= pq.topVal().d) {
pq.pop();
continue;
}
}
EDijkstra::ITERS++;
cur = pq.topVal();
pq.pop();
settled[cur.e] = cur;
if (to.find(cur.e) != to.end()) {
found++;
costs[cur.e] = cur.d;
buildPathInit(cur.e, settled, resNodes[cur.e], resEdges[cur.e]);
if (found == to.size()) return costs;
}
relaxInit(cur, to, stall, costFunc, heurFunc, pq);
}
return costs;
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
std::unordered_map<Edge<N, E>*, std::pair<Edge<N, E>*, C>>
EDijkstra::shortestPathImpl(const std::set<Edge<N, E>*>& from,
const std::set<Edge<N, E>*>& to,
const std::unordered_map<Edge<N, E>*, C>& initCosts,
C stall,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc) {
/**
* Shortest paths from the set <from> to ALL nodes in <TO>, but
* init <from> nodes with costs (this is equivalent to adding an auxiliary
* node S, connecting it with directed edges to all <from>, setting the
* costs of these edges to the initial costs and run a 1->N Dijkstra from S
**/
std::unordered_map<Edge<N, E>*, std::pair<Edge<N, E>*, C>> costs;
if (to.size() == 0) return costs;
// init costs with inf
for (auto e : to) costs[e] = {0, costFunc.inf()};
SettledInitNoRes<N, E, C> settled;
PQInitNoRes<N, E, C> pq;
size_t found = 0;
// put all nodes in from onto the PQ with their initial costs, also set
// the initial cost as a heuristic starting point!
// set the parent to the edge itself - in this version, the parent is ALWAYS
// the start edge, as we don't need the exact paths later on
for (auto e : from) {
C iCost = initCosts.find(e)->second;
assert(iCost + heurFunc(e, to) >= iCost);
pq.push(iCost + heurFunc(e, to), {e, e, iCost});
}
RouteEdgeInitNoRes<N, E, C> cur;
while (!pq.empty()) {
if (costFunc.inf() <= pq.topKey()) return costs;
if (stall <= pq.topVal().dwi) return costs;
auto se = settled.find(pq.topVal().e);
if (se != settled.end()) {
// to allow non-consistent heuristics
if (se->second.d <= pq.topVal().d) {
pq.pop();
continue;
}
}
EDijkstra::ITERS++;
cur = pq.topVal();
pq.pop();
settled[cur.e] = cur;
if (to.find(cur.e) != to.end()) {
found++;
costs[cur.e] = {cur.parent, cur.d};
if (found == to.size()) return costs;
}
relaxInitNoResEdgs(cur, to, stall, costFunc, heurFunc, pq);
}
return costs;
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
void EDijkstra::relaxInv(RouteEdge<N, E, C>& cur,
const util::graph::CostFunc<N, E, C>& costFunc,
PQ<N, E, C>& pq) {
// handling undirected graph makes no sense here
for (const auto edge : cur.e->getFrom()->getAdjListIn()) {
@ -204,42 +353,161 @@ void EDijkstra::relaxInv(RouteEdge<N, E, C>& cur,
C newC = costFunc(edge, cur.e->getFrom(), cur.e);
newC = cur.d + newC;
if (costFunc.inf() <= newC) continue;
if (newC < cur.d) continue; // cost overflow!
pq.emplace(edge, cur.e, cur.e->getFrom(), newC, C());
pq.push(C(), {edge, cur.e, cur.e->getFrom(), newC});
}
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
void EDijkstra::relax(RouteEdge<N, E, C>& cur, const std::set<Edge<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
PQ<N, E, C>& pq) {
if (cur.e->getFrom()->hasEdgeIn(cur.e)) {
void EDijkstra::relaxInitNoResEdgs(
RouteEdgeInitNoRes<N, E, C>& cur, const std::set<Edge<N, E>*>& to, C stall,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc, PQInitNoRes<N, E, C>& pq) {
if (cur.e->getFrom()->hasEdgeIn(cur.e) &&
cur.e->getFrom() != cur.e->getTo()) {
// for undirected graphs
for (const auto edge : cur.e->getFrom()->getAdjListOut()) {
if (edge == cur.e) continue;
C newC = costFunc(cur.e, cur.e->getFrom(), edge);
C newDwi = cur.dwi + newC;
if (stall <= newDwi) continue;
if (costFunc.inf() <= newC) continue;
newC = cur.d + newC;
if (newC < cur.d) continue; // cost overflow!
if (costFunc.inf() <= newC) continue;
const C& h = heurFunc(edge, to);
if (costFunc.inf() <= h) continue;
newC = cur.d + newC;
const C& newH = newC + h;
if (newH < newC) continue; // cost overflow!
pq.emplace(edge, cur.e, cur.e->getFrom(), newC, newH);
pq.push(newH, {edge, cur.parent, newC, newDwi});
}
}
for (const auto edge : cur.e->getTo()->getAdjListOut()) {
if (edge == cur.e) continue;
C newC = costFunc(cur.e, cur.e->getTo(), edge);
C newDwi = cur.dwi + newC;
if (stall <= newDwi) continue;
if (costFunc.inf() <= newC) continue;
newC = cur.d + newC;
if (newC < cur.d) continue; // cost overflow!
if (costFunc.inf() <= newC) continue;
const C& h = heurFunc(edge, to);
if (costFunc.inf() <= h) continue;
const C& newH = newC + h;
if (newH < newC) continue; // cost overflow!
pq.emplace(edge, cur.e, cur.e->getTo(), newC, newH);
pq.push(newH, {edge, cur.parent, newC, newDwi});
}
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
void EDijkstra::relaxInit(RouteEdgeInit<N, E, C>& cur,
const std::set<Edge<N, E>*>& to, C stall,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQInit<N, E, C>& pq) {
if (cur.e->getFrom()->hasEdgeIn(cur.e) &&
cur.e->getFrom() != cur.e->getTo()) {
// for undirected graphs
for (const auto edge : cur.e->getFrom()->getAdjListOut()) {
if (edge == cur.e) continue;
C newC = costFunc(cur.e, cur.e->getFrom(), edge);
C newDwi = cur.dwi + newC;
if (stall <= newDwi) continue;
if (costFunc.inf() <= newC) continue;
newC = cur.d + newC;
if (newC < cur.d) continue; // cost overflow!
if (costFunc.inf() <= newC) continue;
const C& h = heurFunc(edge, to);
if (costFunc.inf() <= h) continue;
newC = cur.d + newC;
const C& newH = newC + h;
if (newH < newC) continue; // cost overflow!
pq.push(newH, {edge, cur.e, cur.e->getFrom(), newC, newDwi});
}
}
for (const auto edge : cur.e->getTo()->getAdjListOut()) {
if (edge == cur.e) continue;
C newC = costFunc(cur.e, cur.e->getTo(), edge);
C newDwi = cur.dwi + newC;
if (stall <= newDwi) continue;
if (costFunc.inf() <= newC) continue;
newC = cur.d + newC;
if (newC < cur.d) continue; // cost overflow!
if (costFunc.inf() <= newC) continue;
const C& h = heurFunc(edge, to);
if (costFunc.inf() <= h) continue;
const C& newH = newC + h;
if (newH < newC) continue; // cost overflow!
pq.push(newH, {edge, cur.e, cur.e->getTo(), newC, newDwi});
}
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
void EDijkstra::relax(RouteEdge<N, E, C>& cur, const std::set<Edge<N, E>*>& to,
const util::graph::CostFunc<N, E, C>& costFunc,
const util::graph::HeurFunc<N, E, C>& heurFunc,
PQ<N, E, C>& pq) {
if (cur.e->getFrom()->hasEdgeIn(cur.e) &&
cur.e->getFrom() != cur.e->getTo()) {
// for undirected graphs
for (const auto edge : cur.e->getFrom()->getAdjListOut()) {
if (edge == cur.e) continue;
C newC = costFunc(cur.e, cur.e->getFrom(), edge);
if (costFunc.inf() <= newC) continue;
newC = cur.d + newC;
if (newC < cur.d) continue; // cost overflow!
if (costFunc.inf() <= newC) continue;
const C& h = heurFunc(edge, to);
if (costFunc.inf() <= h) continue;
const C& newH = newC + h;
if (newH < newC) continue; // cost overflow!
pq.push(newH, {edge, cur.e, cur.e->getFrom(), newC});
}
}
for (const auto edge : cur.e->getTo()->getAdjListOut()) {
if (edge == cur.e) continue;
C newC = costFunc(cur.e, cur.e->getTo(), edge);
if (costFunc.inf() <= newC) continue;
newC = cur.d + newC;
if (newC < cur.d) continue; // cost overflow!
if (costFunc.inf() <= newC) continue;
const C& h = heurFunc(edge, to);
if (costFunc.inf() <= h) continue;
const C& newH = newC + h;
if (newH < newC) continue; // cost overflow!
pq.push(newH, {edge, cur.e, cur.e->getTo(), newC});
}
}
@ -249,7 +517,22 @@ void EDijkstra::buildPath(Edge<N, E>* curE, const Settled<N, E, C>& settled,
NList<N, E>* resNodes, EList<N, E>* resEdges) {
const RouteEdge<N, E, C>* curEdge = &settled.find(curE)->second;
if (resNodes) resNodes->push_back(curEdge->e->getOtherNd(curEdge->n));
while (true) {
while (resNodes || resEdges) {
if (resNodes && curEdge->n) resNodes->push_back(curEdge->n);
if (resEdges) resEdges->push_back(curEdge->e);
if (!curEdge->parent) break;
curEdge = &settled.find(curEdge->parent)->second;
}
}
// _____________________________________________________________________________
template <typename N, typename E, typename C>
void EDijkstra::buildPathInit(Edge<N, E>* curE,
const SettledInit<N, E, C>& settled,
NList<N, E>* resNodes, EList<N, E>* resEdges) {
const RouteEdgeInit<N, E, C>* curEdge = &settled.find(curE)->second;
if (resNodes) resNodes->push_back(curEdge->e->getOtherNd(curEdge->n));
while (resNodes || resEdges) {
if (resNodes && curEdge->n) resNodes->push_back(curEdge->n);
if (resEdges) resEdges->push_back(curEdge->e);
if (!curEdge->parent) break;

View file

@ -21,9 +21,6 @@ class Edge {
Node<N, E>* getOtherNd(const Node<N, E>* notNode) const;
void setFrom(Node<N, E>* from);
void setTo(Node<N, E>* to);
E& pl();
const E& pl() const;

View file

@ -30,15 +30,16 @@ class Graph {
virtual Node<N, E>* mergeNds(Node<N, E>* a, Node<N, E>* b) = 0;
const std::set<Node<N, E>*>& getNds() const;
std::set<Node<N, E>*>* getNds();
static Node<N, E>* sharedNode(const Edge<N, E>* a, const Edge<N, E>* b);
typename std::set<Node<N, E>*>::iterator delNd(Node<N, E>* n);
typename std::set<Node<N, E>*>::iterator delNd(
typename std::set<Node<N, E>*>::iterator i);
void delEdg(Node<N, E>* from, Node<N, E>* to);
private:
std::set<Node<N, E>*> _nodes;
protected:
std::set<Node<N, E>*> _nodes;
};
#include "util/graph/Graph.tpp"

View file

@ -20,12 +20,6 @@ const std::set<Node<N, E>*>& Graph<N, E>::getNds() const {
return _nodes;
}
// _____________________________________________________________________________
template <typename N, typename E>
std::set<Node<N, E>*>* Graph<N, E>::getNds() {
return &_nodes;
}
// _____________________________________________________________________________
template <typename N, typename E>
typename std::set<Node<N, E>*>::iterator Graph<N, E>::delNd(
@ -65,6 +59,17 @@ Edge<N, E>* Graph<N, E>::getEdg(Node<N, E>* from, Node<N, E>* to) {
return 0;
}
// _____________________________________________________________________________
template <typename N, typename E>
Node<N, E>* Graph<N, E>::sharedNode(const Edge<N, E>* a, const Edge<N, E>* b) {
Node<N, E>* r = 0;
if (a->getFrom() == b->getFrom() || a->getFrom() == b->getTo())
r = a->getFrom();
if (a->getTo() == b->getFrom() || a->getTo() == b->getTo()) r = a->getTo();
return r;
}
// _____________________________________________________________________________
template <typename N, typename E>
const Edge<N, E>* Graph<N, E>::getEdg(Node<N, E>* from, Node<N, E>* to) const {

View file

@ -5,8 +5,8 @@
#ifndef UTIL_GRAPH_NODE_H_
#define UTIL_GRAPH_NODE_H_
#include <vector>
#include <cstddef>
#include <vector>
namespace util {
namespace graph {
@ -43,6 +43,7 @@ class Node {
template <typename N, typename E>
inline Node<N, E>::~Node() {}
}}
} // namespace graph
} // namespace util
#endif // UTIL_GRAPH_NODE_H_

View file

@ -19,9 +19,46 @@
namespace util {
namespace graph {
using util::graph::Edge;
using util::graph::Graph;
using util::graph::Node;
using util::graph::Edge;
template <typename N, typename E>
using EList = std::vector<Edge<N, E>*>;
template <typename N, typename E>
using NList = std::vector<Node<N, E>*>;
template <typename N, typename E, typename C>
struct CostFunc {
virtual C operator()(const Node<N, E>* from, const Edge<N, E>* e,
const Node<N, E>* to) const = 0;
virtual C operator()(const Edge<N, E>* from, const Node<N, E>* n,
const Edge<N, E>* to) const = 0;
virtual C inf() const = 0;
};
template <typename N, typename E, typename C>
struct HeurFunc {
virtual C operator()(const Node<N, E>* a,
const std::set<Node<N, E>*>& b) const = 0;
virtual C operator()(const Edge<N, E>* a,
const std::set<Edge<N, E>*>& b) const = 0;
};
template <typename N, typename E, typename C>
struct ZeroHeurFunc : public HeurFunc<N, E, C> {
C operator()(const Node<N, E>* a, const std::set<Node<N, E>*>& b) const {
UNUSED(a);
UNUSED(b);
return C();
}
C operator()(const Edge<N, E>* a, const std::set<Edge<N, E>*>& b) const {
UNUSED(a);
UNUSED(b);
return C();
}
};
// shortest path base class
template <class D>
@ -33,37 +70,6 @@ class ShortestPath {
template <typename N, typename E>
using NList = std::vector<Node<N, E>*>;
template <typename N, typename E, typename C>
struct CostFunc {
virtual C operator()(const Node<N, E>* from, const Edge<N, E>* e,
const Node<N, E>* to) const = 0;
virtual C operator()(const Edge<N, E>* from, const Node<N, E>* n,
const Edge<N, E>* to) const = 0;
virtual C inf() const = 0;
};
template <typename N, typename E, typename C>
struct HeurFunc {
virtual C operator()(const Node<N, E>* a,
const std::set<Node<N, E>*>& b) const = 0;
virtual C operator()(const Edge<N, E>* a,
const std::set<Edge<N, E>*>& b) const = 0;
};
template <typename N, typename E, typename C>
struct ZeroHeurFunc : public HeurFunc<N, E, C> {
C operator()(const Node<N, E>* a, const std::set<Node<N, E>*>& b) const {
UNUSED(a);
UNUSED(b);
return C();
}
C operator()(const Edge<N, E>* a, const std::set<Edge<N, E>*>& b) const {
UNUSED(a);
UNUSED(b);
return C();
}
};
template <typename N, typename E, typename C>
static C shortestPath(Node<N, E>* from, const std::set<Node<N, E>*>& to,
const CostFunc<N, E, C>& costFunc,
@ -74,7 +80,8 @@ class ShortestPath {
}
template <typename N, typename E, typename C>
static C shortestPath(const std::set<Node<N, E>*> from, const std::set<Node<N, E>*>& to,
static C shortestPath(const std::set<Node<N, E>*> from,
const std::set<Node<N, E>*>& to,
const CostFunc<N, E, C>& costFunc,
const HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes) {
@ -82,6 +89,15 @@ class ShortestPath {
resNodes);
}
template <typename N, typename E, typename C>
static C shortestPath(const std::set<Node<N, E>*> from,
const std::set<Node<N, E>*>& to,
const CostFunc<N, E, C>& costFunc,
EList<N, E>* resEdges, NList<N, E>* resNodes) {
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(),
resEdges, resNodes);
}
template <typename N, typename E, typename C>
static C shortestPath(Node<N, E>* from, const std::set<Node<N, E>*>& to,
const CostFunc<N, E, C>& costFunc,
@ -160,6 +176,14 @@ class ShortestPath {
return shortestPath(from, tos, costFunc, resEdges, resNodes);
}
template <typename N, typename E, typename C>
static C shortestPath(Node<N, E>* from, const std::set<Node<N, E>*>& tos,
const CostFunc<N, E, C>& costFunc) {
EList<N, E>* el = 0;
NList<N, E>* nl = 0;
return shortestPath(from, tos, costFunc, el, nl);
}
template <typename N, typename E, typename C>
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
const CostFunc<N, E, C>& costFunc) {
@ -263,11 +287,22 @@ class ShortestPath {
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges, nl);
}
template <typename N, typename E, typename C>
static C shortestPath(Edge<N, E>* from, Edge<N, E>* to,
const CostFunc<N, E, C>& costFunc,
const HeurFunc<N, E, C>& heurFunc,
EList<N, E>* resEdges) {
NList<N, E> dummyRet;
std::set<Edge<N, E>*> froms{from};
std::set<Edge<N, E>*> tos{to};
return D::shortestPathImpl(froms, tos, costFunc, heurFunc, resEdges,
&dummyRet);
}
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPath(
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const HeurFunc<N, E, C>& heurFunc,
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
@ -277,8 +312,20 @@ class ShortestPath {
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPath(
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const HeurFunc<N, E, C>& heurFunc,
const CostFunc<N, E, C>& costFunc,
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>()
, resEdges,
resNodes);
}
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPath(
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges) {
std::unordered_map<Edge<N, E>*, NList<N, E>*> dummyRet;
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
@ -288,14 +335,35 @@ class ShortestPath {
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPath(
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
const ShortestPath::CostFunc<N, E, C>& costFunc,
const HeurFunc<N, E, C>& heurFunc) {
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc) {
std::unordered_map<Edge<N, E>*, NList<N, E>*> dummyRet;
std::unordered_map<Edge<N, E>*, EList<N, E>*> dummyRetE;
return D::shortestPathImpl(from, to, costFunc, heurFunc, dummyRetE,
dummyRet);
}
template <typename N, typename E, typename C>
static C shortestPath(Edge<N, E>* from,
const std::set<Edge<N, E>*>& to,
const CostFunc<N, E, C>& costFunc) {
NList<N, E>* nl = 0;
EList<N, E>* el = 0;
std::set<Edge<N, E>*> fromS;
fromS.insert(from);
return D::shortestPathImpl(fromS, to, costFunc, ZeroHeurFunc<N, E, C>(), el,
nl);
}
template <typename N, typename E, typename C>
static C shortestPath(const std::set<Edge<N, E>*>& from,
const std::set<Edge<N, E>*>& to,
const CostFunc<N, E, C>& costFunc) {
NList<N, E>* nl = 0;
EList<N, E>* el = 0;
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(), el,
nl);
}
template <typename N, typename E, typename C>
static C shortestPath(const std::set<Edge<N, E>*>& from,
const std::set<Edge<N, E>*>& to,
@ -306,17 +374,6 @@ class ShortestPath {
return D::shortestPathImpl(from, to, costFunc, heurFunc, el, nl);
}
template <typename N, typename E, typename C>
static C shortestPath(Edge<N, E>* from,
Edge<N, E>* to,
const CostFunc<N, E, C>& costFunc ) {
NList<N, E>* nl = 0;
EList<N, E>* el = 0;
std::set<Edge<N, E>*> tos{to};
std::set<Edge<N, E>*> froms{from};
return D::shortestPathImpl(froms, tos, costFunc, ZeroHeurFunc<N, E, C>(), el, nl);
}
template <typename N, typename E, typename C>
static C shortestPath(const std::set<Edge<N, E>*>& from,
const std::set<Edge<N, E>*>& to,
@ -326,6 +383,91 @@ class ShortestPath {
return D::shortestPathImpl(from, to, costFunc, heurFunc, el, nl);
}
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPath(
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
const std::unordered_map<Edge<N, E>*, C>& initCosts,
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
return D::shortestPathImpl(from, to, initCosts, costFunc.inf(), costFunc,
heurFunc, resEdges, resNodes);
}
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPath(
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
const std::unordered_map<Edge<N, E>*, C>& initCosts,
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges) {
std::unordered_map<Edge<N, E>*, NList<N, E>*> dummyRet;
return D::shortestPathImpl(from, to, initCosts, costFunc.inf(), costFunc,
heurFunc, resEdges, dummyRet);
}
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPath(
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
const std::unordered_map<Edge<N, E>*, C>& initCosts,
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc) {
std::unordered_map<Edge<N, E>*, NList<N, E>*> dummyRet;
std::unordered_map<Edge<N, E>*, EList<N, E>*> dummyRetE;
return D::shortestPathImpl(from, to, initCosts, costFunc.inf(), costFunc,
heurFunc, dummyRetE, dummyRet);
}
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPath(
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
const std::unordered_map<Edge<N, E>*, C>& initCosts, C stall,
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
return D::shortestPathImpl(from, to, initCosts, stall, costFunc, heurFunc,
resEdges, resNodes);
}
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPath(
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
const std::unordered_map<Edge<N, E>*, C>& initCosts, C stall,
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges) {
std::unordered_map<Edge<N, E>*, NList<N, E>*> dummyRet;
return D::shortestPathImpl(from, to, initCosts, stall, costFunc, heurFunc,
resEdges, dummyRet);
}
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, std::pair<Edge<N, E>*, C>> shortestPath(
const std::set<Edge<N, E>*>& from, const std::set<Edge<N, E>*>& to,
const std::unordered_map<Edge<N, E>*, C>& initCosts, C stall,
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc) {
return D::shortestPathImpl(from, to, initCosts, stall, costFunc, heurFunc);
}
template <typename N, typename E, typename C>
static C shortestPath(Edge<N, E>* from, Edge<N, E>* to,
const CostFunc<N, E, C>& costFunc) {
NList<N, E>* nl = 0;
EList<N, E>* el = 0;
std::set<Edge<N, E>*> tos{to};
std::set<Edge<N, E>*> froms{from};
return D::shortestPathImpl(froms, tos, costFunc, ZeroHeurFunc<N, E, C>(),
el, nl);
}
template <typename N, typename E, typename C>
static C shortestPath(Edge<N, E>* from, Edge<N, E>* to,
const CostFunc<N, E, C>& costFunc,
const HeurFunc<N, E, C>& heurFunc) {
NList<N, E>* nl = 0;
EList<N, E>* el = 0;
std::set<Edge<N, E>*> tos{to};
std::set<Edge<N, E>*> froms{from};
return D::shortestPathImpl(froms, tos, costFunc, heurFunc, el, nl);
}
template <typename N, typename E, typename C>
static C shortestPath(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
const CostFunc<N, E, C>& costFunc,
@ -336,8 +478,7 @@ class ShortestPath {
template <typename N, typename E, typename C>
static C shortestPath(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
const CostFunc<N, E, C>& costFunc,
EList<N, E>* el,
const CostFunc<N, E, C>& costFunc, EList<N, E>* el,
NList<N, E>* nl) {
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(), el,
nl);
@ -363,24 +504,21 @@ class ShortestPath {
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPath(
Edge<N, E>* from,
const ShortestPath::CostFunc<N, E, C>& costFunc) {
std::set<Edge<N, E>*> froms { from };
Edge<N, E>* from, const CostFunc<N, E, C>& costFunc) {
std::set<Edge<N, E>*> froms{from};
return D::shortestPathImpl(froms, costFunc, false);
}
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPathRev(
Edge<N, E>* from,
const ShortestPath::CostFunc<N, E, C>& costFunc) {
std::set<Edge<N, E>*> froms { from };
Edge<N, E>* from, const CostFunc<N, E, C>& costFunc) {
std::set<Edge<N, E>*> froms{from};
return D::shortestPathImpl(froms, costFunc, true);
}
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPath(
Node<N, E>* from,
const ShortestPath::CostFunc<N, E, C>& costFunc) {
Node<N, E>* from, const CostFunc<N, E, C>& costFunc) {
std::set<Edge<N, E>*> froms;
froms.insert(from->getAdjListOut().begin(), from->getAdjListOut().end());
return D::shortestPathImpl(froms, costFunc, false);
@ -388,14 +526,13 @@ class ShortestPath {
template <typename N, typename E, typename C>
static std::unordered_map<Edge<N, E>*, C> shortestPathRev(
Node<N, E>* from,
const ShortestPath::CostFunc<N, E, C>& costFunc) {
Node<N, E>* from, const CostFunc<N, E, C>& costFunc) {
std::set<Edge<N, E>*> froms;
froms.insert(from->getAdjListOut().begin(), from->getAdjListOut().end());
return D::shortestPathImpl(froms, costFunc, true);
}
};
}
}
} // namespace graph
} // namespace util
#endif // UTIL_GRAPH_SHORTESTPATH_H_

View file

@ -21,7 +21,7 @@ Node<N, E>* UndirGraph<N, E>::addNd() {
// _____________________________________________________________________________
template <typename N, typename E>
Node<N, E>* UndirGraph<N, E>::addNd(UndirNode<N, E>* n) {
auto ins = Graph<N, E>::getNds()->insert(n);
auto ins = Graph<N, E>::_nodes.insert(n);
return *ins.first;
}

226
src/util/graph/radix_heap.h Normal file
View file

@ -0,0 +1,226 @@
// based on https://github.com/iwiwi/radix-heap
#include <algorithm>
#include <array>
#include <cassert>
#include <climits>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <utility>
#include <vector>
namespace radix_heap {
namespace internal {
template <bool Is64bit>
class find_bucket_impl;
template <>
class find_bucket_impl<false> {
public:
static inline constexpr size_t find_bucket(uint32_t x, uint32_t last) {
return x == last ? 0 : 32 - __builtin_clz(x ^ last);
}
};
template <>
class find_bucket_impl<true> {
public:
static inline constexpr size_t find_bucket(uint64_t x, uint64_t last) {
return x == last ? 0 : 64 - __builtin_clzll(x ^ last);
}
};
template <typename T>
inline constexpr size_t find_bucket(T x, T last) {
return find_bucket_impl<sizeof(T) == 8>::find_bucket(x, last);
}
template <typename KeyType, bool IsSigned>
class encoder_impl_integer;
template <typename KeyType>
class encoder_impl_integer<KeyType, false> {
public:
typedef KeyType key_type;
typedef KeyType unsigned_key_type;
inline static constexpr unsigned_key_type encode(key_type x) { return x; }
inline static constexpr key_type decode(unsigned_key_type x) { return x; }
};
template <typename KeyType>
class encoder_impl_integer<KeyType, true> {
public:
typedef KeyType key_type;
typedef typename std::make_unsigned<KeyType>::type unsigned_key_type;
inline static constexpr unsigned_key_type encode(key_type x) {
return static_cast<unsigned_key_type>(x) ^
(unsigned_key_type(1) << unsigned_key_type(
std::numeric_limits<unsigned_key_type>::digits - 1));
}
inline static constexpr key_type decode(unsigned_key_type x) {
return static_cast<key_type>(
x ^ (unsigned_key_type(1)
<< (std::numeric_limits<unsigned_key_type>::digits - 1)));
}
};
template <typename KeyType, typename UnsignedKeyType>
class encoder_impl_decimal {
public:
typedef KeyType key_type;
typedef UnsignedKeyType unsigned_key_type;
inline static constexpr unsigned_key_type encode(key_type x) {
return raw_cast<key_type, unsigned_key_type>(x) ^
((-(raw_cast<key_type, unsigned_key_type>(x) >>
(std::numeric_limits<unsigned_key_type>::digits - 1))) |
(unsigned_key_type(1)
<< (std::numeric_limits<unsigned_key_type>::digits - 1)));
}
inline static constexpr key_type decode(unsigned_key_type x) {
return raw_cast<unsigned_key_type, key_type>(
x ^ (((x >> (std::numeric_limits<unsigned_key_type>::digits - 1)) - 1) |
(unsigned_key_type(1)
<< (std::numeric_limits<unsigned_key_type>::digits - 1))));
}
private:
template <typename T, typename U>
union raw_cast {
public:
constexpr raw_cast(T t) : t_(t) {}
operator U() const { return u_; }
private:
T t_;
U u_;
};
};
template <typename KeyType>
class encoder
: public encoder_impl_integer<KeyType, std::is_signed<KeyType>::value> {};
template <>
class encoder<float> : public encoder_impl_decimal<float, uint32_t> {};
template <>
class encoder<double> : public encoder_impl_decimal<double, uint64_t> {};
} // namespace internal
template <typename KeyType, typename ValueType,
typename EncoderType = internal::encoder<KeyType>>
class pair_radix_heap {
public:
typedef KeyType key_type;
typedef ValueType value_type;
typedef EncoderType encoder_type;
typedef typename encoder_type::unsigned_key_type unsigned_key_type;
pair_radix_heap() : size_(0), last_(), buckets_() {
buckets_min_.fill(std::numeric_limits<unsigned_key_type>::max());
}
void push(key_type key, const value_type &value) {
unsigned_key_type x = encoder_type::encode(key);
if (last_ > x) {
std::cerr << "Not monotone: " << last_ << " vs " << x << std::endl;
x = last_;
}
++size_;
const size_t k = internal::find_bucket(x, last_);
buckets_[k].emplace_back(x, value);
buckets_min_[k] = std::min(buckets_min_[k], x);
}
void push(key_type key, value_type &&value) {
unsigned_key_type x = encoder_type::encode(key);
if (last_ > x) {
std::cerr << "Not monotone: " << last_ << " vs " << x << std::endl;
x = last_;
}
++size_;
const size_t k = internal::find_bucket(x, last_);
buckets_[k].emplace_back(x, std::move(value));
buckets_min_[k] = std::min(buckets_min_[k], x);
}
template <class... Args>
void emplace(key_type key, Args &&... args) {
unsigned_key_type x = encoder_type::encode(key);
if (last_ > x) x = last_;
++size_;
const size_t k = internal::find_bucket(x, last_);
buckets_[k].emplace_back(std::piecewise_construct, std::forward_as_tuple(x),
std::forward_as_tuple(args...));
buckets_min_[k] = std::min(buckets_min_[k], x);
}
key_type topKey() {
pull();
return encoder_type::decode(last_);
}
value_type &topVal() {
pull();
return buckets_[0].back().second;
}
void pop() {
pull();
buckets_[0].pop_back();
--size_;
}
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
void clear() {
size_ = 0;
last_ = key_type();
for (auto &b : buckets_) b.clear();
buckets_min_.fill(std::numeric_limits<unsigned_key_type>::max());
}
void swap(pair_radix_heap<KeyType, ValueType, EncoderType> &a) {
std::swap(size_, a.size_);
std::swap(last_, a.last_);
buckets_.swap(a.buckets_);
buckets_min_.swap(a.buckets_min_);
}
private:
size_t size_;
unsigned_key_type last_;
std::array<std::vector<std::pair<unsigned_key_type, value_type>>,
std::numeric_limits<unsigned_key_type>::digits + 1>
buckets_;
std::array<unsigned_key_type,
std::numeric_limits<unsigned_key_type>::digits + 1>
buckets_min_;
void pull() {
assert(size_ > 0);
if (!buckets_[0].empty()) return;
size_t i;
for (i = 1; buckets_[i].empty(); ++i)
;
last_ = buckets_min_[i];
for (size_t j = 0; j < buckets_[i].size(); ++j) {
const unsigned_key_type x = buckets_[i][j].first;
const size_t k = internal::find_bucket(x, last_);
buckets_[k].emplace_back(std::move(buckets_[i][j]));
buckets_min_[k] = std::min(buckets_min_[k], x);
}
buckets_[i].clear();
buckets_min_[i] = std::numeric_limits<unsigned_key_type>::max();
}
};
} // namespace radix_heap

View file

@ -0,0 +1,348 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ROBIN_GROWTH_POLICY_H
#define TSL_ROBIN_GROWTH_POLICY_H
#include <algorithm>
#include <array>
#include <climits>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <limits>
#include <ratio>
#include <stdexcept>
#ifdef TSL_DEBUG
# define tsl_rh_assert(expr) assert(expr)
#else
# define tsl_rh_assert(expr) (static_cast<void>(0))
#endif
/**
* If exceptions are enabled, throw the exception passed in parameter, otherwise call std::terminate.
*/
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (defined (_MSC_VER) && defined (_CPPUNWIND))) && !defined(TSL_NO_EXCEPTIONS)
# define TSL_RH_THROW_OR_TERMINATE(ex, msg) throw ex(msg)
#else
# define TSL_RH_NO_EXCEPTIONS
# ifdef NDEBUG
# define TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate()
# else
# include <iostream>
# define TSL_RH_THROW_OR_TERMINATE(ex, msg) do { std::cerr << msg << std::endl; std::terminate(); } while(0)
# endif
#endif
#if defined(__GNUC__) || defined(__clang__)
# define TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true))
#else
# define TSL_RH_LIKELY(exp) (exp)
#endif
namespace tsl {
namespace rh {
/**
* Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows
* the table to use a mask operation instead of a modulo operation to map a hash to a bucket.
*
* GrowthFactor must be a power of two >= 2.
*/
template<std::size_t GrowthFactor>
class power_of_two_growth_policy {
public:
/**
* Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter.
* This number is a minimum, the policy may update this value with a higher value if needed (but not lower).
*
* If 0 is given, min_bucket_count_in_out must still be 0 after the policy creation and
* bucket_for_hash must always return 0 in this case.
*/
explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
if(min_bucket_count_in_out > max_bucket_count()) {
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
}
if(min_bucket_count_in_out > 0) {
min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
m_mask = min_bucket_count_in_out - 1;
}
else {
m_mask = 0;
}
}
/**
* Return the bucket [0, bucket_count()) to which the hash belongs.
* If bucket_count() is 0, it must always return 0.
*/
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return hash & m_mask;
}
/**
* Return the number of buckets that should be used on next growth.
*/
std::size_t next_bucket_count() const {
if((m_mask + 1) > max_bucket_count() / GrowthFactor) {
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
}
return (m_mask + 1) * GrowthFactor;
}
/**
* Return the maximum number of buckets supported by the policy.
*/
std::size_t max_bucket_count() const {
// Largest power of two.
return (std::numeric_limits<std::size_t>::max() / 2) + 1;
}
/**
* Reset the growth policy as if it was created with a bucket count of 0.
* After a clear, the policy must always return 0 when bucket_for_hash is called.
*/
void clear() noexcept {
m_mask = 0;
}
private:
static std::size_t round_up_to_power_of_two(std::size_t value) {
if(is_power_of_two(value)) {
return value;
}
if(value == 0) {
return 1;
}
--value;
for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
value |= value >> i;
}
return value + 1;
}
static constexpr bool is_power_of_two(std::size_t value) {
return value != 0 && (value & (value - 1)) == 0;
}
protected:
static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2.");
std::size_t m_mask;
};
/**
* Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash
* to a bucket. Slower but it can be useful if you want a slower growth.
*/
template<class GrowthFactor = std::ratio<3, 2>>
class mod_growth_policy {
public:
explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
if(min_bucket_count_in_out > max_bucket_count()) {
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
}
if(min_bucket_count_in_out > 0) {
m_mod = min_bucket_count_in_out;
}
else {
m_mod = 1;
}
}
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return hash % m_mod;
}
std::size_t next_bucket_count() const {
if(m_mod == max_bucket_count()) {
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
}
const double next_bucket_count = std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
if(!std::isnormal(next_bucket_count)) {
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
}
if(next_bucket_count > double(max_bucket_count())) {
return max_bucket_count();
}
else {
return std::size_t(next_bucket_count);
}
}
std::size_t max_bucket_count() const {
return MAX_BUCKET_COUNT;
}
void clear() noexcept {
m_mod = 1;
}
private:
static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den;
static const std::size_t MAX_BUCKET_COUNT =
std::size_t(double(
std::numeric_limits<std::size_t>::max() / REHASH_SIZE_MULTIPLICATION_FACTOR
));
static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1.");
std::size_t m_mod;
};
namespace detail {
#if SIZE_MAX >= ULLONG_MAX
#define TSL_RH_NB_PRIMES 51
#elif SIZE_MAX >= ULONG_MAX
#define TSL_RH_NB_PRIMES 40
#else
#define TSL_RH_NB_PRIMES 23
#endif
static constexpr const std::array<std::size_t, TSL_RH_NB_PRIMES> PRIMES = {{
1u, 5u, 17u, 29u, 37u, 53u, 67u, 79u, 97u, 131u, 193u, 257u, 389u, 521u, 769u, 1031u,
1543u, 2053u, 3079u, 6151u, 12289u, 24593u, 49157u,
#if SIZE_MAX >= ULONG_MAX
98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, 6291469ul, 12582917ul,
25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul,
3221225473ul, 4294967291ul,
#endif
#if SIZE_MAX >= ULLONG_MAX
6442450939ull, 12884901893ull, 25769803751ull, 51539607551ull, 103079215111ull, 206158430209ull,
412316860441ull, 824633720831ull, 1649267441651ull, 3298534883309ull, 6597069766657ull,
#endif
}};
template<unsigned int IPrime>
static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; }
// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the
// compiler can optimize the modulo code better with a constant known at the compilation.
static constexpr const std::array<std::size_t(*)(std::size_t), TSL_RH_NB_PRIMES> MOD_PRIME = {{
&mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>,
&mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>,
&mod<21>, &mod<22>,
#if SIZE_MAX >= ULONG_MAX
&mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>, &mod<31>, &mod<32>,
&mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>, &mod<39>,
#endif
#if SIZE_MAX >= ULLONG_MAX
&mod<40>, &mod<41>, &mod<42>, &mod<43>, &mod<44>, &mod<45>, &mod<46>, &mod<47>, &mod<48>, &mod<49>,
&mod<50>,
#endif
}};
}
/**
* Grow the hash table by using prime numbers as bucket count. Slower than tsl::rh::power_of_two_growth_policy in
* general but will probably distribute the values around better in the buckets with a poor hash function.
*
* To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers.
*
* With a switch the code would look like:
* \code
* switch(iprime) { // iprime is the current prime of the hash table
* case 0: hash % 5ul;
* break;
* case 1: hash % 17ul;
* break;
* case 2: hash % 29ul;
* break;
* ...
* }
* \endcode
*
* Due to the constant variable in the modulo the compiler is able to optimize the operation
* by a series of multiplications, substractions and shifts.
*
* The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environment.
*/
class prime_growth_policy {
public:
explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
auto it_prime = std::lower_bound(detail::PRIMES.begin(),
detail::PRIMES.end(), min_bucket_count_in_out);
if(it_prime == detail::PRIMES.end()) {
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
}
m_iprime = static_cast<unsigned int>(std::distance(detail::PRIMES.begin(), it_prime));
if(min_bucket_count_in_out > 0) {
min_bucket_count_in_out = *it_prime;
}
else {
min_bucket_count_in_out = 0;
}
}
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return detail::MOD_PRIME[m_iprime](hash);
}
std::size_t next_bucket_count() const {
if(m_iprime + 1 >= detail::PRIMES.size()) {
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maximum size.");
}
return detail::PRIMES[m_iprime + 1];
}
std::size_t max_bucket_count() const {
return detail::PRIMES.back();
}
void clear() noexcept {
m_iprime = 0;
}
private:
unsigned int m_iprime;
static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
"The type of m_iprime is not big enough.");
};
}
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,715 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ROBIN_MAP_H
#define TSL_ROBIN_MAP_H
#include <cstddef>
#include <functional>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <utility>
#include "robin_hash.h"
namespace tsl {
/**
* Implementation of a hash map using open-addressing and the robin hood hashing algorithm with backward shift deletion.
*
* For operations modifying the hash map (insert, erase, rehash, ...), the strong exception guarantee
* is only guaranteed when the expression `std::is_nothrow_swappable<std::pair<Key, T>>::value &&
* std::is_nothrow_move_constructible<std::pair<Key, T>>::value` is true, otherwise if an exception
* is thrown during the swap or the move, the hash map may end up in a undefined state. Per the standard
* a `Key` or `T` with a noexcept copy constructor and no move constructor also satisfies the
* `std::is_nothrow_move_constructible<std::pair<Key, T>>::value` criterion (and will thus guarantee the
* strong exception for the map).
*
* When `StoreHash` is true, 32 bits of the hash are stored alongside the values. It can improve
* the performance during lookups if the `KeyEqual` function takes time (if it engenders a cache-miss for example)
* as we then compare the stored hashes before comparing the keys. When `tsl::rh::power_of_two_growth_policy` is used
* as `GrowthPolicy`, it may also speed-up the rehash process as we can avoid to recalculate the hash.
* When it is detected that storing the hash will not incur any memory penalty due to alignment (i.e.
* `sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, true>) ==
* sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, false>)`) and `tsl::rh::power_of_two_growth_policy` is
* used, the hash will be stored even if `StoreHash` is false so that we can speed-up the rehash (but it will
* not be used on lookups unless `StoreHash` is true).
*
* `GrowthPolicy` defines how the map grows and consequently how a hash value is mapped to a bucket.
* By default the map uses `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of buckets
* to a power of two and uses a mask to map the hash to a bucket instead of the slow modulo.
* Other growth policies are available and you may define your own growth policy,
* check `tsl::rh::power_of_two_growth_policy` for the interface.
*
* `std::pair<Key, T>` must be swappable.
*
* `Key` and `T` must be copy and/or move constructible.
*
* If the destructor of `Key` or `T` throws an exception, the behaviour of the class is undefined.
*
* Iterators invalidation:
* - clear, operator=, reserve, rehash: always invalidate the iterators.
* - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators.
* - erase: always invalidate the iterators.
*/
template<class Key,
class T,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key, T>>,
bool StoreHash = false,
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
class robin_map {
private:
template<typename U>
using has_is_transparent = tsl::detail_robin_hash::has_is_transparent<U>;
class KeySelect {
public:
using key_type = Key;
const key_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
return key_value.first;
}
key_type& operator()(std::pair<Key, T>& key_value) noexcept {
return key_value.first;
}
};
class ValueSelect {
public:
using value_type = T;
const value_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
return key_value.second;
}
value_type& operator()(std::pair<Key, T>& key_value) noexcept {
return key_value.second;
}
};
using ht = detail_robin_hash::robin_hash<std::pair<Key, T>, KeySelect, ValueSelect,
Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
public:
using key_type = typename ht::key_type;
using mapped_type = T;
using value_type = typename ht::value_type;
using size_type = typename ht::size_type;
using difference_type = typename ht::difference_type;
using hasher = typename ht::hasher;
using key_equal = typename ht::key_equal;
using allocator_type = typename ht::allocator_type;
using reference = typename ht::reference;
using const_reference = typename ht::const_reference;
using pointer = typename ht::pointer;
using const_pointer = typename ht::const_pointer;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
public:
/*
* Constructors
*/
robin_map(): robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {
}
explicit robin_map(size_type bucket_count,
const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator()):
m_ht(bucket_count, hash, equal, alloc)
{
}
robin_map(size_type bucket_count,
const Allocator& alloc): robin_map(bucket_count, Hash(), KeyEqual(), alloc)
{
}
robin_map(size_type bucket_count,
const Hash& hash,
const Allocator& alloc): robin_map(bucket_count, hash, KeyEqual(), alloc)
{
}
explicit robin_map(const Allocator& alloc): robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
}
template<class InputIt>
robin_map(InputIt first, InputIt last,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator()): robin_map(bucket_count, hash, equal, alloc)
{
insert(first, last);
}
template<class InputIt>
robin_map(InputIt first, InputIt last,
size_type bucket_count,
const Allocator& alloc): robin_map(first, last, bucket_count, Hash(), KeyEqual(), alloc)
{
}
template<class InputIt>
robin_map(InputIt first, InputIt last,
size_type bucket_count,
const Hash& hash,
const Allocator& alloc): robin_map(first, last, bucket_count, hash, KeyEqual(), alloc)
{
}
robin_map(std::initializer_list<value_type> init,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator()):
robin_map(init.begin(), init.end(), bucket_count, hash, equal, alloc)
{
}
robin_map(std::initializer_list<value_type> init,
size_type bucket_count,
const Allocator& alloc):
robin_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
{
}
robin_map(std::initializer_list<value_type> init,
size_type bucket_count,
const Hash& hash,
const Allocator& alloc):
robin_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
{
}
robin_map& operator=(std::initializer_list<value_type> ilist) {
m_ht.clear();
m_ht.reserve(ilist.size());
m_ht.insert(ilist.begin(), ilist.end());
return *this;
}
allocator_type get_allocator() const { return m_ht.get_allocator(); }
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
std::pair<iterator, bool> insert(const value_type& value) {
return m_ht.insert(value);
}
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
std::pair<iterator, bool> insert(P&& value) {
return m_ht.emplace(std::forward<P>(value));
}
std::pair<iterator, bool> insert(value_type&& value) {
return m_ht.insert(std::move(value));
}
iterator insert(const_iterator hint, const value_type& value) {
return m_ht.insert_hint(hint, value);
}
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
iterator insert(const_iterator hint, P&& value) {
return m_ht.emplace_hint(hint, std::forward<P>(value));
}
iterator insert(const_iterator hint, value_type&& value) {
return m_ht.insert_hint(hint, std::move(value));
}
template<class InputIt>
void insert(InputIt first, InputIt last) {
m_ht.insert(first, last);
}
void insert(std::initializer_list<value_type> ilist) {
m_ht.insert(ilist.begin(), ilist.end());
}
template<class M>
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) {
return m_ht.insert_or_assign(k, std::forward<M>(obj));
}
template<class M>
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {
return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj));
}
template<class M>
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {
return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));
}
template<class M>
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));
}
/**
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template<class... Args>
std::pair<iterator, bool> emplace(Args&&... args) {
return m_ht.emplace(std::forward<Args>(args)...);
}
/**
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template<class... Args>
iterator emplace_hint(const_iterator hint, Args&&... args) {
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
}
template<class... Args>
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) {
return m_ht.try_emplace(k, std::forward<Args>(args)...);
}
template<class... Args>
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {
return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);
}
template<class... Args>
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {
return m_ht.try_emplace_hint(hint, k, std::forward<Args>(args)...);
}
template<class... Args>
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {
return m_ht.try_emplace_hint(hint, std::move(k), std::forward<Args>(args)...);
}
iterator erase(iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
size_type erase(const key_type& key) { return m_ht.erase(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
size_type erase(const key_type& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key) { return m_ht.erase(key); }
/**
* @copydoc erase(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
void swap(robin_map& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
T& at(const Key& key) { return m_ht.at(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
const T& at(const Key& key) const { return m_ht.at(key); }
/**
* @copydoc at(const Key& key, std::size_t precalculated_hash)
*/
const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
T& at(const K& key) { return m_ht.at(key); }
/**
* @copydoc at(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
/**
* @copydoc at(const K& key)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const T& at(const K& key) const { return m_ht.at(key); }
/**
* @copydoc at(const K& key, std::size_t precalculated_hash)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
T& operator[](const Key& key) { return m_ht[key]; }
T& operator[](Key&& key) { return m_ht[std::move(key)]; }
size_type count(const Key& key) const { return m_ht.count(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
size_type count(const Key& key, std::size_t precalculated_hash) const {
return m_ht.count(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key) const { return m_ht.count(key); }
/**
* @copydoc count(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
iterator find(const Key& key) { return m_ht.find(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
const_iterator find(const Key& key) const { return m_ht.find(key); }
/**
* @copydoc find(const Key& key, std::size_t precalculated_hash)
*/
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
return m_ht.find(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key) { return m_ht.find(key); }
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
/**
* @copydoc find(const K& key)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key) const { return m_ht.find(key); }
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key, std::size_t precalculated_hash) const {
return m_ht.find(key, precalculated_hash);
}
bool contains(const Key& key) const { return m_ht.contains(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
bool contains(const Key& key, std::size_t precalculated_hash) const {
return m_ht.contains(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
bool contains(const K& key) const { return m_ht.contains(key); }
/**
* @copydoc contains(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
bool contains(const K& key, std::size_t precalculated_hash) const {
return m_ht.contains(key, precalculated_hash);
}
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
/**
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
/**
* @copydoc equal_range(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* @copydoc equal_range(const K& key)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
/**
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/*
* Bucket interface
*/
size_type bucket_count() const { return m_ht.bucket_count(); }
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
/*
* Hash policy
*/
float load_factor() const { return m_ht.load_factor(); }
float min_load_factor() const { return m_ht.min_load_factor(); }
float max_load_factor() const { return m_ht.max_load_factor(); }
/**
* Set the `min_load_factor` to `ml`. When the `load_factor` of the map goes
* below `min_load_factor` after some erase operations, the map will be
* shrunk when an insertion occurs. The erase method itself never shrinks
* the map.
*
* The default value of `min_load_factor` is 0.0f, the map never shrinks by default.
*/
void min_load_factor(float ml) { m_ht.min_load_factor(ml); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
void rehash(size_type count) { m_ht.rehash(count); }
void reserve(size_type count) { m_ht.reserve(count); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
key_equal key_eq() const { return m_ht.key_eq(); }
/*
* Other
*/
/**
* Convert a const_iterator to an iterator.
*/
iterator mutable_iterator(const_iterator pos) {
return m_ht.mutable_iterator(pos);
}
friend bool operator==(const robin_map& lhs, const robin_map& rhs) {
if(lhs.size() != rhs.size()) {
return false;
}
for(const auto& element_lhs: lhs) {
const auto it_element_rhs = rhs.find(element_lhs.first);
if(it_element_rhs == rhs.cend() || element_lhs.second != it_element_rhs->second) {
return false;
}
}
return true;
}
friend bool operator!=(const robin_map& lhs, const robin_map& rhs) {
return !operator==(lhs, rhs);
}
friend void swap(robin_map& lhs, robin_map& rhs) {
lhs.swap(rhs);
}
private:
ht m_ht;
};
/**
* Same as `tsl::robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>`.
*/
template<class Key,
class T,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key, T>>,
bool StoreHash = false>
using robin_pg_map = robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>;
} // end namespace tsl
#endif

View file

@ -0,0 +1,582 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ROBIN_SET_H
#define TSL_ROBIN_SET_H
#include <cstddef>
#include <functional>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <utility>
#include "robin_hash.h"
namespace tsl {
/**
* Implementation of a hash set using open-addressing and the robin hood hashing algorithm with backward shift deletion.
*
* For operations modifying the hash set (insert, erase, rehash, ...), the strong exception guarantee
* is only guaranteed when the expression `std::is_nothrow_swappable<Key>::value &&
* std::is_nothrow_move_constructible<Key>::value` is true, otherwise if an exception
* is thrown during the swap or the move, the hash set may end up in a undefined state. Per the standard
* a `Key` with a noexcept copy constructor and no move constructor also satisfies the
* `std::is_nothrow_move_constructible<Key>::value` criterion (and will thus guarantee the
* strong exception for the set).
*
* When `StoreHash` is true, 32 bits of the hash are stored alongside the values. It can improve
* the performance during lookups if the `KeyEqual` function takes time (or engenders a cache-miss for example)
* as we then compare the stored hashes before comparing the keys. When `tsl::rh::power_of_two_growth_policy` is used
* as `GrowthPolicy`, it may also speed-up the rehash process as we can avoid to recalculate the hash.
* When it is detected that storing the hash will not incur any memory penalty due to alignment (i.e.
* `sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, true>) ==
* sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, false>)`) and `tsl::rh::power_of_two_growth_policy` is
* used, the hash will be stored even if `StoreHash` is false so that we can speed-up the rehash (but it will
* not be used on lookups unless `StoreHash` is true).
*
* `GrowthPolicy` defines how the set grows and consequently how a hash value is mapped to a bucket.
* By default the set uses `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of buckets
* to a power of two and uses a mask to set the hash to a bucket instead of the slow modulo.
* Other growth policies are available and you may define your own growth policy,
* check `tsl::rh::power_of_two_growth_policy` for the interface.
*
* `Key` must be swappable.
*
* `Key` must be copy and/or move constructible.
*
* If the destructor of `Key` throws an exception, the behaviour of the class is undefined.
*
* Iterators invalidation:
* - clear, operator=, reserve, rehash: always invalidate the iterators.
* - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators.
* - erase: always invalidate the iterators.
*/
template<class Key,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<Key>,
bool StoreHash = false,
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
class robin_set {
private:
template<typename U>
using has_is_transparent = tsl::detail_robin_hash::has_is_transparent<U>;
class KeySelect {
public:
using key_type = Key;
const key_type& operator()(const Key& key) const noexcept {
return key;
}
key_type& operator()(Key& key) noexcept {
return key;
}
};
using ht = detail_robin_hash::robin_hash<Key, KeySelect, void,
Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
public:
using key_type = typename ht::key_type;
using value_type = typename ht::value_type;
using size_type = typename ht::size_type;
using difference_type = typename ht::difference_type;
using hasher = typename ht::hasher;
using key_equal = typename ht::key_equal;
using allocator_type = typename ht::allocator_type;
using reference = typename ht::reference;
using const_reference = typename ht::const_reference;
using pointer = typename ht::pointer;
using const_pointer = typename ht::const_pointer;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
/*
* Constructors
*/
robin_set(): robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {
}
explicit robin_set(size_type bucket_count,
const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator()):
m_ht(bucket_count, hash, equal, alloc)
{
}
robin_set(size_type bucket_count,
const Allocator& alloc): robin_set(bucket_count, Hash(), KeyEqual(), alloc)
{
}
robin_set(size_type bucket_count,
const Hash& hash,
const Allocator& alloc): robin_set(bucket_count, hash, KeyEqual(), alloc)
{
}
explicit robin_set(const Allocator& alloc): robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
}
template<class InputIt>
robin_set(InputIt first, InputIt last,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator()): robin_set(bucket_count, hash, equal, alloc)
{
insert(first, last);
}
template<class InputIt>
robin_set(InputIt first, InputIt last,
size_type bucket_count,
const Allocator& alloc): robin_set(first, last, bucket_count, Hash(), KeyEqual(), alloc)
{
}
template<class InputIt>
robin_set(InputIt first, InputIt last,
size_type bucket_count,
const Hash& hash,
const Allocator& alloc): robin_set(first, last, bucket_count, hash, KeyEqual(), alloc)
{
}
robin_set(std::initializer_list<value_type> init,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator()):
robin_set(init.begin(), init.end(), bucket_count, hash, equal, alloc)
{
}
robin_set(std::initializer_list<value_type> init,
size_type bucket_count,
const Allocator& alloc):
robin_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
{
}
robin_set(std::initializer_list<value_type> init,
size_type bucket_count,
const Hash& hash,
const Allocator& alloc):
robin_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
{
}
robin_set& operator=(std::initializer_list<value_type> ilist) {
m_ht.clear();
m_ht.reserve(ilist.size());
m_ht.insert(ilist.begin(), ilist.end());
return *this;
}
allocator_type get_allocator() const { return m_ht.get_allocator(); }
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
std::pair<iterator, bool> insert(const value_type& value) {
return m_ht.insert(value);
}
std::pair<iterator, bool> insert(value_type&& value) {
return m_ht.insert(std::move(value));
}
iterator insert(const_iterator hint, const value_type& value) {
return m_ht.insert_hint(hint, value);
}
iterator insert(const_iterator hint, value_type&& value) {
return m_ht.insert_hint(hint, std::move(value));
}
template<class InputIt>
void insert(InputIt first, InputIt last) {
m_ht.insert(first, last);
}
void insert(std::initializer_list<value_type> ilist) {
m_ht.insert(ilist.begin(), ilist.end());
}
/**
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template<class... Args>
std::pair<iterator, bool> emplace(Args&&... args) {
return m_ht.emplace(std::forward<Args>(args)...);
}
/**
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template<class... Args>
iterator emplace_hint(const_iterator hint, Args&&... args) {
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
}
iterator erase(iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
size_type erase(const key_type& key) { return m_ht.erase(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
size_type erase(const key_type& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key) { return m_ht.erase(key); }
/**
* @copydoc erase(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
void swap(robin_set& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
size_type count(const Key& key) const { return m_ht.count(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
size_type count(const Key& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key) const { return m_ht.count(key); }
/**
* @copydoc count(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
iterator find(const Key& key) { return m_ht.find(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
const_iterator find(const Key& key) const { return m_ht.find(key); }
/**
* @copydoc find(const Key& key, std::size_t precalculated_hash)
*/
const_iterator find(const Key& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key) { return m_ht.find(key); }
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
/**
* @copydoc find(const K& key)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key) const { return m_ht.find(key); }
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
bool contains(const Key& key) const { return m_ht.contains(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
bool contains(const Key& key, std::size_t precalculated_hash) const {
return m_ht.contains(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
bool contains(const K& key) const { return m_ht.contains(key); }
/**
* @copydoc contains(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
bool contains(const K& key, std::size_t precalculated_hash) const {
return m_ht.contains(key, precalculated_hash);
}
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
/**
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
* If so, K must be hashable and comparable to Key.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
/**
* @copydoc equal_range(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* @copydoc equal_range(const K& key)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
/**
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
*/
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/*
* Bucket interface
*/
size_type bucket_count() const { return m_ht.bucket_count(); }
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
/*
* Hash policy
*/
float load_factor() const { return m_ht.load_factor(); }
float min_load_factor() const { return m_ht.min_load_factor(); }
float max_load_factor() const { return m_ht.max_load_factor(); }
/**
* Set the `min_load_factor` to `ml`. When the `load_factor` of the set goes
* below `min_load_factor` after some erase operations, the set will be
* shrunk when an insertion occurs. The erase method itself never shrinks
* the set.
*
* The default value of `min_load_factor` is 0.0f, the set never shrinks by default.
*/
void min_load_factor(float ml) { m_ht.min_load_factor(ml); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
void rehash(size_type count) { m_ht.rehash(count); }
void reserve(size_type count) { m_ht.reserve(count); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
key_equal key_eq() const { return m_ht.key_eq(); }
/*
* Other
*/
/**
* Convert a const_iterator to an iterator.
*/
iterator mutable_iterator(const_iterator pos) {
return m_ht.mutable_iterator(pos);
}
friend bool operator==(const robin_set& lhs, const robin_set& rhs) {
if(lhs.size() != rhs.size()) {
return false;
}
for(const auto& element_lhs: lhs) {
const auto it_element_rhs = rhs.find(element_lhs);
if(it_element_rhs == rhs.cend()) {
return false;
}
}
return true;
}
friend bool operator!=(const robin_set& lhs, const robin_set& rhs) {
return !operator==(lhs, rhs);
}
friend void swap(robin_set& lhs, robin_set& rhs) {
lhs.swap(rhs);
}
private:
ht m_ht;
};
/**
* Same as `tsl::robin_set<Key, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>`.
*/
template<class Key,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<Key>,
bool StoreHash = false>
using robin_pg_set = robin_set<Key, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>;
} // end namespace tsl
#endif

View file

@ -11,6 +11,7 @@
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <algorithm>
#include <csignal>
#include <memory>
#include <sstream>
#include <stdexcept>
@ -24,12 +25,12 @@
#include "util/String.h"
#include "util/log/Log.h"
using util::http::Socket;
using util::http::Queue;
using util::http::Req;
using util::http::HeaderState;
using util::http::HttpErr;
using util::http::HttpServer;
using util::http::HeaderState;
using util::http::Queue;
using util::http::Req;
using util::http::Socket;
// _____________________________________________________________________________
Socket::Socket(int port) {
@ -71,6 +72,9 @@ int Socket::wait() {
// _____________________________________________________________________________
void HttpServer::send(int sock, Answer* aw) {
// ignore SIGPIPE
signal(SIGPIPE, SIG_IGN);
std::string enc = "identity";
if (aw->gzip) aw->pl = compress(aw->pl, &enc);

View file

@ -43,7 +43,7 @@ void Writer::obj() {
void Writer::key(const std::string& k) {
if (_stack.empty() || _stack.top().type != OBJ)
throw WriterException("Keys only allowed in objects.");
if (!_stack.top().empty) (*_out) << "," << (_pretty ? " " : "");
if (!_stack.top().empty) (*_out) << ",";
_stack.top().empty = 0;
prettor();
*_out << "\"" << k << "\""
@ -86,6 +86,12 @@ void Writer::val(int v) {
*_out << v;
}
// _____________________________________________________________________________
void Writer::val(size_t v) {
valCheck();
*_out << v;
}
// _____________________________________________________________________________
void Writer::val(double v) {
valCheck();
@ -104,6 +110,9 @@ void Writer::val(const Val& v) {
case Val::JSNULL:
val(Null());
return;
case Val::UINT:
val(v.ui);
return;
case Val::INT:
val(v.i);
return;

View file

@ -28,9 +28,10 @@ class WriterException : public std::exception {
struct Null {};
struct Val {
enum VAL_T { INT, FLOAT, STRING, ARRAY, DICT, BOOL, JSNULL };
enum VAL_T { UINT, INT, FLOAT, STRING, ARRAY, DICT, BOOL, JSNULL };
VAL_T type;
int i;
uint64_t ui;
double f;
std::string str;
std::vector<Val> arr;
@ -43,6 +44,8 @@ struct Val {
Val(const std::string& strC) { str = strC, type = STRING; }
Val(const char* strC) { str = strC, type = STRING; }
Val(double fC) { f = fC, type = FLOAT; }
Val(size_t iC) { ui = iC, type = UINT; }
Val(uint32_t iC) { ui = iC, type = UINT; }
Val(int iC) { i = iC, type = INT; }
Val(bool fC) { i = fC, type = BOOL; }
};
@ -69,6 +72,7 @@ class Writer {
void val(const std::string& v);
void val(const char* v);
void val(double v);
void val(size_t v);
void val(int v);
void val(bool v);
void val(Null);

View file

@ -8,6 +8,7 @@
#include <chrono>
#include <iomanip>
#include <iostream>
#include <sstream>
#define VDEBUG 4
#define DEBUG 3
@ -21,6 +22,7 @@
// compiler will optimize statement away if x > LOGLEVEL
#define LOG(x) if (x <= LOGLEVEL) util::Log<x>().log()
#define LOGTO(x, os) if (x <= LOGLEVEL) util::Log<x>(&os).log()
using std::setfill;
using std::setw;
@ -33,12 +35,14 @@ static const char* LOGS[] = {"ERROR", "WARN ", "INFO ", "DEBUG", "DEBUG"};
template <char LVL>
class Log {
public:
Log() { if (LVL == ERROR) os = &std::cerr; else os = &std::cout; }
~Log() { (*os) << std::endl; }
Log() { if (LVL < INFO) os = &std::cerr; else os = &std::cout; }
Log(std::ostream* s) { os = s; }
~Log() { buf << std::endl; (*os) << buf.str(); }
std::ostream& log() { return ts() << LOGS[(size_t)LVL] << ": "; }
private:
std::ostream* os;
std::ostringstream buf;
std::ostream& ts() {
char tl[20];
auto n = system_clock::now();
@ -46,7 +50,7 @@ class Log {
int m = duration_cast<milliseconds>(n-time_point_cast<seconds>(n)).count();
struct tm t = *localtime(&tt);
strftime(tl, 20, "%Y-%m-%d %H:%M:%S", &t);
return (*os) << "[" << tl << "." << setfill('0') << setw(3) << m << "] ";
return buf << "[" << tl << "." << setfill('0') << setw(3) << m << "] ";
}
};
}

View file

@ -0,0 +1,38 @@
// Copyright 2016
// Author: Patrick Brosi
#include "util/Misc.h"
#include "util/geo/QuadTree.h"
#include "util/tests/QuadTreeTest.h"
using util::approx;
using util::geo::QuadTree;
using util::geo::DPoint;
using util::geo::DBox;
// _____________________________________________________________________________
void QuadTreeTest::run() {
// ___________________________________________________________________________
{
QuadTree<int, double> qt(4, 4, DBox(DPoint(0, 0), DPoint(10, 10)));
qt.insert(0, {2, 2});
TEST(qt.size(), ==, 1);
qt.insert(666, {-1, 0});
TEST(qt.size(), ==, 1);
qt.insert(1, {0, 0});
TEST(qt.size(), ==, 2);
qt.insert(2, {0, 1});
TEST(qt.size(), ==, 3);
qt.insert(3, {6, 9});
TEST(qt.size(), ==, 4);
qt.insert(4, {9, 0});
TEST(qt.size(), ==, 5);
}
}

View file

@ -0,0 +1,12 @@
// Copyright 2016
// Author: Patrick Brosi
#ifndef UTIL_TEST_QUADTREETEST_H_
#define UTIL_TEST_QUADTREETEST_H_
class QuadTreeTest {
public:
void run();
};
#endif

File diff suppressed because it is too large Load diff