1 /**
2 Copyright: Copyright (c) 2020, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 */
6 module my.set;
7 
8 import std.algorithm : filter;
9 import std.range : ElementType, isOutputRange;
10 
11 struct Set(T) {
12     alias Type = void[0][T];
13     Type data;
14 
15     bool opBinaryRight(string op)(T key) if (op == "in") {
16         return (key in data) !is null;
17     }
18 
19     bool empty() @safe pure nothrow const @nogc {
20         return data.length == 0;
21     }
22 
23     size_t length() @safe pure nothrow const @nogc {
24         return data.length;
25     }
26 
27     void add(T value) @safe pure nothrow {
28         data[value] = (void[0]).init;
29     }
30 
31     void add(Set!T set) @safe pure nothrow {
32         add(set.data);
33     }
34 
35     void add(Type set) @safe pure nothrow {
36         foreach (key; set.byKey)
37             data[key] = (void[0]).init;
38     }
39 
40     void add(Range)(Range r) @safe pure nothrow if (is(ElementType!Range == T)) {
41         foreach (v; r)
42             data[v] = (void[0]).init;
43     }
44 
45     void remove(T value) {
46         data.remove(value);
47     }
48 
49     Set!T clone() @safe pure nothrow {
50         Set!T result;
51         result.add(data);
52         return result;
53     }
54 
55     bool contains(T value) {
56         return (value in data) !is null;
57     }
58 
59     /** The set difference according to Set Theory.
60      *
61      * It is the set of all members in self that are not members of set.
62      */
63     Set!T setDifference(Set!T set) {
64         typeof(this) r;
65         foreach (k; toRange.filter!(a => !set.contains(a)))
66             r.add(k);
67 
68         return r;
69     }
70 
71     /** The symmetric difference according to Set Theory.
72      *
73      * It is the set of all objects that are a member of exactly one of self and set.
74      */
75     Set!T symmetricDifference(Set!T set) {
76         typeof(this) r;
77         foreach (k; toRange.filter!(a => !set.contains(a)))
78             r.add(k);
79         foreach (k; set.toRange.filter!(a => !contains(a)))
80             r.add(k);
81 
82         return r;
83     }
84 
85     /** The intersection according to Set Theory.
86      *
87      * It is the set of all objects that are members of both self and set.
88      */
89     Set!T intersect(Set!T set) {
90 
91         typeof(this) r;
92         foreach (k; toRange.filter!(a => set.contains(a)))
93             r.add(k);
94 
95         return r;
96     }
97 
98     auto toArray() inout {
99         import std.array : appender;
100 
101         auto app = appender!(T[])();
102         foreach (key; toRange)
103             app.put(key);
104         return app.data;
105     }
106 
107     auto toRange() inout {
108         return data.byKey;
109     }
110 
111     string toString() {
112         import std.array : appender;
113 
114         auto buf = appender!string;
115         toString(buf);
116         return buf.data;
117     }
118 
119     void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, char)) {
120         import std.format : formattedWrite;
121 
122         formattedWrite(w, "Set!(%s)(%-(%s, %))", T.stringof, toRange);
123     }
124 }
125 
126 auto toSet(RangeT)(RangeT range) {
127     import std.traits : Unqual;
128 
129     alias T = ElementType!RangeT;
130 
131     Set!(Unqual!T) result;
132     foreach (item; range)
133         result.add(item);
134     return result;
135 }
136 
137 @("shall calculate the symmetric difference between two sets")
138 unittest {
139     import std.algorithm : sort;
140     import std.array : array;
141 
142     auto a = [1, 2, 3, 4, 5].toSet;
143     auto b = [4, 5, 6, 7, 8].toSet;
144 
145     assert(a.symmetricDifference(b).toArray.sort.array == [1, 2, 3, 6, 7, 8]);
146 }