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!string);
123 }
124 
125 
126 
127 /**
128 * Tests if something is a $(B StaticNeighbourhood).
129 *
130 * returns `true` if `T` is a $(B StaticNeighbourhood) of dimension `N`.
131 *
132 * A $(B StaticNeighbourhood) is a $(NEIGHBOURHOOD) that never changes.
133 * If a particular $(I cell) has a particular $(I neighbour) it will always have
134 * that $(I neighbour). Also, if a $(I cell) where to have a neighbour "to the right"
135 * all other $(I cells) will also have a neighbour "to the right".
136 * A $(B StaticNeighbourhood) dosen't really add any additional functionality.
137 * All it does is having the a uint enum NeighboursAmount.
138 *
139 * Params:
140 *     T  = type to be tested
141 *     N  = number of dimensions
142 *
143 * Returns: true if T is a $(B StaticNeighbourhood), false if not
144 */
145 template isStaticNeighbourhood(T, uint N)
146 {
147 	alias Coord = Repeat!(N, int);
148 
149     enum isStaticNeighbourhood =
150         isNeighbourhood!(T, N) &&
151         is(typeof(T.NeighboursAmount) : uint) &&
152         is(typeof(T.init.NeighboursAmount) : uint);
153 }
154 
155 ///
156 unittest
157 {
158 	struct A {
159 		enum uint Dimension = 2;
160 		enum uint NeighboursAmount = 1;
161 		enum isStatic;
162 		int[2][] getNeighboursCoordinates(int x, int y) {
163 			return [[x+1, y+1]];
164 		}
165 	}
166 
167 	static assert( isNeighbourhood!(A, 2));
168 	static assert( isStaticNeighbourhood!(A, 2));
169 	static assert(!isStaticNeighbourhood!(string, 1));
170 }
171 
172 unittest
173 {
174 	static assert( isStaticNeighbourhood!(StaticNeighbourhood!(1), 1));
175 	static assert( isStaticNeighbourhood!(StaticNeighbourhood!(2), 2));
176 	static assert( isStaticNeighbourhood!(StaticNeighbourhood!(3), 3));
177 
178 	static assert(!isStaticNeighbourhood!(StaticNeighbourhood!(3), 2));
179 	static assert(!isStaticNeighbourhood!(Neighbourhood!(1), 1));
180 }
181 
182 
183 
184 ///$(IS_ANY StaticNeighbourhood)
185 template isAnyStaticNeighbourhood(T)
186 {
187 	static if(hasDimension!T)
188 		enum isAnyStaticNeighbourhood = isStaticNeighbourhood!(T, T.Dimension);
189 	else
190 		enum isAnyStaticNeighbourhood = false;
191 }
192 
193 ///
194 unittest
195 {
196 	struct Foo {
197 		enum uint Dimension = 2;
198 		enum uint NeighboursAmount = 1;
199 		int[2][] getNeighboursCoordinates(int x, int y) {
200 			return [[x+1, y+1]];
201 		}
202 	}
203 	static assert(!   isStaticNeighbourhood!(Foo, 3));
204 	static assert( isAnyStaticNeighbourhood!(Foo   ));
205 }
206 
207 unittest
208 {
209 	static assert(!isAnyStaticNeighbourhood!(Neighbourhood!(1)));
210     static assert( isAnyStaticNeighbourhood!(StaticNeighbourhood!(2)));
211     static assert(!isAnyStaticNeighbourhood!string);
212 }
213 
214 
215 
216 version(unittest)
217 {
218 	struct Neighbourhood(uint N)
219 	{
220 		enum uint Dimension = N;
221 
222 		alias Coord = Repeat!(N, int);
223 
224 		int[N][] getNeighboursCoordinates(Coord) { int[N][]a; return a.init; }
225 	}
226 
227 	struct StaticNeighbourhood(uint N)
228 	{
229 		Neighbourhood!N neighbourhood;
230 		alias neighbourhood this;
231 
232 		enum uint NeighboursAmount = 0;
233 	}
234 
235 	struct BlockNeighbourhood(uint N)
236 	{
237 		alias Coord = Repeat!(N, int);
238 
239 		Neighbourhood!N neighbourhood;
240 		alias neighbourhood this;
241 
242 		int[N][] getBlockCoordinates(Coord) { int[N][]a; return a.init; }
243 	}
244 }