-
Notifications
You must be signed in to change notification settings - Fork 0
/
LatinSquare.cs
241 lines (220 loc) · 7.97 KB
/
LatinSquare.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
using System;
using System.Collections.Generic;
using System.Linq;
namespace WinSudoku
{
/// <summary>
/// An n times n matrix with entries from 0 to n-1 so that each entry occurs exactly once in each row and column. This class may
/// represent and complete partial latin squares.
/// </summary>
public class LatinSquare
{
/**
* Allowed or determined values in each cell
*/
internal NumberSet[][] entries;
/**
* Entries which are determined logically while setting other entries.
*/
internal HashSet<Entry> pendingFindings = new HashSet<Entry>();
/// <summary>
/// Looking at a partial latin square as a mathematician, it appears to be a set of triples from {1..n}x{1..n}x{1..n} where no two
/// triples equal in exactly 2 components.The actual matrix is only a representation of it choosing one component as entry and
/// the other two as row and column index.There are obviously 3 such matrices (if rows and colums are interchangeable), this attribute
/// is the one where the original column index plays the role of the row index, the original entry values become column indices and the original
/// rows become entries.
/// </summary>
internal LatinSquare nextTransposition;
/**
* Call create(int) for new instance!
*/
protected LatinSquare(int size)
{
entries = new NumberSet[size][];
for (int i = 0; i < size; i++)
{
entries[i] = new NumberSet[size];
for (int j = 0; j < size; j++)
{
entries[i][j] = new NumberSet(size);
}
}
}
/**
* Not to be used except in inheriting classes. Protected would not permit call from static methods.
*/
internal LatinSquare(LatinSquare nt) : this(nt.entries.Length)
{
nextTransposition = nt;
}
/**
* Creates an instance linked with its two alternative forms.
*/
public static LatinSquare create(int size)
{
LatinSquare result = new LatinSquare(size);
result.nextTransposition = new LatinSquare(new LatinSquare(result));
return result;
}
public virtual LatinSquare CreateCopy()
{
LatinSquare result = new LatinSquare(entries, this);
LatinSquare nnt = new LatinSquare(nextTransposition.nextTransposition.entries, result);
LatinSquare nt = new LatinSquare(nextTransposition.entries, nnt);
result.nextTransposition = nt;
return result;
}
internal LatinSquare(NumberSet[][] originalEntries, LatinSquare nt )
{
entries = CloneEntries(originalEntries);
nextTransposition = nt;
}
internal static NumberSet[][] CloneEntries(NumberSet[][] original)
{
NumberSet[][] clone = new NumberSet[original.Length][];
for (int i = 0; i < original.Length; i++)
{
clone[i] = new NumberSet[original.Length];
for (int j = 0; j < original.Length; j++)
{
clone[i][j] = new NumberSet(original[i][j]);
}
}
return clone;
}
/**
* Specifies an entry value which might have various implications abaut the other entries.
*/
public virtual void SetEntry(int row, int col, int num)
{
if (entries[row][col].SetValue(num) == NumberSet.ALREADYKNOWN)
{
return;
}
nextTransposition.SetEntry(col, num, row);
for (int i = 0; i < entries.Length; i++)
{
if (i != row)
{
forbidValue(i, col, num);
}
if (i != col)
{
forbidValue(row, i, num);
}
}
}
/// <summary>
/// Returns the entry in specified cell or OK when unknown
/// </summary>
/// <param name="row"></param>
/// <param name="col"></param>
public int GetEntry(int row, int col)
{
return entries[row][col].getValue();
}
/// <summary>
/// Enter all numbers which have been been derived from entries made so far.
/// </summary>
public void AddFindings()
{
while (AddOwnFindings() || nextTransposition.AddOwnFindings() || nextTransposition.nextTransposition.AddOwnFindings())
{
// just repeat
}
}
private String toString(NumberSet[][] value)
{
var sb = new System.Text.StringBuilder();
for (int row = 0; row < value.Length; row++)
{
for (int col = 0; col < value[row].Length; col++)
{
sb.Append(value[row][col]);
}
sb.Append("\n");
}
return sb.ToString();
}
private bool AddOwnFindings()
{
bool changed = false;
while (pendingFindings.Count > 0)
{
Entry finding = pendingFindings.First();
SetEntry(finding.Row, finding.Col, finding.Num);
pendingFindings.Remove(finding);
changed = true;
}
return changed;
}
/// <summary>
/// Specifies that an entry cannot be made.
/// </summary>
/// <param name="row"></param>
/// <param name="col"></param>
/// <param name="num"></param>
/// <exception cref="IllegalEntryException">In case it is known that the restriction makes the matrix impossible to complete.</exception>
protected virtual void forbidValue(int row, int col, int num)
{
int state = entries[row][col].ForbidValue(num);
switch (state)
{
case NumberSet.ALREADYKNOWN: break;
case NumberSet.UNKNOWN:
nextTransposition.forbidValue(col, num, row);
break;
default:
pendingFindings.Add(new Entry(row, col, state));
break;
}
}
public override string ToString()
{
var sb = new System.Text.StringBuilder();
for (int row = 0; row < entries.Length; row++)
{
for (int col = 0; col < entries[0].Length; col++)
{
int num = entries[row][col].getValue();
sb.Append(num == NumberSet.UNKNOWN ? " ." : String.Format("{0, 3}", num));
}
sb.Append("\n");
}
return sb.ToString();
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
LatinSquare other = (LatinSquare)obj;
for (int row = 0; row < entries.Length; row++)
{
for (int col = 0; col < entries[row].Length; col++)
{
if (GetEntry(row, col) != other.GetEntry(row, col))
{
return false;
}
}
}
return true;
}
// override object.GetHashCode
public override int GetHashCode()
{
int result = 0;
int prime = 3;
for (int row = 0; row < entries.Length; row++)
{
for (int col = 0; col < entries[row].Length; col++)
{
result = prime * result + GetEntry(row, col);
}
}
return result;
}
}
}