1 /**
2 * This module defines the notion of a $(NEIGHBOURHOOD). A $(NEIGHBOURHOOD)
3 * defines what cells in the lattice that are "neighbours". Most ca $(RULE)s
4 * change the state of a $(I cell) depending on the $(I cells) $(I neighbours).
5 *
6 * It provides templates for testing whether a given object is a $(NEIGHBOURHOOD),
7 * and what kind of $(NEIGHBOURHOOD) it is.
8 *
9 * $(CALIB_ABSTRACT_DESC)
10 *
11 * Macros:
12 * IS_ANY =
13 * Tests if something is a $(B $0).
14 * $(DDOC_BLANKLINE)
15 * returns $(DDOC_BACKQUOTED true) if $(DDOC_BACKQUOTED T) is a $(B $0). of
16 * any dimension.
17 * $(DDOC_BLANKLINE)
18 * This template is the same as $(B is$0) but the dimension need not be
19 * specified. Refer to $(B is$0) for more details about what a $(B $0) is.
20 * $(DDOC_BLANKLINE)
21 * $(DDOC_PARAMS T type to be tested)
22 * $(DDOC_RETURNS $(B true) if $(B T) is any $(B $0), $(B false) if not)
23 */
24
25 module caLib_abstract.neighbourhood;
26
27 import std.meta : Repeat;
28 import caLib_abstract.util : hasDimension, isDimensionsCompatible;
29
30
31
32 /**
33 * Tests if something is a $(B Neighbourhood).
34 *
35 * returns `true` if `T` is a $(B Neighbourhood) of dimension `N`.
36 *
37 * A $(B Neighbourhood) is the most basic form of a $(NEIGHBOURHOOD).
38 * It's `getNeighboursCoordinates` function takes a $(I cells) position
39 * and returns a list of coordinate pairs for it's neighbours positions.
40 *
41 * It must define the primitive: $(BR)`int[N][] getNeighboursCoordinates(Coord)`. $(BR)
42 * Where N is the dimension of the $(B Neighbourhood) and `Coord` is an
43 * alias for a typetuple containing `int`'s, one `int` for each
44 * dimension (in a 3 dimensional $(B Neighbourhood) `Coord` would be: `(int, int, int)`).
45 *
46 * Params:
47 * T = type to be tested
48 * N = number of dimensions
49 *
50 * Returns: true if T is a $(B Neighbourhood), false if not
51 */
52
53 template isNeighbourhood(T, uint N)
54 {
55 alias Coord = Repeat!(N, int);
56
57 static if(hasDimension!T)
58 {
59 enum isNeighbourhood =
60 isDimensionsCompatible!(T.Dimension, N) &&
61 is(typeof(T.init.getNeighboursCoordinates(Coord.init)) : int[N][]);
62 }
63 else
64 {
65 enum isNeighbourhood = false;
66 }
67 }
68
69 ///
70 unittest
71 {
72 struct A {
73 enum uint Dimension = 2;
74 int[2][] getNeighboursCoordinates(int x, int y) {
75 return [[x+1, y+1], [x+1, y], [x+1, y-1]];
76 }
77 }
78
79 static assert( isNeighbourhood!(A, 2));
80 static assert(!isNeighbourhood!(string, 1));
81 }
82
83 unittest
84 {
85 static assert( isNeighbourhood!(Neighbourhood!(1), 1));
86 static assert( isNeighbourhood!(Neighbourhood!(2), 2));
87 static assert( isNeighbourhood!(Neighbourhood!(3), 3));
88 static assert(!isNeighbourhood!(Neighbourhood!(3), 2));
89 }
90
91
92
93 ///$(IS_ANY Neighbourhood)
94 template isAnyNeighbourhood(T)
95 {
96 static if(hasDimension!T)
97 enum isAnyNeighbourhood = isNeighbourhood!(T, T.Dimension);
98 else
99 enum isAnyNeighbourhood = false;
100 }
101
102 ///
103 unittest
104 {
105 struct Foo {
106 enum uint Dimension = 2;
107
108 int[Dimension][] getNeighboursCoordinates(int x, int y) {
109 return [[x+1, y+1], [x+1, y], [x+1, y-1]];
110 }
111 }
112
113 static assert(! isNeighbourhood!(Foo, 3));
114 static assert( isAnyNeighbourhood!(Foo ));
115 }
116
117 unittest
118 {
119 static assert( isAnyNeighbourhood!(Neighbourhood!(1)));
120 static assert( isAnyNeighbourhood!(StaticNeighbourhood!(2)));
121 static assert(!isAnyNeighbourhood!string);
122 }
123
124
125
126 /// Example of a $(B Neighbourhood)
127 struct Neighbourhood(uint N)
128 {
129 enum uint Dimension = N;
130
131 alias Coord = Repeat!(N, int);
132
133 int[N][] getNeighboursCoordinates(Coord) { int[N][]a; return a.init; }
134 }
135
136 ///
137 unittest
138 {
139 static assert(isNeighbourhood!(Neighbourhood!(2), 2));
140 static assert(isAnyNeighbourhood!(Neighbourhood!(4)));
141 }
142
143
144
145 /**
146 * Tests if something is a $(B StaticNeighbourhood).
147 *
148 * returns `true` if `T` is a $(B StaticNeighbourhood) of dimension `N`.
149 *
150 * A $(B StaticNeighbourhood) is a $(NEIGHBOURHOOD) that never changes.
151 * If a particular $(I cell) has a particular $(I neighbour) it will always have
152 * that $(I neighbour). Also, if a $(I cell) where to have a neighbour "to the right"
153 * all other $(I cells) will also have a neighbour "to the right".
154 * A $(B StaticNeighbourhood) dosen't really add any additional functionality.
155 * All it does is having the a uint enum NeighboursAmount.
156 *
157 * Params:
158 * T = type to be tested
159 * N = number of dimensions
160 *
161 * Returns: true if T is a $(B StaticNeighbourhood), false if not
162 */
163 template isStaticNeighbourhood(T, uint N)
164 {
165 alias Coord = Repeat!(N, int);
166
167 enum isStaticNeighbourhood =
168 isNeighbourhood!(T, N) &&
169 is(typeof(T.NeighboursAmount) : uint) &&
170 is(typeof(T.init.NeighboursAmount) : uint);
171 }
172
173 ///
174 unittest
175 {
176 struct A {
177 enum uint Dimension = 2;
178 enum uint NeighboursAmount = 1;
179 enum isStatic;
180 int[2][] getNeighboursCoordinates(int x, int y) {
181 return [[x+1, y+1]];
182 }
183 }
184
185 static assert( isNeighbourhood!(A, 2));
186 static assert( isStaticNeighbourhood!(A, 2));
187 static assert(!isStaticNeighbourhood!(string, 1));
188 }
189
190 unittest
191 {
192 static assert( isStaticNeighbourhood!(StaticNeighbourhood!(1), 1));
193 static assert( isStaticNeighbourhood!(StaticNeighbourhood!(2), 2));
194 static assert( isStaticNeighbourhood!(StaticNeighbourhood!(3), 3));
195
196 static assert(!isStaticNeighbourhood!(StaticNeighbourhood!(3), 2));
197 static assert(!isStaticNeighbourhood!(Neighbourhood!(1), 1));
198 }
199
200
201
202 ///$(IS_ANY StaticNeighbourhood)
203 template isAnyStaticNeighbourhood(T)
204 {
205 static if(hasDimension!T)
206 enum isAnyStaticNeighbourhood = isStaticNeighbourhood!(T, T.Dimension);
207 else
208 enum isAnyStaticNeighbourhood = false;
209 }
210
211 ///
212 unittest
213 {
214 struct Foo {
215 enum uint Dimension = 2;
216 enum uint NeighboursAmount = 1;
217 int[2][] getNeighboursCoordinates(int x, int y) {
218 return [[x+1, y+1]];
219 }
220 }
221 static assert(! isStaticNeighbourhood!(Foo, 3));
222 static assert( isAnyStaticNeighbourhood!(Foo ));
223 }
224
225 unittest
226 {
227 static assert(!isAnyStaticNeighbourhood!(Neighbourhood!(1)));
228 static assert( isAnyStaticNeighbourhood!(StaticNeighbourhood!(2)));
229 static assert(!isAnyStaticNeighbourhood!string);
230 }
231
232
233
234 /// Example of a $(B StaticNeighbourhood)
235 struct StaticNeighbourhood(uint N)
236 {
237 Neighbourhood!N neighbourhood;
238 alias neighbourhood this;
239
240 enum uint NeighboursAmount = 0;
241 }
242
243 ///
244 unittest
245 {
246 static assert(isNeighbourhood!(StaticNeighbourhood!(2), 2));
247 static assert(isStaticNeighbourhood!(StaticNeighbourhood!(2), 2));
248 static assert(isAnyStaticNeighbourhood!(StaticNeighbourhood!(2)));
249 }