1 /**
2     Sets
3 
4     This module implements @nogc BTree-backed sets, replacement for std::set.
5 
6     Copyright:
7         Copyright © 2023-2025, Kitsunebi Games
8         Copyright © 2023-2025, Inochi2D Project
9     
10     Copyright: Guillaume Piolat 2015-2024.
11     Copyright: Copyright (C) 2008- by Steven Schveighoffer. Other code
12     Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
13     License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
14     Authors:   
15         Steven Schveighoffer,
16         $(HTTP erdani.com, Andrei Alexandrescu), 
17         Guillaume Piolat,
18         Luna Nielsen
19 */
20 module nulib.collections.set;
21 import nulib.collections.internal.btree;
22 import numem;
23 
24 @nogc:
25 
26 /**
27     An associative container which contains sets of objects of type $(D Key).
28 
29     Notes:
30         Weak types do not own the memory of their contents.
31         It is up to you to free any indicies.
32 */
33 alias weak_set(Key) = SetImpl!(Key, (a, b) => a < b, false, false);
34 
35 /**
36     An associative container which contains sets of objects of type $(D Key).
37 */
38 alias set(Key) = SetImpl!(Key, (a, b) => a < b, false, true);
39 
40 /**
41     An associative container which contains sets of objects of type $(D Key).
42 
43     A multiset may contain multiple entries with the same $(D Key) value.
44 
45     Notes:
46         Weak types do not own the memory of their contents.
47         It is up to you to free any indicies.
48 */
49 alias weak_multiset(Key) = SetImpl!(Key, (a, b) => a < b, true, true);
50 
51 /**
52     An associative container which contains sets of objects of type $(D Key).
53 
54     A multiset may contain multiple entries with the same $(D Key) value.
55 */
56 alias multiset(Key) = SetImpl!(Key, (a, b) => a < b, true, true);
57 
58 /**
59     Set, designed to replace std::set usage.
60     O(log(n)) insertion, removal, and search time.
61     `Set` is designed to operate even without initialization through `makeSet`.
62 */
63 struct SetImpl(K, alias less = (a, b) => a < b, bool allowDuplicates = false, bool ownsMemory = false) {
64 public:
65 @nogc:
66 
67     @trusted
68     this(int dummy) {
69     }
70 
71     @disable this(this);
72 
73     @trusted
74     ~this() {
75     }
76 
77     /// Insert an element in the container. 
78     /// If allowDuplicates is false, this can fail and return `false` 
79     /// if the already contains an element with equivalent key. 
80     /// Returns: `true` if the insertion took place.
81     @trusted
82     bool insert(K key) {
83         ubyte whatever = 0;
84         return _tree.insert(key, whatever);
85     }
86 
87     /// Removes an element from the container.
88     /// Returns: `true` if the removal took place.
89     @trusted
90     bool remove(K key) {
91 
92         // Delete memory if this map owns it.
93         static if (ownsMemory) {
94             if (key in _tree) {
95                 nogc_delete(_tree[key]);
96             }
97         }
98 
99         return _tree.remove(key) != 0;
100     }
101 
102     /// Removes all elements from the set.
103     @trusted
104     void clearContents() {
105         nogc_delete(_tree);
106         // _tree reset to .init, still valid
107     }
108 
109     /// Returns: `true` if the element is present.
110     @trusted
111     bool opBinaryRight(string op)(K key) inout if (op == "in") {
112         return (key in _tree) !is null;
113     }
114 
115     /// Returns: `true` if the element is present.
116     @trusted
117     bool opIndex(K key) const {
118         return (key in _tree) !is null;
119     }
120 
121     /// Returns: `true` if the element is present.
122     @trusted
123     bool contains(K key) const {
124         return (key in _tree) !is null;
125     }
126 
127     /// Returns: Number of elements in the set.
128     @trusted
129     size_t length() const {
130         return _tree.length();
131     }
132 
133     /// Returns: `ttue` is the set has no element.
134     @trusted
135     bool empty() const {
136         return _tree.empty();
137     }
138 
139     // Iterate by value only
140 
141     /// Fetch a forward range on all keys.
142     @trusted
143     auto byKey() {
144         return _tree.byKey();
145     }
146 
147     /// ditto
148     @trusted
149     auto byKey() const {
150         return _tree.byKey();
151     }
152 
153     // default opSlice is like byKey for sets, since the value is a fake value.
154     alias opSlice = byKey;
155 
156 private:
157 
158     // dummy type
159     alias V = ubyte;
160 
161     alias InternalTree = BTree!(K, V, less, allowDuplicates, false);
162     InternalTree _tree;
163 
164 }
165 
166 @("set: instantiation")
167 unittest {
168     // It should be possible to use most function of an uninitialized Set
169     // All except functions returning a range will work.
170     set!(string) set;
171 
172     assert(set.length == 0);
173     assert(set.empty);
174     set.clearContents();
175     assert(!set.contains("toto"));
176 
177     auto range = set[];
178     assert(range.empty);
179     foreach (e; range) {
180     }
181 
182     // Finally create the internal state
183     set.insert("titi");
184     assert(set.contains("titi"));
185 }
186 
187 @("set: insertion, deletion and testing")
188 unittest {
189     set!(string) keywords;
190 
191     assert(keywords.insert("public"));
192     assert(keywords.insert("private"));
193     assert(!keywords.insert("private"));
194 
195     assert(keywords.remove("public"));
196     assert(!keywords.remove("non-existent"));
197 
198     assert(keywords.contains("private"));
199     assert(!keywords.contains("public"));
200 }