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