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