-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathSource.cpp
More file actions
261 lines (225 loc) · 7.7 KB
/
Copy pathSource.cpp
File metadata and controls
261 lines (225 loc) · 7.7 KB
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
#include <vector>
#include <boost/multiprecision/cpp_int.hpp>
#include <stdint.h>
#include <string.h>
#include <memory>
typedef boost::multiprecision::cpp_int TINT;
typedef std::vector<TINT> TINTVec;
const float c_pi = 3.14159265359f;
//=================================================================================
void WaitForEnter ()
{
printf("\nPress Enter to quit");
fflush(stdin);
getchar();
}
//=================================================================================
static TINT ExtendedEuclidianAlgorithm (TINT smaller, TINT larger, TINT &s, TINT &t)
{
// make sure A <= B before starting
bool swapped = false;
if (larger < smaller)
{
swapped = true;
std::swap(smaller, larger);
}
// set up our storage for the loop. We only need the last two values so will
// just use a 2 entry circular buffer for each data item
std::array<TINT, 2> remainders = { larger, smaller };
std::array<TINT, 2> ss = { 1, 0 };
std::array<TINT, 2> ts = { 0, 1 };
size_t indexNeg2 = 0;
size_t indexNeg1 = 1;
// loop
while (1)
{
// calculate our new quotient and remainder
TINT newQuotient = remainders[indexNeg2] / remainders[indexNeg1];
TINT newRemainder = remainders[indexNeg2] - newQuotient * remainders[indexNeg1];
// if our remainder is zero we are done.
if (newRemainder == 0)
{
// return our s and t values as well as the quotient as the GCD
s = ss[indexNeg1];
t = ts[indexNeg1];
if (swapped)
std::swap(s, t);
// if t < 0, add the modulus divisor to it, to make it positive
if (t < 0)
t += smaller;
return remainders[indexNeg1];
}
// calculate this round's s and t
TINT newS = ss[indexNeg2] - newQuotient * ss[indexNeg1];
TINT newT = ts[indexNeg2] - newQuotient * ts[indexNeg1];
// store our values for the next iteration
remainders[indexNeg2] = newRemainder;
ss[indexNeg2] = newS;
ts[indexNeg2] = newT;
// move to the next iteration
std::swap(indexNeg1, indexNeg2);
}
}
//=================================================================================
void MakeKey (TINTVec &keys, TINT &keysLCM, size_t index)
{
// if this is the first key, use 3
if (index == 0)
{
keys[index] = 3;
keysLCM = keys[index];
return;
}
// Else start at the last number and keep checking odd numbers beyond that
// until you find one that is co-prime.
TINT nextNumber = keys[index - 1];
while (1)
{
nextNumber += 2;
if (std::all_of(
keys.begin(),
keys.begin() + index,
[&nextNumber] (const TINT& v) -> bool
{
TINT s, t;
return ExtendedEuclidianAlgorithm(v, nextNumber, s, t) == 1;
}))
{
keys[index] = nextNumber;
keysLCM *= nextNumber;
return;
}
}
}
//=================================================================================
void CalculateLookupTable (
TINT &lut,
const std::vector<uint64_t> &output,
const TINTVec &keys,
const TINT &keysLCM,
const TINTVec &coefficients,
size_t bitMask
)
{
// figure out how much to multiply each coefficient by to make it have the specified modulus residue (remainder)
lut = 0;
for (size_t i = 0, c = keys.size(); i < c; ++i)
{
// we either want this term to be 0 or 1 mod the key. if zero, we can multiply by zero, and
// not add anything into the bit value!
if ((output[i] & bitMask) == 0)
continue;
// if 1, use chinese remainder theorem
TINT s, t;
ExtendedEuclidianAlgorithm(coefficients[i], keys[i], s, t);
lut = (lut + ((coefficients[i] * t) % keysLCM)) % keysLCM;
}
}
//=================================================================================
template <typename TINPUT, typename TOUTPUT, typename LAMBDA>
void MakeModulus (TINTVec &luts, TINTVec &keys, LAMBDA &lambda)
{
// to keep things simple, input sizes are being constrained.
// Do this in x64 instead of win32 to make size_t 8 bytes instead of 4
static_assert(sizeof(TINPUT) < sizeof(size_t), "Input too large");
static_assert(sizeof(TOUTPUT) < sizeof(uint64_t), "Output too large");
// calculate some constants
const size_t c_numInputBits = sizeof(TINPUT) * 8;
const size_t c_numInputValues = 1 << c_numInputBits;
const size_t c_numOutputBits = sizeof(TOUTPUT) * 8;
// Generate the keys (coprimes)
TINT keysLCM;
keys.resize(c_numInputValues);
for (size_t index = 0; index < c_numInputValues; ++index)
MakeKey(keys, keysLCM, index);
// calculate co-efficients for use in the chinese remainder theorem
TINTVec coefficients;
coefficients.resize(c_numInputValues);
fill(coefficients.begin(), coefficients.end(), 1);
for (size_t i = 0; i < c_numInputValues; ++i)
{
for (size_t j = 0; j < c_numInputValues; ++j)
{
if (i != j)
coefficients[i] *= keys[j];
}
}
// gather all the input to output mappings by permuting the input space
// and storing the output for each input index
std::vector<uint64_t> output;
output.resize(c_numInputValues);
union
{
TINPUT value;
size_t index;
} input;
union
{
TOUTPUT value;
size_t index;
} outputConverter;
for (input.index = 0; input.index < c_numInputValues; ++input.index)
{
outputConverter.value = lambda(input.value);
output[input.index] = outputConverter.index;
}
// iterate through each possible output bit, since each bit is it's own lut
luts.resize(c_numOutputBits);
for (size_t i = 0; i < c_numOutputBits; ++i)
{
const size_t bitMask = 1 << i;
CalculateLookupTable(
luts[i],
output,
keys,
keysLCM,
coefficients,
bitMask
);
}
}
//=================================================================================
int main (int argc, char **argv)
{
// Look up tables encodes each bit, keys is used to decode each bit for specific
// input values.
TINTVec luts;
TINTVec keys;
// this is the function that it turns into modulus work
typedef uint8_t TINPUT;
typedef float TOUTPUT;
auto lambda = [] (TINPUT input) -> TOUTPUT
{
return sin(((TOUTPUT)input) / 255.0f * 2.0f * c_pi);
};
MakeModulus<TINPUT, TOUTPUT>(luts, keys, lambda);
// show last lut and key to show what kind of numbers they are
std::cout << "Last Lut: " << *luts.rbegin() << "\n";
std::cout << "Last Key: " << *keys.rbegin() << "\n";
// Decode all input values
std::cout << "\n" << sizeof(TINPUT) << " bytes input, " << sizeof(TOUTPUT) << " bytes output\n";
for (size_t keyIndex = 0, keyCount = keys.size(); keyIndex < keyCount; ++keyIndex)
{
union
{
TOUTPUT value;
size_t index;
} result;
result.index = 0;
for (size_t lutIndex = 0, lutCount = luts.size(); lutIndex < lutCount; ++lutIndex)
{
TINT remainder = luts[lutIndex] % keys[keyIndex];
size_t remainderSizeT = size_t(remainder);
result.index += (remainderSizeT << lutIndex);
}
TINT remainder = luts[0] % keys[keyIndex];
std::cout << "i:" << keyIndex << " o:" << result.value << "\n";
}
WaitForEnter();
return 0;
}
/*
TODO:
? is union thing safe?
* talk about needing a large number of co-primes. That this is what it's for (in FB post).
*/