-
Notifications
You must be signed in to change notification settings - Fork 0
/
Nintendo.cpp
344 lines (277 loc) · 9.28 KB
/
Nintendo.cpp
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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
// NintendoForward.cpp : Defines the entry point for the console application.
//
#include <iostream>
#include <iomanip>
#include <bitset>
#include <deque>
#include <vector>
#include <array>
#include <math.h>
using namespace std;
int main()
{
bool goForward = false;
if (goForward)
{
int size;
cin >> size;
unsigned int* a = new unsigned int[size / 16]; // <- input tab to encrypt
unsigned int* b = new unsigned int[size / 16]; // <- output tab
for (int i = 0; i < size / 16; i++) { // Read size / 16 integers to a
cin >> hex >> a[i];
}
for (int i = 0; i < size / 16; i++) { // Write size / 16 zeros to b
b[i] = 0;
}
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++) {
int test = ((a[i / 32] >> (i % 32)) &
(a[j / 32 + size / 32] >> (j % 32)) & 1) << ((i + j) % 32);
b[(i + j) / 32] ^= test;
}
for (int i = 0; i < size / 16; i++) {
if (i > 0) {
cout << ' ';
}
cout << setfill('0') << setw(8) << hex << b[i]; // print result
}
cout << endl;
}
else
{
// Parse input
int size;
cin >> size;
//ASSUME 32 BIT FOR NOW, ARRAY SIZE OF 2
array<unsigned int, 2> outputs;
for (int i = 0; i < 2; i++)
{
cin >> hex >> outputs[i];
}
// Translate both outputs to a bit array
array<bitset<32>, 2> outputBits;
for (int i = 0; i < 2; i++)
{
outputBits[i] = bitset<32>(outputs[i]);
}
// A list of potential solutions will be kept
deque<array<vector<bool>, 2>> potentialSolutions;
potentialSolutions.push_back({ { {},{} } });
// Work on first output independently for now
for (int bit = 0; bit < 32; bit++)
{
bool bitVal = outputBits[bit / 32].test(bit % 32);
// Find all the i,j pairs that can affect this bit
vector<array<int, 2>> ijPairs;
ijPairs.clear();
for (int i = 0; i <= bit; i++)
{
if (i < 32 && (bit - i) < 32)
{
ijPairs.push_back({ i, bit - i });
}
}
size_t initialSize = potentialSolutions.size();
// Temporarily add all four 0/1 combos to each potential solution for analysis
// Room for optimization here for sure
for (int i = 0; i < initialSize; i++)
{
// I am purposely creating a copy of the solution, not taking a reference
array<vector<bool>, 2> solution = potentialSolutions[i];
// I can save time by calculating all inner-flips ahead of time
// these will be the same regardless of last move
int innerFlips = 0;
for (int i = 1; i < ijPairs.size() - 1; i++)
{
if (solution[0][ijPairs[i][0]] && solution[1][ijPairs[i][1]])
{
innerFlips++;
}
}
int outerFlips;
// first test the 0/0 case
solution[0].push_back(false);
solution[1].push_back(false);
// Check if this solution would work
// Count all the outer ijPairs that would lead to a flip of the bit
// The numbers in the ij pairs should never exceed the size of the solution thus far
outerFlips = 0;
if (solution[0][ijPairs[0][0]] && solution[1][ijPairs[0][1]])
{
outerFlips++;
}
if (bit != 0 && solution[0][ijPairs.back()[0]] && solution[1][ijPairs.back()[1]])
{
outerFlips++;
}
// I need odd flips if the bitval is true, and vice-versa
if ((innerFlips + outerFlips) % 2 == bitVal)
{
// See if I can immediately rule out this solution if possible
// This is a valid potential solution
// Create a branch
potentialSolutions.push_back(solution);
}
else
{
// This solution is not valid
// do not create a branch
}
// now test 0 / 1
solution[1].back() = true;
outerFlips = 0;
if (solution[0][ijPairs[0][0]] && solution[1][ijPairs[0][1]])
{
outerFlips++;
}
if (bit != 0 && solution[0][ijPairs.back()[0]] && solution[1][ijPairs.back()[1]])
{
outerFlips++;
}
// I need odd flips if the bitval is true, and vice-versa
if ((innerFlips + outerFlips) % 2 == bitVal)
{
potentialSolutions.push_back(solution);
}
// Don't need to test 1 / 0 since it will produce a symmetrical result, which I don't need to explicitly calculate
// Not sure actually
solution[1].back() = false;
solution[0].back() = true;
outerFlips = 0;
if (solution[0][ijPairs[0][0]] && solution[1][ijPairs[0][1]])
{
outerFlips++;
}
if (bit != 0 && solution[0][ijPairs.back()[0]] && solution[1][ijPairs.back()[1]])
{
outerFlips++;
}
// I need odd flips if the bitval is true, and vice-versa
if ((innerFlips + outerFlips) % 2 == bitVal)
{
potentialSolutions.push_back(solution);
}
// now test 1 / 1
solution[1].back() = true;
solution[0].back() = true;
outerFlips = 0;
if (solution[0][ijPairs[0][0]] && solution[1][ijPairs[0][1]])
{
outerFlips++;
}
if (bit != 0 && solution[0][ijPairs.back()[0]] && solution[1][ijPairs.back()[1]])
{
outerFlips++;
}
// I need odd flips if the bitval is true, and vice-versa
if ((innerFlips + outerFlips) % 2 == bitVal)
{
potentialSolutions.push_back(solution);
}
}
// Now that all the subsequent potential solutions have been added to the back of the queue, delete the initial ones from the front
for (int i = 0; i < initialSize; i++)
{
potentialSolutions.pop_front();
}
cout << potentialSolutions.size() << endl;
}
cerr << "first output done" << endl;
// Now work on second output
// At this point, all my potential solutions are a pair of 32-bit numbers ready to go
// However, this part will restrict that list
for (int bit = 32; bit < 64; bit++)
{
bool bitVal = outputBits[bit / 32].test(bit % 32);
// Find all the i,j pairs that can affect this bit
vector<array<int, 2>> ijPairs;
ijPairs.clear();
for (int i = 0; i <= bit; i++)
{
if (i < 32 && (bit - i) < 32)
{
ijPairs.push_back({ i, bit - i });
}
}
// Look at every solution and see if it still holds up
// Room for optimization here for sure
size_t initialSize = potentialSolutions.size();
for (int i = 0; i < initialSize; i++)
{
array<vector<bool>, 2> & solution = potentialSolutions.front();
// check if the solution satisifies the new requirements
int flips = 0;
for (auto& pair : ijPairs)
{
if (solution[0][pair[0]] && solution[1][pair[1]])
{
flips++;
}
}
// I need odd flips if the bitval is true, and vice-versa
if (flips % 2 != bitVal)
{
//not a valid solution anymore
// delete it
potentialSolutions.pop_front();
}
else
{
// still a valid solution, move to back
potentialSolutions.push_back(potentialSolutions.front());
potentialSolutions.pop_front();
}
}
cerr << potentialSolutions.size() << endl;
}
// Print the solutions
cerr << "Final hex nums" << endl;
for (auto& solution : potentialSolutions)
{
int firstNum = 0;
for (int i = 0; i < 32; i++)
{
firstNum += solution[0][i] * pow(2, i);
}
int secondNum = 0;
for (int i = 0; i < 32; i++)
{
secondNum += solution[1][i] * pow(2, i);
}
cout << setfill('0') << setw(8) << hex << firstNum << " " << setfill('0') << setw(8) << hex << secondNum << endl; // print result
}
}
char exit;
cin >> exit;
return 0;
}
// Break it down
// First figure out exactly what happens on a single iteration of updating b
//a[i / 32] >> (i % 32) cannot be inverted easily with <<. I lose the digits that get pushed off
// The first big operation is x & y & 1, the only possible results for this are 0 and 1
// It's one if both x and y are odd
// It then gets shifted to the left by a certain number of digits. Therefore, the final result is either 0, or a single 1 in a 32 bit number
// a ^= operation will invert every bit that's a 1. So nothing will happen when it's all 0s, and a single bit will be inverted if there's a single 1.
// Therefore, b is a series of inidividual bit flips
// There can be multiple solutions to the problem, display them in ascending order
// Start with the 32-bit case
// This means that a and b are both size 2
// There is a maximum of 1024 possible bit flips, starting to seem like a discrete optimization problem
// In the first example, the 15th bit is 1 for one of the numbers
// this means that there needs to be an odd number of times where this bit was flipped
// This flip can happen when i+j % 32 == 14 (15 different ways to do this)
// This leads me to believe I should start with far right bit
// The first bit is 1 for the first number
// This flip can happen if i + j < 32 and i + j % 32 ==0 aka i and j is 0. Look at this case more closely
// For this flip to go through, the first and last bit of both inputs need to be 1
// Next step: the second bit for the first number is 1
// This flip can happen if i + j < 32 and 1 + j % 32 == 1. Acceptable pairs are 1,0 and 0,1
// I need exactly one of these flips to go through
// 1,0 will flip if second bit of first input is 1 and first bit of second input is 1
// 0,1 will flip if vice-versa
// Therefore, I need 1 second bit to be 1, and the other to be 0
// Always symmetrical solutions, so don't keep both
// I think I can just build up this way continuously, managing all possible paths
// I have done this in theory, but it takes way too long
// It can very quickly reach over 100000 paths, can't keep them all in memory
// might need a backtracking approach instead