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 }