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 import caLib_abstract.neighbourhood : Neighbourhood;
38 
39 
40 
41 /**
42 * Tests if something is a $(B Lattice).
43 *
44 * returns `true` if `T` is a $(B Lattice) of $(I dimension) `N`, Storing cells of type `Ct`.
45 *
46 * A $(B Lattice) is the most basic form of a $(LATTICE). 
47 * It must define the functions: $(BR)
48 * `Ct get(string)(Coord)`, `Ct get(Coord)`, $(BR)
49 * `void set(string)(Coord, Ct)`, `void set(Coord, Ct)`, $(BR)
50 * `Ct[] getNeighbours(string)(Coord)`, `Ct[] getNeighbours(Coord)`, $(BR)
51 * `void iterate(string)(ubyte delegate(Ct cellState, Ct[] neighbours, Coord c))`,
52 * `void iterate(ubyte delegate(Ct cellState, Ct[] neighbours, Coord c))`, $(BR)
53 * `void nextGen(string)()`, `void nextGen()`, $(BR)
54 * Where `Coord` is an alias for a typetuple containing `int`'s, one `int` for each
55 * $(I dimension) (in a 3 dimensional $(B Lattice) `Coord` would be: `(int, int, int)`).
56 *
57 * Params:
58 *     T  = type to be tested
59 *     Ct = type of the cells the $(B Lattice) should contain
60 *     N  = number of dimensions
61 *
62 * Returns: true if T is a $(B Lattice), false if not
63 */
64 template isLattice(T, Ct, uint N)
65 {
66     alias Coord = Repeat!(N, int);
67 
68     static if(hasCellStateType!T && hasDimension!T && hasNeighbourhoodType!T)
69     {
70         enum isLattice =
71             T.Dimension == N &&
72             is(T.CellStateType : Ct) &&
73             isNeighbourhood!(T.NeighbourhoodType, N) &&
74             is(typeof(T.init.get!"_test"(Coord.init)) : Ct) &&
75             is(typeof(T.init.get(Coord.init)) : Ct) &&
76             is(typeof(T.init.set!"_test"(Coord.init, Ct.init)) : void) &&
77             is(typeof(T.init.set(Coord.init, Ct.init)) : void) &&
78             is(typeof(T.init.getNeighbours!"_test"(Coord.init)) : Ct[]) &&
79             is(typeof(T.init.getNeighbours(Coord.init)) : Ct[]) &&
80             is(typeof(T.init.iterate!"_test"((Ct cellState, Ct[] neighbours, Coord c)
81                 { return Ct.init; })) : void) &&
82             is(typeof(T.init.iterate((Ct cellState, Ct[] neighbours, Coord c)
83                 { return Ct.init; })) : void) &&
84             is(typeof(T.init.nextGen!"_test"()) : void) &&
85             is(typeof(T.init.nextGen()) : void);   
86     }
87     else
88     {
89         enum isLattice = false;
90     }
91     
92 }
93 
94 unittest
95 {
96     static assert( isLattice!(Lattice!(int, 1), int, 1));
97     static assert( isLattice!(Lattice!(int, 2), int, 2));
98     static assert( isLattice!(Lattice!(int, 3), int, 3));
99 
100     static assert(!isLattice!(Lattice!(int, 3), uint, 2));
101     static assert(!isLattice!(Lattice!(int, 1), uint, 1));
102 }
103 
104 
105 
106 /// $(IS_ANY Lattice)
107 template isAnyLattice(T)
108 {
109     static if(hasCellStateType!T && hasDimension!T)
110         enum isAnyLattice = isLattice!(T, T.CellStateType, T.Dimension);
111     else
112         enum isAnyLattice = false;
113 }
114 
115 unittest
116 {
117     static assert( isAnyLattice!(Lattice!(char, 1)));
118     static assert( isAnyLattice!(BoundedLattice!(float, 2)));
119     static assert(!isAnyLattice!string);
120 }
121 
122 
123 
124 /// Example of a $(B Lattice)
125 struct Lattice(Ct, uint N, neighbourhood=Neighbourhood!N)
126 if(isNeighbourhood!(neighbourhood, N))
127 {
128     alias CellStateType = Ct;
129     alias NeighbourhoodType = neighbourhood;
130     enum uint Dimension = N;
131 
132     alias Coord = Repeat!(N, int);
133 
134     Ct get(string s)(Coord) { return Ct.init; }
135     Ct get(Coord) { return Ct.init; }
136 
137     void set(string s)(Coord, Ct) {}
138     void set(Coord, Ct) {}
139 
140     Ct[] getNeighbours(string s)(Coord) { return new Ct[0]; }
141     Ct[] getNeighbours(Coord) { return new Ct[0]; }
142 
143     void iterate(string s)(Ct delegate(Ct, Ct[], Coord)) {}
144     void iterate(Ct delegate(Ct, Ct[], Coord)) {}
145 
146     void nextGen(string s)() {}
147     void nextGen() {}
148 }
149 
150 ///
151 unittest
152 {
153     static assert(isLattice!(Lattice!(int, 2), int, 2));
154     static assert(isAnyLattice!(Lattice!(char, 3)));
155 }
156 
157 
158 
159 /**
160 * Tests if something is a $(B BoundedLattice). 
161 *
162 * returns `true` if `T` is a $(B BoundedLattice) of $(I dimension) `N`, Storing cells of type `Ct`.
163 * False if not.
164 *
165 * A $(B BoundedLattice) is a $(LATTICE) with a fixed size. It's function `getLatticeBounds`
166 * returns a list of `uint`'s representing the length of the $(I lattice's) $(I dimensions)
167 *
168 * A $(B BoundedLattice) is a $(B Lattice) with the additional function: `void getLatticeBounds()`.
169 *
170 * Params:
171 *     T  = type to be tested
172 *     Ct = type of the cells the $(B BoundedLattice) should contain
173 *     N  = number of dimensions
174 *
175 * Returns: true if T is a $(B BoundedLattice), false if not
176 */
177 template isBoundedLattice(T, Ct, uint N)
178 {
179     alias Coord = Repeat!(N, int);
180 
181     enum isBoundedLattice =
182         isLattice!(T, Ct, N) &&
183         is(typeof(T.init.getLatticeBounds()) : int[N]);
184 }
185 
186 unittest
187 {
188     static assert( isLattice!(BoundedLattice!(int, 1), int, 1));
189     static assert( isLattice!(BoundedLattice!(int, 2), int, 2));
190     static assert( isLattice!(BoundedLattice!(int, 3), int, 3));
191     static assert( isBoundedLattice!(BoundedLattice!(int, 1), int, 1));
192     static assert( isBoundedLattice!(BoundedLattice!(int, 2), int, 2));
193     static assert( isBoundedLattice!(BoundedLattice!(int, 3), int, 3));
194 
195     static assert(!isBoundedLattice!(BoundedLattice!(int, 3), uint, 2));
196     static assert(!isBoundedLattice!(BoundedLattice!(int, 1), uint, 1));
197 }
198 
199 
200 
201 ///$(IS_ANY BoundedLattice)
202 template isAnyBoundedLattice(T)
203 {
204     static if(hasCellStateType!T && hasDimension!T)
205         enum isAnyBoundedLattice = isBoundedLattice!(T, T.CellStateType, T.Dimension);
206     else
207         enum isAnyBoundedLattice = false;
208 }
209 
210 unittest
211 {
212     static assert(!isAnyBoundedLattice!(Lattice!(char, 1)));
213     static assert( isAnyBoundedLattice!(BoundedLattice!(float, 2)));
214     static assert(!isAnyBoundedLattice!string);
215 }
216 
217 
218 
219 /// Example of a $(B BoundedLattice)
220 struct BoundedLattice(Ct, uint N)
221 {
222     Lattice!(Ct, N) lattice;
223     alias lattice this;
224 
225     int[N] getLatticeBounds() {
226         // "return int[N]"" makes compiler think
227         // "int[N]" is a call to "opIndex"
228         int[N] a; return a.init;
229     }
230 }
231 
232 ///
233 unittest
234 {
235     static assert(isBoundedLattice!(BoundedLattice!(int, 3), int, 3));
236     static assert(isAnyLattice!(BoundedLattice!(char, 7)));
237 }