1 /**
2 * This module defines the notion of a $(LATTICE). A $(LATTICE) is the
3 * data structure in wich all the $(I cells) are stored. Each generation,
4 * a $(RULE) changes the cells in the $(LATTICE). The most common example
5 * of a $(LATTICE) is a infinite square grid. Each square is the $(I cell) and it
6 * can contain any data such as a number or color. All of the $(I cells) then
7 * have a x,y coordinates associated with them. But some $(LATTICE)s can have
8 * other $(I dimensions), like a 3-dimensional grid of cubes where each cube have an
9 * x,y,z coordinate. Or it can have som other shape, like a grid with hexagons
10 * instead of squares. The only limitation is that the coordinates must be integers.
11 *
12 * This module provides templates for testing whether a given object is a $(LATTICE),
13 * and what kind of $(LATTICE) it is.
14 *
15 * $(CALIB_ABSTRACT_DESC)
16 *
17 * Macros:
18 * IS_ANY = 
19 *     Tests if something is a $(B $0).
20 *     $(DDOC_BLANKLINE)
21 *     returns $(DDOC_BACKQUOTED true) if $(DDOC_BACKQUOTED T) is a $(B $0). of
22 *     any $(I dimension), Storing any type of cells.
23 *     $(DDOC_BLANKLINE)
24 *     This template is the same as $(B is$0) but the $(I dimension) and type
25 *     of cells need not be specified. Refer to $(B is$0) for more details about
26 *     what a $(B $0) is.
27 *     $(DDOC_BLANKLINE)
28 *     $(DDOC_PARAMS T type to be tested)
29 *     $(DDOC_RETURNS $(B true) if $(B T) is any $(B $0), $(B false) if not)
30 */
31 
32 module caLib_abstract.lattice;
33 
34 import std.meta : Repeat;
35 import caLib_abstract.util : hasCellStateType, hasDimension, hasNeighbourhoodType;
36 import caLib_abstract.neighbourhood : isNeighbourhood;
37 
38 
39 
40 /**
41 * Tests if something is a $(B Lattice).
42 *
43 * returns `true` if `T` is a $(B Lattice) of $(I dimension) `N`, Storing cells of type `Ct`.
44 *
45 * A $(B Lattice) is the most basic form of a $(LATTICE). 
46 * It must define the functions: $(BR)
47 * `Ct get(string)(Coord)`, `Ct get(Coord)`, $(BR)
48 * `void set(string)(Coord, Ct)`, `void set(Coord, Ct)`, $(BR)
49 * `Ct[] getNeighbours(string)(Coord)`, `Ct[] getNeighbours(Coord)`, $(BR)
50 * `void iterate(string)(void delegate(Coord position))`, `void iterate(void delegate(Coord position))`, $(BR)
51 * `void nextGen(string)()`, `void nextGen()`, $(BR)
52 * Where `Coord` is an alias for a typetuple containing `int`'s, one `int` for each
53 * $(I dimension) (in a 3 dimensional $(B Lattice) `Coord` would be: `(int, int, int)`).
54 *
55 * Params:
56 *     T  = type to be tested
57 *     Ct = type of the cells the $(B Lattice) should contain
58 *     N  = number of dimensions
59 *
60 * Returns: true if T is a $(B Lattice), false if not
61 */
62 template isLattice(T, Ct, uint N)
63 {
64     alias Coord = Repeat!(N, int);
65 
66     static if(hasCellStateType!T && hasDimension!T && hasNeighbourhoodType!T)
67     {
68         enum isLattice =
69             T.Dimension == N &&
70             is(T.CellStateType : Ct) &&
71             isNeighbourhood!(T.NeighbourhoodType, N) &&
72             is(typeof(T.init.get!"_test"(Coord.init)) : Ct) &&
73             is(typeof(T.init.get(Coord.init)) : Ct) &&
74             is(typeof(T.init.set!"_test"(Coord.init, Ct.init)) : void) &&
75             is(typeof(T.init.set(Coord.init, Ct.init)) : void) &&
76             is(typeof(T.init.getNeighbours!"_test"(Coord.init)) : Ct[]) &&
77             is(typeof(T.init.getNeighbours(Coord.init)) : Ct[]) &&
78             is(typeof(T.init.iterate!"_test"((Coord c) {})) : void) &&
79             is(typeof(T.init.iterate((Coord c) {})) : void) &&
80             is(typeof(T.init.nextGen!"_test"()) : void) &&
81             is(typeof(T.init.nextGen()) : void);   
82     }
83     else
84     {
85         enum isLattice = false;
86     }
87     
88 }
89 
90 unittest
91 {
92     static assert( isLattice!(Lattice!(int, 1), int, 1));
93     static assert( isLattice!(Lattice!(int, 2), int, 2));
94     static assert( isLattice!(Lattice!(int, 3), int, 3));
95 
96     static assert(!isLattice!(Lattice!(int, 3), uint, 2));
97     static assert(!isLattice!(Lattice!(int, 1), uint, 1));
98 }
99 
100 
101 
102 ///$(IS_ANY Lattice)
103 template isAnyLattice(T)
104 {
105     static if(hasCellStateType!T && hasDimension!T)
106         enum isAnyLattice = isLattice!(T, T.CellStateType, T.Dimension);
107     else
108         enum isAnyLattice = false;
109 }
110 
111 unittest
112 {
113     static assert( isAnyLattice!(Lattice!(char, 1)));
114     static assert( isAnyLattice!(BoundedLattice!(float, 2)));
115     static assert( isAnyLattice!(BlockLattice!(int, 3)));
116     static assert(!isAnyLattice!string);
117 }
118 
119 
120 
121 /**
122 * Tests if something is a $(B BoundedLattice). 
123 *
124 * returns `true` if `T` is a $(B BoundedLattice) of $(I dimension) `N`, Storing cells of type `Ct`.
125 * False if not.
126 *
127 * A $(B BoundedLattice) is a $(LATTICE) with a fixed size. It's function `getLatticeBounds`
128 * returns a list of `uint`'s representing the length of the $(I lattice's) $(I dimensions)
129 *
130 * A $(B BoundedLattice) is a $(B Lattice) with the additional function: `void getLatticeBounds()`.
131 *
132 * Params:
133 *     T  = type to be tested
134 *     Ct = type of the cells the $(B BoundedLattice) should contain
135 *     N  = number of dimensions
136 *
137 * Returns: true if T is a $(B BoundedLattice), false if not
138 */
139 template isBoundedLattice(T, Ct, uint N)
140 {
141     alias Coord = Repeat!(N, int);
142 
143     enum isBoundedLattice =
144         isLattice!(T, Ct, N) &&
145         is(typeof(T.init.getLatticeBounds()) : uint[N]);
146 }
147 
148 unittest
149 {
150     static assert( isLattice!(BoundedLattice!(int, 1), int, 1));
151     static assert( isLattice!(BoundedLattice!(int, 2), int, 2));
152     static assert( isLattice!(BoundedLattice!(int, 3), int, 3));
153     static assert( isBoundedLattice!(BoundedLattice!(int, 1), int, 1));
154     static assert( isBoundedLattice!(BoundedLattice!(int, 2), int, 2));
155     static assert( isBoundedLattice!(BoundedLattice!(int, 3), int, 3));
156 
157     static assert(!isBoundedLattice!(BoundedLattice!(int, 3), uint, 2));
158     static assert(!isBoundedLattice!(BoundedLattice!(int, 1), uint, 1));
159 }
160 
161 
162 
163 ///$(IS_ANY BoundedLattice)
164 template isAnyBoundedLattice(T)
165 {
166     static if(hasCellStateType!T && hasDimension!T)
167         enum isAnyBoundedLattice = isBoundedLattice!(T, T.CellStateType, T.Dimension);
168     else
169         enum isAnyBoundedLattice = false;
170 }
171 
172 unittest
173 {
174     static assert(!isAnyBoundedLattice!(Lattice!(char, 1)));
175     static assert( isAnyBoundedLattice!(BoundedLattice!(float, 2)));
176     static assert(!isAnyBoundedLattice!(BlockLattice!(int, 3)));
177     static assert(!isAnyBoundedLattice!string);
178 }
179 
180 
181 
182 /**
183 * Tests if something is a $(B BlockLattice).
184 *
185 * returns `true` if `T` is a $(B BlockLattice) of $(I dimension) `N`, Storing cells of type `Ct`.
186 * 
187 * A $(B BlockLattice) is a $(B Lattice) with the additional functions: $(BR)
188 * `Ct[] getBlock(string)(Coord)`, `Ct[] getBlock(Coord)`, $(BR)
189 * `void setBlock(string)(Coord, Ct[])`, `void setBlock(Coord, Ct[])`, $(BR)
190 * `void iterateBlocks(string)(void delegate(Coord position))`, `void iterate(void delegate(Coord position))`, $(BR)
191 * Where `Coord` is an alias for a typetuple containing `int`'s, one `int` for each
192 * $(I dimension) (in a 3 dimensional $(B BlockLattice) `Coord` would be: `(int, int, int)`).
193 *
194 * Params:
195 *     T  = type to be tested
196 *     Ct = type of the cells the $(B BlockLattice) should contain
197 *     N  = number of dimensions
198 *
199 * Returns: true if T is a $(B BlockLattice), false if not
200 */
201 template isBlockLattice(T, Ct, uint N)
202 {
203     alias Coord = Repeat!(N, int);
204 
205     enum isBlockLattice =
206         isLattice!(T, Ct, N) &&
207         is(typeof(T.init.getBlock!""(Coord.init)) : Ct[]) &&
208         is(typeof(T.init.getBlock(Coord.init)) : Ct[]) &&
209         is(typeof(T.init.setBlock!""(Coord.init, Ct[].init)) : void) &&
210         is(typeof(T.init.setBlock(Coord.init, Ct[].init)) : void) &&
211         is(typeof(T.init.iterateBlocks!""((Coord c) {})) : void) &&
212         is(typeof(T.init.iterateBlocks((Coord c) {})) : void);
213 }
214 
215 unittest
216 {
217     static assert( isLattice!(BlockLattice!(int, 1), int, 1));
218     static assert( isLattice!(BlockLattice!(int, 2), int, 2));
219     static assert( isLattice!(BlockLattice!(int, 3), int, 3));
220     static assert( isBlockLattice!(BlockLattice!(int, 1), int, 1));
221     static assert( isBlockLattice!(BlockLattice!(int, 2), int, 2));
222     static assert( isBlockLattice!(BlockLattice!(int, 3), int, 3));
223 
224     static assert(!isBlockLattice!(BlockLattice!(int, 3), uint, 2));
225     static assert(!isBlockLattice!(BlockLattice!(int, 1), uint, 1));
226 }
227 
228 
229 
230 ///$(IS_ANY BlockLattice)
231 template isAnyBlockLattice(T)
232 {
233     static if(hasCellStateType!T && hasDimension!T)
234         enum isAnyBlockLattice = isBlockLattice!(T, T.CellStateType, T.Dimension);
235     else
236         enum isAnyBlockLattice = false;
237 }
238 
239 unittest
240 {
241     static assert(!isAnyBlockLattice!(Lattice!(char, 1)));
242     static assert(!isAnyBlockLattice!(BoundedLattice!(float, 2)));
243     static assert( isAnyBlockLattice!(BlockLattice!(int, 3)));
244     static assert(!isAnyBlockLattice!string);
245 }
246 
247 
248 
249 version(unittest)
250 {
251     import caLib_abstract.neighbourhood : Neighbourhood;
252 
253     struct Lattice(Ct, uint N)
254     {
255         alias CellStateType = Ct;
256         alias NeighbourhoodType = Neighbourhood!Dimension;
257         enum uint Dimension = N;
258 
259         alias Coord = Repeat!(N, int);
260 
261         Ct get(string s)(Coord) { return Ct.init; }
262         Ct get(Coord) { return Ct.init; }
263 
264         void set(string s)(Coord, Ct) {}
265         void set(Coord, Ct) {}
266 
267         Ct[] getNeighbours(string s)(Coord) { return new Ct[0]; }
268         Ct[] getNeighbours(Coord) { return new Ct[0]; }
269 
270         void iterate(string s)(void delegate(Coord)) {}
271         void iterate(void delegate(Coord)) {}
272 
273         void nextGen(string s)() {}
274         void nextGen() {}
275     }
276 
277     struct BoundedLattice(Ct, uint N)
278     {
279         Lattice!(Ct, N) lattice;
280         alias lattice this;
281 
282         uint[N] getLatticeBounds() {
283             // "return uint[N]"" makes compiler think
284             // "uint[N]" is a call to "opIndex"
285             uint[N] a; return a.init;
286         }
287     }
288 
289     struct BlockLattice(Ct, uint N)
290     {
291         alias Coord = Repeat!(N, int);
292 
293         Lattice!(Ct, N) lattice;
294         alias lattice this;
295 
296         Ct[] getBlock(string behaviour)(Coord){ return new Ct[0]; }
297         Ct[] getBlock()(Coord) { return new Ct[0]; }
298 
299         void setBlock(string behaviour)(Coord, Ct[]) {}
300         void setBlock()(Coord, Ct[]) {}
301 
302         void iterateBlocks(string s)(void delegate(Coord) d) {}
303         void iterateBlocks()(void delegate(Coord) d) {}
304     }
305 }