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         typeof(this) r;
91         foreach (k; toRange.filter!(a => set.contains(a)))
92             r.add(k);
93 
94         return r;
95     }
96 
97     auto toArray() inout {
98         import std.array : appender;
99 
100         auto app = appender!(T[])();
101         foreach (key; toRange)
102             app.put(key);
103         return app.data;
104     }
105 
106     auto toRange() inout {
107         return data.byKey;
108     }
109 
110     string toString() {
111         import std.array : appender;
112 
113         auto buf = appender!string;
114         toString(buf);
115         return buf.data;
116     }
117 
118     void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, char)) {
119         import std.format : formattedWrite;
120 
121         formattedWrite(w, "Set!(%s)(%-(%s, %))", T.stringof, toRange);
122     }
123 }
124 
125 auto toSet(RangeT)(RangeT range) {
126     import std.traits : Unqual;
127 
128     alias T = ElementType!RangeT;
129 
130     Set!(Unqual!T) result;
131     foreach (item; range)
132         result.add(item);
133     return result;
134 }
135 
136 @("shall calculate the symmetric difference between two sets")
137 unittest {
138     import std.algorithm : sort;
139     import std.array : array;
140 
141     auto a = [1, 2, 3, 4, 5].toSet;
142     auto b = [4, 5, 6, 7, 8].toSet;
143 
144     assert(a.symmetricDifference(b).toArray.sort.array == [1, 2, 3, 6, 7, 8]);
145 }