1 module caLib.rules.BinaryRule;
2 
3 import std.math : pow;
4 import std.bigint;
5 import std.conv : to;
6 import std.array : appender;
7 import caLib_abstract.lattice : isAnyLattice;
8 import caLib_abstract.neighbourhood : isStaticNeighbourhood;
9 
10 
11 
12 auto create_BinaryRule(Lt)(Lt* lattice, BigInt ruleNumber)
13 {
14 	return new BinaryRule!Lt(lattice, ruleNumber);
15 }
16 
17 
18 
19 auto create_BinaryRule(Lt)(Lt* lattice, ubyte[] ruleSet)
20 in
21 { assert(BinaryRule!Lt.configurations == ruleSet.length); }
22 body
23 {
24 	return new BinaryRule!Lt(lattice, ruleSet);
25 }
26 
27 
28 
29 struct BinaryRule(Lt)
30 if(isAnyLattice!Lt && Lt.Dimension == 2 && is(Lt.CellStateType : ubyte)
31 && isStaticNeighbourhood!(Lt.NeighbourhoodType, 2) && Lt.NeighbourhoodType.NeighboursAmount+1 < uint.max)
32 {
33 
34 private:
35 
36 	Lt* lattice;
37 
38 	BigInt ruleNumber;
39 	ubyte[] ruleSet;
40 
41 public:
42 
43 	enum uint configurations = pow(2, Lt.NeighbourhoodType.NeighboursAmount+1); 
44 	BigInt maxRuleNumber;
45 
46 	this(Lt* lattice, BigInt ruleNumber)
47 	in
48 	{ assert(ruleNumber <= calculateMaxRuleNumber); }
49 	body
50 	{
51 		this(lattice, createRuleSetFromNumber(ruleNumber));
52 	}
53 
54 
55 
56 	this(Lt* lattice, ubyte[] ruleSet)
57 	in
58 	{ assert(BinaryRule!Lt.configurations == ruleSet.length); }
59 	body
60 	{
61 		this.lattice = lattice;
62 
63 		this.ruleSet = ruleSet;
64 		this.ruleNumber = createNumberFromRuleSet(ruleSet);
65 
66 		maxRuleNumber = calculateMaxRuleNumber();
67 	}
68 
69 
70 
71 	void applyRule()
72 	{
73 		lattice.iterate((ubyte cellState, ubyte[] neighbours, int x, int y)
74 		{
75 			uint n = cellState << neighbours.length;
76 			foreach(i; 0 .. neighbours.length)
77 			{
78 				n += neighbours[i] << i;
79 			}
80 
81 			return ruleSet[n];
82 		});
83 
84 		lattice.nextGen();
85 	}
86 
87 
88 
89 	BigInt getRuleNumber() { return ruleNumber; }
90 	ubyte[] getRuleSet() { return ruleSet.dup; }
91 
92 
93 
94 	static BigInt calculateMaxRuleNumber()
95 	{
96 		BigInt n = BigInt("1");
97 		foreach(i; 0 .. configurations) { n *= 2; }
98 		return n-1;
99 	}
100 
101 
102 
103 private:
104 
105 	static  ubyte[] createRuleSetFromNumber(BigInt ruleNumber)
106 	out(result)
107 	{
108 		assert(BinaryRule!Lt.configurations == result.length);
109 	}
110 	body
111 	{
112 		string binaryRuleString = toBinaryString(ruleNumber);
113 
114 		ubyte[] ruleSet = new ubyte[BinaryRule!Lt.configurations];
115 		foreach(i; 0 .. binaryRuleString.length)
116 		{
117 			ruleSet[i] = to!ubyte(
118 				binaryRuleString[binaryRuleString.length-1-i] - '0');
119 		}
120 
121 		return ruleSet;
122 	}
123 
124 
125 
126 	static BigInt createNumberFromRuleSet(const ubyte[] ruleSet)
127 	in
128 	{
129 		assert(BinaryRule!Lt.configurations == ruleSet.length);
130 	}
131 	body
132 	{
133 		BigInt n = BigInt("0");
134 		foreach(i; 0 .. configurations)
135 		{
136 			if(ruleSet[i] == 1)
137 			{
138 				BigInt k = BigInt("1");
139 				foreach(j; 0 .. i)
140 				{
141 					k *= 2;
142 				}
143 				n += k;
144 			}
145 		}
146 		return n;
147 	}
148 
149 
150 
151 	static string toBinaryString(const BigInt num)
152 	{
153 		string hex = num.toHex();
154 		auto bin = appender!string();
155 
156 		foreach(s; hex)
157 		{
158 			if(s != '_')
159 				bin.put(hexToBinary(s));
160 		}
161 
162 		return bin.data;
163 	}
164 
165 
166 
167 	static string hexToBinary(char hex)
168 	{
169 		string bin =
170 		[
171 			'0':"0000", '1':"0001", '2':"0010", '3':"0011", '4':"0100",
172 			'5':"0101", '6':"0110", '7':"0111", '8':"1000", '9':"1001",
173 			'A':"1010", 'B':"1011", 'C':"1100", 'D':"1101", 'E':"1110",
174 			'F':"1111"
175 		].get(hex,null);
176 
177 		assert(bin != null);
178 		return bin;
179 	}
180 }
181 
182 
183 
184 version(unittest)
185 {
186 	import caLib_abstract.rule : isRule;
187 	import caLib_abstract.lattice : Lattice;
188 	import caLib_abstract.neighbourhood : StaticNeighbourhood;
189 }
190 
191 unittest
192 {
193 	alias lattice = Lattice!(ubyte, 2, StaticNeighbourhood!2);
194 	static assert(isRule!(BinaryRule!lattice));
195 }