1 /**
2 Copyright: Copyright (c) 2016-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.container.multihashset;
7 
8 import voxelman.log;
9 import voxelman.container.hash.map;
10 
11 /// Stores number of entries for each key
12 struct MultiHashSet(Key, Key emptyKey, Key deletedKey)
13 {
14 	mixin MultiHashSetImpl!(Key, HashMap!(Key, size_t, emptyKey, deletedKey));
15 }
16 
17 /// ditto
18 struct MultiHashSet(Key)
19 {
20 	mixin MultiHashSetImpl!(Key, HashMap!(Key, size_t));
21 }
22 
23 private mixin template MultiHashSetImpl(Key, HashMapT) {
24 	HashMapT keys;
25 
26 	size_t get(Key key) {
27 		return keys.get(key, 0);
28 	}
29 
30 	alias opIndex = get;
31 
32 	// returns true if key was first time added after this call
33 	bool add(Key key, size_t addedEntries = 1) {
34 		size_t* numEntries = keys.getOrCreate(key, 0);
35 		(*numEntries) += addedEntries;
36 		return (*numEntries) == addedEntries; // new observer
37 	}
38 
39 	// returns true if key was removed after this call
40 	bool remove(Key key, size_t removedEntries = 1) {
41 		size_t* numEntries = key in keys;
42 		if (numEntries) {
43 			size_t numToRemove = (*numEntries) >= removedEntries ? removedEntries : (*numEntries);
44 			(*numEntries) -= numToRemove;
45 			if ((*numEntries) == 0) {
46 				keys.remove(key);
47 				return true;
48 			}
49 		}
50 		return false;
51 	}
52 
53 	// iterate over all keys
54 	int opApply(scope int delegate(in Key) del) {
55 		foreach (Key key; keys.byKey())
56 			if (auto ret = del(key))
57 				return ret;
58 		return 0;
59 	}
60 
61 	int opApply(scope int delegate(in Key, size_t) del) {
62 		foreach (key, numEntries; keys)
63 			if (auto ret = del(key, numEntries))
64 				return ret;
65 		return 0;
66 	}
67 }
68 
69 unittest {
70 	import std.stdio;
71 	MultiHashSet!int multiset;
72 
73 	enum iter = 64;
74 	foreach (i; 0..iter) {
75 		foreach (j; 0..iter) {
76 			multiset.add(j);
77 			assert(multiset[j] == i+1);
78 		}
79 	}
80 
81 	foreach (i; 0..iter) {
82 		foreach (j; 0..iter) {
83 			multiset.remove(j);
84 			assert(multiset[j] == iter-i-1);
85 		}
86 	}
87 
88 	foreach (i; 0..iter) {
89 		foreach (j; 0..iter) {
90 			multiset.add(j);
91 			assert(multiset[j] == i+1);
92 		}
93 	}
94 }