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 
89 	static assert(!isNeighbourhood!(Neighbourhood!(3), 2));
90 }
91 
92 
93 
94 ///$(IS_ANY Neighbourhood)
95 template isAnyNeighbourhood(T)
96 {
97 	static if(hasDimension!T)
98 		enum isAnyNeighbourhood = isNeighbourhood!(T, T.Dimension);
99 	else
100 		enum isAnyNeighbourhood = false;
101 }
102 
103 ///
104 unittest
105 {
106 	struct Foo {
107 		enum uint Dimension = 2;
108 
109 		int[Dimension][] getNeighboursCoordinates(int x, int y) {
110 			return [[x+1, y+1], [x+1, y], [x+1, y-1]];
111 		}
112 	}
113 
114 	static assert(!   isNeighbourhood!(Foo, 3));
115 	static assert( isAnyNeighbourhood!(Foo   ));
116 }
117 
118 unittest
119 {
120 	static assert( isAnyNeighbourhood!(Neighbourhood!(1)));
121     static assert( isAnyNeighbourhood!(StaticNeighbourhood!(2)));
122     static assert( isAnyNeighbourhood!(ShiftingNeighbourhood!(3)));
123     static assert(!isAnyNeighbourhood!string);
124 }
125 
126 
127 
128 /**
129 * Tests if something is a $(B StaticNeighbourhood).
130 *
131 * returns `true` if `T` is a $(B StaticNeighbourhood) of dimension `N`.
132 *
133 * A $(B StaticNeighbourhood) is a $(NEIGHBOURHOOD) that never changes.
134 * If a particular $(I cell) has a particular $(I neighbour) it will always have
135 * that $(I neighbour). Also, if a $(I cell) where to have a neighbour "to the right"
136 * all other $(I cells) will also have a neighbour "to the right".
137 * A $(B StaticNeighbourhood) dosen't really add any additional functionality.
138 * All it does is having the a uint enum NeighboursAmount.
139 *
140 * Params:
141 *     T  = type to be tested
142 *     N  = number of dimensions
143 *
144 * Returns: true if T is a $(B StaticNeighbourhood), false if not
145 */
146 template isStaticNeighbourhood(T, uint N)
147 {
148 	alias Coord = Repeat!(N, int);
149 
150     enum isStaticNeighbourhood =
151         isNeighbourhood!(T, N) &&
152         is(typeof(T.NeighboursAmount) : uint) &&
153         is(typeof(T.init.NeighboursAmount) : uint);
154 }
155 
156 ///
157 unittest
158 {
159 	struct A {
160 		enum uint Dimension = 2;
161 		enum uint NeighboursAmount = 1;
162 		enum isStatic;
163 		int[2][] getNeighboursCoordinates(int x, int y) {
164 			return [[x+1, y+1]];
165 		}
166 	}
167 
168 	static assert( isNeighbourhood!(A, 2));
169 	static assert( isStaticNeighbourhood!(A, 2));
170 	static assert(!isStaticNeighbourhood!(string, 1));
171 }
172 
173 unittest
174 {
175 	static assert( isStaticNeighbourhood!(StaticNeighbourhood!(1), 1));
176 	static assert( isStaticNeighbourhood!(StaticNeighbourhood!(2), 2));
177 	static assert( isStaticNeighbourhood!(StaticNeighbourhood!(3), 3));
178 
179 	static assert(!isStaticNeighbourhood!(StaticNeighbourhood!(3), 2));
180 	static assert(!isStaticNeighbourhood!(Neighbourhood!(1), 1));
181 }
182 
183 
184 
185 ///$(IS_ANY StaticNeighbourhood)
186 template isAnyStaticNeighbourhood(T)
187 {
188 	static if(hasDimension!T)
189 		enum isAnyStaticNeighbourhood = isStaticNeighbourhood!(T, T.Dimension);
190 	else
191 		enum isAnyStaticNeighbourhood = false;
192 }
193 
194 ///
195 unittest
196 {
197 	struct Foo {
198 		enum uint Dimension = 2;
199 		enum uint NeighboursAmount = 1;
200 		int[2][] getNeighboursCoordinates(int x, int y) {
201 			return [[x+1, y+1]];
202 		}
203 	}
204 	static assert(!   isStaticNeighbourhood!(Foo, 3));
205 	static assert( isAnyStaticNeighbourhood!(Foo   ));
206 }
207 
208 unittest
209 {
210 	static assert(!isAnyStaticNeighbourhood!(Neighbourhood!(1)));
211     static assert( isAnyStaticNeighbourhood!(StaticNeighbourhood!(2)));
212     static assert(!isAnyStaticNeighbourhood!(ShiftingNeighbourhood!(3)));
213     static assert(!isAnyStaticNeighbourhood!string);
214 }
215 
216 
217 
218 /**
219 * Tests if something is a $(B ShiftingNeighbourhood).
220 *
221 * returns `true` if `T` is a $(B ShiftingNeighbourhood) of dimension `N`.
222 *
223 * A $(B ShiftingNeighbourhood) is a $(NEIGHBOURHOOD) that can change each
224 * generation. It's `shift` function is meant to be called by the $(I ca)'s
225 * $(LATTICE) every time a generation changes.
226 *
227 * A $(B ShiftingNeighbourhood) is a $(B neighbourhood) with the additional
228 * primitive `void shift()`
229 *
230 * Params:
231 *     T  = type to be tested
232 *     N  = number of dimensions
233 *
234 * Returns: true if T is a $(B ShiftingNeighbourhood), false if not
235 */
236 template isShiftingNeighbourhood(T, uint N)
237 {
238 	enum isShiftingNeighbourhood = 
239 		isNeighbourhood!(T, N) &&
240 		is(typeof(T.init.shift()) : void);
241 }
242 
243 ///
244 unittest
245 {
246 	struct Foo {
247 		enum uint Dimension = 2;
248 
249 		private bool state = false;
250 
251 		int[2][] getNeighboursCoordinates(int x, int y) {
252 			if(state) {
253 				return [[x+1, y+1]];
254 			} else {
255 				return [[x-1, y-1]];
256 			}
257 		}
258 
259 		void shift() { state = !state; }
260 	}
261 
262 	static assert( isNeighbourhood!(Foo, 2));
263 	static assert( isShiftingNeighbourhood!(Foo, 2));
264 	static assert(!isShiftingNeighbourhood!(string, 1));
265 }
266 
267 unittest
268 {
269 	static assert( isShiftingNeighbourhood!(ShiftingNeighbourhood!(1), 1));
270 	static assert( isShiftingNeighbourhood!(ShiftingNeighbourhood!(2), 2));
271 	static assert( isShiftingNeighbourhood!(ShiftingNeighbourhood!(3), 3));
272 
273 	static assert(!isShiftingNeighbourhood!(ShiftingNeighbourhood!(3), 2));
274 	static assert(!isShiftingNeighbourhood!(Neighbourhood!(1), 1));
275 }
276 
277 
278 
279 ///$(IS_ANY ShiftingNeighbourhood)
280 template isAnyShiftingNeighbourhood(T)
281 {
282 	static if(hasDimension!T)
283 		enum isAnyShiftingNeighbourhood = isShiftingNeighbourhood!(T, T.Dimension);
284 	else
285 		enum isAnyShiftingNeighbourhood = false;
286 }
287 
288 ///
289 unittest
290 {
291 	struct Foo {
292 		enum uint Dimension = 2;
293 
294 		private bool state = false;
295 
296 		int[2][] getNeighboursCoordinates(int x, int y) {
297 			if(state) {
298 				return [[x+1, y+1]];
299 			} else {
300 				return [[x-1, y-1]];
301 			}
302 		}
303 
304 		void shift() { state = !state; }
305 	}
306 
307 	static assert(!   isShiftingNeighbourhood!(Foo, 3));
308 	static assert( isAnyShiftingNeighbourhood!(Foo   ));
309 }
310 
311 unittest
312 {
313 	static assert( isAnyShiftingNeighbourhood!(ShiftingNeighbourhood!(1)));
314     static assert( isAnyShiftingNeighbourhood!(ShiftingNeighbourhood!(2)));
315     static assert( isAnyShiftingNeighbourhood!(ShiftingNeighbourhood!(3)));
316     static assert(!isAnyShiftingNeighbourhood!(Neighbourhood!(1)));
317     static assert(!isAnyShiftingNeighbourhood!string);
318 }
319 
320 
321 
322 /+
323 + This template is undocumented since it is unclear wheater it should exist or not
324 +/
325 template isBlockNeighbourhood(T, uint N)
326 {
327 	alias Coord = Repeat!(N, int);
328 
329 	enum isBlockNeighbourhood =
330 		isNeighbourhood!(T, N) &&
331 		is(typeof(T.init.getBlockCoordinates(Coord.init)) : int[N][]); 
332 }
333 
334 unittest
335 {
336 	static assert( isBlockNeighbourhood!(BlockNeighbourhood!(1), 1));
337 	static assert( isBlockNeighbourhood!(BlockNeighbourhood!(2), 2));
338 	static assert( isBlockNeighbourhood!(BlockNeighbourhood!(3), 3));
339 
340 	static assert(!isBlockNeighbourhood!(BlockNeighbourhood!(3), 2));
341 }
342 
343 
344 
345 version(unittest)
346 {
347 	struct Neighbourhood(uint N)
348 	{
349 		enum uint Dimension = N;
350 
351 		alias Coord = Repeat!(N, int);
352 
353 		int[N][] getNeighboursCoordinates(Coord) { int[N][]a; return a.init; }
354 	}
355 
356 	struct StaticNeighbourhood(uint N)
357 	{
358 		Neighbourhood!N neighbourhood;
359 		alias neighbourhood this;
360 
361 		enum uint NeighboursAmount = 0;
362 	}
363 
364 	struct ShiftingNeighbourhood(uint N)
365 	{
366 		Neighbourhood!N neighbourhood;
367 		alias neighbourhood this;
368 
369 		void shift() {}
370 	}
371 
372 	struct BlockNeighbourhood(uint N)
373 	{
374 		alias Coord = Repeat!(N, int);
375 
376 		Neighbourhood!N neighbourhood;
377 		alias neighbourhood this;
378 
379 		int[N][] getBlockCoordinates(Coord) { int[N][]a; return a.init; }
380 	}
381 }