1 module caLib.rules.TotalisticRule;
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 import std.stdio;
12 
13 
14 
15 auto create_TotalisticRule(Lt)(Lt* lattice, BigInt ruleNumber)
16 {
17 	return new TotalisticRule!Lt(lattice, ruleNumber);
18 }
19 
20 
21 
22 auto create_TotalisticRule(Lt)(Lt* lattice, ubyte[] ruleSet)
23 in
24 { assert(TotalisticRule!Lt.configurations == ruleSet.length); }
25 body
26 {
27 	return new TotalisticRule!Lt(lattice, ruleSet);
28 }
29 
30 
31 
32 struct TotalisticRule(Lt)
33 if(isAnyLattice!Lt && Lt.Dimension == 2 && is(Lt.CellStateType : ubyte)
34 && isStaticNeighbourhood!(Lt.NeighbourhoodType, 2) && Lt.NeighbourhoodType.NeighboursAmount+1 < uint.max)
35 {
36 
37 private:
38 
39 	Lt* lattice;
40 
41 	BigInt ruleNumber;
42 	ubyte[] ruleSet;
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); }
52 	body
53 	{
54 		this(lattice, createRuleSetFromNumber(ruleNumber));
55 	}
56 
57 
58 
59 	this(Lt* lattice, ubyte[] ruleSet)
60 	in
61 	{ assert(TotalisticRule!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((int x, int y)
77 		{
78 			auto neighbours = lattice.getNeighbours(x, y);
79 			
80 			uint n = lattice.get(x, y) << neighbours.length;
81 			foreach(i; 0 .. neighbours.length)
82 			{
83 				n += neighbours[i] << i;
84 			}
85 
86 			lattice.set(x, y, ruleSet[n]);
87 		});
88 
89 		lattice.nextGen();
90 	}
91 
92 
93 
94 	BigInt getRuleNumber() { return ruleNumber; }
95 	ubyte[] getRuleSet() { return ruleSet.dup; }
96 
97 
98 
99 private:
100 
101 	private static  ubyte[] createRuleSetFromNumber(BigInt ruleNumber)
102 	out(result)
103 	{
104 		assert(TotalisticRule!Lt.configurations == result.length);
105 	}
106 	body
107 	{
108 		string binaryRuleString = toBinaryString(ruleNumber);
109 
110 		ubyte[] ruleSet = new ubyte[TotalisticRule!Lt.configurations];
111 		foreach(i; 0 .. binaryRuleString.length)
112 		{
113 			ruleSet[i] = to!ubyte(
114 				binaryRuleString[binaryRuleString.length-1-i] - '0');
115 		}
116 
117 		return ruleSet;
118 	}
119 
120 
121 
122 	private static BigInt createNumberFromRuleSet(const ubyte[] ruleSet)
123 	in
124 	{
125 		assert(TotalisticRule!Lt.configurations == ruleSet.length);
126 	}
127 	body
128 	{
129 		BigInt n = BigInt("0");
130 		foreach(i; 0 .. configurations)
131 		{
132 			if(ruleSet[i] == 1)
133 			{
134 				BigInt k = BigInt("1");
135 				foreach(j; 0 .. i)
136 				{
137 					k *= 2;
138 				}
139 				n += k;
140 			}
141 		}
142 		return n;
143 	}
144 
145 
146 
147 	private static BigInt calculateMaxRuleNumber()
148 	{
149 		BigInt n = BigInt("1");
150 		foreach(i; 0 .. configurations) { n *= 2; }
151 		return n-1;
152 	}
153 
154 
155 
156 	private static string toBinaryString(const BigInt num)
157 	{
158 		string hex = num.toHex();
159 		auto bin = appender!string();
160 
161 		foreach(s; hex)
162 		{
163 			if(s != '_')
164 				bin.put(hexToBinary(s));
165 		}
166 
167 		return bin.data;
168 	}
169 
170 
171 
172 	private static string hexToBinary(char hex)
173 	{
174 		string bin =
175 		[
176 			'0':"0000", '1':"0001", '2':"0010", '3':"0011", '4':"0100",
177 			'5':"0101", '6':"0110", '7':"0111", '8':"1000", '9':"1001",
178 			'A':"1010", 'B':"1011", 'C':"1100", 'D':"1101", 'E':"1110",
179 			'F':"1111"
180 		].get(hex,null);
181 
182 		assert(bin != null);
183 		return bin;
184 	}
185 }