use util submodule
This commit is contained in:
parent
3f43538010
commit
1d8ce1aa7c
77 changed files with 4 additions and 18658 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -7,3 +7,6 @@
|
||||||
[submodule "src/configparser"]
|
[submodule "src/configparser"]
|
||||||
path = src/configparser
|
path = src/configparser
|
||||||
url = https://git.patrickbrosi.de/patrick/configparser
|
url = https://git.patrickbrosi.de/patrick/configparser
|
||||||
|
[submodule "src/util"]
|
||||||
|
path = src/util
|
||||||
|
url = https://github.com/ad-freiburg/util
|
||||||
|
|
1
src/util
Submodule
1
src/util
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit dc9d4a6c701bd2b88c09683a4f901d743dc8999e
|
436
src/util/3rdparty/MurmurHash3.cpp
vendored
436
src/util/3rdparty/MurmurHash3.cpp
vendored
|
@ -1,436 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// 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;
|
|
||||||
// fall through
|
|
||||||
case 2:
|
|
||||||
k1 ^= tail[1] << 8;
|
|
||||||
// fall through
|
|
||||||
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;
|
|
||||||
// fall through
|
|
||||||
case 14:
|
|
||||||
k4 ^= tail[13] << 8;
|
|
||||||
// fall through
|
|
||||||
case 13:
|
|
||||||
k4 ^= tail[12] << 0;
|
|
||||||
k4 *= c4;
|
|
||||||
k4 = ROTL32(k4, 18);
|
|
||||||
k4 *= c1;
|
|
||||||
h4 ^= k4;
|
|
||||||
// fall through
|
|
||||||
case 12:
|
|
||||||
k3 ^= tail[11] << 24;
|
|
||||||
// fall through
|
|
||||||
case 11:
|
|
||||||
k3 ^= tail[10] << 16;
|
|
||||||
// fall through
|
|
||||||
case 10:
|
|
||||||
k3 ^= tail[9] << 8;
|
|
||||||
// fall through
|
|
||||||
case 9:
|
|
||||||
k3 ^= tail[8] << 0;
|
|
||||||
k3 *= c3;
|
|
||||||
k3 = ROTL32(k3, 17);
|
|
||||||
k3 *= c4;
|
|
||||||
h3 ^= k3;
|
|
||||||
// fall through
|
|
||||||
case 8:
|
|
||||||
k2 ^= tail[7] << 24;
|
|
||||||
// fall through
|
|
||||||
case 7:
|
|
||||||
k2 ^= tail[6] << 16;
|
|
||||||
// fall through
|
|
||||||
case 6:
|
|
||||||
k2 ^= tail[5] << 8;
|
|
||||||
// fall through
|
|
||||||
case 5:
|
|
||||||
k2 ^= tail[4] << 0;
|
|
||||||
k2 *= c2;
|
|
||||||
k2 = ROTL32(k2, 16);
|
|
||||||
k2 *= c3;
|
|
||||||
h2 ^= k2;
|
|
||||||
// fall through
|
|
||||||
case 4:
|
|
||||||
k1 ^= tail[3] << 24;
|
|
||||||
// fall through
|
|
||||||
case 3:
|
|
||||||
k1 ^= tail[2] << 16;
|
|
||||||
// fall through
|
|
||||||
case 2:
|
|
||||||
k1 ^= tail[1] << 8;
|
|
||||||
// fall through
|
|
||||||
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;
|
|
||||||
// fall through
|
|
||||||
case 14:
|
|
||||||
k2 ^= ((uint64_t)tail[13]) << 40;
|
|
||||||
// fall through
|
|
||||||
case 13:
|
|
||||||
k2 ^= ((uint64_t)tail[12]) << 32;
|
|
||||||
// fall through
|
|
||||||
case 12:
|
|
||||||
k2 ^= ((uint64_t)tail[11]) << 24;
|
|
||||||
// fall through
|
|
||||||
case 11:
|
|
||||||
k2 ^= ((uint64_t)tail[10]) << 16;
|
|
||||||
// fall through
|
|
||||||
case 10:
|
|
||||||
k2 ^= ((uint64_t)tail[9]) << 8;
|
|
||||||
// fall through
|
|
||||||
case 9:
|
|
||||||
k2 ^= ((uint64_t)tail[8]) << 0;
|
|
||||||
k2 *= c2;
|
|
||||||
k2 = ROTL64(k2, 33);
|
|
||||||
k2 *= c1;
|
|
||||||
h2 ^= k2;
|
|
||||||
// fall through
|
|
||||||
case 8:
|
|
||||||
k1 ^= ((uint64_t)tail[7]) << 56;
|
|
||||||
// fall through
|
|
||||||
case 7:
|
|
||||||
k1 ^= ((uint64_t)tail[6]) << 48;
|
|
||||||
// fall through
|
|
||||||
case 6:
|
|
||||||
k1 ^= ((uint64_t)tail[5]) << 40;
|
|
||||||
// fall through
|
|
||||||
case 5:
|
|
||||||
k1 ^= ((uint64_t)tail[4]) << 32;
|
|
||||||
// fall through
|
|
||||||
case 4:
|
|
||||||
k1 ^= ((uint64_t)tail[3]) << 24;
|
|
||||||
// fall through
|
|
||||||
case 3:
|
|
||||||
k1 ^= ((uint64_t)tail[2]) << 16;
|
|
||||||
// fall through
|
|
||||||
case 2:
|
|
||||||
k1 ^= ((uint64_t)tail[1]) << 8;
|
|
||||||
// fall through
|
|
||||||
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
37
src/util/3rdparty/MurmurHash3.h
vendored
|
@ -1,37 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// 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_
|
|
1700
src/util/3rdparty/RTree.h
vendored
1700
src/util/3rdparty/RTree.h
vendored
File diff suppressed because it is too large
Load diff
454
src/util/3rdparty/dtoa_milo.h
vendored
454
src/util/3rdparty/dtoa_milo.h
vendored
|
@ -1,454 +0,0 @@
|
||||||
// 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
|
|
|
@ -1,29 +0,0 @@
|
||||||
file(GLOB_RECURSE util_SRC *.cpp)
|
|
||||||
list(REMOVE_ITEM util_SRC TestMain.cpp)
|
|
||||||
add_library(util ${util_SRC})
|
|
||||||
|
|
||||||
if (!BZIP2_FOUND)
|
|
||||||
find_package(BZip2)
|
|
||||||
if (ZLIB_FOUND)
|
|
||||||
add_definitions( -DBZLIB_FOUND=${BZIP2_FOUND} )
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BZIP2_FOUND)
|
|
||||||
include_directories( ${BZIP2_INCLUDE_DIR} )
|
|
||||||
target_link_libraries( util ${BZIP2_LIBRARIES} )
|
|
||||||
endif(BZIP2_FOUND)
|
|
||||||
|
|
||||||
if (!ZLIB_FOUND)
|
|
||||||
find_package(ZLIB)
|
|
||||||
if (ZLIB_FOUND)
|
|
||||||
add_definitions( -DZLIB_FOUND=${ZLIB_FOUND} )
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (ZLIB_FOUND)
|
|
||||||
include_directories( ${ZLIB_INCLUDE_DIRS} )
|
|
||||||
target_link_libraries( util ${ZLIB_LIBRARIES} )
|
|
||||||
endif(ZLIB_FOUND)
|
|
||||||
|
|
||||||
add_subdirectory(tests)
|
|
706
src/util/Misc.h
706
src/util/Misc.h
|
@ -1,706 +0,0 @@
|
||||||
// Copyright 2017, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_MISC_H_
|
|
||||||
#define UTIL_MISC_H_
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstring>
|
|
||||||
#include <chrono>
|
|
||||||
#include <sstream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <vector>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <map>
|
|
||||||
#include <thread>
|
|
||||||
#include "3rdparty/dtoa_milo.h"
|
|
||||||
#include "util/String.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<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<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, ==, e)
|
|
||||||
#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 {
|
|
||||||
|
|
||||||
const static std::map<std::string, std::string> HTML_COLOR_NAMES = {
|
|
||||||
{"aliceblue","F0F8FF"},
|
|
||||||
{"antiquewhite","FAEBD7"},
|
|
||||||
{"aqua","00FFFF"},
|
|
||||||
{"aquamarine","7FFFD4"},
|
|
||||||
{"azure","F0FFFF"},
|
|
||||||
{"beige","F5F5DC"},
|
|
||||||
{"bisque","FFE4C4"},
|
|
||||||
{"black","000000"},
|
|
||||||
{"blanchedalmond","FFEBCD"},
|
|
||||||
{"blue","0000FF"},
|
|
||||||
{"blueviolet","8A2BE2"},
|
|
||||||
{"brown","A52A2A"},
|
|
||||||
{"burlywood","DEB887"},
|
|
||||||
{"cadetblue","5F9EA0"},
|
|
||||||
{"chartreuse","7FFF00"},
|
|
||||||
{"chocolate","D2691E"},
|
|
||||||
{"coral","FF7F50"},
|
|
||||||
{"cornflowerblue","6495ED"},
|
|
||||||
{"cornsilk","FFF8DC"},
|
|
||||||
{"crimson","DC143C"},
|
|
||||||
{"cyan","00FFFF"},
|
|
||||||
{"darkblue","00008B"},
|
|
||||||
{"darkcyan","008B8B"},
|
|
||||||
{"darkgoldenrod","B8860B"},
|
|
||||||
{"darkgray","A9A9A9"},
|
|
||||||
{"darkgreen","006400"},
|
|
||||||
{"darkgrey","A9A9A9"},
|
|
||||||
{"darkkhaki","BDB76B"},
|
|
||||||
{"darkmagenta","8B008B"},
|
|
||||||
{"darkolivegreen","556B2F"},
|
|
||||||
{"darkorange","FF8C00"},
|
|
||||||
{"darkorchid","9932CC"},
|
|
||||||
{"darkred","8B0000"},
|
|
||||||
{"darksalmon","E9967A"},
|
|
||||||
{"darkseagreen","8FBC8F"},
|
|
||||||
{"darkslateblue","483D8B"},
|
|
||||||
{"darkslategray","2F4F4F"},
|
|
||||||
{"darkslategrey","2F4F4F"},
|
|
||||||
{"darkturquoise","00CED1"},
|
|
||||||
{"darkviolet","9400D3"},
|
|
||||||
{"deeppink","FF1493"},
|
|
||||||
{"deepskyblue","00BFFF"},
|
|
||||||
{"dimgray","696969"},
|
|
||||||
{"dimgrey","696969"},
|
|
||||||
{"dodgerblue","1E90FF"},
|
|
||||||
{"firebrick","B22222"},
|
|
||||||
{"floralwhite","FFFAF0"},
|
|
||||||
{"forestgreen","228B22"},
|
|
||||||
{"fuchsia","FF00FF"},
|
|
||||||
{"gainsboro","DCDCDC"},
|
|
||||||
{"ghostwhite","F8F8FF"},
|
|
||||||
{"gold","FFD700"},
|
|
||||||
{"goldenrod","DAA520"},
|
|
||||||
{"gray","808080"},
|
|
||||||
{"green","008000"},
|
|
||||||
{"greenyellow","ADFF2F"},
|
|
||||||
{"grey","808080"},
|
|
||||||
{"honeydew","F0FFF0"},
|
|
||||||
{"hotpink","FF69B4"},
|
|
||||||
{"indianred","CD5C5C"},
|
|
||||||
{"indigo","4B0082"},
|
|
||||||
{"ivory","FFFFF0"},
|
|
||||||
{"khaki","F0E68C"},
|
|
||||||
{"lavender","E6E6FA"},
|
|
||||||
{"lavenderblush","FFF0F5"},
|
|
||||||
{"lawngreen","7CFC00"},
|
|
||||||
{"lemonchiffon","FFFACD"},
|
|
||||||
{"lightblue","ADD8E6"},
|
|
||||||
{"lightcoral","F08080"},
|
|
||||||
{"lightcyan","E0FFFF"},
|
|
||||||
{"lightgoldenrodyellow","FAFAD2"},
|
|
||||||
{"lightgray","D3D3D3"},
|
|
||||||
{"lightgreen","90EE90"},
|
|
||||||
{"lightgrey","D3D3D3"},
|
|
||||||
{"lightpink","FFB6C1"},
|
|
||||||
{"lightsalmon","FFA07A"},
|
|
||||||
{"lightseagreen","20B2AA"},
|
|
||||||
{"lightskyblue","87CEFA"},
|
|
||||||
{"lightslategray","778899"},
|
|
||||||
{"lightslategrey","778899"},
|
|
||||||
{"lightsteelblue","B0C4DE"},
|
|
||||||
{"lightyellow","FFFFE0"},
|
|
||||||
{"lime","00FF00"},
|
|
||||||
{"limegreen","32CD32"},
|
|
||||||
{"linen","FAF0E6"},
|
|
||||||
{"magenta","FF00FF"},
|
|
||||||
{"maroon","800000"},
|
|
||||||
{"mediumaquamarine","66CDAA"},
|
|
||||||
{"mediumblue","0000CD"},
|
|
||||||
{"mediumorchid","BA55D3"},
|
|
||||||
{"mediumpurple","9370DB"},
|
|
||||||
{"mediumseagreen","3CB371"},
|
|
||||||
{"mediumslateblue","7B68EE"},
|
|
||||||
{"mediumspringgreen","00FA9A"},
|
|
||||||
{"mediumturquoise","48D1CC"},
|
|
||||||
{"mediumvioletred","C71585"},
|
|
||||||
{"midnightblue","191970"},
|
|
||||||
{"mintcream","F5FFFA"},
|
|
||||||
{"mistyrose","FFE4E1"},
|
|
||||||
{"moccasin","FFE4B5"},
|
|
||||||
{"navajowhite","FFDEAD"},
|
|
||||||
{"navy","000080"},
|
|
||||||
{"oldlace","FDF5E6"},
|
|
||||||
{"olive","808000"},
|
|
||||||
{"olivedrab","6B8E23"},
|
|
||||||
{"orange","FFA500"},
|
|
||||||
{"orangered","FF4500"},
|
|
||||||
{"orchid","DA70D6"},
|
|
||||||
{"palegoldenrod","EEE8AA"},
|
|
||||||
{"palegreen","98FB98"},
|
|
||||||
{"paleturquoise","AFEEEE"},
|
|
||||||
{"palevioletred","DB7093"},
|
|
||||||
{"papayawhip","FFEFD5"},
|
|
||||||
{"peachpuff","FFDAB9"},
|
|
||||||
{"peru","CD853F"},
|
|
||||||
{"pink","FFC0CB"},
|
|
||||||
{"plum","DDA0DD"},
|
|
||||||
{"powderblue","B0E0E6"},
|
|
||||||
{"purple","800080"},
|
|
||||||
{"red","FF0000"},
|
|
||||||
{"rosybrown","BC8F8F"},
|
|
||||||
{"royalblue","4169E1"},
|
|
||||||
{"saddlebrown","8B4513"},
|
|
||||||
{"salmon","FA8072"},
|
|
||||||
{"sandybrown","F4A460"},
|
|
||||||
{"seagreen","2E8B57"},
|
|
||||||
{"seashell","FFF5EE"},
|
|
||||||
{"sienna","A0522D"},
|
|
||||||
{"silver","C0C0C0"},
|
|
||||||
{"skyblue","87CEEB"},
|
|
||||||
{"slateblue","6A5ACD"},
|
|
||||||
{"slategray","708090"},
|
|
||||||
{"slategrey","708090"},
|
|
||||||
{"snow","FFFAFA"},
|
|
||||||
{"springgreen","00FF7F"},
|
|
||||||
{"steelblue","4682B4"},
|
|
||||||
{"tan","D2B48C"},
|
|
||||||
{"teal","008080"},
|
|
||||||
{"thistle","D8BFD8"},
|
|
||||||
{"tomato","FF6347"},
|
|
||||||
{"turquoise","40E0D0"},
|
|
||||||
{"violet","EE82EE"},
|
|
||||||
{"wheat","F5DEB3"},
|
|
||||||
{"white","FFFFFF"},
|
|
||||||
{"whitesmoke","F5F5F5"},
|
|
||||||
{"yellow","FFFF00"},
|
|
||||||
{"yellowgreen","9ACD32"}
|
|
||||||
};
|
|
||||||
|
|
||||||
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,
|
|
||||||
100000, 1000000, 10000000, 100000000, 1000000000};
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline uint64_t factorial(uint64_t n) {
|
|
||||||
if (n < 2) return 1;
|
|
||||||
return n * factorial(n - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline uint64_t atoul(const char* p) {
|
|
||||||
uint64_t ret = 0;
|
|
||||||
|
|
||||||
while (*p) {
|
|
||||||
ret = ret * 10 + (*p++ - '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline bool isFloatingPoint(const std::string& str) {
|
|
||||||
std::stringstream ss(str);
|
|
||||||
double f;
|
|
||||||
ss >> std::noskipws >> f;
|
|
||||||
return ss.eof() && ! ss.fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string formatFloat(double f, size_t digits) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << std::fixed << std::setprecision(digits) << f;
|
|
||||||
std::string ret = ss.str();
|
|
||||||
|
|
||||||
if (ret.find('.') != std::string::npos) {
|
|
||||||
auto p = ret.find_last_not_of('0');
|
|
||||||
if (ret[p] == '.') return ret.substr(0, p);
|
|
||||||
return ret.substr(0, p + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline double atof(const char* p, uint8_t mn) {
|
|
||||||
// this atof implementation works only on "normal" float strings like
|
|
||||||
// 56.445 or -345.00, but should be faster than std::atof
|
|
||||||
double ret = 0.0;
|
|
||||||
bool neg = false;
|
|
||||||
if (*p == '-') {
|
|
||||||
neg = true;
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (*p >= '0' && *p <= '9') {
|
|
||||||
ret = ret * 10.0 + (*p - '0');
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p == '.') {
|
|
||||||
p++;
|
|
||||||
double f = 0;
|
|
||||||
uint8_t n = 0;
|
|
||||||
|
|
||||||
for (; n < mn && *p >= '0' && *p <= '9'; n++, p++) {
|
|
||||||
f = f * 10.0 + (*p - '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n < 10)
|
|
||||||
ret += f / pow10[n];
|
|
||||||
else
|
|
||||||
ret += f / std::pow(10, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (neg) return -ret;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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
|
|
||||||
const char* homedir = 0;
|
|
||||||
char* buf = 0;
|
|
||||||
|
|
||||||
if ((homedir = getenv("HOME")) == 0) {
|
|
||||||
homedir = "";
|
|
||||||
struct passwd pwd;
|
|
||||||
struct passwd* result;
|
|
||||||
size_t bufsize;
|
|
||||||
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
|
|
||||||
if (bufsize == static_cast<size_t>(-1)) bufsize = 0x4000;
|
|
||||||
buf = static_cast<char*>(malloc(bufsize));
|
|
||||||
if (buf != 0) {
|
|
||||||
getpwuid_r(getuid(), &pwd, buf, bufsize, &result);
|
|
||||||
if (result != NULL) homedir = result->pw_dir;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ret(homedir);
|
|
||||||
if (buf) free(buf);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string getTmpDir() {
|
|
||||||
// first, check if an env variable is set
|
|
||||||
const char* tmpdir = getenv("TMPDIR");
|
|
||||||
if (tmpdir && std::strlen(tmpdir)) return std::string(tmpdir);
|
|
||||||
|
|
||||||
// second, check if /tmp is writable
|
|
||||||
if (access("/tmp/", W_OK) == 0) return "/tmp";
|
|
||||||
|
|
||||||
// third, check if the cwd is writable
|
|
||||||
if (access(".", W_OK) == 0) return ".";
|
|
||||||
|
|
||||||
// lastly, return the users home directory as a fallback
|
|
||||||
return getHomeDir();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string getTmpFName(std::string dir, std::string name,
|
|
||||||
std::string postf) {
|
|
||||||
if (postf.size()) postf = "-" + postf;
|
|
||||||
if (dir == "<tmp>") dir = util::getTmpDir();
|
|
||||||
if (dir.size() && dir.back() != '/') dir = dir + "/";
|
|
||||||
|
|
||||||
std::string f = dir + name + postf;
|
|
||||||
|
|
||||||
size_t c = 0;
|
|
||||||
|
|
||||||
while (access(f.c_str(), F_OK) != -1) {
|
|
||||||
c++;
|
|
||||||
if (c > 10000) {
|
|
||||||
// giving up...
|
|
||||||
std::cerr << "Could not find temporary file name!" << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << dir << name << postf << "-" << std::rand();
|
|
||||||
f = ss.str().c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ___________________________________________________________________________
|
|
||||||
inline std::string rgbToHex(int r, int g, int b) {
|
|
||||||
char hexcol[16];
|
|
||||||
snprintf(hexcol, sizeof hexcol, "%02x%02x%02x", r, g, b);
|
|
||||||
return hexcol;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ___________________________________________________________________________
|
|
||||||
inline void hsvToRgb(float* r, float* g, float* b, float h, float s, float v) {
|
|
||||||
int i;
|
|
||||||
float f, p, q, t;
|
|
||||||
|
|
||||||
if (s == 0) {
|
|
||||||
*r = *g = *b = v;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
h /= 60;
|
|
||||||
i = floor(h);
|
|
||||||
f = h - i;
|
|
||||||
p = v * (1 - s);
|
|
||||||
q = v * (1 - s * f);
|
|
||||||
t = v * (1 - s * (1 - f));
|
|
||||||
|
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
*r = v;
|
|
||||||
*g = t;
|
|
||||||
*b = p;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
*r = q;
|
|
||||||
*g = v;
|
|
||||||
*b = p;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
*r = p;
|
|
||||||
*g = v;
|
|
||||||
*b = t;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
*r = p;
|
|
||||||
*g = q;
|
|
||||||
*b = v;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
*r = t;
|
|
||||||
*g = p;
|
|
||||||
*b = v;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
*r = v;
|
|
||||||
*g = p;
|
|
||||||
*b = q;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ___________________________________________________________________________
|
|
||||||
inline std::string normHtmlColor(const std::string& col) {
|
|
||||||
auto i = HTML_COLOR_NAMES.find(toLower(col));
|
|
||||||
if (i != HTML_COLOR_NAMES.end()) return i->second;
|
|
||||||
return col;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ___________________________________________________________________________
|
|
||||||
inline std::string randomHtmlColor() {
|
|
||||||
double goldenRatio = 0.618033988749895;
|
|
||||||
double h = static_cast<double>(rand()) / static_cast<double>(RAND_MAX);
|
|
||||||
h += goldenRatio;
|
|
||||||
h = fmod(h, 1.0);
|
|
||||||
float r, g, b;
|
|
||||||
hsvToRgb(&r, &g, &b, h * 360, 0.95, 0.95);
|
|
||||||
return rgbToHex(r * 256, g * 256, b * 256);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline char* readableSize(double size, size_t n, char* buf) {
|
|
||||||
int i = 0;
|
|
||||||
const char* units[] = {"B", "kB", "MB", "GB", "TB", "PB"};
|
|
||||||
while (size > 1024 && i < 5) {
|
|
||||||
size /= 1024;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
snprintf(buf, n, "%.*f %s", i, size, units[i]);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string readableSize(double size) {
|
|
||||||
char buffer[30];
|
|
||||||
return readableSize(size, 30, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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_
|
|
|
@ -1,116 +0,0 @@
|
||||||
// Copyright 2017, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#ifndef UTIL_NULLABLE_H_
|
|
||||||
#define UTIL_NULLABLE_H_
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class Nullable {
|
|
||||||
public:
|
|
||||||
Nullable()
|
|
||||||
: val(), null(true) {}
|
|
||||||
Nullable(T* valPointer)
|
|
||||||
: val(), null(true) {
|
|
||||||
if (valPointer) {
|
|
||||||
assign(*valPointer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Nullable(const T& value)
|
|
||||||
: val(value), null(false) {}
|
|
||||||
Nullable(const Nullable& other)
|
|
||||||
: val(other.val), null(other.isNull()) {}
|
|
||||||
|
|
||||||
Nullable& operator=(const Nullable& other) {
|
|
||||||
if (!other.isNull()) val = other.get();
|
|
||||||
null = other.isNull();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
T operator=(const T& other) {
|
|
||||||
assign(other);
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Passing through comparision operators
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool operator==(const Nullable& other) const {
|
|
||||||
return (other.isNull() && isNull()) || other.get() == get();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const Nullable& other) const {
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator<(const Nullable& other) const {
|
|
||||||
return !other.isNull() && !isNull() && get() < other.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator>(const Nullable& other) const {
|
|
||||||
return !(*this < other || *this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator<=(const Nullable& other) const {
|
|
||||||
return *this < other || *this == other;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator>=(const Nullable& other) const {
|
|
||||||
return *this > other || *this == other;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const T& other) const {
|
|
||||||
return !isNull() && other == get();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const T& other) const {
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator<(const T& other) const {
|
|
||||||
return !isNull() && get() < other;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator>(const T& other) const {
|
|
||||||
return !(*this < other || *this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator<=(const T& other) const {
|
|
||||||
return *this < other || *this == other;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator>=(const T& other) const {
|
|
||||||
return *this > other || *this == other;
|
|
||||||
}
|
|
||||||
|
|
||||||
operator T() const {
|
|
||||||
return get();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isNull() const {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
T get() const {
|
|
||||||
if (!isNull()) return val;
|
|
||||||
else throw std::runtime_error("Trying to retrieve value of NULL object.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void assign(T v) {
|
|
||||||
val = v;
|
|
||||||
null = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
T val;
|
|
||||||
bool null;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // UTIL_NULLABLE_H_
|
|
|
@ -1,39 +0,0 @@
|
||||||
// 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
|
|
|
@ -1,36 +0,0 @@
|
||||||
// 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 (key < _last) 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();
|
|
||||||
}
|
|
|
@ -1,458 +0,0 @@
|
||||||
// Copyright 2017, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_STRING_H_
|
|
||||||
#define UTIL_STRING_H_
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <bitset>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cmath>
|
|
||||||
#include <codecvt>
|
|
||||||
#include <cstring>
|
|
||||||
#include <exception>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <iostream>
|
|
||||||
#include <locale>
|
|
||||||
#include <set>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline bool endsWith(const std::string& a, const std::string& suff) {
|
|
||||||
if (suff.size() > a.size()) return false;
|
|
||||||
return a.compare(a.length() - suff.length(), suff.length(), suff) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string urlDecode(const std::string& encoded) {
|
|
||||||
std::string decoded;
|
|
||||||
for (size_t i = 0; i < encoded.size(); ++i) {
|
|
||||||
char c = encoded[i];
|
|
||||||
if (c == '%') {
|
|
||||||
std::string ah = encoded.substr(i + 1, 2);
|
|
||||||
char* nonProced = 0;
|
|
||||||
char hexVal = strtol(ah.c_str(), &nonProced, 16);
|
|
||||||
|
|
||||||
if (ah.find_first_of("+-") > 1 && ah.size() - strlen(nonProced) == 2) {
|
|
||||||
c = hexVal;
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
} else if (c == '+') {
|
|
||||||
c = ' ';
|
|
||||||
}
|
|
||||||
decoded += c;
|
|
||||||
}
|
|
||||||
return decoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string jsonStringEscape(const std::string& unesc) {
|
|
||||||
// modified code from
|
|
||||||
// http://stackoverflow.com/questions/7724448/simple-json-string-escape-for-c
|
|
||||||
std::ostringstream o;
|
|
||||||
for (auto c = unesc.cbegin(); c != unesc.cend(); c++) {
|
|
||||||
switch (*c) {
|
|
||||||
case '"':
|
|
||||||
o << "\\\"";
|
|
||||||
break;
|
|
||||||
case '\\':
|
|
||||||
o << "\\\\";
|
|
||||||
break;
|
|
||||||
case '\b':
|
|
||||||
o << "\\b";
|
|
||||||
break;
|
|
||||||
case '\f':
|
|
||||||
o << "\\f";
|
|
||||||
break;
|
|
||||||
case '\n':
|
|
||||||
o << "\\n";
|
|
||||||
break;
|
|
||||||
case '\r':
|
|
||||||
o << "\\r";
|
|
||||||
break;
|
|
||||||
case '\t':
|
|
||||||
o << "\\t";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if ('\x00' <= *c && *c <= '\x1f') {
|
|
||||||
o << "\\u" << std::hex << std::setw(4) << std::setfill('0')
|
|
||||||
<< static_cast<int>(*c);
|
|
||||||
} else {
|
|
||||||
o << *c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return o.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline bool replace(std::string& subj, const std::string& from,
|
|
||||||
const std::string& to) {
|
|
||||||
if (from.empty()) return false;
|
|
||||||
size_t start_pos = subj.find(from);
|
|
||||||
if (start_pos != std::string::npos) {
|
|
||||||
subj.replace(start_pos, from.length(), to);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline bool replaceAll(std::string& subj, const std::string& from,
|
|
||||||
const std::string& to) {
|
|
||||||
if (from.empty()) return false;
|
|
||||||
bool found = false;
|
|
||||||
size_t s = subj.find(from, 0);
|
|
||||||
for (; s != std::string::npos; s = subj.find(from, s + to.length())) {
|
|
||||||
found = true;
|
|
||||||
subj.replace(s, from.length(), to);
|
|
||||||
}
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string unixBasename(const std::string& pathname) {
|
|
||||||
return {std::find_if(pathname.rbegin(), pathname.rend(),
|
|
||||||
[](char c) { return c == '/'; })
|
|
||||||
.base(),
|
|
||||||
pathname.end()};
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
inline std::string toString(T obj) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << obj;
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::vector<std::string> split(std::string in, char sep) {
|
|
||||||
std::stringstream ss(in);
|
|
||||||
std::vector<std::string> ret(1);
|
|
||||||
while (std::getline(ss, ret.back(), sep)) {
|
|
||||||
ret.push_back("");
|
|
||||||
}
|
|
||||||
ret.pop_back();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string ltrim(std::string str, std::string c) {
|
|
||||||
str.erase(0, str.find_first_not_of(c));
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string rtrim(std::string str, std::string c) {
|
|
||||||
str.erase(str.find_last_not_of(c) + 1);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string trim(std::string str, std::string c) {
|
|
||||||
return ltrim(rtrim(str, c), c);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string ltrim(std::string str) { return ltrim(str, " \t\n\v\f\r"); }
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string rtrim(std::string str) { return rtrim(str, " \t\n\v\f\r"); }
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string trim(std::string str) { return trim(str, " \t\n\v\f\r"); }
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline size_t editDist(const std::string& s1, const std::string& s2) {
|
|
||||||
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++
|
|
||||||
size_t len1 = s1.size();
|
|
||||||
size_t len2 = s2.size();
|
|
||||||
std::vector<size_t> cur(len2 + 1);
|
|
||||||
std::vector<size_t> prev(len2 + 1);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < prev.size(); i++) prev[i] = i;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < len1; i++) {
|
|
||||||
cur[0] = i + 1;
|
|
||||||
for (size_t j = 0; j < len2; j++) {
|
|
||||||
cur[j + 1] =
|
|
||||||
std::min(prev[1 + j] + 1,
|
|
||||||
std::min(cur[j] + 1, prev[j] + (s1[i] == s2[j] ? 0 : 1)));
|
|
||||||
}
|
|
||||||
std::swap(cur, prev);
|
|
||||||
}
|
|
||||||
|
|
||||||
return prev[len2];
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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();
|
|
||||||
size_t len2 = std::min(s.size(), prefix.size() + deltaMax + 1);
|
|
||||||
std::vector<size_t> d((len1 + 1) * (len2 + 1));
|
|
||||||
|
|
||||||
d[0] = 0;
|
|
||||||
for (size_t i = 1; i <= len1; ++i) d[i * (len2 + 1)] = i;
|
|
||||||
for (size_t i = 1; i <= len2; ++i) d[i] = i;
|
|
||||||
|
|
||||||
for (size_t i = 1; i <= len1; i++) {
|
|
||||||
for (size_t j = 1; j <= len2; j++) {
|
|
||||||
d[i * (len2 + 1) + j] = std::min(std::min(d[(i - 1) * (len2 + 1) + j] + 1,
|
|
||||||
d[i * (len2 + 1) + j - 1] + 1),
|
|
||||||
d[(i - 1) * (len2 + 1) + j - 1] +
|
|
||||||
(prefix[i - 1] == s[j - 1] ? 0 : 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// take min of last row
|
|
||||||
size_t deltaMin = std::max(std::max(deltaMax + 1, prefix.size()), s.size());
|
|
||||||
for (size_t i = 0; i <= len2; i++) {
|
|
||||||
if (d[len1 * (len2 + 1) + i] < deltaMin)
|
|
||||||
deltaMin = d[len1 * (len2 + 1) + i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return deltaMin;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string toLower(std::string str) {
|
|
||||||
std::transform(str.begin(), str.end(), str.begin(), tolower);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <class Iter>
|
|
||||||
inline std::string implode(Iter begin, const Iter& end, const char* del) {
|
|
||||||
std::stringstream ss;
|
|
||||||
size_t i = 0;
|
|
||||||
while (begin != end) {
|
|
||||||
if (i != 0) ss << del;
|
|
||||||
ss << *begin;
|
|
||||||
begin++;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <class T>
|
|
||||||
inline std::string implode(const std::vector<T>& vec, const char* del) {
|
|
||||||
return implode(vec.begin(), vec.end(), del);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline std::string normalizeWhiteSpace(const std::string& input) {
|
|
||||||
std::string ret;
|
|
||||||
bool ws = false;
|
|
||||||
for (size_t i = 0; i < input.size(); i++) {
|
|
||||||
if (std::isspace(input[i])) {
|
|
||||||
if (!ws) {
|
|
||||||
ret += " ";
|
|
||||||
ws = true;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
ws = false;
|
|
||||||
ret += input[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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) {
|
|
||||||
if (a == b) return 1;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline double btsSimiInner(const std::vector<std::string>& toks,
|
|
||||||
const std::string& b, double best) {
|
|
||||||
std::set<std::string> toksSet;
|
|
||||||
toksSet.insert(toks.begin(), toks.end());
|
|
||||||
std::vector<std::string> toksUniqSorted;
|
|
||||||
toksUniqSorted.insert(toksUniqSorted.begin(), toksSet.begin(), toksSet.end());
|
|
||||||
|
|
||||||
assert(toksUniqSorted.size() <= 8);
|
|
||||||
|
|
||||||
for (uint8_t v = 1; v <= pow(2, toksUniqSorted.size()); v++) {
|
|
||||||
std::bitset<8> bs(v);
|
|
||||||
std::vector<std::string> cur(bs.count());
|
|
||||||
|
|
||||||
size_t i = 0;
|
|
||||||
for (size_t j = 0; j < toksUniqSorted.size(); j++) {
|
|
||||||
if (bs[j]) {
|
|
||||||
cur[i] = toksUniqSorted[j];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double tmp = util::implode(cur, " ").size();
|
|
||||||
|
|
||||||
// ed between the two string will always be at least their length
|
|
||||||
// difference - if this is already too big, skip it right now
|
|
||||||
double dt = 1 - (fabs(tmp - b.size()) * 1.0) / (fmax(tmp, b.size()) * 1.0);
|
|
||||||
|
|
||||||
if (dt <= best) continue;
|
|
||||||
|
|
||||||
// cur is guaranteed to be sorted now
|
|
||||||
do {
|
|
||||||
const auto& comb = util::implode(cur, " ");
|
|
||||||
|
|
||||||
double d =
|
|
||||||
1 - ((editDist(comb, b) * 1.0) / (fmax(comb.size(), b.size()) * 1.0));
|
|
||||||
|
|
||||||
if (fabs(d - 1) < 0.0001) return 1;
|
|
||||||
|
|
||||||
if (d > best) best = d;
|
|
||||||
} while (std::next_permutation(cur.begin(), cur.end()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return best;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline double btsSimi(std::string a, std::string b) {
|
|
||||||
// this follows the implementation for the station similarity paper in
|
|
||||||
// https://github.com/ad-freiburg/statsimi/
|
|
||||||
if (a == b) return 1;
|
|
||||||
|
|
||||||
std::set<std::string> sa, sb;
|
|
||||||
|
|
||||||
auto toksA = tokenize(a);
|
|
||||||
auto toksB = tokenize(b);
|
|
||||||
|
|
||||||
// fallback to jaccard if the token set is too large
|
|
||||||
if (toksA.size() > 6 || toksB.size() > 6) {
|
|
||||||
return jaccardSimi(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toksA.size() > toksB.size()) {
|
|
||||||
std::swap(a, b);
|
|
||||||
std::swap(toksA, toksB);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is already our best known value - simply the edit
|
|
||||||
// distance similarity between the strings
|
|
||||||
double best = 1 - (editDist(a, b) * 1.0) / std::fmax(a.size(), b.size());
|
|
||||||
|
|
||||||
if (fabs(best) < 0.0001) return 0;
|
|
||||||
|
|
||||||
best = btsSimiInner(toksA, b, best);
|
|
||||||
|
|
||||||
if (fabs(best - 1) < 0.0001) return 1;
|
|
||||||
|
|
||||||
return btsSimiInner(toksB, a, best);
|
|
||||||
}
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_STRING_H_
|
|
|
@ -1,55 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GEO_BEZIERCURVE_H_
|
|
||||||
#define UTIL_GEO_BEZIERCURVE_H_
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include "util/geo/Geo.h"
|
|
||||||
#include "util/geo/PolyLine.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace geo {
|
|
||||||
|
|
||||||
struct CubicPolynom {
|
|
||||||
CubicPolynom(double a, double b, double c, double d, double x)
|
|
||||||
: a(a), b(b), c(c), d(d), x(x) {}
|
|
||||||
CubicPolynom() : a(0), b(0), c(0), d(0), x(0) {}
|
|
||||||
double a, b, c, d, x;
|
|
||||||
|
|
||||||
double valueAt(double x) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bezier curve
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
class BezierCurve {
|
|
||||||
public:
|
|
||||||
BezierCurve(const Point<T>& a, const Point<T>& b, const Point<T>& c,
|
|
||||||
const Point<T>& d);
|
|
||||||
|
|
||||||
const PolyLine<T>& render(double d);
|
|
||||||
|
|
||||||
private:
|
|
||||||
double _d;
|
|
||||||
|
|
||||||
// the x and y polynoms for this spline
|
|
||||||
CubicPolynom _xp, _yp;
|
|
||||||
|
|
||||||
// store the rendered polyline for quicker access
|
|
||||||
PolyLine<T> _rendered;
|
|
||||||
bool _didRender;
|
|
||||||
|
|
||||||
void recalcPolynoms(const Point<T>& x, const Point<T>& b, const Point<T>& c,
|
|
||||||
const Point<T>& d);
|
|
||||||
|
|
||||||
Point<T> valueAt(double t) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/geo/BezierCurve.tpp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // UTIL_GEO_BEZIERCURVE_H_
|
|
|
@ -1,70 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
BezierCurve<T>::BezierCurve(const Point<T>& a, const Point<T>& b,
|
|
||||||
const Point<T>& c, const Point<T>& d)
|
|
||||||
: _d(dist(a, d)) {
|
|
||||||
assert(_d > 0);
|
|
||||||
recalcPolynoms(a, b, c, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void BezierCurve<T>::recalcPolynoms(const Point<T>& a, const Point<T>& b,
|
|
||||||
const Point<T>& c, const Point<T>& d) {
|
|
||||||
_xp.a = a.getX();
|
|
||||||
_xp.b = 3.0 * (b.getX() - a.getX());
|
|
||||||
_xp.c = 3.0 * (c.getX() - b.getX()) - _xp.b;
|
|
||||||
_xp.d = d.getX() - a.getX() - _xp.c - _xp.b;
|
|
||||||
|
|
||||||
_yp.a = a.getY();
|
|
||||||
_yp.b = 3.0 * (b.getY() - a.getY());
|
|
||||||
_yp.c = 3.0 * (c.getY() - b.getY()) - _yp.b;
|
|
||||||
_yp.d = d.getY() - a.getY() - _yp.c - _yp.b;
|
|
||||||
|
|
||||||
_didRender = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
Point<T> BezierCurve<T>::valueAt(double t) const {
|
|
||||||
return Point<T>(_xp.valueAt(t), _yp.valueAt(t));
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
const PolyLine<T>& BezierCurve<T>::render(double d) {
|
|
||||||
assert(d > 0);
|
|
||||||
if (_didRender) return _rendered;
|
|
||||||
|
|
||||||
if (_d == 0) {
|
|
||||||
_rendered << Point<T>(_xp.a, _yp.a) << Point<T>(_xp.a, _yp.a);
|
|
||||||
return _rendered;
|
|
||||||
}
|
|
||||||
|
|
||||||
_rendered.empty();
|
|
||||||
double n = _d / d, dt = 1 / n, t = 0;
|
|
||||||
|
|
||||||
bool cancel = false;
|
|
||||||
while (true) {
|
|
||||||
_rendered << valueAt(t);
|
|
||||||
t += dt;
|
|
||||||
if (cancel) break;
|
|
||||||
if (t > 1) {
|
|
||||||
t = 1;
|
|
||||||
cancel = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_didRender = true;
|
|
||||||
return _rendered;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
inline double CubicPolynom::valueAt(double atx) const {
|
|
||||||
double dx = atx - x;
|
|
||||||
return a + b * dx + c * dx * dx + d * dx * dx * dx;
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GEO_BOX_H_
|
|
||||||
#define UTIL_GEO_BOX_H_
|
|
||||||
|
|
||||||
#include "./Point.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace geo {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Box {
|
|
||||||
public:
|
|
||||||
// maximum inverse box as default value of box
|
|
||||||
Box()
|
|
||||||
: _ll(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()),
|
|
||||||
_ur(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()) {}
|
|
||||||
Box(const Point<T>& ll, const Point<T>& ur) : _ll(ll), _ur(ur) {}
|
|
||||||
const Point<T>& getLowerLeft() const { return _ll; }
|
|
||||||
const Point<T>& getUpperRight() const { return _ur; }
|
|
||||||
|
|
||||||
Point<T>& getLowerLeft() { return _ll; }
|
|
||||||
Point<T>& getUpperRight() { return _ur; }
|
|
||||||
|
|
||||||
Point<T> getUpperLeft() { return {_ll.getX(), _ur.getY()}; }
|
|
||||||
Point<T> getLowerRight() { return {_ur.getX(), _ll.getY()}; }
|
|
||||||
|
|
||||||
void setLowerLeft(const Point<T>& ll) { _ll = ll; }
|
|
||||||
void setUpperRight(const Point<T>& ur) { _ur = ur; }
|
|
||||||
|
|
||||||
bool operator==(const Box<T>& b) const {
|
|
||||||
return getLowerLeft() == b.getLowerLeft() &&
|
|
||||||
getUpperRight() == b.getUpperRight();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const Box<T>& p) const { return !(*this == p); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Point<T> _ll, _ur;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class RotatedBox {
|
|
||||||
public:
|
|
||||||
RotatedBox() : _box(), _deg(0), _center() {}
|
|
||||||
RotatedBox(const Box<T>& box)
|
|
||||||
: _box(box),
|
|
||||||
_deg(0),
|
|
||||||
_center(Point<T>(
|
|
||||||
(box.getUpperRight().getX() - box.getLowerLeft().getX()) / T(2),
|
|
||||||
(box.getUpperRight().getY() - box.getLowerLeft().getY()) / T(2))) {}
|
|
||||||
RotatedBox(const Point<T>& ll, const Point<T>& ur)
|
|
||||||
: _box(ll, ur),
|
|
||||||
_deg(0),
|
|
||||||
_center(Point<T>((ur.getX() - ll.getX()) / T(2),
|
|
||||||
(ur.getY() - ll.getY()) / T(2))) {}
|
|
||||||
RotatedBox(const Box<T>& box, double deg)
|
|
||||||
: _box(box),
|
|
||||||
_deg(deg),
|
|
||||||
_center(Point<T>(
|
|
||||||
(box.getUpperRight().getX() - box.getLowerLeft().getX()) / T(2),
|
|
||||||
(box.getUpperRight().getY() - box.getLowerLeft().getY()) / T(2))) {}
|
|
||||||
RotatedBox(const Point<T>& ll, const Point<T>& ur, double deg)
|
|
||||||
: _box(ll, ur),
|
|
||||||
_deg(deg),
|
|
||||||
_center(Point<T>((ur.getX() - ll.getX()) / T(2),
|
|
||||||
(ur.getY() - ll.getY()) / T(2))) {}
|
|
||||||
RotatedBox(const Box<T>& box, double deg, const Point<T>& center)
|
|
||||||
: _box(box), _deg(deg), _center(center) {}
|
|
||||||
RotatedBox(const Point<T>& ll, const Point<T>& ur, double deg,
|
|
||||||
const Point<T>& center)
|
|
||||||
: _box(ll, ur), _deg(deg), _center(center) {}
|
|
||||||
|
|
||||||
const Box<T>& getBox() const { return _box; }
|
|
||||||
Box<T>& getBox() { return _box; }
|
|
||||||
|
|
||||||
double getDegree() const { return _deg; }
|
|
||||||
const Point<T>& getCenter() const { return _center; }
|
|
||||||
Point<T>& getCenter() { return _center; }
|
|
||||||
|
|
||||||
void setDegree(double deg) { _deg = deg; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Box<T> _box;
|
|
||||||
double _deg;
|
|
||||||
Point<T> _center;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace geo
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GEO_BOX_H_
|
|
|
@ -1,41 +0,0 @@
|
||||||
// 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_
|
|
|
@ -1,51 +0,0 @@
|
||||||
// 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;
|
|
||||||
}
|
|
2339
src/util/geo/Geo.h
2339
src/util/geo/Geo.h
File diff suppressed because it is too large
Load diff
|
@ -1,32 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GEOGRAPH_H_
|
|
||||||
#define UTIL_GEOGRAPH_H_
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include "util/geo/Geo.h"
|
|
||||||
#include "util/json/Writer.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace geograph {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class GeoEdgePL {
|
|
||||||
public:
|
|
||||||
virtual const util::geo::Line<T>* getGeom() const = 0;
|
|
||||||
virtual json::Dict getAttrs() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class GeoNodePL {
|
|
||||||
public:
|
|
||||||
virtual const util::geo::Point<T>* getGeom() const = 0;
|
|
||||||
virtual json::Dict getAttrs() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace geograph
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GEOGRAPH_H_
|
|
|
@ -1,138 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GEO_GRID_H_
|
|
||||||
#define UTIL_GEO_GRID_H_
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <vector>
|
|
||||||
#include "util/geo/Geo.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace geo {
|
|
||||||
|
|
||||||
class GridException : public std::runtime_error {
|
|
||||||
public:
|
|
||||||
GridException(std::string const& msg) : std::runtime_error(msg) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// initialization of a point grid with cell width w and cell height h
|
|
||||||
// that covers the area of bounding box bbox
|
|
||||||
// optional parameters specifies whether a value->cell index
|
|
||||||
// should be kept (true by default!)
|
|
||||||
Grid(double w, double h, const Box<T>& bbox, bool buildValIdx);
|
|
||||||
|
|
||||||
// the empty grid
|
|
||||||
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);
|
|
||||||
|
|
||||||
void get(const Box<T>& btbox, std::set<V>* s) const;
|
|
||||||
|
|
||||||
template <template <typename> class GG>
|
|
||||||
void get(const GG<T>& geom, double d, std::set<V>* s) const;
|
|
||||||
|
|
||||||
template <template <typename> class GG>
|
|
||||||
void get(const std::vector<GG<T>>& geom, double d, std::set<V>* s) const;
|
|
||||||
|
|
||||||
void get(size_t x, size_t y, std::set<V>* s) const;
|
|
||||||
void remove(V val);
|
|
||||||
|
|
||||||
const std::set<V>& getCell(size_t x, size_t y) const;
|
|
||||||
|
|
||||||
void getNeighbors(const V& val, double d, std::set<V>* s) const;
|
|
||||||
void getCellNeighbors(const V& val, size_t d, std::set<V>* s) const;
|
|
||||||
void getCellNeighbors(size_t x, size_t y, size_t xPerm, size_t yPerm,
|
|
||||||
std::set<V>* s) const;
|
|
||||||
|
|
||||||
std::set<std::pair<size_t, size_t> > getCells(const V& val) const;
|
|
||||||
|
|
||||||
size_t getXWidth() const;
|
|
||||||
size_t getYHeight() const;
|
|
||||||
|
|
||||||
size_t getCellXFromX(double lon) const;
|
|
||||||
size_t getCellYFromY(double lat) const;
|
|
||||||
|
|
||||||
Box<T> getBox(size_t x, size_t y) const;
|
|
||||||
Box<T> getBBox() const { return _bb; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
double _width;
|
|
||||||
double _height;
|
|
||||||
|
|
||||||
double _cellWidth;
|
|
||||||
double _cellHeight;
|
|
||||||
|
|
||||||
Box<T> _bb;
|
|
||||||
|
|
||||||
size_t _xWidth;
|
|
||||||
size_t _yHeight;
|
|
||||||
|
|
||||||
bool _hasValIdx;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/geo/Grid.tpp"
|
|
||||||
|
|
||||||
} // namespace geo
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GEO_GRID_H_
|
|
|
@ -1,246 +0,0 @@
|
||||||
// Copyright 2017, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Author: Patrick Brosi <brosip@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
Grid<V, G, T>::Grid(bool bldIdx)
|
|
||||||
: _width(0),
|
|
||||||
_height(0),
|
|
||||||
_cellWidth(0),
|
|
||||||
_cellHeight(0),
|
|
||||||
_xWidth(0),
|
|
||||||
_yHeight(0),
|
|
||||||
_hasValIdx(bldIdx),
|
|
||||||
_grid(0) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
Grid<V, G, T>::Grid() : Grid<V, G, T>(true) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
Grid<V, G, T>::Grid(double w, double h, const Box<T>& bbox)
|
|
||||||
: Grid<V, G, T>(w, h, bbox, true) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
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),
|
|
||||||
_grid(0) {
|
|
||||||
_width = bbox.getUpperRight().getX() - bbox.getLowerLeft().getX();
|
|
||||||
_height = bbox.getUpperRight().getY() - bbox.getLowerLeft().getY();
|
|
||||||
|
|
||||||
if (_width < 0 || _height < 0) {
|
|
||||||
_width = 0;
|
|
||||||
_height = 0;
|
|
||||||
_xWidth = 0;
|
|
||||||
_yHeight = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_xWidth = ceil(_width / _cellWidth);
|
|
||||||
_yHeight = ceil(_height / _cellHeight);
|
|
||||||
|
|
||||||
// resize rows
|
|
||||||
_grid = new std::set<V>*[_xWidth];
|
|
||||||
|
|
||||||
// resize columns
|
|
||||||
for (size_t x = 0; x < _xWidth; x++) {
|
|
||||||
_grid[x] = new std::set<V>[_yHeight];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
void Grid<V, G, T>::add(G<T> geom, V val) {
|
|
||||||
Box<T> box = getBoundingBox(geom);
|
|
||||||
size_t swX = getCellXFromX(box.getLowerLeft().getX());
|
|
||||||
size_t swY = getCellYFromY(box.getLowerLeft().getY());
|
|
||||||
|
|
||||||
size_t neX = getCellXFromX(box.getUpperRight().getX());
|
|
||||||
size_t neY = getCellYFromY(box.getUpperRight().getY());
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
void Grid<V, G, T>::add(size_t x, size_t y, V val) {
|
|
||||||
_grid[x][y].insert(val);
|
|
||||||
if (_hasValIdx) _index[val].insert(std::pair<size_t, size_t>(x, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
void Grid<V, G, T>::get(const Box<T>& box, std::set<V>* s) const {
|
|
||||||
size_t swX = getCellXFromX(box.getLowerLeft().getX());
|
|
||||||
size_t swY = getCellYFromY(box.getLowerLeft().getY());
|
|
||||||
|
|
||||||
size_t neX = getCellXFromX(box.getUpperRight().getX());
|
|
||||||
size_t neY = getCellYFromY(box.getUpperRight().getY());
|
|
||||||
|
|
||||||
for (size_t x = swX; x <= neX && x < _xWidth; x++)
|
|
||||||
for (size_t y = swY; y <= neY && y < _yHeight; y++) get(x, y, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
template <template <typename> class GG>
|
|
||||||
void Grid<V, G, T>::get(const GG<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));
|
|
||||||
return get(b, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
template <template <typename> class GG>
|
|
||||||
void Grid<V, G, T>::get(const std::vector<GG<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));
|
|
||||||
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 || _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; });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
const std::set<V>& Grid<V, G, T>::getCell(size_t x, size_t y) const {
|
|
||||||
return _grid[x][y];
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
void Grid<V, G, T>::remove(V val) {
|
|
||||||
if (_hasValIdx) {
|
|
||||||
auto i = _index.find(val);
|
|
||||||
if (i == _index.end()) return;
|
|
||||||
|
|
||||||
for (auto pair : i->second) {
|
|
||||||
_grid[pair.first][pair.second].erase(
|
|
||||||
_grid[pair.first][pair.second].find(val));
|
|
||||||
}
|
|
||||||
|
|
||||||
_index.erase(i);
|
|
||||||
} else {
|
|
||||||
_removed.insert(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
void Grid<V, G, T>::getNeighbors(const V& val, double d, std::set<V>* s) const {
|
|
||||||
if (!_hasValIdx) throw GridException("No value index build!");
|
|
||||||
auto it = _index.find(val);
|
|
||||||
if (it == _index.end()) return;
|
|
||||||
|
|
||||||
size_t xPerm = ceil(d / _cellWidth);
|
|
||||||
size_t yPerm = ceil(d / _cellHeight);
|
|
||||||
|
|
||||||
for (auto pair : it->second) {
|
|
||||||
getCellNeighbors(pair.first, pair.second, xPerm, yPerm, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
void Grid<V, G, T>::getCellNeighbors(const V& val, size_t d,
|
|
||||||
std::set<V>* s) const {
|
|
||||||
if (!_hasValIdx) throw GridException("No value index build!");
|
|
||||||
auto it = _index.find(val);
|
|
||||||
if (it == _index.end()) return;
|
|
||||||
|
|
||||||
for (auto pair : it->second) {
|
|
||||||
getCellNeighbors(pair.first, pair.second, d, d, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
void Grid<V, G, T>::getCellNeighbors(size_t cx, size_t cy, size_t xPerm,
|
|
||||||
size_t yPerm, std::set<V>* s) const {
|
|
||||||
size_t swX = xPerm > cx ? 0 : cx - xPerm;
|
|
||||||
size_t swY = yPerm > cy ? 0 : cy - yPerm;
|
|
||||||
|
|
||||||
size_t neX = xPerm + cx + 1 > _xWidth ? _xWidth : cx + xPerm + 1;
|
|
||||||
size_t neY = yPerm + cy + 1 > _yHeight ? _yHeight : cy + yPerm + 1;
|
|
||||||
|
|
||||||
for (size_t x = swX; x < neX; x++) {
|
|
||||||
for (size_t y = swY; y < neY; y++) {
|
|
||||||
s->insert(_grid[x][y].begin(), _grid[x][y].end());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
std::set<std::pair<size_t, size_t>> Grid<V, G, T>::getCells(
|
|
||||||
const V& val) const {
|
|
||||||
if (!_hasValIdx) throw GridException("No value index build!");
|
|
||||||
return _index.find(val)->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
Box<T> Grid<V, G, T>::getBox(size_t x, size_t y) const {
|
|
||||||
Point<T> sw(_bb.getLowerLeft().getX() + x * _cellWidth,
|
|
||||||
_bb.getLowerLeft().getY() + y * _cellHeight);
|
|
||||||
Point<T> ne(_bb.getLowerLeft().getX() + (x + 1) * _cellWidth,
|
|
||||||
_bb.getLowerLeft().getY() + (y + 1) * _cellHeight);
|
|
||||||
return Box<T>(sw, ne);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
size_t Grid<V, G, T>::getCellXFromX(double x) const {
|
|
||||||
float dist = x - _bb.getLowerLeft().getX();
|
|
||||||
if (dist < 0) dist = 0;
|
|
||||||
return floor(dist / _cellWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
size_t Grid<V, G, T>::getCellYFromY(double y) const {
|
|
||||||
float dist = y - _bb.getLowerLeft().getY();
|
|
||||||
if (dist < 0) dist = 0;
|
|
||||||
return floor(dist / _cellHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
size_t Grid<V, G, T>::getXWidth() const {
|
|
||||||
return _xWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
size_t Grid<V, G, T>::getYHeight() const {
|
|
||||||
return _yHeight;
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GEO_LINE_H_
|
|
||||||
#define UTIL_GEO_LINE_H_
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include "./Point.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace geo {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Line : public std::vector<Point<T>> {
|
|
||||||
using std::vector<Point<T>>::vector;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using LineSegment = std::pair<Point<T>, Point<T>>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using MultiLine = std::vector<Line<T>>;
|
|
||||||
|
|
||||||
} // namespace geo
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GEO_LINE_H_
|
|
|
@ -1,58 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GEO_POINT_H_
|
|
||||||
#define UTIL_GEO_POINT_H_
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace geo {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Point {
|
|
||||||
public:
|
|
||||||
Point() : _x(0), _y(0) {}
|
|
||||||
Point(T x, T y) : _x(x), _y(y) {}
|
|
||||||
T getX() const { return _x; }
|
|
||||||
T getY() const { return _y; }
|
|
||||||
|
|
||||||
void setX(T x) { _x = x; }
|
|
||||||
void setY(T y) { _y = y; }
|
|
||||||
|
|
||||||
Point<T> operator+(const Point<T>& p) const {
|
|
||||||
return Point<T>(_x + p.getX(), _y + p.getY());
|
|
||||||
}
|
|
||||||
|
|
||||||
Point<T> operator-(const Point<T>& p) const {
|
|
||||||
return Point<T>(_x - p.getX(), _y - p.getY());
|
|
||||||
}
|
|
||||||
|
|
||||||
// bool operator==(const Point<T>& p) const {
|
|
||||||
// return p.getX() == _x && p.getY() == _y;
|
|
||||||
// }
|
|
||||||
|
|
||||||
bool operator==(const Point<T>& p) const {
|
|
||||||
return fabs(p.getX() - _x) < 0.00001 && fabs(p.getY() - _y) < 0.00001;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const Point<T>& p) const {
|
|
||||||
return !(*this == p);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator<(const Point<T>& p) const {
|
|
||||||
return _x < p.getX() || (_x == p.getX() && _y < p.getY());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
T _x, _y;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using MultiPoint = std::vector<Point<T>>;
|
|
||||||
|
|
||||||
} // namespace geo
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GEO_POINT_H_
|
|
|
@ -1,156 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GEO_POLYLINE_H_
|
|
||||||
#define UTIL_GEO_POLYLINE_H_
|
|
||||||
|
|
||||||
#include <cfloat>
|
|
||||||
#include <ostream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <string>
|
|
||||||
#include <set>
|
|
||||||
#include <vector>
|
|
||||||
#include "Geo.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace geo {
|
|
||||||
|
|
||||||
static const double MAX_EQ_DISTANCE = 15;
|
|
||||||
|
|
||||||
// legacy code, will be removed in the future
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct LinePoint {
|
|
||||||
LinePoint() : lastIndex(0), totalPos(-1), p() {}
|
|
||||||
|
|
||||||
LinePoint(size_t i, double pos, const Point<T>& p)
|
|
||||||
: lastIndex(i), totalPos(pos), p(p) {}
|
|
||||||
size_t lastIndex;
|
|
||||||
double totalPos;
|
|
||||||
Point<T> p;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct LinePointCmp {
|
|
||||||
bool operator()(const LinePoint<T>& lh, const LinePoint<T>& rh) const {
|
|
||||||
return lh.totalPos < rh.totalPos;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using LinePointPair = std::pair<LinePoint<T>, LinePoint<T>>;
|
|
||||||
template <typename T>
|
|
||||||
using SharedSegment = std::pair<LinePointPair<T>, LinePointPair<T>>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct SharedSegments {
|
|
||||||
std::vector<SharedSegment<T>> segments;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class PolyLine {
|
|
||||||
public:
|
|
||||||
PolyLine();
|
|
||||||
PolyLine(const Point<T>& from, const Point<T>& to);
|
|
||||||
PolyLine(const Line<T>& l);
|
|
||||||
PolyLine(const PolyLine<T>& l);
|
|
||||||
PolyLine(PolyLine<T>&& l);
|
|
||||||
|
|
||||||
PolyLine& operator=(PolyLine<T>&& other) {
|
|
||||||
_line = std::move(other._line);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PolyLine& operator=(const PolyLine<T>& other) {
|
|
||||||
_line = other._line;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PolyLine& operator<<(const Point<T>& p);
|
|
||||||
PolyLine& operator>>(const Point<T>& p);
|
|
||||||
|
|
||||||
void reverse();
|
|
||||||
PolyLine reversed() const;
|
|
||||||
|
|
||||||
void offsetPerp(double units);
|
|
||||||
|
|
||||||
PolyLine offsetted(double units) const;
|
|
||||||
|
|
||||||
const Line<T>& getLine() const;
|
|
||||||
Line<T>& getLine();
|
|
||||||
|
|
||||||
double distTo(const PolyLine<T>& g) const;
|
|
||||||
double distTo(const Point<T>& p) const;
|
|
||||||
|
|
||||||
double getLength() const;
|
|
||||||
bool shorterThan(double d) const;
|
|
||||||
bool longerThan(double d) const;
|
|
||||||
|
|
||||||
// return point at dist
|
|
||||||
LinePoint<T> getPointAtDist(double dist) const;
|
|
||||||
|
|
||||||
// return point at [0..1]
|
|
||||||
LinePoint<T> getPointAt(double dist) const;
|
|
||||||
|
|
||||||
PolyLine<T> getSegment(double a, double b) const;
|
|
||||||
PolyLine<T> getSegmentAtDist(double dista, double distb) const;
|
|
||||||
PolyLine<T> getSegment(const LinePoint<T>& start, const LinePoint<T>& end) const;
|
|
||||||
PolyLine<T> getSegment(const Point<T>& a, const Point<T>& b) const;
|
|
||||||
|
|
||||||
std::set<LinePoint<T>, LinePointCmp<T>> getIntersections(const PolyLine<T>& g) const;
|
|
||||||
|
|
||||||
static PolyLine<T> average(const std::vector<const PolyLine<T>*>& lines);
|
|
||||||
static PolyLine<T> average(const std::vector<const PolyLine<T>*>& lines,
|
|
||||||
const std::vector<double>& weights);
|
|
||||||
|
|
||||||
void simplify(double d);
|
|
||||||
void empty();
|
|
||||||
|
|
||||||
void smoothenOutliers(double d);
|
|
||||||
|
|
||||||
std::pair<size_t, double> nearestSegment(const Point<T>& p) const;
|
|
||||||
std::pair<size_t, double> nearestSegmentAfter(const Point<T>& p,
|
|
||||||
size_t after) const;
|
|
||||||
|
|
||||||
LinePoint<T> projectOn(const Point<T>& p) const;
|
|
||||||
LinePoint<T> projectOnAfter(const Point<T>& p, size_t after) const;
|
|
||||||
|
|
||||||
void move(double vx, double vy);
|
|
||||||
|
|
||||||
std::pair<double, double> getSlopeBetween(double ad, double bd) const;
|
|
||||||
std::pair<double, double> getSlopeBetweenDists(double ad, double bd) const;
|
|
||||||
|
|
||||||
// equality operator, will hold frechet-distance equality check in
|
|
||||||
// the dmax
|
|
||||||
bool operator==(const PolyLine& rhs) const;
|
|
||||||
bool contains(const PolyLine& rhs, double dmax) const;
|
|
||||||
bool equals(const PolyLine& rhs) const;
|
|
||||||
bool equals(const PolyLine& rhs, double dmax) const;
|
|
||||||
|
|
||||||
std::string getWKT() const;
|
|
||||||
|
|
||||||
PolyLine getOrthoLineAtDist(double d, double lengt) const;
|
|
||||||
PolyLine getOrthoLineAt(double d, double lengt) const;
|
|
||||||
|
|
||||||
Point<T> interpolate(const Point<T>& a, const Point<T>& b, double p) const;
|
|
||||||
|
|
||||||
void fixTopology(double maxl);
|
|
||||||
void applyChaikinSmooth(size_t depth);
|
|
||||||
|
|
||||||
const Point<T>& front() const;
|
|
||||||
const Point<T>& back() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::set<LinePoint<T>, LinePointCmp<T>> getIntersections(const PolyLine& p,
|
|
||||||
size_t a, size_t b) const;
|
|
||||||
Line<T> _line;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/geo/PolyLine.tpp"
|
|
||||||
|
|
||||||
} // namespace geo
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GEO_POLYLINE_H_
|
|
|
@ -1,690 +0,0 @@
|
||||||
// Copyright 2016, University of Freibur
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T>::PolyLine() {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T>::PolyLine(const Point<T>& from, const Point<T>& to) {
|
|
||||||
*this << from << to;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T>::PolyLine(const PolyLine<T>& l) : _line(l._line) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T>::PolyLine(PolyLine<T>&& l) : _line(std::move(l._line)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T>::PolyLine(const Line<T>& l) : _line(l) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T>& PolyLine<T>::operator<<(const Point<T>& p) {
|
|
||||||
_line.push_back(p);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T>& PolyLine<T>::operator>>(const Point<T>& p) {
|
|
||||||
_line.insert(_line.begin(), p);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void PolyLine<T>::reverse() {
|
|
||||||
std::reverse(_line.begin(), _line.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T> PolyLine<T>::reversed() const {
|
|
||||||
PolyLine ret = *this;
|
|
||||||
ret.reverse();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
const Line<T>& PolyLine<T>::getLine() const {
|
|
||||||
return _line;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
Line<T>& PolyLine<T>::getLine() {
|
|
||||||
return _line;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T> PolyLine<T>::offsetted(double units) const {
|
|
||||||
PolyLine p = *this;
|
|
||||||
p.offsetPerp(units);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void PolyLine<T>::offsetPerp(double units) {
|
|
||||||
/*
|
|
||||||
* calculate perpendicular offset of a polyline
|
|
||||||
*
|
|
||||||
* there doesn't seem to be any library which reliably does that,
|
|
||||||
* so we do it ourself here until we find one...
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (fabs(units) < 0.001) return;
|
|
||||||
|
|
||||||
if (_line.size() < 2) return;
|
|
||||||
|
|
||||||
Line<T> ret;
|
|
||||||
ret.reserve(_line.size());
|
|
||||||
Point<T> lastP = _line.front();
|
|
||||||
|
|
||||||
Point<T>*lastIns = 0, *befLastIns = 0;
|
|
||||||
|
|
||||||
for (size_t i = 1; i < _line.size(); i++) {
|
|
||||||
Point<T> curP = _line[i];
|
|
||||||
|
|
||||||
double n1 = lastP.getY() - curP.getY();
|
|
||||||
double n2 = curP.getX() - lastP.getX();
|
|
||||||
double n = sqrt(n1 * n1 + n2 * n2);
|
|
||||||
|
|
||||||
// if n == 0, the segment is effectively a point
|
|
||||||
// we would get into all sorts of troubles if we tried to offset a point...
|
|
||||||
if (!(n > 0)) continue;
|
|
||||||
|
|
||||||
n1 = n1 / n;
|
|
||||||
n2 = n2 / n;
|
|
||||||
|
|
||||||
lastP.setX(lastP.getX() + (n1 * units));
|
|
||||||
lastP.setY(lastP.getY() + (n2 * units));
|
|
||||||
|
|
||||||
curP.setX(curP.getX() + (n1 * units));
|
|
||||||
curP.setY(curP.getY() + (n2 * units));
|
|
||||||
|
|
||||||
if (lastIns && befLastIns &&
|
|
||||||
lineIntersects(*lastIns, *befLastIns, lastP, curP)) {
|
|
||||||
auto iSect = intersection(*lastIns, *befLastIns, lastP, curP);
|
|
||||||
|
|
||||||
double d = fmax(dist(lastP, iSect), dist(*lastIns, iSect));
|
|
||||||
double d2 = distToSegment(iSect, *befLastIns, lastP);
|
|
||||||
|
|
||||||
if (d > fabs(units) * 2.0 && d2 < d - (fabs(units))) {
|
|
||||||
PolyLine pl(iSect, *befLastIns);
|
|
||||||
PolyLine pll(iSect, curP);
|
|
||||||
pl = pl.getSegment(0, (d - (fabs(units))) / pl.getLength());
|
|
||||||
pll = pll.getSegment(0, (d - (fabs(units))) / pll.getLength());
|
|
||||||
|
|
||||||
// careful, after push_back() below, lastIns might point to another
|
|
||||||
// point because of reallocation
|
|
||||||
*lastIns = pl.back();
|
|
||||||
|
|
||||||
ret.push_back(pll.back());
|
|
||||||
ret.push_back(curP);
|
|
||||||
} else {
|
|
||||||
// careful, after push_back() below, lastIns might point to another
|
|
||||||
// point because of reallocation
|
|
||||||
*lastIns = iSect;
|
|
||||||
|
|
||||||
ret.push_back(curP);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret.push_back(lastP);
|
|
||||||
ret.push_back(curP);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastIns = &ret[ret.size() - 1];
|
|
||||||
befLastIns = &ret[ret.size() - 2];
|
|
||||||
|
|
||||||
lastP = _line[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
_line = ret;
|
|
||||||
|
|
||||||
// heuristics
|
|
||||||
simplify(1);
|
|
||||||
fixTopology(fabs(2 * 3.14 * units));
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T> PolyLine<T>::getSegment(double a, double b) const {
|
|
||||||
if (a > b) {
|
|
||||||
double c = a;
|
|
||||||
a = b;
|
|
||||||
b = c;
|
|
||||||
}
|
|
||||||
LinePoint<T> start = getPointAt(a);
|
|
||||||
LinePoint<T> end = getPointAt(b);
|
|
||||||
|
|
||||||
return getSegment(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T> PolyLine<T>::getSegmentAtDist(double a, double b) const {
|
|
||||||
if (a > b) {
|
|
||||||
double c = a;
|
|
||||||
a = b;
|
|
||||||
b = c;
|
|
||||||
}
|
|
||||||
LinePoint<T> start = getPointAtDist(a);
|
|
||||||
LinePoint<T> end = getPointAtDist(b);
|
|
||||||
|
|
||||||
return getSegment(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T> PolyLine<T>::getSegment(const Point<T>& a,
|
|
||||||
const Point<T>& b) const {
|
|
||||||
LinePoint<T> start = projectOn(a);
|
|
||||||
LinePoint<T> end = projectOnAfter(b, start.lastIndex);
|
|
||||||
|
|
||||||
return getSegment(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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) {
|
|
||||||
ret._line.insert(ret._line.end(), _line.begin() + start.lastIndex + 1,
|
|
||||||
_line.begin() + end.lastIndex + 1);
|
|
||||||
}
|
|
||||||
ret << end.p;
|
|
||||||
|
|
||||||
// find a more performant way to clear the result of above
|
|
||||||
ret.simplify(0);
|
|
||||||
|
|
||||||
assert(ret.getLine().size());
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
LinePoint<T> PolyLine<T>::getPointAtDist(double atDist) const {
|
|
||||||
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]);
|
|
||||||
|
|
||||||
const Point<T>* last = &_line[0];
|
|
||||||
|
|
||||||
for (size_t i = 1; i < _line.size(); i++) {
|
|
||||||
const Point<T>& cur = _line[i];
|
|
||||||
double d = geo::dist(*last, cur);
|
|
||||||
dist += d;
|
|
||||||
|
|
||||||
if (dist > atDist) {
|
|
||||||
double p = (d - (dist - atDist));
|
|
||||||
return LinePoint<T>(i - 1, atDist / l, interpolate(*last, cur, p));
|
|
||||||
}
|
|
||||||
|
|
||||||
last = &_line[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return LinePoint<T>(_line.size() - 1, 1, _line.back());
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
LinePoint<T> PolyLine<T>::getPointAt(double at) const {
|
|
||||||
at *= getLength();
|
|
||||||
return getPointAtDist(at);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
Point<T> PolyLine<T>::interpolate(const Point<T>& a, const Point<T>& b,
|
|
||||||
double p) const {
|
|
||||||
double n1 = b.getX() - a.getX();
|
|
||||||
double n2 = b.getY() - a.getY();
|
|
||||||
double n = sqrt(n1 * n1 + n2 * n2);
|
|
||||||
n1 = n1 / n;
|
|
||||||
n2 = n2 / n;
|
|
||||||
return Point<T>(a.getX() + (n1 * p), a.getY() + (n2 * p));
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
double PolyLine<T>::distTo(const PolyLine<T>& g) const {
|
|
||||||
return dist(_line, g.getLine());
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
double PolyLine<T>::distTo(const Point<T>& p) const {
|
|
||||||
return dist(_line, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
double PolyLine<T>::getLength() const {
|
|
||||||
return len(_line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
bool PolyLine<T>::shorterThan(double d) const {
|
|
||||||
return util::geo::shorterThan(_line, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
bool PolyLine<T>::longerThan(double d) const {
|
|
||||||
return util::geo::longerThan(_line, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T> PolyLine<T>::average(const std::vector<const PolyLine<T>*>& lines,
|
|
||||||
const std::vector<double>& weights) {
|
|
||||||
bool weighted = lines.size() == weights.size();
|
|
||||||
|
|
||||||
if (!weighted && lines.size() == 2 && lines[0]->getLine().size() == 2 &&
|
|
||||||
lines[1]->getLine().size() == 2) {
|
|
||||||
// simple case
|
|
||||||
util::geo::Line<T> avg(2);
|
|
||||||
const auto& a = lines[0]->getLine();
|
|
||||||
const auto& b = lines[1]->getLine();
|
|
||||||
|
|
||||||
avg[0] = {(a[0].getX() + b[0].getX()) / 2.0,
|
|
||||||
(a[0].getY() + b[0].getY()) / 2.0};
|
|
||||||
avg[1] = {(a[1].getX() + b[1].getX()) / 2.0,
|
|
||||||
(a[1].getY() + b[1].getY()) / 2.0};
|
|
||||||
|
|
||||||
return avg;
|
|
||||||
}
|
|
||||||
|
|
||||||
double stepSize;
|
|
||||||
|
|
||||||
double longestLength = DBL_MIN; // avoid recalc of length on each comparision
|
|
||||||
for (const PolyLine* p : lines) {
|
|
||||||
double l = p->getLength();
|
|
||||||
if (l > longestLength) {
|
|
||||||
longestLength = l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PolyLine 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) {
|
|
||||||
const PolyLine* pl = lines[i];
|
|
||||||
Point<T> p = pl->getPointAt(a).p;
|
|
||||||
if (weighted) {
|
|
||||||
x += p.getX() * weights[i];
|
|
||||||
y += p.getY() * weights[i];
|
|
||||||
} else {
|
|
||||||
x += p.getX();
|
|
||||||
y += p.getY();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret << Point<T>(x / total, y / total);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.simplify(0.0001);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T> PolyLine<T>::average(const std::vector<const PolyLine<T>*>& lines) {
|
|
||||||
return average(lines, std::vector<double>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
std::pair<size_t, double> PolyLine<T>::nearestSegmentAfter(const Point<T>& p,
|
|
||||||
size_t a) const {
|
|
||||||
// returns the index of the starting point of the nearest segment of p
|
|
||||||
assert(a < _line.size());
|
|
||||||
|
|
||||||
double totalLength = getLength();
|
|
||||||
size_t smallest = a;
|
|
||||||
double totalDist = 0;
|
|
||||||
double dist = DBL_MAX;
|
|
||||||
double smallestDist = 0;
|
|
||||||
|
|
||||||
for (size_t i = smallest + 1; i < _line.size(); i++) {
|
|
||||||
Point<T> startP(_line[i - 1]);
|
|
||||||
Point<T> endP(_line[i]);
|
|
||||||
|
|
||||||
if (i > 1) {
|
|
||||||
totalDist += geo::dist(_line[i - 2], _line[i - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
double curDist = distToSegment(startP, endP, p);
|
|
||||||
|
|
||||||
if (curDist < dist) {
|
|
||||||
dist = curDist;
|
|
||||||
smallest = i - 1;
|
|
||||||
smallestDist = totalDist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalLength > 0) {
|
|
||||||
smallestDist /= totalLength;
|
|
||||||
} else {
|
|
||||||
smallestDist = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::pair<size_t, double>(smallest, smallestDist);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
std::pair<size_t, double> PolyLine<T>::nearestSegment(const Point<T>& p) const {
|
|
||||||
return nearestSegmentAfter(p, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
LinePoint<T> PolyLine<T>::projectOn(const Point<T>& p) const {
|
|
||||||
return projectOnAfter(p, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
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);
|
|
||||||
|
|
||||||
size_t next = bc.first + 1;
|
|
||||||
if (next >= _line.size()) next = bc.first;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void PolyLine<T>::simplify(double d) {
|
|
||||||
_line = geo::simplify(_line, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void PolyLine<T>::smoothenOutliers(double d) {
|
|
||||||
if (_line.size() < 3) return;
|
|
||||||
for (size_t i = 1; i < _line.size() - 3; ++i) {
|
|
||||||
double ang = innerProd(_line[i], _line[i - 1], _line[i + 1]);
|
|
||||||
|
|
||||||
if (dist(_line[i], _line[i + 1]) < d || dist(_line[i], _line[i - 1]) < d) {
|
|
||||||
if (ang < 35) {
|
|
||||||
_line.erase(_line.begin() + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
bool PolyLine<T>::equals(const PolyLine<T>& rhs) const {
|
|
||||||
// TODO: why 100? make global static or configurable or determine in some
|
|
||||||
// way!
|
|
||||||
return equals(rhs, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
bool PolyLine<T>::operator==(const PolyLine<T>& rhs) const {
|
|
||||||
// TODO: why 100? make global static or configurable or determine in some
|
|
||||||
// way!
|
|
||||||
return equals(rhs, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
bool PolyLine<T>::equals(const PolyLine<T>& rhs, double dmax) const {
|
|
||||||
// check if two lines are equal, THE DIRECTION DOES NOT MATTER HERE!!!!!
|
|
||||||
|
|
||||||
if (_line.size() == 2 && _line.size() == rhs.getLine().size()) {
|
|
||||||
// trivial case, straight line, implement directly
|
|
||||||
return (dist(_line[0], rhs.getLine()[0]) < dmax &&
|
|
||||||
dist(_line.back(), rhs.back()) < dmax) ||
|
|
||||||
(dist(_line[0], rhs.back()) < dmax &&
|
|
||||||
dist(_line.back(), rhs.getLine()[0]) < dmax);
|
|
||||||
} else {
|
|
||||||
return contains(rhs, dmax) && rhs.contains(*this, dmax);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
bool PolyLine<T>::contains(const PolyLine<T>& rhs, double dmax) const {
|
|
||||||
// check if two lines are equal. Line direction does not matter here.
|
|
||||||
|
|
||||||
for (size_t i = 0; i < rhs.getLine().size(); ++i) {
|
|
||||||
double d = dist(rhs.getLine()[i], getLine());
|
|
||||||
if (d > dmax) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void PolyLine<T>::move(double vx, double vy) {
|
|
||||||
for (size_t i = 0; i < _line.size(); i++) {
|
|
||||||
_line[i].setX(_line[i].getX() + vx);
|
|
||||||
_line[i].setY(_line[i].getY() + vy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
std::set<LinePoint<T>, LinePointCmp<T>> PolyLine<T>::getIntersections(
|
|
||||||
const PolyLine<T>& g) const {
|
|
||||||
std::set<LinePoint<T>, LinePointCmp<T>> ret;
|
|
||||||
|
|
||||||
for (size_t i = 1; i < g.getLine().size(); ++i) {
|
|
||||||
// for each line segment, check if it intersects with a line segment in g
|
|
||||||
const std::set<LinePoint<T>, LinePointCmp<T>> a =
|
|
||||||
getIntersections(g, i - 1, i);
|
|
||||||
ret.insert(a.begin(), a.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
std::set<LinePoint<T>, LinePointCmp<T>> PolyLine<T>::getIntersections(
|
|
||||||
const PolyLine<T>& p, size_t a, size_t b) const {
|
|
||||||
std::set<LinePoint<T>, LinePointCmp<T>> ret;
|
|
||||||
|
|
||||||
if (dist(p.getLine()[a], p.getLine()[b]) == 0) {
|
|
||||||
// we cannot intersect with a point
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 1; i < _line.size(); ++i) {
|
|
||||||
if (intersects(_line[i - 1], _line[i], p.getLine()[a], p.getLine()[b])) {
|
|
||||||
Point<T> isect =
|
|
||||||
intersection(_line[i - 1], _line[i], p.getLine()[a], p.getLine()[b]);
|
|
||||||
ret.insert(p.projectOn(isect));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T> PolyLine<T>::getOrthoLineAt(double d, double length) const {
|
|
||||||
return getOrthoLineAtDist(getLength() * d, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
PolyLine<T> PolyLine<T>::getOrthoLineAtDist(double d, double length) const {
|
|
||||||
Point<T> avgP = getPointAtDist(d).p;
|
|
||||||
|
|
||||||
double angle = angBetween(getPointAtDist(d - 5).p, getPointAtDist(d + 5).p);
|
|
||||||
|
|
||||||
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 PolyLine(Point<T>(angleX1, angleY1), Point<T>(angleX2, angleY2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void PolyLine<T>::empty() {
|
|
||||||
_line.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
std::pair<double, double> PolyLine<T>::getSlopeBetween(double ad,
|
|
||||||
double bd) const {
|
|
||||||
LinePoint<T> a = getPointAt(ad);
|
|
||||||
LinePoint<T> b = getPointAt(bd);
|
|
||||||
|
|
||||||
double d = dist(a.p, b.p);
|
|
||||||
|
|
||||||
double dx = (b.p.getX() - a.p.getX()) / d;
|
|
||||||
double dy = (b.p.getY() - a.p.getY()) / d;
|
|
||||||
|
|
||||||
return std::pair<double, double>(dx, dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
std::pair<double, double> PolyLine<T>::getSlopeBetweenDists(double ad,
|
|
||||||
double bd) const {
|
|
||||||
double l = getLength();
|
|
||||||
return getSlopeBetween(ad / l, bd / l);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
std::string PolyLine<T>::getWKT() const {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << std::setprecision(12) << geo::getWKT(_line);
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void PolyLine<T>::fixTopology(double maxl) {
|
|
||||||
double distA = 0;
|
|
||||||
|
|
||||||
for (size_t i = 1; i < _line.size() - 1; i++) {
|
|
||||||
double distB =
|
|
||||||
distA + dist(_line[i - 1], _line[i]) + dist(_line[i], _line[i + 1]);
|
|
||||||
for (size_t j = i + 2; j < _line.size(); j++) {
|
|
||||||
if (intersects(_line[i - 1], _line[i], _line[j - 1], _line[j])) {
|
|
||||||
Point<T> p =
|
|
||||||
intersection(_line[i - 1], _line[i], _line[j - 1], _line[j]);
|
|
||||||
|
|
||||||
double posA = dist(_line[i - 1], p) + distA;
|
|
||||||
double posB = dist(_line[j - 1], p) + distB;
|
|
||||||
|
|
||||||
if (fabs(posA - posB) < maxl) {
|
|
||||||
_line[i] = p;
|
|
||||||
_line.erase(_line.begin() + i + 1, _line.begin() + j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
distB += dist(_line[j - 1], _line[j]);
|
|
||||||
}
|
|
||||||
distA += dist(_line[i - 1], _line[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void PolyLine<T>::applyChaikinSmooth(size_t depth) {
|
|
||||||
for (size_t i = 0; i < depth; i++) {
|
|
||||||
Line<T> smooth;
|
|
||||||
|
|
||||||
smooth.push_back(_line.front());
|
|
||||||
|
|
||||||
for (size_t i = 1; i < _line.size(); i++) {
|
|
||||||
Point<T> pA = _line[i - 1];
|
|
||||||
Point<T> pB = _line[i];
|
|
||||||
|
|
||||||
smooth.push_back(Point<T>(0.75 * pA.getX() + 0.25 * pB.getX(),
|
|
||||||
0.75 * pA.getY() + 0.25 * pB.getY()));
|
|
||||||
smooth.push_back(Point<T>(0.25 * pA.getX() + 0.75 * pB.getX(),
|
|
||||||
0.25 * pA.getY() + 0.75 * pB.getY()));
|
|
||||||
}
|
|
||||||
|
|
||||||
smooth.push_back(_line.back());
|
|
||||||
_line = smooth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
const Point<T>& PolyLine<T>::front() const {
|
|
||||||
return _line.front();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
const Point<T>& PolyLine<T>::back() const {
|
|
||||||
return _line.back();
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GEO_POLYGON_H_
|
|
||||||
#define UTIL_GEO_POLYGON_H_
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include "./Box.h"
|
|
||||||
#include "./Line.h"
|
|
||||||
#include "./Point.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace geo {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Polygon {
|
|
||||||
public:
|
|
||||||
Polygon() {}
|
|
||||||
|
|
||||||
Polygon(const Line<T>& l) : _outer(l) {}
|
|
||||||
Polygon(const Box<T>& b)
|
|
||||||
: _outer({b.getLowerLeft(),
|
|
||||||
Point<T>(b.getLowerLeft().getX(), b.getUpperRight().getY()),
|
|
||||||
b.getUpperRight(),
|
|
||||||
Point<T>(b.getUpperRight().getX(), b.getLowerLeft().getY()),
|
|
||||||
b.getLowerLeft()}) {}
|
|
||||||
|
|
||||||
const Line<T>& getOuter() const { return _outer; }
|
|
||||||
Line<T>& getOuter() { return _outer; }
|
|
||||||
|
|
||||||
const std::vector<Line<T>>& getInners() const { return _inners; }
|
|
||||||
std::vector<Line<T>>& getInners() { return _inners; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Line<T> _outer;
|
|
||||||
std::vector<Line<T>> _inners;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using MultiPolygon = std::vector<Polygon<T>>;
|
|
||||||
|
|
||||||
} // namespace geo
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GEO_LINE_H_
|
|
|
@ -1,94 +0,0 @@
|
||||||
// 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_
|
|
|
@ -1,117 +0,0 @@
|
||||||
// _____________________________________________________________________________
|
|
||||||
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)}});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
// Copyright 2023, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GEO_RTREE_H_
|
|
||||||
#define UTIL_GEO_RTREE_H_
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <set>
|
|
||||||
#include <vector>
|
|
||||||
#include "util/3rdparty/RTree.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace geo {
|
|
||||||
|
|
||||||
class RTreeException : public std::runtime_error {
|
|
||||||
public:
|
|
||||||
RTreeException(std::string const& msg) : std::runtime_error(msg) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
class RTree {
|
|
||||||
public:
|
|
||||||
// RTree(const RTree<V, G, T>&) = delete;
|
|
||||||
// RTree(RTree<V, G, T>&& o) = delete;
|
|
||||||
|
|
||||||
// the empty RTree
|
|
||||||
RTree() : _rtree(new DA::RTree<V, T, 2, T>()) {};
|
|
||||||
|
|
||||||
~RTree() {if (_rtree) delete _rtree;};
|
|
||||||
|
|
||||||
RTree(const RTree<V, G, T>&) = delete;
|
|
||||||
RTree(RTree<V, G, T>&& o) {
|
|
||||||
_valIdx = std::move(o._valIdx);
|
|
||||||
_rtree = o._rtree;
|
|
||||||
o._rtree = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
RTree<V, G, T>& operator=(RTree<V, G, T>&& o) {
|
|
||||||
_valIdx = std::move(o._valIdx);
|
|
||||||
_rtree = o._rtree;
|
|
||||||
o._rtree = 0;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
};
|
|
||||||
|
|
||||||
// add object t to this RTree
|
|
||||||
void add(G<T> geom, V val);
|
|
||||||
|
|
||||||
void get(const Box<T>& btbox, std::set<V>* s) const;
|
|
||||||
|
|
||||||
template <template <typename> class GG>
|
|
||||||
void get(const GG<T>& geom, double d, std::set<V>* s) const;
|
|
||||||
|
|
||||||
template <template <typename> class GG>
|
|
||||||
void get(const std::vector<GG<T>>& geom, double d, std::set<V>* s) const;
|
|
||||||
|
|
||||||
void get(const Box<T>& btbox, std::vector<V>* s) const;
|
|
||||||
|
|
||||||
template <template <typename> class GG>
|
|
||||||
void get(const GG<T>& geom, double d, std::vector<V>* s) const;
|
|
||||||
|
|
||||||
template <template <typename> class GG>
|
|
||||||
void get(const std::vector<GG<T>>& geom, double d, std::vector<V>* s) const;
|
|
||||||
|
|
||||||
void remove(V val);
|
|
||||||
|
|
||||||
void getNeighbors(const V& val, double d, std::set<V>* s) const;
|
|
||||||
private:
|
|
||||||
DA::RTree<V, T, 2, T>* _rtree = 0;
|
|
||||||
|
|
||||||
std::unordered_map<V, util::geo::Box<T>> _valIdx;
|
|
||||||
|
|
||||||
static bool searchCb(V val, void* arg);
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/geo/RTree.tpp"
|
|
||||||
|
|
||||||
} // namespace geo
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GEO_RTREE_H_
|
|
|
@ -1,134 +0,0 @@
|
||||||
// Copyright 2023, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Author: Patrick Brosi <brosip@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
void RTree<V, G, T>::add(G<T> geom, V val) {
|
|
||||||
Box<T> box = getBoundingBox(geom);
|
|
||||||
|
|
||||||
T minCoords[2];
|
|
||||||
T maxCoords[2];
|
|
||||||
|
|
||||||
minCoords[0] = box.getLowerLeft().getX();
|
|
||||||
minCoords[1] = box.getLowerLeft().getY();
|
|
||||||
|
|
||||||
maxCoords[0] = box.getUpperRight().getX();
|
|
||||||
maxCoords[1] = box.getUpperRight().getY();
|
|
||||||
|
|
||||||
if (_valIdx.count(val)) assert(false);
|
|
||||||
|
|
||||||
_valIdx[val] = box;
|
|
||||||
|
|
||||||
_rtree->Insert(minCoords, maxCoords, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
bool RTree<V, G, T>::searchCb(V val, void* s) {
|
|
||||||
static_cast<std::set<V>*>(s)->insert(val);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
void RTree<V, G, T>::get(const Box<T>& box, std::set<V>* s) const {
|
|
||||||
T minCoords[2];
|
|
||||||
T maxCoords[2];
|
|
||||||
|
|
||||||
minCoords[0] = box.getLowerLeft().getX();
|
|
||||||
minCoords[1] = box.getLowerLeft().getY();
|
|
||||||
|
|
||||||
maxCoords[0] = box.getUpperRight().getX();
|
|
||||||
maxCoords[1] = box.getUpperRight().getY();
|
|
||||||
|
|
||||||
std::function<bool(const V&)> f = [s](const V& val) {
|
|
||||||
s->insert(val);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
_rtree->Search(minCoords, maxCoords, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
void RTree<V, G, T>::get(const Box<T>& box, std::vector<V>* s) const {
|
|
||||||
T minCoords[2];
|
|
||||||
T maxCoords[2];
|
|
||||||
|
|
||||||
minCoords[0] = box.getLowerLeft().getX();
|
|
||||||
minCoords[1] = box.getLowerLeft().getY();
|
|
||||||
|
|
||||||
maxCoords[0] = box.getUpperRight().getX();
|
|
||||||
maxCoords[1] = box.getUpperRight().getY();
|
|
||||||
|
|
||||||
std::function<bool(const V&)> f = [s](const V& val) {
|
|
||||||
s->push_back(val);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
_rtree->Search(minCoords, maxCoords, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
void RTree<V, G, T>::remove(V val) {
|
|
||||||
auto bit = _valIdx.find(val);
|
|
||||||
if (bit == _valIdx.end()) return;
|
|
||||||
|
|
||||||
Box<T> box = bit->second;
|
|
||||||
|
|
||||||
T minCoords[2];
|
|
||||||
T maxCoords[2];
|
|
||||||
|
|
||||||
minCoords[0] = box.getLowerLeft().getX();
|
|
||||||
minCoords[1] = box.getLowerLeft().getY();
|
|
||||||
|
|
||||||
maxCoords[0] = box.getUpperRight().getX();
|
|
||||||
maxCoords[1] = box.getUpperRight().getY();
|
|
||||||
|
|
||||||
_valIdx.erase(bit);
|
|
||||||
|
|
||||||
bool notFound = _rtree->Remove(minCoords, maxCoords, val);
|
|
||||||
assert(!notFound);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
template <template <typename> class GG>
|
|
||||||
void RTree<V, G, T>::get(const GG<T>& geom, double d, std::set<V>* s) const {
|
|
||||||
return get(util::geo::pad(getBoundingBox(geom), d), s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
template <template <typename> class GG>
|
|
||||||
void RTree<V, G, T>::get(const std::vector<GG<T>>& geom, double d,
|
|
||||||
std::set<V>* s) const {
|
|
||||||
return get(util::geo::pad(getBoundingBox(geom), d), s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
template <template <typename> class GG>
|
|
||||||
void RTree<V, G, T>::get(const GG<T>& geom, double d, std::vector<V>* s) const {
|
|
||||||
return get(util::geo::pad(getBoundingBox(geom), d), s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
template <template <typename> class GG>
|
|
||||||
void RTree<V, G, T>::get(const std::vector<GG<T>>& geom, double d,
|
|
||||||
std::vector<V>* s) const {
|
|
||||||
return get(util::geo::pad(getBoundingBox(geom), d), s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename V, template <typename> class G, typename T>
|
|
||||||
void RTree<V, G, T>::getNeighbors(const V& val, double d,
|
|
||||||
std::set<V>* s) const {
|
|
||||||
auto bit = _valIdx.find(val);
|
|
||||||
if (bit == _valIdx.end()) return;
|
|
||||||
|
|
||||||
Box<T> box = util::geo::pad(bit->second, d);
|
|
||||||
|
|
||||||
return get(box, s);
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GEO_OUTPUT_GEOGRAPHJSONOUTPUT_H_
|
|
||||||
#define UTIL_GEO_OUTPUT_GEOGRAPHJSONOUTPUT_H_
|
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
#include <string>
|
|
||||||
#include "util/String.h"
|
|
||||||
#include "util/geo/output/GeoJsonOutput.h"
|
|
||||||
#include "util/graph/Graph.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace geo {
|
|
||||||
namespace output {
|
|
||||||
|
|
||||||
class GeoGraphJsonOutput {
|
|
||||||
public:
|
|
||||||
inline GeoGraphJsonOutput(){};
|
|
||||||
|
|
||||||
// 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, 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);
|
|
||||||
|
|
||||||
// print a graph to the provided GeoJsonOutput, but treat coordinates as Web Mercator
|
|
||||||
// coordinates and reproject to WGS84
|
|
||||||
template <typename N, typename E>
|
|
||||||
void printLatLng(const util::graph::Graph<N, E>& outG, GeoJsonOutput* out);
|
|
||||||
|
|
||||||
// print a graph to the provided GeoJsonOutput
|
|
||||||
template <typename N, typename E>
|
|
||||||
void print(const util::graph::Graph<N, E>& outG, GeoJsonOutput* out);
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <typename T>
|
|
||||||
Line<T> createLine(const util::geo::Point<T>& a,
|
|
||||||
const util::geo::Point<T>& b);
|
|
||||||
|
|
||||||
// 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, json::Val attrs);
|
|
||||||
|
|
||||||
// print a graph to the provided path
|
|
||||||
template <typename N, typename E>
|
|
||||||
void printImpl(const util::graph::Graph<N, E>& outG,
|
|
||||||
bool proj, GeoJsonOutput* out);
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/geo/output/GeoGraphJsonOutput.tpp"
|
|
||||||
} // namespace output
|
|
||||||
} // namespace geo
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GEO_OUTPUT_GEOGRAPHJSONOUTPUT_H_
|
|
|
@ -1,123 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
Line<T> GeoGraphJsonOutput::createLine(const util::geo::Point<T>& a,
|
|
||||||
const util::geo::Point<T>& b) {
|
|
||||||
Line<T> ret;
|
|
||||||
ret.push_back(a);
|
|
||||||
ret.push_back(b);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
void GeoGraphJsonOutput::print(const util::graph::Graph<N, E>& outG,
|
|
||||||
std::ostream& str) {
|
|
||||||
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, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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::printLatLng(const util::graph::Graph<N, E>& outG,
|
|
||||||
GeoJsonOutput* out) {
|
|
||||||
printImpl(outG, true, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
void GeoGraphJsonOutput::print(const util::graph::Graph<N, E>& outG,
|
|
||||||
GeoJsonOutput* out) {
|
|
||||||
printImpl(outG, false, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
void GeoGraphJsonOutput::printImpl(const util::graph::Graph<N, E>& outG,
|
|
||||||
std::ostream& str, bool proj,
|
|
||||||
json::Val attrs) {
|
|
||||||
GeoJsonOutput out(str, attrs);
|
|
||||||
printImpl(outG, proj, &out);
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
void GeoGraphJsonOutput::printImpl(const util::graph::Graph<N, E>& outG,
|
|
||||||
bool proj, GeoJsonOutput* out) {
|
|
||||||
// first pass, nodes
|
|
||||||
for (util::graph::Node<N, E>* n : outG.getNds()) {
|
|
||||||
if (!n->pl().getGeom()) continue;
|
|
||||||
|
|
||||||
json::Dict props{{"id", util::toString(n)},
|
|
||||||
{"deg", util::toString(n->getDeg())},
|
|
||||||
{"deg_out", util::toString(n->getOutDeg())},
|
|
||||||
{"deg_in", util::toString(n->getInDeg())}};
|
|
||||||
|
|
||||||
auto addProps = n->pl().getAttrs();
|
|
||||||
props.insert(addProps.begin(), addProps.end());
|
|
||||||
|
|
||||||
if (proj) {
|
|
||||||
out->printLatLng(*n->pl().getGeom(), props);
|
|
||||||
} else {
|
|
||||||
out->print(*n->pl().getGeom(), props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// second pass, edges
|
|
||||||
for (graph::Node<N, E>* n : outG.getNds()) {
|
|
||||||
for (graph::Edge<N, E>* e : n->getAdjListOut()) {
|
|
||||||
// to avoid double output for undirected graphs
|
|
||||||
if (e->getFrom() != n) continue;
|
|
||||||
json::Dict props{{"from", util::toString(e->getFrom())},
|
|
||||||
{"to", util::toString(e->getTo())},
|
|
||||||
{"id", util::toString(e)}};
|
|
||||||
|
|
||||||
auto addProps = e->pl().getAttrs();
|
|
||||||
props.insert(addProps.begin(), addProps.end());
|
|
||||||
|
|
||||||
if (!e->pl().getGeom() || !e->pl().getGeom()->size()) {
|
|
||||||
if (e->getFrom()->pl().getGeom()) {
|
|
||||||
auto a = *e->getFrom()->pl().getGeom();
|
|
||||||
if (e->getTo()->pl().getGeom()) {
|
|
||||||
auto b = *e->getTo()->pl().getGeom();
|
|
||||||
if (proj) {
|
|
||||||
out->printLatLng(createLine(a, b), props);
|
|
||||||
} else {
|
|
||||||
out->print(createLine(a, b), props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (proj) {
|
|
||||||
out->printLatLng(*e->pl().getGeom(), props);
|
|
||||||
} else {
|
|
||||||
out->print(*e->pl().getGeom(), props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
//
|
|
||||||
#include "util/geo/output/GeoJsonOutput.h"
|
|
||||||
|
|
||||||
using namespace util;
|
|
||||||
using namespace geo;
|
|
||||||
using namespace output;
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
GeoJsonOutput::GeoJsonOutput(std::ostream& str) : GeoJsonOutput(str, false) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
GeoJsonOutput::GeoJsonOutput(std::ostream& str, bool raw) : _wr(&str, 10, true) {
|
|
||||||
if (!raw) {
|
|
||||||
_wr.obj();
|
|
||||||
_wr.keyVal("type", "FeatureCollection");
|
|
||||||
_wr.key("features");
|
|
||||||
_wr.arr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
GeoJsonOutput::GeoJsonOutput(std::ostream& str, json::Val attrs)
|
|
||||||
: _wr(&str, 10, true) {
|
|
||||||
_wr.obj();
|
|
||||||
_wr.keyVal("type", "FeatureCollection");
|
|
||||||
_wr.key("properties");
|
|
||||||
_wr.val(attrs);
|
|
||||||
_wr.key("features");
|
|
||||||
_wr.arr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
GeoJsonOutput::~GeoJsonOutput() { flush(); }
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void GeoJsonOutput::flush() { _wr.closeAll(); }
|
|
|
@ -1,73 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GEO_OUTPUT_GEOJSONOUTPUT_H_
|
|
||||||
#define UTIL_GEO_OUTPUT_GEOJSONOUTPUT_H_
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <ostream>
|
|
||||||
#include <string>
|
|
||||||
#include "util/String.h"
|
|
||||||
#include "util/geo/Geo.h"
|
|
||||||
#include "util/json/Writer.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace geo {
|
|
||||||
namespace output {
|
|
||||||
|
|
||||||
class GeoJsonOutput {
|
|
||||||
public:
|
|
||||||
GeoJsonOutput(std::ostream& str);
|
|
||||||
GeoJsonOutput(std::ostream& str, bool raw);
|
|
||||||
GeoJsonOutput(std::ostream& str, json::Val attrs);
|
|
||||||
~GeoJsonOutput();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void print(const Point<T>& p, json::Val attrs);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void print(const MultiPoint<T>& ps, json::Val attrs);
|
|
||||||
|
|
||||||
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 MultiPoint<T>& ps, 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:
|
|
||||||
json::Writer _wr;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/geo/output/GeoJsonOutput.tpp"
|
|
||||||
} // namespace output
|
|
||||||
} // namespace geo
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GEO_OUTPUT_GEOJSONOUTPUT_H_
|
|
|
@ -1,174 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void GeoJsonOutput::print(const Point<T>& p, json::Val attrs) {
|
|
||||||
_wr.obj();
|
|
||||||
_wr.keyVal("type", "Feature");
|
|
||||||
|
|
||||||
_wr.key("geometry");
|
|
||||||
_wr.obj();
|
|
||||||
_wr.keyVal("type", "Point");
|
|
||||||
_wr.key("coordinates");
|
|
||||||
_wr.arr();
|
|
||||||
_wr.val(p.getX());
|
|
||||||
_wr.val(p.getY());
|
|
||||||
_wr.close();
|
|
||||||
_wr.close();
|
|
||||||
_wr.key("properties");
|
|
||||||
_wr.val(attrs);
|
|
||||||
_wr.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void GeoJsonOutput::print(const MultiPoint<T>& ps, json::Val attrs) {
|
|
||||||
if (!ps.size()) return;
|
|
||||||
_wr.obj();
|
|
||||||
_wr.keyVal("type", "Feature");
|
|
||||||
|
|
||||||
_wr.key("geometry");
|
|
||||||
_wr.obj();
|
|
||||||
_wr.keyVal("type", "MultiPoint");
|
|
||||||
_wr.key("coordinates");
|
|
||||||
_wr.arr();
|
|
||||||
for (auto p : ps) {
|
|
||||||
_wr.arr();
|
|
||||||
_wr.val(p.getX());
|
|
||||||
_wr.val(p.getY());
|
|
||||||
_wr.close();
|
|
||||||
}
|
|
||||||
_wr.close();
|
|
||||||
_wr.close();
|
|
||||||
_wr.key("properties");
|
|
||||||
_wr.val(attrs);
|
|
||||||
_wr.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void GeoJsonOutput::print(const Line<T>& line, json::Val attrs) {
|
|
||||||
if (!line.size()) return;
|
|
||||||
_wr.obj();
|
|
||||||
_wr.keyVal("type", "Feature");
|
|
||||||
|
|
||||||
_wr.key("geometry");
|
|
||||||
_wr.obj();
|
|
||||||
_wr.keyVal("type", "LineString");
|
|
||||||
_wr.key("coordinates");
|
|
||||||
_wr.arr();
|
|
||||||
for (auto p : line) {
|
|
||||||
_wr.arr();
|
|
||||||
_wr.val(p.getX());
|
|
||||||
_wr.val(p.getY());
|
|
||||||
_wr.close();
|
|
||||||
}
|
|
||||||
_wr.close();
|
|
||||||
_wr.close();
|
|
||||||
_wr.key("properties");
|
|
||||||
_wr.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();
|
|
||||||
|
|
||||||
for (const auto& inner : poly.getInners()) {
|
|
||||||
_wr.arr();
|
|
||||||
for (auto p : inner) {
|
|
||||||
_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) {
|
|
||||||
auto projP = util::geo::webMercToLatLng<T>(p.getX(), p.getY());
|
|
||||||
print(projP, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename T>
|
|
||||||
void GeoJsonOutput::printLatLng(const MultiPoint<T>& ps, json::Val attrs) {
|
|
||||||
MultiPoint<T> projPs;
|
|
||||||
for (auto p : ps)
|
|
||||||
projPs.push_back(util::geo::webMercToLatLng<T>(p.getX(), p.getY()));
|
|
||||||
|
|
||||||
print(projPs, 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<T>(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.getOuter())
|
|
||||||
projP.getOuter().push_back(util::geo::webMercToLatLng<T>(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);
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
// Copyright 2017, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GRAPH_ALGORITHM_H_
|
|
||||||
#define UTIL_GRAPH_ALGORITHM_H_
|
|
||||||
|
|
||||||
#include <stack>
|
|
||||||
#include "util/graph/Edge.h"
|
|
||||||
#include "util/graph/Node.h"
|
|
||||||
#include "util/graph/UndirGraph.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace graph {
|
|
||||||
|
|
||||||
using util::graph::Graph;
|
|
||||||
using util::graph::Node;
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // UTIL_GRAPH_ALGORITHM_H_
|
|
|
@ -1,40 +0,0 @@
|
||||||
// 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, const EdgeCheckFunc<N, E>& checkFunc) {
|
|
||||||
std::vector<std::set<Node<N, E>*>> ret;
|
|
||||||
std::set<Node<N, E>*> visited;
|
|
||||||
|
|
||||||
for (auto* n : g.getNds()) {
|
|
||||||
if (!visited.count(n)) {
|
|
||||||
ret.resize(ret.size() + 1);
|
|
||||||
std::stack<Node<N, E>*> q;
|
|
||||||
q.push(n);
|
|
||||||
while (!q.empty()) {
|
|
||||||
Node<N, E>* cur = q.top();
|
|
||||||
q.pop();
|
|
||||||
|
|
||||||
ret.back().insert(cur);
|
|
||||||
visited.insert(cur);
|
|
||||||
|
|
||||||
for (auto* e : cur->getAdjList()) {
|
|
||||||
if (!checkFunc(cur, e)) continue;
|
|
||||||
if (!visited.count(e->getOtherNd(cur))) q.push(e->getOtherNd(cur));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
// 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;
|
|
|
@ -1,129 +0,0 @@
|
||||||
// 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_
|
|
|
@ -1,293 +0,0 @@
|
||||||
// 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());
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
// Copyright 2017, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#include "util/graph/Dijkstra.h"
|
|
||||||
|
|
||||||
size_t util::graph::Dijkstra::ITERS = 0;
|
|
|
@ -1,116 +0,0 @@
|
||||||
// Copyright 2017, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GRAPH_DIJKSTRA_H_
|
|
||||||
#define UTIL_GRAPH_DIJKSTRA_H_
|
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
#include <list>
|
|
||||||
#include <queue>
|
|
||||||
#include <set>
|
|
||||||
#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;
|
|
||||||
|
|
||||||
// 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() {}
|
|
||||||
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 void buildPath(Node<N, E>* curN, Settled<N, E, C>& settled,
|
|
||||||
NList<N, E>* resNodes, EList<N, E>* resEdges);
|
|
||||||
|
|
||||||
static size_t ITERS;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/graph/Dijkstra.tpp"
|
|
||||||
} // namespace graph
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GRAPH_DIJKSTRA_H_
|
|
|
@ -1,195 +0,0 @@
|
||||||
// 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 Dijkstra::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();
|
|
||||||
|
|
||||||
Settled<N, E, C> settled;
|
|
||||||
PQ<N, E, C> pq;
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
pq.emplace(from);
|
|
||||||
RouteNode<N, E, C> cur;
|
|
||||||
|
|
||||||
while (!pq.empty()) {
|
|
||||||
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();
|
|
||||||
|
|
||||||
settled[cur.n] = cur;
|
|
||||||
|
|
||||||
if (to.find(cur.n) != to.end()) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
relax(cur, to, costFunc, heurFunc, pq);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) return costFunc.inf();
|
|
||||||
|
|
||||||
buildPath(cur.n, settled, resNodes, resEdges);
|
|
||||||
|
|
||||||
return cur.d;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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 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;
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
// put all nodes in from onto PQ
|
|
||||||
for (auto n : from) pq.emplace(n);
|
|
||||||
RouteNode<N, E, C> cur;
|
|
||||||
|
|
||||||
while (!pq.empty()) {
|
|
||||||
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();
|
|
||||||
|
|
||||||
settled[cur.n] = cur;
|
|
||||||
|
|
||||||
if (to.find(cur.n) != to.end()) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
relax(cur, to, costFunc, heurFunc, pq);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) return costFunc.inf();
|
|
||||||
|
|
||||||
buildPath(cur.n, settled, resNodes, resEdges);
|
|
||||||
|
|
||||||
return cur.d;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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 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;
|
|
||||||
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;
|
|
||||||
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();
|
|
||||||
|
|
||||||
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 Dijkstra::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 (newC < cur.d) continue; // cost overflow!
|
|
||||||
if (costFunc.inf() <= newC) continue;
|
|
||||||
|
|
||||||
// addition done here to avoid it in the PQ
|
|
||||||
auto h = heurFunc(edge->getOtherNd(cur.n), to);
|
|
||||||
if (costFunc.inf() <= h) continue;
|
|
||||||
|
|
||||||
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 (resNodes || resEdges) {
|
|
||||||
const RouteNode<N, E, C>& curNode = settled[curN];
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
curN = curNode.parent;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GRAPH_DIRGRAPH_H_
|
|
||||||
#define UTIL_GRAPH_DIRGRAPH_H_
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "util/graph/Graph.h"
|
|
||||||
#include "util/graph/Edge.h"
|
|
||||||
#include "util/graph/DirNode.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace graph {
|
|
||||||
|
|
||||||
template <typename N, typename E>
|
|
||||||
using UndirEdge = Edge<N, E>;
|
|
||||||
|
|
||||||
template <typename N, typename E>
|
|
||||||
class DirGraph : public Graph<N, E> {
|
|
||||||
public:
|
|
||||||
using Graph<N, E>::addEdg;
|
|
||||||
|
|
||||||
Node<N, E>* addNd();
|
|
||||||
Node<N, E>* addNd(DirNode<N, E>* n);
|
|
||||||
Node<N, E>* addNd(const N& pl);
|
|
||||||
Edge<N, E>* addEdg(Node<N, E>* from, Node<N, E>* to, const E& p);
|
|
||||||
Edge<N, E>* addEdg(Node<N, E>* from, Node<N, E>* to, E&& p);
|
|
||||||
|
|
||||||
virtual Node<N, E>* mergeNds(Node<N, E>* a, Node<N, E>* b);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/graph/DirGraph.tpp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // UTIL_GRAPH_DIRGRAPH_H_
|
|
|
@ -1,67 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Node<N, E>* DirGraph<N, E>::addNd(const N& pl) {
|
|
||||||
return addNd(new DirNode<N, E>(pl));
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Node<N, E>* DirGraph<N, E>::addNd() {
|
|
||||||
return addNd(new DirNode<N, E>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Node<N, E>* DirGraph<N, E>::addNd(DirNode<N, E>* n) {
|
|
||||||
auto ins = Graph<N, E>::_nodes.insert(n);
|
|
||||||
return *ins.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Edge<N, E>* DirGraph<N, E>::addEdg(Node<N, E>* from, Node<N, E>* to,
|
|
||||||
const E& p) {
|
|
||||||
Edge<N, E>* e = Graph<N, E>::getEdg(from, to);
|
|
||||||
if (!e) {
|
|
||||||
e = new Edge<N, E>(from, to, p);
|
|
||||||
from->addEdge(e);
|
|
||||||
to->addEdge(e);
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Edge<N, E>* DirGraph<N, E>::addEdg(Node<N, E>* from, Node<N, E>* to, E&& p) {
|
|
||||||
Edge<N, E>* e = Graph<N, E>::getEdg(from, to);
|
|
||||||
if (!e) {
|
|
||||||
e = new Edge<N, E>(from, to, p);
|
|
||||||
from->addEdge(e);
|
|
||||||
to->addEdge(e);
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Node<N, E>* DirGraph<N, E>::mergeNds(Node<N, E>* a, Node<N, E>* b) {
|
|
||||||
for (auto e : a->getAdjListOut()) {
|
|
||||||
if (e->getTo() != b) {
|
|
||||||
addEdg(b, e->getTo(), e->pl());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto e : a->getAdjListIn()) {
|
|
||||||
if (e->getFrom() != b) {
|
|
||||||
addEdg(e->getFrom(), b, e->pl());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DirGraph<N, E>::delNd(a);
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GRAPH_DIRNODE_H_
|
|
||||||
#define UTIL_GRAPH_DIRNODE_H_
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <algorithm>
|
|
||||||
#include "util/graph/Node.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace graph {
|
|
||||||
|
|
||||||
// forward declaration of Edge
|
|
||||||
|
|
||||||
template <typename N, typename E>
|
|
||||||
class DirNode : public Node<N, E> {
|
|
||||||
public:
|
|
||||||
DirNode();
|
|
||||||
DirNode(const N& pl);
|
|
||||||
~DirNode();
|
|
||||||
|
|
||||||
const std::vector<Edge<N, E>*>& getAdjList() const;
|
|
||||||
const std::vector<Edge<N, E>*>& getAdjListIn() const;
|
|
||||||
const std::vector<Edge<N, E>*>& getAdjListOut() const;
|
|
||||||
|
|
||||||
size_t getDeg() const;
|
|
||||||
size_t getInDeg() const;
|
|
||||||
size_t getOutDeg() const;
|
|
||||||
|
|
||||||
bool hasEdgeIn(const Edge<N, E>* e) const;
|
|
||||||
bool hasEdgeOut(const Edge<N, E>* e) const;
|
|
||||||
bool hasEdge(const Edge<N, E>* e) const;
|
|
||||||
|
|
||||||
// add edge to this node's adjacency lists
|
|
||||||
void addEdge(Edge<N, E>* e);
|
|
||||||
|
|
||||||
// remove edge from this node's adjacency lists
|
|
||||||
void removeEdge(Edge<N, E>* e);
|
|
||||||
|
|
||||||
N& pl();
|
|
||||||
const N& pl() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Edge<N, E>*> _adjListIn;
|
|
||||||
std::vector<Edge<N, E>*> _adjListOut;
|
|
||||||
N _pl;
|
|
||||||
|
|
||||||
bool adjInContains(const Edge<N, E>* e) const;
|
|
||||||
bool adjOutContains(const Edge<N, E>* e) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/graph/DirNode.tpp"
|
|
||||||
|
|
||||||
}}
|
|
||||||
|
|
||||||
#endif // UTIL_GRAPH_DIRNODE_H_
|
|
|
@ -1,157 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
DirNode<N, E>::DirNode() : _pl() {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
DirNode<N, E>::DirNode(const N& pl) : _pl(pl) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
DirNode<N, E>::~DirNode() {
|
|
||||||
// delete self edges
|
|
||||||
for (auto e = _adjListOut.begin(); e != _adjListOut.end();) {
|
|
||||||
Edge<N, E>* eP = *e;
|
|
||||||
if (eP->getTo() == this) {
|
|
||||||
_adjListIn.erase(std::find(_adjListIn.begin(), _adjListIn.end(), eP));
|
|
||||||
e = _adjListOut.erase(e);
|
|
||||||
delete eP;
|
|
||||||
} else {
|
|
||||||
e++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto e = _adjListOut.begin(); e != _adjListOut.end(); e++) {
|
|
||||||
Edge<N, E>* eP = *e;
|
|
||||||
|
|
||||||
if (eP->getTo() != this) {
|
|
||||||
eP->getTo()->removeEdge(eP);
|
|
||||||
delete eP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto e = _adjListIn.begin(); e != _adjListIn.end(); e++) {
|
|
||||||
Edge<N, E>* eP = *e;
|
|
||||||
|
|
||||||
if (eP->getFrom() != this) {
|
|
||||||
eP->getFrom()->removeEdge(eP);
|
|
||||||
delete eP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
void DirNode<N, E>::addEdge(Edge<N, E>* e) {
|
|
||||||
if (e->getFrom() == this && !adjOutContains(e)) {
|
|
||||||
_adjListOut.reserve(_adjListOut.size() + 1);
|
|
||||||
_adjListOut.push_back(e);
|
|
||||||
}
|
|
||||||
if (e->getTo() == this && !adjInContains(e)) {
|
|
||||||
_adjListIn.reserve(_adjListIn.size() + 1);
|
|
||||||
_adjListIn.push_back(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
void DirNode<N, E>::removeEdge(Edge<N, E>* e) {
|
|
||||||
if (e->getFrom() == this) {
|
|
||||||
auto p = std::find(_adjListOut.begin(), _adjListOut.end(), e);
|
|
||||||
if (p != _adjListOut.end()) _adjListOut.erase(p);
|
|
||||||
}
|
|
||||||
if (e->getTo() == this) {
|
|
||||||
auto p = std::find(_adjListIn.begin(), _adjListIn.end(), e);
|
|
||||||
if (p != _adjListIn.end()) _adjListIn.erase(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
bool DirNode<N, E>::hasEdgeIn(const Edge<N, E>* e) const {
|
|
||||||
return e->getTo() == this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
bool DirNode<N, E>::hasEdgeOut(const Edge<N, E>* e) const {
|
|
||||||
return e->getFrom() == this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
bool DirNode<N, E>::hasEdge(const Edge<N, E>* e) const {
|
|
||||||
return hasEdgeOut(e) || hasEdgeIn(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
const std::vector<Edge<N, E>*>& DirNode<N, E>::getAdjList() const {
|
|
||||||
return _adjListOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
const std::vector<Edge<N, E>*>& DirNode<N, E>::getAdjListOut() const {
|
|
||||||
return _adjListOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
const std::vector<Edge<N, E>*>& DirNode<N, E>::getAdjListIn() const {
|
|
||||||
return _adjListIn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
size_t DirNode<N, E>::getDeg() const {
|
|
||||||
return _adjListOut.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
size_t DirNode<N, E>::getInDeg() const {
|
|
||||||
return _adjListIn.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
size_t DirNode<N, E>::getOutDeg() const {
|
|
||||||
return _adjListOut.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
N& DirNode<N, E>::pl() {
|
|
||||||
return _pl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
const N& DirNode<N, E>::pl() const {
|
|
||||||
return _pl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
// Copyright 2017, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#include "util/graph/EDijkstra.h"
|
|
||||||
|
|
||||||
size_t util::graph::EDijkstra::ITERS = 0;
|
|
|
@ -1,240 +0,0 @@
|
||||||
// Copyright 2017, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GRAPH_EDIJKSTRA_H_
|
|
||||||
#define UTIL_GRAPH_EDIJKSTRA_H_
|
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
#include <list>
|
|
||||||
#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;
|
|
||||||
|
|
||||||
// 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(), 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), n(n) {}
|
|
||||||
|
|
||||||
Edge<N, E>* e;
|
|
||||||
Edge<N, E>* parent;
|
|
||||||
|
|
||||||
// the cost so far
|
|
||||||
C d;
|
|
||||||
|
|
||||||
Node<N, E>* n;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename N, typename E, typename 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);
|
|
||||||
UNUSED(e);
|
|
||||||
UNUSED(to);
|
|
||||||
return C();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename N, typename E, typename 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);
|
|
||||||
UNUSED(to);
|
|
||||||
return C();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
using Settled = tsl::robin_map<Edge<N, E>*, RouteEdge<N, E, C>>;
|
|
||||||
|
|
||||||
template <typename N, typename E, typename 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 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 C shortestPathImpl(Edge<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(const std::set<Edge<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(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 std::unordered_map<Edge<N, E>*, C> shortestPathImpl(
|
|
||||||
const std::set<Edge<N, E>*>& from,
|
|
||||||
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 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,
|
|
||||||
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 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 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_
|
|
|
@ -1,564 +0,0 @@
|
||||||
// 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 EDijkstra::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) {
|
|
||||||
std::set<Edge<N, E>*> frEs;
|
|
||||||
std::set<Edge<N, E>*> toEs;
|
|
||||||
|
|
||||||
frEs.insert(from->getAdjListOut().begin(), from->getAdjListOut().end());
|
|
||||||
|
|
||||||
for (auto n : to) {
|
|
||||||
toEs.insert(n->getAdjListIn().begin(), n->getAdjListIn().end());
|
|
||||||
}
|
|
||||||
|
|
||||||
C cost = shortestPathImpl(frEs, toEs, costFunc, heurFunc, resEdges, resNodes);
|
|
||||||
|
|
||||||
// the beginning node is not included in our edge based dijkstra
|
|
||||||
if (resNodes) resNodes->push_back(from);
|
|
||||||
|
|
||||||
return cost;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
C EDijkstra::shortestPathImpl(Edge<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) {
|
|
||||||
std::set<Edge<N, E>*> frEs;
|
|
||||||
std::set<Edge<N, E>*> toEs;
|
|
||||||
|
|
||||||
frEs.insert(from);
|
|
||||||
|
|
||||||
for (auto n : to) {
|
|
||||||
toEs.insert(n->getAdjListIn().begin(), n->getAdjListIn().end());
|
|
||||||
}
|
|
||||||
|
|
||||||
C cost = shortestPathImpl(frEs, toEs, costFunc, heurFunc, resEdges, resNodes);
|
|
||||||
|
|
||||||
return cost;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
C EDijkstra::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) {
|
|
||||||
std::set<Edge<N, E>*> frEs;
|
|
||||||
std::set<Edge<N, E>*> toEs;
|
|
||||||
|
|
||||||
for (auto n : from) {
|
|
||||||
frEs.insert(n->getAdjListIn().begin(), n->getAdjListIn().end());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto n : to) {
|
|
||||||
toEs.insert(n->getAdjListIn().begin(), n->getAdjListIn().end());
|
|
||||||
}
|
|
||||||
|
|
||||||
C cost = shortestPathImpl(frEs, toEs, costFunc, heurFunc, resEdges, resNodes);
|
|
||||||
|
|
||||||
return cost;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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 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();
|
|
||||||
|
|
||||||
Settled<N, E, C> settled;
|
|
||||||
PQ<N, E, C> pq;
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
// at the beginning, put all edges on the priority queue,
|
|
||||||
// init them with their own cost
|
|
||||||
for (auto e : from) {
|
|
||||||
C c = costFunc(0, 0, e);
|
|
||||||
C h = heurFunc(e, to);
|
|
||||||
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++;
|
|
||||||
|
|
||||||
cur = pq.topVal();
|
|
||||||
pq.pop();
|
|
||||||
|
|
||||||
settled[cur.e] = cur;
|
|
||||||
|
|
||||||
if (to.find(cur.e) != to.end()) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
relax(cur, to, costFunc, heurFunc, pq);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) return costFunc.inf();
|
|
||||||
|
|
||||||
buildPath(cur.e, settled, resNodes, resEdges);
|
|
||||||
|
|
||||||
return cur.d;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
|
|
||||||
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;
|
|
||||||
PQ<N, E, C> pq;
|
|
||||||
|
|
||||||
std::set<Edge<N, E>*> to;
|
|
||||||
|
|
||||||
for (auto e : from) {
|
|
||||||
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++;
|
|
||||||
|
|
||||||
cur = pq.topVal();
|
|
||||||
pq.pop();
|
|
||||||
|
|
||||||
settled[cur.e] = cur;
|
|
||||||
|
|
||||||
costs[cur.e] = cur.d;
|
|
||||||
buildPath(cur.e, settled, (NList<N, E>*)0, (EList<N, E>*)0);
|
|
||||||
|
|
||||||
if (rev)
|
|
||||||
relaxInv(cur, costFunc, pq);
|
|
||||||
else
|
|
||||||
relax(cur, to, costFunc, ZeroHeurFunc<N, E, C>(), pq);
|
|
||||||
}
|
|
||||||
|
|
||||||
return costs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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 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;
|
|
||||||
if (to.size() == 0) return costs;
|
|
||||||
|
|
||||||
// init costs with inf
|
|
||||||
for (auto e : to) costs[e] = costFunc.inf();
|
|
||||||
|
|
||||||
Settled<N, E, C> settled;
|
|
||||||
PQ<N, E, C> pq;
|
|
||||||
|
|
||||||
size_t found = 0;
|
|
||||||
|
|
||||||
C c = costFunc(0, 0, from);
|
|
||||||
C h = heurFunc(from, to);
|
|
||||||
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++;
|
|
||||||
|
|
||||||
cur = pq.topVal();
|
|
||||||
pq.pop();
|
|
||||||
|
|
||||||
settled[cur.e] = cur;
|
|
||||||
|
|
||||||
if (to.find(cur.e) != to.end()) {
|
|
||||||
found++;
|
|
||||||
costs[cur.e] = cur.d;
|
|
||||||
buildPath(cur.e, settled, resNodes[cur.e], resEdges[cur.e]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found == to.size()) return costs;
|
|
||||||
|
|
||||||
relax(cur, to, costFunc, heurFunc, pq);
|
|
||||||
}
|
|
||||||
|
|
||||||
return costs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
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()) {
|
|
||||||
if (edge == cur.e) continue;
|
|
||||||
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.push(C(), {edge, cur.e, cur.e->getFrom(), newC});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
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.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.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});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
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 (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;
|
|
||||||
curEdge = &settled.find(curEdge->parent)->second;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GRAPH_EDGE_H_
|
|
||||||
#define UTIL_GRAPH_EDGE_H_
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include "util/graph/Node.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace graph {
|
|
||||||
|
|
||||||
template <typename N, typename E>
|
|
||||||
class Edge {
|
|
||||||
public:
|
|
||||||
Edge(Node<N, E>* from, Node<N, E>* to, const E& pl);
|
|
||||||
Edge(Node<N, E>* from, Node<N, E>* to, E&& pl);
|
|
||||||
|
|
||||||
Node<N, E>* getFrom() const;
|
|
||||||
Node<N, E>* getTo() const;
|
|
||||||
|
|
||||||
Node<N, E>* getOtherNd(const Node<N, E>* notNode) const;
|
|
||||||
|
|
||||||
E& pl();
|
|
||||||
const E& pl() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Node<N, E>* _from;
|
|
||||||
Node<N, E>* _to;
|
|
||||||
E _pl;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/graph/Edge.tpp"
|
|
||||||
|
|
||||||
}}
|
|
||||||
|
|
||||||
#endif // UTIL_GRAPH_EDGE_H_
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Edge<N, E>::Edge(Node<N, E>* from, Node<N, E>* to, const E& pl)
|
|
||||||
: _from(from), _to(to), _pl(pl) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Edge<N, E>::Edge(Node<N, E>* from, Node<N, E>* to, E&& pl)
|
|
||||||
: _from(from), _to(to), _pl(std::move(pl)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Node<N, E>* Edge<N, E>::getFrom() const {
|
|
||||||
return _from;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Node<N, E>* Edge<N, E>::getTo() const {
|
|
||||||
return _to;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Node<N, E>* Edge<N, E>::getOtherNd(const Node<N, E>* notNode) const {
|
|
||||||
if (_to == notNode) return _from;
|
|
||||||
return _to;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
E& Edge<N, E>::pl() {
|
|
||||||
return _pl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
const E& Edge<N, E>::pl() const {
|
|
||||||
return _pl;
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GRAPH_GRAPH_H_
|
|
||||||
#define UTIL_GRAPH_GRAPH_H_
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <iostream>
|
|
||||||
#include <set>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "util/graph/Edge.h"
|
|
||||||
#include "util/graph/Node.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace graph {
|
|
||||||
|
|
||||||
template <typename N, typename E>
|
|
||||||
class Graph {
|
|
||||||
public:
|
|
||||||
Graph() {} ;
|
|
||||||
Graph(const Graph& g) = delete;
|
|
||||||
Graph(Graph& g) = delete;
|
|
||||||
void operator=(const Graph& other) = delete;
|
|
||||||
void operator=(Graph& other) = delete;
|
|
||||||
virtual ~Graph();
|
|
||||||
virtual Node<N, E>* addNd() = 0;
|
|
||||||
virtual Node<N, E>* addNd(const N& pl) = 0;
|
|
||||||
Edge<N, E>* addEdg(Node<N, E>* from, Node<N, E>* to);
|
|
||||||
virtual Edge<N, E>* addEdg(Node<N, E>* from, Node<N, E>* to, const E& p) = 0;
|
|
||||||
virtual Edge<N, E>* addEdg(Node<N, E>* from, Node<N, E>* to, E&& p) = 0;
|
|
||||||
Edge<N, E>* getEdg(Node<N, E>* from, Node<N, E>* to);
|
|
||||||
const Edge<N, E>* getEdg(const Node<N, E>* from, const Node<N, E>* to) const;
|
|
||||||
|
|
||||||
virtual Node<N, E>* mergeNds(Node<N, E>* a, Node<N, E>* b) = 0;
|
|
||||||
|
|
||||||
const std::set<Node<N, E>*>& getNds() const;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::set<Node<N, E>*> _nodes;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/graph/Graph.tpp"
|
|
||||||
} // namespace graph
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GRAPH_GRAPH_H_
|
|
|
@ -1,80 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Graph<N, E>::~Graph() {
|
|
||||||
for (auto n : _nodes) delete n;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Edge<N, E>* Graph<N, E>::addEdg(Node<N, E>* from, Node<N, E>* to) {
|
|
||||||
return addEdg(from, to, E());
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
const std::set<Node<N, E>*>& Graph<N, E>::getNds() const {
|
|
||||||
return _nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
typename std::set<Node<N, E>*>::iterator Graph<N, E>::delNd(Node<N, E>* n) {
|
|
||||||
return delNd(_nodes.find(n));
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
typename std::set<Node<N, E>*>::iterator Graph<N, E>::delNd(
|
|
||||||
typename std::set<Node<N, E>*>::iterator i) {
|
|
||||||
delete *i;
|
|
||||||
return _nodes.erase(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
void Graph<N, E>::delEdg(Node<N, E>* from, Node<N, E>* to) {
|
|
||||||
Edge<N, E>* toDel = getEdg(from, to);
|
|
||||||
if (!toDel) return;
|
|
||||||
|
|
||||||
from->removeEdge(toDel);
|
|
||||||
to->removeEdge(toDel);
|
|
||||||
|
|
||||||
assert(!getEdg(from, to));
|
|
||||||
|
|
||||||
delete toDel;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Edge<N, E>* Graph<N, E>::getEdg(Node<N, E>* from, Node<N, E>* to) {
|
|
||||||
for (auto e : from->getAdjList()) {
|
|
||||||
if (e->getOtherNd(from) == to) return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
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(const Node<N, E>* from,
|
|
||||||
const Node<N, E>* to) const {
|
|
||||||
for (auto e : from->getAdjList()) {
|
|
||||||
if (e->getOtherNd(from) == to) return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GRAPH_NODE_H_
|
|
||||||
#define UTIL_GRAPH_NODE_H_
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace graph {
|
|
||||||
|
|
||||||
// forward declaration of Edge
|
|
||||||
template <typename N, typename E>
|
|
||||||
class Edge;
|
|
||||||
|
|
||||||
template <typename N, typename E>
|
|
||||||
class Node {
|
|
||||||
public:
|
|
||||||
virtual const std::vector<Edge<N, E>*>& getAdjList() const = 0;
|
|
||||||
virtual const std::vector<Edge<N, E>*>& getAdjListOut() const = 0;
|
|
||||||
virtual const std::vector<Edge<N, E>*>& getAdjListIn() const = 0;
|
|
||||||
|
|
||||||
virtual size_t getDeg() const = 0;
|
|
||||||
virtual size_t getInDeg() const = 0;
|
|
||||||
virtual size_t getOutDeg() const = 0;
|
|
||||||
|
|
||||||
virtual bool hasEdgeIn(const Edge<N, E>* e) const = 0;
|
|
||||||
virtual bool hasEdgeOut(const Edge<N, E>* e) const = 0;
|
|
||||||
virtual bool hasEdge(const Edge<N, E>* e) const = 0;
|
|
||||||
|
|
||||||
// add edge to this node's adjacency lists
|
|
||||||
virtual void addEdge(Edge<N, E>* e) = 0;
|
|
||||||
virtual void removeEdge(Edge<N, E>* e) = 0;
|
|
||||||
|
|
||||||
virtual ~Node() = 0;
|
|
||||||
|
|
||||||
virtual N& pl() = 0;
|
|
||||||
virtual const N& pl() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename N, typename E>
|
|
||||||
inline Node<N, E>::~Node() {}
|
|
||||||
|
|
||||||
} // namespace graph
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_GRAPH_NODE_H_
|
|
|
@ -1,548 +0,0 @@
|
||||||
// Copyright 2017, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GRAPH_SHORTESTPATH_H_
|
|
||||||
#define UTIL_GRAPH_SHORTESTPATH_H_
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <iostream>
|
|
||||||
#include <limits>
|
|
||||||
#include <list>
|
|
||||||
#include <queue>
|
|
||||||
#include <set>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include "util/graph/Edge.h"
|
|
||||||
#include "util/graph/Graph.h"
|
|
||||||
#include "util/graph/Node.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace graph {
|
|
||||||
|
|
||||||
using util::graph::Edge;
|
|
||||||
using util::graph::Graph;
|
|
||||||
using util::graph::Node;
|
|
||||||
|
|
||||||
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>
|
|
||||||
class ShortestPath {
|
|
||||||
public:
|
|
||||||
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>
|
|
||||||
static C shortestPath(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) {
|
|
||||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
|
|
||||||
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>* el = 0;
|
|
||||||
NList<N, E>* nl = 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<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) {
|
|
||||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
|
|
||||||
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,
|
|
||||||
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 std::unordered_map<Node<N, E>*, C> shortestPath(
|
|
||||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
|
||||||
const CostFunc<N, E, C>& costFunc, const 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) {
|
|
||||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
|
|
||||||
resNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
static std::unordered_map<Node<N, E>*, C> shortestPath(
|
|
||||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
|
||||||
const CostFunc<N, E, C>& costFunc,
|
|
||||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
|
|
||||||
std::unordered_map<Node<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<Node<N, E>*, C> shortestPath(
|
|
||||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
|
||||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
|
|
||||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges) {
|
|
||||||
std::unordered_map<Node<N, E>*, NList<N, E>*> dummyRet;
|
|
||||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
|
|
||||||
dummyRet);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
static std::unordered_map<Node<N, E>*, C> shortestPath(
|
|
||||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
|
||||||
const CostFunc<N, E, C>& costFunc,
|
|
||||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges) {
|
|
||||||
std::unordered_map<Node<N, E>*, NList<N, E>*> dummyRet;
|
|
||||||
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(),
|
|
||||||
resEdges, dummyRet);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
static std::unordered_map<Node<N, E>*, C> shortestPath(
|
|
||||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
|
||||||
const CostFunc<N, E, C>& costFunc,
|
|
||||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNodes) {
|
|
||||||
std::unordered_map<Node<N, E>*, EList<N, E>*> dummyRet;
|
|
||||||
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(),
|
|
||||||
dummyRet, resNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
static std::unordered_map<Node<N, E>*, C> shortestPath(
|
|
||||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
|
||||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
|
|
||||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNodes) {
|
|
||||||
std::unordered_map<Node<N, E>*, EList<N, E>*> dummyRet;
|
|
||||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, dummyRet,
|
|
||||||
resNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
|
|
||||||
const CostFunc<N, E, C>& costFunc,
|
|
||||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
|
||||||
if (to->getInDeg() == 0) return costFunc.inf();
|
|
||||||
std::set<Node<N, E>*> tos;
|
|
||||||
tos.insert(to);
|
|
||||||
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) {
|
|
||||||
if (to->getInDeg() == 0) return costFunc.inf();
|
|
||||||
std::set<Node<N, E>*> tos;
|
|
||||||
tos.insert(to);
|
|
||||||
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,
|
|
||||||
NList<N, E>* resNodes) {
|
|
||||||
if (to->getInDeg() == 0) return costFunc.inf();
|
|
||||||
std::set<Node<N, E>*> tos;
|
|
||||||
tos.insert(to);
|
|
||||||
EList<N, E>* el = 0;
|
|
||||||
return shortestPath(from, tos, costFunc, el, resNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
|
|
||||||
const CostFunc<N, E, C>& costFunc,
|
|
||||||
EList<N, E>* resEdges) {
|
|
||||||
if (to->getInDeg() == 0) return costFunc.inf();
|
|
||||||
std::set<Node<N, E>*> tos;
|
|
||||||
tos.insert(to);
|
|
||||||
NList<N, E>* nl = 0;
|
|
||||||
return shortestPath(from, tos, costFunc, resEdges, nl);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
NList<N, E>* resNodes) {
|
|
||||||
EList<N, E>* el = 0;
|
|
||||||
return shortestPath(from, to, costFunc, el, 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,
|
|
||||||
EList<N, E>* resEdges) {
|
|
||||||
NList<N, E>* nl = 0;
|
|
||||||
return shortestPath(from, to, costFunc, resEdges, 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,
|
|
||||||
const HeurFunc<N, E, C>& heurFunc,
|
|
||||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
|
||||||
if (to->getInDeg() == 0) return costFunc.inf();
|
|
||||||
std::set<Node<N, E>*> tos;
|
|
||||||
tos.insert(to);
|
|
||||||
return D::shortestPathImpl(from, tos, costFunc, heurFunc, resEdges,
|
|
||||||
resNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
|
|
||||||
const CostFunc<N, E, C>& costFunc,
|
|
||||||
const HeurFunc<N, E, C>& heurFunc,
|
|
||||||
NList<N, E>* resNodes) {
|
|
||||||
if (to->getInDeg() == 0) return costFunc.inf();
|
|
||||||
std::set<Node<N, E>*> tos;
|
|
||||||
tos.insert(to);
|
|
||||||
EList<N, E>* el = 0;
|
|
||||||
return D::shortestPathImpl(from, tos, costFunc, heurFunc, el, resNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
|
|
||||||
const CostFunc<N, E, C>& costFunc,
|
|
||||||
const HeurFunc<N, E, C>& heurFunc,
|
|
||||||
EList<N, E>* resEdges) {
|
|
||||||
if (to->getInDeg() == 0) return costFunc.inf();
|
|
||||||
std::set<Node<N, E>*> tos;
|
|
||||||
tos.insert(to);
|
|
||||||
NList<N, E>* nl = 0;
|
|
||||||
return D::shortestPathImpl(from, tos, costFunc, heurFunc, resEdges, nl);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
const HeurFunc<N, E, C>& heurFunc,
|
|
||||||
NList<N, E>* resNodes) {
|
|
||||||
EList<N, E>* el = 0;
|
|
||||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, el, 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,
|
|
||||||
const HeurFunc<N, E, C>& heurFunc,
|
|
||||||
EList<N, E>* resEdges) {
|
|
||||||
NList<N, E>* nl = 0;
|
|
||||||
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 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,
|
|
||||||
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,
|
|
||||||
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,
|
|
||||||
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 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,
|
|
||||||
const CostFunc<N, E, C>& costFunc,
|
|
||||||
const HeurFunc<N, E, C>& heurFunc) {
|
|
||||||
NList<N, E>* nl = 0;
|
|
||||||
EList<N, E>* el = 0;
|
|
||||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, 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,
|
|
||||||
const HeurFunc<N, E, C>& heurFunc, EList<N, E>* el) {
|
|
||||||
NList<N, E>* nl = 0;
|
|
||||||
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,
|
|
||||||
const HeurFunc<N, E, C>& heurFunc, EList<N, E>* el,
|
|
||||||
NList<N, E>* nl) {
|
|
||||||
return D::shortestPathImpl(from, to, 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, EList<N, E>* el,
|
|
||||||
NList<N, E>* nl) {
|
|
||||||
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(), el,
|
|
||||||
nl);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
static C shortestPath(Edge<N, E>* from, Node<N, E>* to,
|
|
||||||
const CostFunc<N, E, C>& costFunc,
|
|
||||||
const HeurFunc<N, E, C>& heurFunc, EList<N, E>* el,
|
|
||||||
NList<N, E>* nl) {
|
|
||||||
std::set<Node<N, E>*> tos{to};
|
|
||||||
return D::shortestPathImpl(from, tos, costFunc, heurFunc, el, nl);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
static C shortestPath(Edge<N, E>* from, Node<N, E>* to,
|
|
||||||
const CostFunc<N, E, C>& costFunc, EList<N, E>* el,
|
|
||||||
NList<N, E>* nl) {
|
|
||||||
std::set<Node<N, E>*> tos{to};
|
|
||||||
return D::shortestPathImpl(from, tos, costFunc, ZeroHeurFunc<N, E, C>(), el,
|
|
||||||
nl);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
|
||||||
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 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 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename N, typename E, typename C>
|
|
||||||
static std::unordered_map<Edge<N, E>*, C> shortestPathRev(
|
|
||||||
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_
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GRAPH_UNDIRGRAPH_H_
|
|
||||||
#define UTIL_GRAPH_UNDIRGRAPH_H_
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "util/graph/Graph.h"
|
|
||||||
#include "util/graph/Edge.h"
|
|
||||||
#include "util/graph/UndirNode.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace graph {
|
|
||||||
|
|
||||||
template <typename N, typename E>
|
|
||||||
using UndirEdge = Edge<N, E>;
|
|
||||||
|
|
||||||
template <typename N, typename E>
|
|
||||||
class UndirGraph : public Graph<N, E> {
|
|
||||||
public:
|
|
||||||
explicit UndirGraph();
|
|
||||||
|
|
||||||
using Graph<N, E>::addEdg;
|
|
||||||
|
|
||||||
Node<N, E>* addNd();
|
|
||||||
Node<N, E>* addNd(UndirNode<N, E>* n);
|
|
||||||
Node<N, E>* addNd(const N& pl);
|
|
||||||
Edge<N, E>* addEdg(Node<N, E>* from, Node<N, E>* to, const E& p);
|
|
||||||
Edge<N, E>* addEdg(Node<N, E>* from, Node<N, E>* to, E&& p);
|
|
||||||
|
|
||||||
virtual Node<N, E>* mergeNds(Node<N, E>* a, Node<N, E>* b);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/graph/UndirGraph.tpp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // UTIL_GRAPH_UNDIRGRAPH_H_
|
|
|
@ -1,74 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
UndirGraph<N, E>::UndirGraph() {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Node<N, E>* UndirGraph<N, E>::addNd(const N& pl) {
|
|
||||||
return addNd(new UndirNode<N, E>(pl));
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Node<N, E>* UndirGraph<N, E>::addNd() {
|
|
||||||
return addNd(new UndirNode<N, E>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Node<N, E>* UndirGraph<N, E>::addNd(UndirNode<N, E>* n) {
|
|
||||||
auto ins = Graph<N, E>::_nodes.insert(n);
|
|
||||||
return *ins.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Edge<N, E>* UndirGraph<N, E>::addEdg(Node<N, E>* from, Node<N, E>* to,
|
|
||||||
const E& p) {
|
|
||||||
Edge<N, E>* e = Graph<N, E>::getEdg(from, to);
|
|
||||||
if (!e) {
|
|
||||||
e = new Edge<N, E>(from, to, p);
|
|
||||||
from->addEdge(e);
|
|
||||||
to->addEdge(e);
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Edge<N, E>* UndirGraph<N, E>::addEdg(Node<N, E>* from, Node<N, E>* to,
|
|
||||||
E&& p) {
|
|
||||||
Edge<N, E>* e = Graph<N, E>::getEdg(from, to);
|
|
||||||
if (!e) {
|
|
||||||
e = new Edge<N, E>(from, to, std::move(p));
|
|
||||||
from->addEdge(e);
|
|
||||||
to->addEdge(e);
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
Node<N, E>* UndirGraph<N, E>::mergeNds(Node<N, E>* a, Node<N, E>* b) {
|
|
||||||
for (auto e : a->getAdjListOut()) {
|
|
||||||
if (e->getFrom() != a) continue;
|
|
||||||
if (e->getTo() != b) {
|
|
||||||
addEdg(b, e->getTo(), e->pl());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto e : a->getAdjListIn()) {
|
|
||||||
if (e->getTo() != a) continue;
|
|
||||||
if (e->getFrom() != b) {
|
|
||||||
addEdg(e->getFrom(), b, e->pl());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UndirGraph<N, E>::delNd(a);
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_GRAPH_UNDIRNODE_H_
|
|
||||||
#define UTIL_GRAPH_UNDIRNODE_H_
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <algorithm>
|
|
||||||
#include "util/graph/Node.h"
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace graph {
|
|
||||||
|
|
||||||
template <typename N, typename E>
|
|
||||||
class UndirNode : public Node<N, E> {
|
|
||||||
public:
|
|
||||||
UndirNode();
|
|
||||||
UndirNode(const N& pl);
|
|
||||||
~UndirNode();
|
|
||||||
|
|
||||||
const std::vector<Edge<N, E>*>& getAdjList() const;
|
|
||||||
const std::vector<Edge<N, E>*>& getAdjListIn() const;
|
|
||||||
const std::vector<Edge<N, E>*>& getAdjListOut() const;
|
|
||||||
|
|
||||||
size_t getDeg() const;
|
|
||||||
size_t getInDeg() const;
|
|
||||||
size_t getOutDeg() const;
|
|
||||||
|
|
||||||
bool hasEdgeIn(const Edge<N, E>* e) const;
|
|
||||||
bool hasEdgeOut(const Edge<N, E>* e) const;
|
|
||||||
bool hasEdge(const Edge<N, E>* e) const;
|
|
||||||
|
|
||||||
// add edge to this node's adjacency lists
|
|
||||||
void addEdge(Edge<N, E>* e);
|
|
||||||
|
|
||||||
// remove edge from this node's adjacency lists
|
|
||||||
void removeEdge(Edge<N, E>* e);
|
|
||||||
|
|
||||||
N& pl();
|
|
||||||
const N& pl() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Edge<N, E>*> _adjList;
|
|
||||||
N _pl;
|
|
||||||
|
|
||||||
bool adjContains(const Edge<N, E>* e) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "util/graph/UndirNode.tpp"
|
|
||||||
|
|
||||||
}}
|
|
||||||
|
|
||||||
#endif // UTIL_GRAPH_UNDIRNODE_H_
|
|
|
@ -1,130 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
UndirNode<N, E>::UndirNode() : _pl() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
UndirNode<N, E>::UndirNode(const N& pl) : _pl(pl) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
UndirNode<N, E>::~UndirNode() {
|
|
||||||
// delete self edges
|
|
||||||
for (auto e = _adjList.begin(); e != _adjList.end();) {
|
|
||||||
Edge<N, E>* eP = *e;
|
|
||||||
if (eP->getTo() == this && eP->getFrom() == this) {
|
|
||||||
e = _adjList.erase(e);
|
|
||||||
delete eP;
|
|
||||||
} else {
|
|
||||||
e++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto e = _adjList.begin(); e != _adjList.end(); e++) {
|
|
||||||
Edge<N, E>* eP = *e;
|
|
||||||
|
|
||||||
if (eP->getTo() != this) {
|
|
||||||
eP->getTo()->removeEdge(eP);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eP->getFrom() != this) {
|
|
||||||
eP->getFrom()->removeEdge(eP);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete eP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
bool UndirNode<N, E>::hasEdgeIn(const Edge<N, E>* e) const {
|
|
||||||
return hasEdge(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
bool UndirNode<N, E>::hasEdgeOut(const Edge<N, E>* e) const {
|
|
||||||
return hasEdge(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
bool UndirNode<N, E>::hasEdge(const Edge<N, E>* e) const {
|
|
||||||
return e->getFrom() == this || e->getTo() == this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
bool UndirNode<N, E>::adjContains(const Edge<N, E>* e) const {
|
|
||||||
for (size_t i = 0; i < _adjList.size(); i++) if (_adjList[i] == e) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
void UndirNode<N, E>::addEdge(Edge<N, E>* e) {
|
|
||||||
if (adjContains(e)) return;
|
|
||||||
_adjList.reserve(_adjList.size() + 1);
|
|
||||||
_adjList.push_back(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
void UndirNode<N, E>::removeEdge(Edge<N, E>* e) {
|
|
||||||
auto p = std::find(_adjList.begin(), _adjList.end(), e);
|
|
||||||
if (p != _adjList.end()) _adjList.erase(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
const std::vector<Edge<N, E>*>& UndirNode<N, E>::getAdjList() const {
|
|
||||||
return _adjList;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
const std::vector<Edge<N, E>*>& UndirNode<N, E>::getAdjListOut() const {
|
|
||||||
return _adjList;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
const std::vector<Edge<N, E>*>& UndirNode<N, E>::getAdjListIn() const {
|
|
||||||
return _adjList;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
size_t UndirNode<N, E>::getDeg() const {
|
|
||||||
return _adjList.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
size_t UndirNode<N, E>::getInDeg() const {
|
|
||||||
return getDeg();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
size_t UndirNode<N, E>::getOutDeg() const {
|
|
||||||
return getDeg();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
N& UndirNode<N, E>::pl() {
|
|
||||||
return _pl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
template <typename N, typename E>
|
|
||||||
const N& UndirNode<N, E>::pl() const {
|
|
||||||
return _pl;
|
|
||||||
}
|
|
|
@ -1,226 +0,0 @@
|
||||||
// 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 << "PQ: 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 << "PQ: 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
|
|
|
@ -1,348 +0,0 @@
|
||||||
/**
|
|
||||||
* 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
|
@ -1,715 +0,0 @@
|
||||||
/**
|
|
||||||
* 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
|
|
|
@ -1,582 +0,0 @@
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
|
|
|
@ -1,360 +0,0 @@
|
||||||
// Copyright 2018, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef ZLIB_CONST
|
|
||||||
#define ZLIB_CONST
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <netinet/tcp.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <csignal>
|
|
||||||
#include <memory>
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <thread>
|
|
||||||
#include <unordered_map>
|
|
||||||
#ifdef ZLIB_FOUND
|
|
||||||
#include <zlib.h>
|
|
||||||
#endif
|
|
||||||
#include <vector>
|
|
||||||
#include "Server.h"
|
|
||||||
#include "util/String.h"
|
|
||||||
#include "util/log/Log.h"
|
|
||||||
#include "util/Misc.h"
|
|
||||||
|
|
||||||
using util::http::HeaderState;
|
|
||||||
using util::http::HttpErr;
|
|
||||||
using util::http::HttpServer;
|
|
||||||
using util::http::Queue;
|
|
||||||
using util::http::Req;
|
|
||||||
using util::http::Socket;
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
Socket::Socket(int port) {
|
|
||||||
int y = 1;
|
|
||||||
_sock = socket(PF_INET, SOCK_STREAM, 0);
|
|
||||||
if (_sock < 0)
|
|
||||||
throw std::runtime_error(std::string("Could not create socket (") +
|
|
||||||
std::strerror(errno) + ")");
|
|
||||||
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(port);
|
|
||||||
addr.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
memset(&(addr.sin_zero), '\0', 8);
|
|
||||||
|
|
||||||
setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y));
|
|
||||||
|
|
||||||
if (bind(_sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
|
|
||||||
throw std::runtime_error(std::string("Could not bind to port ") +
|
|
||||||
std::to_string(port) + " (" +
|
|
||||||
std::strerror(errno) + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
Socket::~Socket() { close(_sock); }
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
int Socket::wait() {
|
|
||||||
if (listen(_sock, BLOG) < 0)
|
|
||||||
throw std::runtime_error(std::string("Cannot listen to socket (") +
|
|
||||||
std::strerror(errno) + ")");
|
|
||||||
sockaddr_in cli_addr;
|
|
||||||
socklen_t clilen = sizeof(cli_addr);
|
|
||||||
int sock = accept(_sock, reinterpret_cast<sockaddr*>(&cli_addr), &clilen);
|
|
||||||
return sock;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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);
|
|
||||||
|
|
||||||
aw->params["Content-Encoding"] = enc;
|
|
||||||
aw->params["Content-Length"] = std::to_string(aw->pl.size());
|
|
||||||
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "HTTP/1.1 " << aw->status << "\r\n";
|
|
||||||
for (const auto& kv : aw->params)
|
|
||||||
ss << kv.first << ": " << kv.second << "\r\n";
|
|
||||||
ss << "\r\n" << aw->pl;
|
|
||||||
std::string buff = ss.str();
|
|
||||||
|
|
||||||
size_t writes = 0;
|
|
||||||
|
|
||||||
while (writes != buff.size()) {
|
|
||||||
int64_t out = write(sock, buff.c_str() + writes, buff.size() - writes);
|
|
||||||
if (out < 0) {
|
|
||||||
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) continue;
|
|
||||||
throw std::runtime_error("Failed to write to socket");
|
|
||||||
}
|
|
||||||
writes += out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void HttpServer::handle() {
|
|
||||||
int connection = -1;
|
|
||||||
while ((connection = _jobs.get()) != -1) {
|
|
||||||
Answer answ;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Req req = getReq(connection);
|
|
||||||
answ = _handler->handle(req, connection);
|
|
||||||
if (answ.raw) {
|
|
||||||
close(connection); // the handle did everything
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
answ.gzip = req.gzip;
|
|
||||||
} catch (const HttpErr& err) {
|
|
||||||
answ = Answer(err.what(), err.what());
|
|
||||||
} catch (...) {
|
|
||||||
// catch everything to make sure the server continues running
|
|
||||||
answ = Answer("500 Internal Server Error", "500 Internal Server Error");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
send(connection, &answ);
|
|
||||||
} catch (const std::runtime_error& err) {
|
|
||||||
LOG(WARN) << err.what();
|
|
||||||
}
|
|
||||||
|
|
||||||
close(connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
bool HttpServer::gzipSupport(const Req& req) {
|
|
||||||
bool accepts = false;
|
|
||||||
// decide according to
|
|
||||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
|
||||||
for (const auto& kv : req.params) {
|
|
||||||
if (kv.first == "Accept-Encoding") {
|
|
||||||
for (const auto& encoding : split(kv.second, ',')) {
|
|
||||||
std::vector<std::string> parts = split(encoding, ';');
|
|
||||||
for (size_t i = 0; i < parts.size(); i++) {
|
|
||||||
parts[i] = trim(parts[i]);
|
|
||||||
}
|
|
||||||
if (parts[0] == "*" && ((parts.size() == 1) || parts[1] != "q=0"))
|
|
||||||
accepts = true;
|
|
||||||
if (parts[0] == "gzip") accepts = true;
|
|
||||||
if (parts.size() > 1 && parts[1] == "q=0") accepts = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return accepts;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
Req HttpServer::getReq(int connection) {
|
|
||||||
char buf[BSIZE + 1];
|
|
||||||
size_t rcvd = 0;
|
|
||||||
int64_t curRcvd = 0;
|
|
||||||
HeaderState state = NONE;
|
|
||||||
Req ret;
|
|
||||||
char* tmp = 0;
|
|
||||||
char* tmp2 = 0;
|
|
||||||
char* brk = 0;
|
|
||||||
|
|
||||||
while ((curRcvd = read(connection, buf + rcvd, BSIZE - rcvd))) {
|
|
||||||
if (curRcvd < 0) {
|
|
||||||
if (errno == EAGAIN || errno == EINTR) continue;
|
|
||||||
throw HttpErr("500 Internal Server Error");
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse request
|
|
||||||
for (int i = 0; i < curRcvd; i++) {
|
|
||||||
if (brk) break;
|
|
||||||
char* c = buf + rcvd + i;
|
|
||||||
switch (state) {
|
|
||||||
case NONE:
|
|
||||||
state = I_COM;
|
|
||||||
tmp = c;
|
|
||||||
continue;
|
|
||||||
case I_VER:
|
|
||||||
if (*c == '\n') {
|
|
||||||
*c = 0;
|
|
||||||
ret.ver = trim(tmp);
|
|
||||||
state = A_KEY;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
case I_URL:
|
|
||||||
if (*c == ' ') {
|
|
||||||
*c = 0, ret.url = trim(tmp);
|
|
||||||
tmp = c + 1;
|
|
||||||
state = I_VER;
|
|
||||||
} else if (*c == '\n') {
|
|
||||||
*c = 0, ret.url = trim(tmp);
|
|
||||||
state = A_KEY;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
case I_COM:
|
|
||||||
if (*c == ' ') {
|
|
||||||
*c = 0, ret.cmd = trim(tmp);
|
|
||||||
tmp = c + 1;
|
|
||||||
state = I_URL;
|
|
||||||
} else if (*c == '\n') {
|
|
||||||
*c = 0, ret.cmd = trim(tmp);
|
|
||||||
state = A_KEY;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
case A_KEY:
|
|
||||||
if (*c == '\r') *c = ' ';
|
|
||||||
if (*c == '\n')
|
|
||||||
brk = c + 1;
|
|
||||||
else if (*c != ' ') {
|
|
||||||
state = I_KEY;
|
|
||||||
tmp = c;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
case I_KEY:
|
|
||||||
if (*c == ':') {
|
|
||||||
*c = 0;
|
|
||||||
state = A_VAL;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
case A_VAL:
|
|
||||||
if (*c != ' ') {
|
|
||||||
state = I_VAL;
|
|
||||||
tmp2 = c;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
case I_VAL:
|
|
||||||
if (*c == '\r') *c = ' ';
|
|
||||||
if (*c == '\n') {
|
|
||||||
*c = 0;
|
|
||||||
ret.params[tmp] = trim(tmp2);
|
|
||||||
state = A_KEY;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rcvd += curRcvd;
|
|
||||||
|
|
||||||
// buffer is full
|
|
||||||
if (rcvd == BSIZE) throw HttpErr("431 Request Header Fields Too Large");
|
|
||||||
if (brk) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST payload
|
|
||||||
if (ret.cmd == "POST") {
|
|
||||||
size_t size = 0;
|
|
||||||
if (ret.params.count("Content-Length"))
|
|
||||||
size = atoi(ret.params["Content-Length"].c_str());
|
|
||||||
if (size) {
|
|
||||||
char* postBuf = new char[size + 1];
|
|
||||||
postBuf[size] = 0;
|
|
||||||
size_t rem = 0;
|
|
||||||
|
|
||||||
// copy existing to new buffer
|
|
||||||
if ((int)rcvd > brk - buf) {
|
|
||||||
rem = std::min(size, rcvd - (brk - buf));
|
|
||||||
memcpy(postBuf, brk, rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
rcvd = 0;
|
|
||||||
|
|
||||||
if (rem < size) {
|
|
||||||
while ((curRcvd = read(connection, postBuf + rcvd + rem, size - rem))) {
|
|
||||||
if (curRcvd == -1 && (errno == EAGAIN || errno == EINTR)) continue;
|
|
||||||
if (curRcvd == -1) {
|
|
||||||
postBuf[rcvd + 1] = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
rcvd += curRcvd;
|
|
||||||
if (rcvd == size - rem) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.payload = postBuf;
|
|
||||||
delete[] postBuf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.gzip = gzipSupport(ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
std::string HttpServer::compress(const std::string& str, std::string* enc) {
|
|
||||||
#ifdef ZLIB_FOUND
|
|
||||||
// do not compress small payloads
|
|
||||||
if (str.size() < 500) return str;
|
|
||||||
|
|
||||||
std::string ret;
|
|
||||||
|
|
||||||
// based on http://www.zlib.net/zlib_how.html
|
|
||||||
z_stream defStr;
|
|
||||||
defStr.zalloc = Z_NULL;
|
|
||||||
defStr.zfree = Z_NULL;
|
|
||||||
defStr.opaque = Z_NULL;
|
|
||||||
defStr.avail_in = 0;
|
|
||||||
defStr.next_in = Z_NULL;
|
|
||||||
|
|
||||||
// fail silently with no compression at all
|
|
||||||
if (deflateInit2(&defStr, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8,
|
|
||||||
Z_DEFAULT_STRATEGY) != Z_OK)
|
|
||||||
return str;
|
|
||||||
|
|
||||||
defStr.next_in = reinterpret_cast<z_const Bytef*>(str.c_str());
|
|
||||||
defStr.avail_in = static_cast<unsigned int>(str.size());
|
|
||||||
|
|
||||||
size_t cSize = 0;
|
|
||||||
do {
|
|
||||||
if (ret.size() < (cSize + BSIZE_C)) ret.resize(cSize + BSIZE_C);
|
|
||||||
defStr.avail_out = BSIZE_C;
|
|
||||||
defStr.next_out = reinterpret_cast<Bytef*>(&ret[0] + cSize);
|
|
||||||
deflate(&defStr, Z_FINISH);
|
|
||||||
cSize += BSIZE_C - defStr.avail_out;
|
|
||||||
} while (defStr.avail_out == 0);
|
|
||||||
|
|
||||||
deflateEnd(&defStr);
|
|
||||||
ret.resize(cSize);
|
|
||||||
|
|
||||||
if (ret.size() > str.size()) return str;
|
|
||||||
*enc = "gzip";
|
|
||||||
return ret;
|
|
||||||
#else
|
|
||||||
UNUSED(enc);
|
|
||||||
return str;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void HttpServer::run() {
|
|
||||||
Socket socket(_port);
|
|
||||||
|
|
||||||
std::vector<std::thread> thrds(_threads);
|
|
||||||
for (auto& thr : thrds) thr = std::thread(&HttpServer::handle, this);
|
|
||||||
|
|
||||||
while (1) _jobs.add(socket.wait());
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Queue::add(int c) {
|
|
||||||
if (c < 0) return;
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(_mut);
|
|
||||||
_jobs.push(c);
|
|
||||||
}
|
|
||||||
_hasNew.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
int Queue::get() {
|
|
||||||
std::unique_lock<std::mutex> lock(_mut);
|
|
||||||
while (_jobs.empty()) _hasNew.wait(lock);
|
|
||||||
int next = _jobs.front();
|
|
||||||
_jobs.pop();
|
|
||||||
return next;
|
|
||||||
}
|
|
|
@ -1,141 +0,0 @@
|
||||||
// Copyright 2018, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <map>
|
|
||||||
#include <mutex>
|
|
||||||
#include <queue>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#ifndef UTIL_HTTP_SERVER_H_
|
|
||||||
#define UTIL_HTTP_SERVER_H_
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace http {
|
|
||||||
|
|
||||||
// socket backlog size
|
|
||||||
const static size_t BLOG = 128;
|
|
||||||
// socket read buffer size
|
|
||||||
const static size_t BSIZE = 16 * 1024;
|
|
||||||
// zlib compression buffer size
|
|
||||||
const size_t BSIZE_C = 128 * 1024;
|
|
||||||
|
|
||||||
// states for HTTP header parser
|
|
||||||
enum HeaderState { NONE, I_COM, I_URL, I_VER, A_KEY, I_KEY, A_VAL, I_VAL };
|
|
||||||
|
|
||||||
/*
|
|
||||||
* HTTP Error
|
|
||||||
*/
|
|
||||||
class HttpErr : public std::exception {
|
|
||||||
public:
|
|
||||||
HttpErr(std::string msg) : _msg(msg) {}
|
|
||||||
~HttpErr() throw() {}
|
|
||||||
|
|
||||||
virtual const char* what() const throw() { return _msg.c_str(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string _msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* HTTP Request
|
|
||||||
*/
|
|
||||||
struct Req {
|
|
||||||
std::string cmd, url, ver, payload;
|
|
||||||
std::unordered_map<std::string, std::string> params;
|
|
||||||
bool gzip = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* HTTP Answer
|
|
||||||
*/
|
|
||||||
struct Answer {
|
|
||||||
Answer() : status(""), pl(""), gzip(false) {}
|
|
||||||
Answer(const std::string& status, const std::string& pl)
|
|
||||||
: status(status), pl(pl), gzip(false) {}
|
|
||||||
Answer(const std::string& status, const std::string& pl, bool gz)
|
|
||||||
: status(status), pl(pl), gzip(gz) {}
|
|
||||||
std::string status, pl;
|
|
||||||
bool gzip = false;
|
|
||||||
bool raw = false;
|
|
||||||
std::unordered_map<std::string, std::string> params;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Virtual handler provider class
|
|
||||||
*/
|
|
||||||
class Handler {
|
|
||||||
public:
|
|
||||||
virtual Answer handle(const Req& request, int connection) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Queue of connections to handle
|
|
||||||
*/
|
|
||||||
class Queue {
|
|
||||||
public:
|
|
||||||
void add(int c);
|
|
||||||
int get();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex _mut;
|
|
||||||
std::queue<int> _jobs;
|
|
||||||
std::condition_variable _hasNew;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Socket wrapper
|
|
||||||
*/
|
|
||||||
class Socket {
|
|
||||||
public:
|
|
||||||
Socket(int port);
|
|
||||||
~Socket();
|
|
||||||
int wait();
|
|
||||||
|
|
||||||
private:
|
|
||||||
int _sock;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Simple HTTP server, must provide a pointer to a class instance implementing
|
|
||||||
* virtual class Handler.
|
|
||||||
*/
|
|
||||||
class HttpServer {
|
|
||||||
public:
|
|
||||||
HttpServer(int port, const Handler* h) : HttpServer(port, h, 0) {}
|
|
||||||
HttpServer(int port, const Handler* h, size_t threads)
|
|
||||||
: _port(port), _handler(h), _threads(threads) {
|
|
||||||
if (!_threads) _threads = 8 * std::thread::hardware_concurrency();
|
|
||||||
}
|
|
||||||
void run();
|
|
||||||
|
|
||||||
private:
|
|
||||||
int _port;
|
|
||||||
Queue _jobs;
|
|
||||||
const Handler* _handler;
|
|
||||||
size_t _threads;
|
|
||||||
|
|
||||||
void handle();
|
|
||||||
|
|
||||||
static void send(int sock, Answer* aw);
|
|
||||||
static Req getReq(int connection);
|
|
||||||
static std::string compress(const std::string& str, std::string* enc);
|
|
||||||
static bool gzipSupport(const Req& req);
|
|
||||||
};
|
|
||||||
} // http
|
|
||||||
} // util
|
|
||||||
|
|
||||||
#endif // UTIL_HTTP_SERVER_H_
|
|
|
@ -1,189 +0,0 @@
|
||||||
// Copyright 2018, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#include <iomanip>
|
|
||||||
#include <limits>
|
|
||||||
#include "Writer.h"
|
|
||||||
#include "util/String.h"
|
|
||||||
using namespace util;
|
|
||||||
using namespace json;
|
|
||||||
|
|
||||||
using std::ostream;
|
|
||||||
using std::string;
|
|
||||||
using std::map;
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
Writer::Writer(std::ostream* out)
|
|
||||||
: _out(out), _pretty(false), _indent(2), _floatPrec(10) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
Writer::Writer(std::ostream* out, size_t prec)
|
|
||||||
: _out(out), _pretty(false), _indent(2), _floatPrec(prec) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
Writer::Writer(std::ostream* out, size_t prec, bool pret)
|
|
||||||
: _out(out), _pretty(pret), _indent(2), _floatPrec(prec) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
Writer::Writer(std::ostream* out, size_t prec, bool pret, size_t indent)
|
|
||||||
: _out(out), _pretty(pret), _indent(indent), _floatPrec(prec) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::obj() {
|
|
||||||
if (!_stack.empty() && _stack.top().type == OBJ)
|
|
||||||
throw WriterException("Object not allowed as key");
|
|
||||||
if (!_stack.empty() && _stack.top().type == KEY) _stack.pop();
|
|
||||||
if (!_stack.empty() && _stack.top().type == ARR) valCheck();
|
|
||||||
if (_stack.size() && _stack.top().type == ARR) prettor();
|
|
||||||
*_out << "{";
|
|
||||||
_stack.push({OBJ, 1});
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
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) << ",";
|
|
||||||
_stack.top().empty = 0;
|
|
||||||
prettor();
|
|
||||||
*_out << "\"" << k << "\""
|
|
||||||
<< ":" << (_pretty ? " " : "");
|
|
||||||
_stack.push({KEY, 1});
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::valCheck() {
|
|
||||||
if (_stack.empty() || (_stack.top().type != KEY && _stack.top().type != ARR))
|
|
||||||
throw WriterException("Value not allowed here.");
|
|
||||||
if (!_stack.empty() && _stack.top().type == KEY) _stack.pop();
|
|
||||||
if (!_stack.empty() && _stack.top().type == ARR) {
|
|
||||||
if (!_stack.top().empty) (*_out) << "," << (_pretty ? " " : "");
|
|
||||||
_stack.top().empty = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::val(const std::string& v) {
|
|
||||||
valCheck();
|
|
||||||
*_out << "\"" << util::jsonStringEscape(v) << "\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::val(const char* v) {
|
|
||||||
valCheck();
|
|
||||||
*_out << "\"" << util::jsonStringEscape(v) << "\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::val(bool v) {
|
|
||||||
valCheck();
|
|
||||||
*_out << (v ? "true" : "false");
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::val(int v) {
|
|
||||||
valCheck();
|
|
||||||
*_out << v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::val(uint64_t v) {
|
|
||||||
valCheck();
|
|
||||||
*_out << v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::val(double v) {
|
|
||||||
valCheck();
|
|
||||||
if (v > std::numeric_limits<double>::max())
|
|
||||||
*_out << std::numeric_limits<double>::max();
|
|
||||||
else if (std::isnan(v))
|
|
||||||
*_out << "NaN";
|
|
||||||
else {
|
|
||||||
*_out << std::fixed << std::setprecision(_floatPrec) << v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::val(Null) {
|
|
||||||
valCheck();
|
|
||||||
*_out << "null";
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::val(const Val& v) {
|
|
||||||
switch (v.type) {
|
|
||||||
case Val::JSNULL:
|
|
||||||
val(Null());
|
|
||||||
return;
|
|
||||||
case Val::UINT:
|
|
||||||
val(v.ui);
|
|
||||||
return;
|
|
||||||
case Val::INT:
|
|
||||||
val(v.i);
|
|
||||||
return;
|
|
||||||
case Val::FLOAT:
|
|
||||||
val(v.f);
|
|
||||||
return;
|
|
||||||
case Val::BOOL:
|
|
||||||
val((bool)v.i);
|
|
||||||
return;
|
|
||||||
case Val::STRING:
|
|
||||||
val(v.str);
|
|
||||||
return;
|
|
||||||
case Val::ARRAY:
|
|
||||||
arr();
|
|
||||||
for (const Val& varr : v.arr) val(varr);
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
case Val::DICT:
|
|
||||||
obj();
|
|
||||||
for (const auto& vdic : v.dict) {
|
|
||||||
keyVal(vdic.first, vdic.second);
|
|
||||||
};
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::arr() {
|
|
||||||
if (!_stack.empty() && _stack.top().type == OBJ)
|
|
||||||
throw WriterException("Array not allowed as key");
|
|
||||||
if (!_stack.empty() && _stack.top().type == KEY) _stack.pop();
|
|
||||||
if (!_stack.empty() && _stack.top().type == ARR) valCheck();
|
|
||||||
*_out << "[";
|
|
||||||
_stack.push({ARR, 1});
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::prettor() {
|
|
||||||
if (_pretty) {
|
|
||||||
*_out << "\n";
|
|
||||||
for (size_t i = 0; i < _indent * _stack.size(); i++) (*_out) << " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::closeAll() {
|
|
||||||
while (!_stack.empty()) close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void Writer::close() {
|
|
||||||
if (_stack.empty()) return;
|
|
||||||
switch (_stack.top().type) {
|
|
||||||
case OBJ:
|
|
||||||
_stack.pop();
|
|
||||||
prettor();
|
|
||||||
(*_out) << "}";
|
|
||||||
break;
|
|
||||||
case ARR:
|
|
||||||
_stack.pop();
|
|
||||||
(*_out) << "]";
|
|
||||||
break;
|
|
||||||
case KEY:
|
|
||||||
throw WriterException("Missing value.");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
// Copyright 2018, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_JSON_WRITER_H_
|
|
||||||
#define UTIL_JSON_WRITER_H_
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <ostream>
|
|
||||||
#include <stack>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace json {
|
|
||||||
|
|
||||||
class WriterException : public std::exception {
|
|
||||||
public:
|
|
||||||
WriterException(std::string msg) : _msg(msg) {}
|
|
||||||
~WriterException() throw() {}
|
|
||||||
|
|
||||||
virtual const char* what() const throw() { return _msg.c_str(); };
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string _msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Null {};
|
|
||||||
|
|
||||||
struct Val {
|
|
||||||
enum VAL_T { UINT, INT, FLOAT, STRING, ARRAY, DICT, BOOL, JSNULL };
|
|
||||||
VAL_T type;
|
|
||||||
int i = 0;
|
|
||||||
uint64_t ui = 0;
|
|
||||||
double f = 0;
|
|
||||||
std::string str;
|
|
||||||
std::vector<Val> arr;
|
|
||||||
std::map<std::string, Val> dict;
|
|
||||||
|
|
||||||
Val() { type = DICT; }
|
|
||||||
Val(Null) { type = JSNULL; }
|
|
||||||
Val(const std::vector<Val>& arrC) { arr = arrC, type = ARRAY; }
|
|
||||||
Val(const std::map<std::string, Val>& dC) { dict = dC, type = DICT; }
|
|
||||||
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; }
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef int Int;
|
|
||||||
typedef double Float;
|
|
||||||
typedef bool Bool;
|
|
||||||
typedef std::string String;
|
|
||||||
typedef std::vector<Val> Array;
|
|
||||||
typedef std::map<std::string, Val> Dict;
|
|
||||||
|
|
||||||
// simple JSON writer class without much overhead
|
|
||||||
class Writer {
|
|
||||||
public:
|
|
||||||
explicit Writer(std::ostream* out);
|
|
||||||
Writer(std::ostream* out, size_t prec);
|
|
||||||
Writer(std::ostream* out, size_t prec, bool pretty);
|
|
||||||
Writer(std::ostream* out, size_t prec, bool pretty, size_t indent);
|
|
||||||
~Writer(){};
|
|
||||||
|
|
||||||
void obj();
|
|
||||||
void arr();
|
|
||||||
void key(const std::string& k);
|
|
||||||
void val(const std::string& v);
|
|
||||||
void val(const char* v);
|
|
||||||
void val(double v);
|
|
||||||
void val(int v);
|
|
||||||
void val(uint64_t v);
|
|
||||||
void val(bool v);
|
|
||||||
void val(Null);
|
|
||||||
void val(const Val& v);
|
|
||||||
template <typename V>
|
|
||||||
void keyVal(const std::string& k, const V& v) {
|
|
||||||
key(k);
|
|
||||||
val(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void close();
|
|
||||||
void closeAll();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::ostream* _out;
|
|
||||||
|
|
||||||
enum NODE_T { OBJ, ARR, KEY };
|
|
||||||
|
|
||||||
struct Node {
|
|
||||||
NODE_T type;
|
|
||||||
bool empty;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::stack<Node> _stack;
|
|
||||||
|
|
||||||
bool _pretty;
|
|
||||||
size_t _indent;
|
|
||||||
size_t _floatPrec;
|
|
||||||
|
|
||||||
void valCheck();
|
|
||||||
void prettor();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace json
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_JSON_WRITER_H_
|
|
|
@ -1,58 +0,0 @@
|
||||||
// Copyright 2017, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_LOG_LOG_H_
|
|
||||||
#define UTIL_LOG_LOG_H_
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#define VDEBUG 4
|
|
||||||
#define DEBUG 3
|
|
||||||
#define INFO 2
|
|
||||||
#define WARN 1
|
|
||||||
#define ERROR 0
|
|
||||||
|
|
||||||
#ifndef LOGLEVEL
|
|
||||||
#define LOGLEVEL 2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
using namespace std::chrono;
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
|
|
||||||
static const char* LOGS[] = {"ERROR", "WARN ", "INFO ", "DEBUG", "DEBUG"};
|
|
||||||
|
|
||||||
template <char LVL>
|
|
||||||
class Log {
|
|
||||||
public:
|
|
||||||
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();
|
|
||||||
time_t tt = system_clock::to_time_t(n);
|
|
||||||
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 buf << "[" << tl << "." << setfill('0') << setw(3) << m << "] ";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // UTIL_LOG_LOG_H_
|
|
|
@ -1,6 +0,0 @@
|
||||||
include_directories(
|
|
||||||
${TRANSITMAP_INCLUDE_DIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
add_executable(utilTest TestMain.cpp)
|
|
||||||
target_link_libraries(utilTest util)
|
|
|
@ -1,38 +0,0 @@
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
// 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
|
@ -1,291 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
|
||||||
#include <map>
|
|
||||||
#include <cstring>
|
|
||||||
#include <ostream>
|
|
||||||
#include <stack>
|
|
||||||
#include <string>
|
|
||||||
#ifdef BZLIB_FOUND
|
|
||||||
#include <bzlib.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "XmlWriter.h"
|
|
||||||
|
|
||||||
using namespace util;
|
|
||||||
using namespace xml;
|
|
||||||
|
|
||||||
using std::map;
|
|
||||||
using std::ostream;
|
|
||||||
using std::string;
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
XmlWriter::XmlWriter(std::ostream* out)
|
|
||||||
: _out(out), _pretty(false), _indent(4), _gzfile(0), _bzfile(0) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
XmlWriter::XmlWriter(std::ostream* out, bool pret)
|
|
||||||
: _out(out), _pretty(pret), _indent(4), _gzfile(0), _bzfile(0) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
XmlWriter::XmlWriter(std::ostream* out, bool pret, size_t indent)
|
|
||||||
: _out(out), _pretty(pret), _indent(indent), _gzfile(0), _bzfile(0) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
XmlWriter::XmlWriter(const std::string& file)
|
|
||||||
: XmlWriter::XmlWriter(file, false, 4) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
XmlWriter::XmlWriter(const std::string& file, bool pret)
|
|
||||||
: XmlWriter::XmlWriter(file, pret, 4) {}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
XmlWriter::XmlWriter(const std::string& file, bool pret, size_t indent)
|
|
||||||
: _out(0), _pretty(pret), _indent(indent), _gzfile(0), _bzfile(0) {
|
|
||||||
if (file.size() > 2 && file[file.size() - 1] == 'z' &&
|
|
||||||
file[file.size() - 2] == 'g' && file[file.size() - 3] == '.') {
|
|
||||||
#ifdef ZLIB_FOUND
|
|
||||||
_gzfile = gzopen(file.c_str(), "w");
|
|
||||||
if (_gzfile == Z_NULL) {
|
|
||||||
throw std::runtime_error("Could not open file for writing.");
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
throw std::runtime_error(
|
|
||||||
"Could not open gzip file for writing, was compiled without gzip "
|
|
||||||
"support");
|
|
||||||
#endif
|
|
||||||
} else if (file.size() > 3 && file[file.size() - 1] == '2' &&
|
|
||||||
file[file.size() - 2] == 'z' && file[file.size() - 3] == 'b' &&
|
|
||||||
file[file.size() - 4] == '.') {
|
|
||||||
#ifdef BZLIB_FOUND
|
|
||||||
_bzbuf = new char[BUFFER_S];
|
|
||||||
|
|
||||||
FILE* f = fopen(file.c_str(), "w");
|
|
||||||
int err;
|
|
||||||
if (!f) throw std::runtime_error("Could not open file for writing.");
|
|
||||||
|
|
||||||
_bzfile = BZ2_bzWriteOpen(&err, f, 9, 0, 30);
|
|
||||||
|
|
||||||
if (err != BZ_OK) {
|
|
||||||
throw std::runtime_error("Could not open bzip file for writing.");
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
throw std::runtime_error(
|
|
||||||
"Could not open bzip file for writing, was compiled without bzip "
|
|
||||||
"support");
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
_outs.open(file);
|
|
||||||
if (_outs.fail()) {
|
|
||||||
throw std::runtime_error("Could not open file for writing.");
|
|
||||||
}
|
|
||||||
_out = &_outs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::openTag(const string& tag, const map<string, string>& attrs) {
|
|
||||||
if (!_nstack.empty() && _nstack.top().t == COMMENT) {
|
|
||||||
throw XmlWriterException("Opening tags not allowed while inside comment.");
|
|
||||||
}
|
|
||||||
|
|
||||||
checkTagName(tag);
|
|
||||||
closeHanging();
|
|
||||||
doIndent();
|
|
||||||
|
|
||||||
put("<");
|
|
||||||
put(tag);
|
|
||||||
|
|
||||||
for (auto kv : attrs) {
|
|
||||||
put(" ");
|
|
||||||
putEsced(kv.first, '"');
|
|
||||||
put("=\"");
|
|
||||||
putEsced(kv.second, '"');
|
|
||||||
put("\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
_nstack.push(XmlNode(TAG, tag, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::openTag(const string& tag) {
|
|
||||||
openTag(tag, map<string, string>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::openTag(const string& tag, const string& k, const string& v) {
|
|
||||||
map<string, string> kv;
|
|
||||||
kv[k] = v;
|
|
||||||
openTag(tag, kv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::openComment() {
|
|
||||||
// don't allow nested comments
|
|
||||||
if (!_nstack.empty() && _nstack.top().t == COMMENT) return;
|
|
||||||
|
|
||||||
closeHanging();
|
|
||||||
doIndent();
|
|
||||||
|
|
||||||
put("<!-- ");
|
|
||||||
|
|
||||||
_nstack.push(XmlNode(COMMENT, "", false));
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::writeText(const string& text) {
|
|
||||||
if (_nstack.empty()) {
|
|
||||||
throw XmlWriterException("Text content not allowed in prolog / trailing.");
|
|
||||||
}
|
|
||||||
closeHanging();
|
|
||||||
doIndent();
|
|
||||||
putEsced(text, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::closeTag() {
|
|
||||||
while (!_nstack.empty() && _nstack.top().t == TEXT) _nstack.pop();
|
|
||||||
|
|
||||||
if (_nstack.empty()) return;
|
|
||||||
|
|
||||||
if (_nstack.top().t == COMMENT) {
|
|
||||||
_nstack.pop();
|
|
||||||
doIndent();
|
|
||||||
put(" -->");
|
|
||||||
} else if (_nstack.top().t == TAG) {
|
|
||||||
if (_nstack.top().hanging) {
|
|
||||||
put(" />");
|
|
||||||
_nstack.pop();
|
|
||||||
} else {
|
|
||||||
string tag = _nstack.top().pload;
|
|
||||||
_nstack.pop();
|
|
||||||
doIndent();
|
|
||||||
put("</");
|
|
||||||
put(tag);
|
|
||||||
put(">");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::closeTags() {
|
|
||||||
while (!_nstack.empty()) closeTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::doIndent() {
|
|
||||||
if (_pretty) {
|
|
||||||
put("\n");
|
|
||||||
for (size_t i = 0; i < _nstack.size() * _indent; i++) put(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::closeHanging() {
|
|
||||||
if (_nstack.empty()) return;
|
|
||||||
|
|
||||||
if (_nstack.top().hanging) {
|
|
||||||
put(">");
|
|
||||||
_nstack.top().hanging = false;
|
|
||||||
} else if (_nstack.top().t == TEXT) {
|
|
||||||
_nstack.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::put(const string& str) {
|
|
||||||
if (_gzfile) {
|
|
||||||
#ifdef ZLIB_FOUND
|
|
||||||
gzwrite(_gzfile, str.c_str(), str.size());
|
|
||||||
#endif
|
|
||||||
} else if (_bzfile) {
|
|
||||||
#ifdef BZLIB_FOUND
|
|
||||||
if (_bzbufpos == BUFFER_S || _bzbufpos + str.size() > BUFFER_S) flushBzip();
|
|
||||||
memcpy( _bzbuf + _bzbufpos, str.c_str(), str.size());
|
|
||||||
_bzbufpos += str.size();
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
_out->write(str.c_str(), str.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::flushBzip() {
|
|
||||||
#ifdef BZLIB_FOUND
|
|
||||||
int err = 0;
|
|
||||||
BZ2_bzWrite(&err, _bzfile, _bzbuf, _bzbufpos);
|
|
||||||
if (err == BZ_IO_ERROR) {
|
|
||||||
BZ2_bzWriteClose(&err, _bzfile, 0, 0, 0);
|
|
||||||
throw std::runtime_error("Could not write to file.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_bzbufpos = 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::put(const char c) {
|
|
||||||
if (_gzfile) {
|
|
||||||
#ifdef ZLIB_FOUND
|
|
||||||
gzputc(_gzfile, c);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
} else if (_bzfile) {
|
|
||||||
#ifdef BZLIB_FOUND
|
|
||||||
_bzbuf[_bzbufpos++] = c;
|
|
||||||
if (_bzbufpos == BUFFER_S) flushBzip();
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
_out->put(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::putEsced(const string& str, char quot) {
|
|
||||||
if (!_nstack.empty() && _nstack.top().t == COMMENT) {
|
|
||||||
put(str);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const char& c : str) {
|
|
||||||
if (quot == '"' && c == '"')
|
|
||||||
put(""");
|
|
||||||
else if (quot == '\'' && c == '\'')
|
|
||||||
put("'");
|
|
||||||
else if (c == '<')
|
|
||||||
put("<");
|
|
||||||
else if (c == '>')
|
|
||||||
put(">");
|
|
||||||
else if (c == '&')
|
|
||||||
put("&");
|
|
||||||
else
|
|
||||||
put(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____________________________________________________________________________
|
|
||||||
void XmlWriter::checkTagName(const string& str) const {
|
|
||||||
if (!isalpha(str[0]) && str[0] != '_')
|
|
||||||
throw XmlWriterException(
|
|
||||||
"XML elements must start with either a letter "
|
|
||||||
"or an underscore");
|
|
||||||
|
|
||||||
string begin = str.substr(0, 3);
|
|
||||||
std::transform(begin.begin(), begin.end(), begin.begin(), ::tolower);
|
|
||||||
if (begin == "xml")
|
|
||||||
throw XmlWriterException(
|
|
||||||
"XML elements cannot start with"
|
|
||||||
" XML, xml, Xml etc.");
|
|
||||||
|
|
||||||
for (const char& c : str) {
|
|
||||||
// we allow colons in tag names for primitive namespace support
|
|
||||||
if (!isalpha(c) && !isdigit(c) && c != '-' && c != '_' && c != '.' &&
|
|
||||||
c != ':')
|
|
||||||
throw XmlWriterException(
|
|
||||||
"XML elements can only contain letters, "
|
|
||||||
"digits, hyphens, underscores and periods.");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,135 +0,0 @@
|
||||||
// Copyright 2016, University of Freiburg,
|
|
||||||
// Chair of Algorithms and Data Structures.
|
|
||||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
|
||||||
|
|
||||||
#ifndef UTIL_XML_XMLWRITER_H_
|
|
||||||
#define UTIL_XML_XMLWRITER_H_
|
|
||||||
|
|
||||||
#ifdef ZLIB_FOUND
|
|
||||||
#include <zlib.h>
|
|
||||||
#endif
|
|
||||||
#ifdef BZLIB_FOUND
|
|
||||||
#include <bzlib.h>
|
|
||||||
#endif
|
|
||||||
#include <map>
|
|
||||||
#include <ostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <stack>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
namespace xml {
|
|
||||||
|
|
||||||
static const size_t BUFFER_S = 32 * 1024 * 1024;
|
|
||||||
|
|
||||||
class XmlWriterException : public std::exception {
|
|
||||||
public:
|
|
||||||
XmlWriterException(std::string msg) : _msg(msg) {}
|
|
||||||
~XmlWriterException() throw() {}
|
|
||||||
|
|
||||||
virtual const char* what() const throw() { return _msg.c_str(); };
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string _msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
// simple XML writer class without much overhead
|
|
||||||
class XmlWriter {
|
|
||||||
public:
|
|
||||||
explicit XmlWriter(std::ostream* out);
|
|
||||||
XmlWriter(std::ostream* out, bool pretty);
|
|
||||||
XmlWriter(std::ostream* out, bool pretty, size_t indent);
|
|
||||||
|
|
||||||
explicit XmlWriter(const std::string& file);
|
|
||||||
XmlWriter(const std::string& file, bool pretty);
|
|
||||||
XmlWriter(const std::string& file, bool pretty, size_t indent);
|
|
||||||
~XmlWriter(){
|
|
||||||
#ifdef ZLIB_FOUND
|
|
||||||
if (_gzfile) gzclose(_gzfile);
|
|
||||||
#endif
|
|
||||||
#ifdef BZLIB_FOUND
|
|
||||||
int err;
|
|
||||||
if (_bzfile) {
|
|
||||||
flushBzip();
|
|
||||||
BZ2_bzWriteClose(&err, _bzfile, 0, 0, 0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
// open tag without attributes
|
|
||||||
void openTag(const std::string& tag);
|
|
||||||
|
|
||||||
// open tag with single attribute (for convenience...)
|
|
||||||
void openTag(const std::string& tag, const std::string& key,
|
|
||||||
const std::string& val);
|
|
||||||
|
|
||||||
// open tag with attribute list
|
|
||||||
void openTag(const std::string& tag,
|
|
||||||
const std::map<std::string, std::string>& attrs);
|
|
||||||
|
|
||||||
// open comment
|
|
||||||
void openComment();
|
|
||||||
|
|
||||||
// write text
|
|
||||||
void writeText(const std::string& text);
|
|
||||||
|
|
||||||
// close tag
|
|
||||||
void closeTag();
|
|
||||||
|
|
||||||
// close all open tags, essentially closing the document
|
|
||||||
void closeTags();
|
|
||||||
|
|
||||||
// pushes XML escaped text to stream
|
|
||||||
void putEsced(const std::string& str, char quot);
|
|
||||||
void put(const std::string& str);
|
|
||||||
void put(const char c);
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum XML_NODE_T { TAG, TEXT, COMMENT };
|
|
||||||
|
|
||||||
struct XmlNode {
|
|
||||||
XmlNode(XML_NODE_T t, const std::string& pload, bool hanging)
|
|
||||||
: t(t), pload(pload), hanging(hanging) {}
|
|
||||||
XML_NODE_T t;
|
|
||||||
std::string pload;
|
|
||||||
bool hanging;
|
|
||||||
};
|
|
||||||
|
|
||||||
void flushBzip();
|
|
||||||
|
|
||||||
std::ostream* _out;
|
|
||||||
std::ofstream _outs;
|
|
||||||
std::stack<XmlNode> _nstack;
|
|
||||||
|
|
||||||
bool _pretty;
|
|
||||||
size_t _indent;
|
|
||||||
|
|
||||||
#ifdef ZLIB_FOUND
|
|
||||||
gzFile _gzfile;
|
|
||||||
#else
|
|
||||||
int _gzfile;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char* _bzbuf;
|
|
||||||
size_t _bzbufpos = 0;
|
|
||||||
#ifdef BZLIB_FOUND
|
|
||||||
BZFILE* _bzfile;
|
|
||||||
#else
|
|
||||||
int _bzfile;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// handles indentation
|
|
||||||
void doIndent();
|
|
||||||
|
|
||||||
// close "hanging" tags
|
|
||||||
void closeHanging();
|
|
||||||
|
|
||||||
|
|
||||||
// checks tag names for validiy
|
|
||||||
void checkTagName(const std::string& str) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace xml
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
#endif // UTIL_XML_XMLWRITER_H_
|
|
Loading…
Reference in a new issue