Repository navigation
Expand file tree
/
Copy pathtests.cpp
More file actions
1961 lines (1656 loc) · 84.1 KB
/
Copy pathtests.cpp
File metadata and controls
1961 lines (1656 loc) · 84.1 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
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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "tests.h"
void save_load_test(string path, string source) {
string x0, w1, b1, w2, b2, fcW, fc_b;
x0.append(path);
x0.append(source);
vector<vector<double> > input = readCSV(x0); // read MNIST number
// Create encryption parameters
EncryptionParameters parms;
parms.set_poly_modulus("1x^2048 + 1");
parms.set_coeff_modulus(ChooserEvaluator::default_parameter_options().at(4096));
parms.set_plain_modulus(1 << 8);
//parms.set_decomposition_bit_count(16); // this number needs to be small to decrease noise;
parms.set_decomposition_bit_count(32); // this number needs to be small to decrease noise;
parms.validate(); // parms.validate() is the end of parms creation
// Generate keys.
cout << "Generating keys ..." << endl;
KeyGenerator generator(parms);
generator.generate(4); // int parameter is the evaluation key count
cout << "... key generation complete" << endl;
Ciphertext public_key = generator.public_key();
saveCipher(public_key, path + "/ciphertext/publickey.txt");
Plaintext secret_key = generator.secret_key();
savePlain(secret_key, path + "/ciphertext/secretkey.txt");
EvaluationKeys evaluation_keys = generator.evaluation_keys();
/*
We will need a fractional encoder for dealing with the rational numbers. Here we reserve
64 coefficients of the polynomial for the integral part (low-degree terms) and expand the
fractional part to 32 terms of precision (base 3) (high-degree terms).
*/
FractionalEncoder encoder(parms.plain_modulus(), parms.poly_modulus(), 1, 8, 2);
// Create encryptor, evaluator, decryptor
Encryptor encryptor(parms, public_key);
Evaluator evaluator(parms, evaluation_keys);
Decryptor decryptor(parms, secret_key);
Plaintext testPlain = encoder.encode(input[0][0]);
savePlain(testPlain, path + "/ciphertext/plaintxt00.txt");
// decode
double dx = encoder.decode(testPlain);
cout << dx << endl;
cout << "encrypting x: " << endl;
vector<vector<Ciphertext>> x = encrypt(input, encoder, encryptor); // encryption
path.append("/ciphertext/");
cout << "Saving ciphertext" << endl;
ciphertextSave2D(x, path);
cout << "Saving keys" << endl;
saveKeys(public_key, secret_key, evaluation_keys, path);
cout << "Saving parms" << endl;
saveParms(parms, path);
//save cipher text into txt file
saveCipher(x[0][0], path + "ciphertxt00.txt");
cout << "Loading" << endl;
int rowSize = x.size();
int colSize = x[0].size();
vector<vector<Ciphertext>> res;
ciphertextLoad2D(res, path);
for (int i = 0; i < x.size(); i++) {
for (int j = 0; j < x[0].size(); j++) {
Plaintext tr = decryptor.decrypt(x[i][j]);
double tr_double = encoder.decode(tr);
Plaintext pr = decryptor.decrypt(res[i][j]);
double pr_double = encoder.decode(pr);
if (tr_double == pr_double)
printf("[%d, %d] matched", i, j);
else {
printf("[%d, %d] not matched", i, j);
break;
}
}
}
saveCipher(res[0][0], path + "test2.txt");
}
void average_pooling_test(string path) {
path.append("/ciphertext/");
cout << "Loading parms ..." << endl;
EncryptionParameters parms;
loadParms(parms, path);
parms.validate(); // parms.validate() is the end of parms creation
cout << "Loading keys ..." << endl;
Ciphertext public_key = Ciphertext::Ciphertext(BigPolyArray());
Plaintext secret_key;
EvaluationKeys evaluation_keys;
loadKeys(public_key, secret_key, evaluation_keys, path);
// Create encryptor, evaluator, decryptor
Decryptor decryptor(parms, secret_key);
Encryptor encryptor(parms, public_key);
Evaluator evaluator(parms, evaluation_keys);
/*
We will need a fractional encoder for dealing with the rational numbers. Here we reserve
64 coefficients of the polynomial for the integral part (low-degree terms) and expand the
fractional part to 32 terms of precision (base 3) (high-degree terms).
*/
FractionalEncoder encoder(parms.plain_modulus(), parms.poly_modulus(), 64, 32, 3);
cout << "Loading" << endl;
int rowSize = 2;
int colSize = 16;
vector<vector<Ciphertext>> loaded;
ciphertextLoad2D(loaded, path);
//averaging
vector<vector<Ciphertext>> res;
res = averagePooling2D(loaded, 2, 2, encoder, evaluator);
// decode
vector<vector<double>> output;
for (int i = 0; i < res.size(); i++) {
vector<double> output1D;
for (int j = 0; j < res[0].size(); j++) {
Plaintext pr = decryptor.decrypt(res[i][j]);
double pr_double = encoder.decode(pr);
output1D.emplace_back(pr_double);
}
output.emplace_back(output1D);
}
return;
}
void monitor_test(string path, string source) {
string x0, w1, b1, w2, b2, fcW, fc_b;
x0.append(path);
x0.append(source);
vector<vector<double> > input = readCSV(x0); // read MNIST number
// Create encryption parameters
EncryptionParameters parms;
parms.set_poly_modulus("1x^4096 + 1");
parms.set_coeff_modulus(ChooserEvaluator::default_parameter_options().at(4096));
parms.set_plain_modulus(1 << 8);
//parms.set_decomposition_bit_count(16); // this number needs to be small to decrease noise;
parms.set_decomposition_bit_count(32); // this number needs to be small to decrease noise;
parms.validate(); // parms.validate() is the end of parms creation
// Generate keys.
cout << "Generating keys ..." << endl;
KeyGenerator generator(parms);
generator.generate(4); // int parameter is the evaluation key count
cout << "... key generation complete" << endl;
Ciphertext public_key = generator.public_key();
saveCipher(public_key, path + "/ciphertext/publickey.txt");
Plaintext secret_key = generator.secret_key();
savePlain(secret_key, path + "/ciphertext/secretkey.txt");
EvaluationKeys evaluation_keys = generator.evaluation_keys();
/*
We will need a fractional encoder for dealing with the rational numbers. Here we reserve
64 coefficients of the polynomial for the integral part (low-degree terms) and expand the
fractional part to 32 terms of precision (base 3) (high-degree terms).
*/
FractionalEncoder encoder(parms.plain_modulus(), parms.poly_modulus(), 16, 32, 2);
// Create encryptor, evaluator, decryptor
Encryptor encryptor(parms, public_key);
Evaluator evaluator(parms, evaluation_keys);
Decryptor decryptor(parms, secret_key);
cout << "original: " << input[0][0] << endl;
Plaintext testPlain = encoder.encode(input[0][0]);
savePlain(testPlain, path + "/ciphertext/plaintxt00.txt");
cout << "Plaintext: " << returnPlain(testPlain) << endl;
// decode
double test = encoder.decode(testPlain);
cout << "Decode: " << test << endl;
cout << "encrypting x[0][0]: " << endl;
Ciphertext x = Ciphertext(BigPolyArray());
encryptor.encrypt(testPlain, x); // encryption
cout << "Noise budget in encryption of x[0][0]" << ": " << decryptor.invariant_noise_budget(x) << " bits" << endl;
saveCipher(x, path + "/ciphertext/ciphertxt00.txt");
cout << "decrypting x[0][0]: " << endl;
Plaintext dx = decryptor.decrypt(x);
savePlain(dx, path + "/ciphertext/dec_plaintxt00.txt");
cout << "Decrypt Plaintext: " << returnPlain(dx) << endl;
// decode again
double test2 = encoder.decode(dx);
cout << "Decode again: " << test2 << endl;
// multiply
cout << "Calculate square: " << endl;
Ciphertext x2 = Ciphertext(BigPolyArray());
x2 = evaluator.square(x);
saveCipher(x2, path + "/ciphertext/square_ciphertxt00.txt");
cout << "Noise budget in encryption of x^2" << ": " << decryptor.invariant_noise_budget(x2) << " bits" << endl;
Plaintext dx2 = decryptor.decrypt(x2);
savePlain(dx2, path + "/ciphertext/square_plaintxt00.txt");
cout << "Decrypt square plaintext: " << returnPlain(dx2) << endl;
double testsquare = encoder.decode(dx2);
cout << "Square: " << testsquare << endl;
cout << "True Square: " << test2 * test2 << endl;
cout << "Calculate square square: " << endl;
Ciphertext x4 = Ciphertext(BigPolyArray());
x4 = evaluator.square(x2);
saveCipher(x4, path + "/ciphertext/square_square_ciphertxt00.txt");
cout << "Noise budget in encryption of x^4" << ": " << decryptor.invariant_noise_budget(x4) << " bits" << endl;
Plaintext dx4 = decryptor.decrypt(x4);
savePlain(dx4, path + "/ciphertext/square_square_plaintxt00.txt");
cout << "Decrypt square sqaure plaintext: " << returnPlain(dx4) << endl;
double testsquaresquare = encoder.decode(dx4);
cout << "Square sqaure: " << testsquaresquare << endl;
cout << "True Square sqaure: " << test2 * test2 * test2 * test2 << endl;
cout << "Calculate square square square: " << endl;
Ciphertext x8 = Ciphertext(BigPolyArray());
x8 = evaluator.square(x4);
saveCipher(x8, path + "/ciphertext/square_square_square_ciphertxt00.txt");
cout << "Noise budget in encryption of x^8" << ": " << decryptor.invariant_noise_budget(x8) << " bits" << endl;
Plaintext dx8 = decryptor.decrypt(x8);
savePlain(dx8, path + "/ciphertext/square_square_square_plaintxt00.txt");
cout << "Decrypt square sqaure plaintext: " << returnPlain(dx8) << endl;
double testsquaresquaresquare = encoder.decode(dx8);
cout << "Square sqaure square: " << testsquaresquaresquare << endl;
cout << "True Square sqaure square: " << test2 * test2 * test2 * test2 * test2 * test2 * test2 * test2 << endl;
// load the pre-existing files
/*path.append("/ciphertext/");
cout << "Loading" << endl;
vector<vector<Ciphertext>> res;
ciphertextLoad2D(res, path);
saveCipher(res[0][0], path + "test.txt");*/
}
bool ifDecodeCorrect(double number, FractionalEncoder encoder, double residue) {
//cout << "Encoding " << number << "..." << endl;
Plaintext testPlain = encoder.encode(number);
double dn = encoder.decode(testPlain);
cout << number << ": " << dn << endl;
if ((number > residue) && (abs(dn - number) > residue))
return false;
return true;
}
void encode_test(string path) {
EncryptionParameters parms;
parms.set_poly_modulus("1x^4096 + 1");
parms.set_coeff_modulus(ChooserEvaluator::default_parameter_options().at(4096));
parms.set_plain_modulus(1 << 16);
//parms.set_decomposition_bit_count(16); // this number needs to be small to decrease noise;
parms.set_decomposition_bit_count(32); // this number needs to be small to decrease noise;
parms.validate(); // parms.validate() is the end of parms creation
// Generate keys.
cout << "Generating keys ..." << endl;
KeyGenerator generator(parms);
generator.generate(4); // int parameter is the evaluation key count
cout << "... key generation complete" << endl;
Ciphertext public_key = generator.public_key();
saveCipher(public_key, path + "/publickey.txt");
Plaintext secret_key = generator.secret_key();
savePlain(secret_key, path + "/secretkey.txt");
EvaluationKeys evaluation_keys = generator.evaluation_keys();
// encode
FractionalEncoder encoder(parms.plain_modulus(), parms.poly_modulus(), 2, 16, 2);
// Create encryptor, evaluator, decryptor
Encryptor encryptor(parms, public_key);
Evaluator evaluator(parms, evaluation_keys);
Decryptor decryptor(parms, secret_key);
// test 1
//double base = 3;
//double power = 0;
//double residue = 1e-9;
//while (ifDecodeCorrect(pow(base, power), encoder, residue)) {
// if (pow(base, power) < residue) {
// cout << "Number is too small!" << endl;
// break;
// }
// cout << "Encode/decode " << base << "^" << power << " correctly" << endl;
// power--;
//}
//cout << "Encode/decode " << base << "^" << power << " incorrectly" << endl;
//test 2
double test = pow(3.0, -1.0);
Plaintext testPlain = encoder.encode(test);
savePlain(testPlain, path + "/plaintxt.txt");
cout << "encrypting 3^-1: " << endl;
Ciphertext x = Ciphertext(BigPolyArray());
encryptor.encrypt(testPlain, x); // encryption
cout << "Noise budget in encryption of 3^-1" << ": " << decryptor.invariant_noise_budget(x) << " bits" << endl;
saveCipher(x, path + "/ciphertxt.txt");
// multiply
cout << "Calculate square: " << endl;
Ciphertext x2 = Ciphertext(BigPolyArray());
x2 = evaluator.square(x);
saveCipher(x2, path + "/square_ciphertxt.txt");
cout << "Noise budget in encryption of x^2" << ": " << decryptor.invariant_noise_budget(x2) << " bits" << endl;
Plaintext dx2 = decryptor.decrypt(x2);
savePlain(dx2, path + "/square_plaintxt.txt");
cout << "Decrypt square plaintext: " << returnPlain(dx2) << endl;
double testsquare = encoder.decode(dx2);
cout << "Square: " << testsquare << endl;
cout << "True Square: " << test * test << endl;
}
void timing_test(string path)
{
print_example_banner("Example: Timing of basic operations");
auto performance_test = [](EncryptionParameters &parms, string path)
{
auto poly_modulus = parms.poly_modulus();
auto coeff_modulus = parms.coeff_modulus();
auto plain_modulus = parms.plain_modulus();
// Generate keys.
KeyGenerator generator(parms);
generator.generate(4); // int parameter is the evaluation key count
Ciphertext public_key = generator.public_key();
Plaintext secret_key = generator.secret_key();
EvaluationKeys evaluation_keys = generator.evaluation_keys();
FractionalEncoder encoder(parms.plain_modulus(), parms.poly_modulus(), 16, 32, 2);
// Create encryptor, evaluator, decryptor
Encryptor encryptor(parms, public_key);
Evaluator evaluator(parms, evaluation_keys);
Decryptor decryptor(parms, secret_key);
chrono::microseconds time_encode_sum(0);
chrono::microseconds time_encrypt_sum(0);
chrono::microseconds time_multiply_sum(0);
chrono::microseconds time_square_sum(0);
chrono::microseconds time_relinearize_sum(0);
chrono::microseconds time_decrypt_sum(0);
chrono::microseconds time_decode_sum(0);
cout << "Encryption parameters:" << endl;
cout << "{ poly_modulus: " << poly_modulus.to_string() << endl;
cout << "{ coeff_modulus: " << coeff_modulus.to_string() << endl;
cout << "{ plain_modulus: " << plain_modulus.to_dec_string() << endl;
cout << "{ decomposition_bit_count: " << parms.decomposition_bit_count() << endl;
cout << "{ noise_standard_deviation: " << parms.noise_standard_deviation() << endl;
cout << "{ noise_max_deviation: " << parms.noise_max_deviation() << endl << endl;
// save to a txt file
string file = path + "basic_timing.txt";
ofstream outputFile(file);
outputFile << "Encryption parameters:" << endl;
outputFile << "{ poly_modulus: " << poly_modulus.to_string() << endl;
outputFile << "{ coeff_modulus: " << coeff_modulus.to_string() << endl;
outputFile << "{ plain_modulus: " << plain_modulus.to_dec_string() << endl;
outputFile << "{ decomposition_bit_count: " << parms.decomposition_bit_count() << endl;
outputFile << "{ noise_standard_deviation: " << parms.noise_standard_deviation() << endl;
outputFile << "{ noise_max_deviation: " << parms.noise_max_deviation() << endl << endl;
int count = 100;
cout << "Running tests ";
for (int i = 0; i < count; ++i)
{
auto time_start = chrono::high_resolution_clock::now();
auto tmp = (double)rand() / RAND_MAX;
auto plain1 = encoder.encode(tmp);
auto plain2 = encoder.encode(tmp + 1);
auto time_encoded = chrono::high_resolution_clock::now();
auto enc1 = encryptor.encrypt(plain1);
auto enc2 = encryptor.encrypt(plain2);
auto time_encrypted = chrono::high_resolution_clock::now();
auto enc_prod = evaluator.multiply(enc1, enc2);
auto time_multiplied = chrono::high_resolution_clock::now();
auto enc_square = evaluator.square(enc1);
auto time_squared = chrono::high_resolution_clock::now();
auto enc_relin_prod = evaluator.relinearize(enc_prod);
auto time_relinearized = chrono::high_resolution_clock::now();
auto plain_prod = decryptor.decrypt(enc_relin_prod);
auto time_decrypted = chrono::high_resolution_clock::now();
int32_t result = encoder.decode(plain_prod);
auto time_decoded = chrono::high_resolution_clock::now();
//// Check the result
//int correct_result = i * (i + 1);
//if (result != correct_result)
//{
// cout << "Something went wrong (result " << result << " != " << correct_result << ")!" << endl;
//}
if (i % 10 == 0 && i > 0)
{
cout << ".";
cout.flush();
}
time_encode_sum += chrono::duration_cast<chrono::microseconds>(time_encoded - time_start);
time_encrypt_sum += chrono::duration_cast<chrono::microseconds>(time_encrypted - time_encoded);
time_multiply_sum += chrono::duration_cast<chrono::microseconds>(time_multiplied - time_encrypted);
time_square_sum += chrono::duration_cast<chrono::microseconds>(time_squared - time_multiplied);
time_relinearize_sum += chrono::duration_cast<chrono::microseconds>(time_relinearized - time_squared);
time_decrypt_sum += chrono::duration_cast<chrono::microseconds>(time_decrypted - time_relinearized);
time_decode_sum += chrono::duration_cast<chrono::microseconds>(time_decoded - time_decrypted);
}
cout << " done." << endl << endl;
cout.flush();
auto avg_encode = time_encode_sum.count() / (2 * count);
auto avg_encrypt = time_encrypt_sum.count() / (2 * count);
auto avg_multiply = time_multiply_sum.count() / count;
auto avg_square = time_square_sum.count() / count;
auto avg_relinearize = time_relinearize_sum.count() / count;
auto avg_decrypt = time_decrypt_sum.count() / count;
auto avg_decode = time_decode_sum.count() / count;
cout << "Average encode: " << avg_encode << " microseconds" << endl;
cout << "Average encrypt: " << avg_encrypt << " microseconds" << endl;
cout << "Average multiply: " << avg_multiply << " microseconds" << endl;
cout << "Average square: " << avg_square << " microseconds" << endl;
cout << "Average relinearize: " << avg_relinearize << " microseconds" << endl;
cout << "Average decrypt: " << avg_decrypt << " microseconds" << endl;
cout << "Average decode: " << avg_decode << " microseconds" << endl;
outputFile << "Average encode: " << avg_encode << " microseconds" << endl;
outputFile << "Average encrypt: " << avg_encrypt << " microseconds" << endl;
outputFile << "Average multiply: " << avg_multiply << " microseconds" << endl;
outputFile << "Average square: " << avg_square << " microseconds" << endl;
outputFile << "Average relinearize: " << avg_relinearize << " microseconds" << endl;
outputFile << "Average decrypt: " << avg_decrypt << " microseconds" << endl;
outputFile << "Average decode: " << avg_decode << " microseconds" << endl;
};
// Create encryption parameters
EncryptionParameters parms;
parms.set_poly_modulus("1x^8192 + 1");
parms.set_coeff_modulus(ChooserEvaluator::default_parameter_options().at(8192));
parms.set_plain_modulus(1 << 30);
//parms.set_decomposition_bit_count(16); // this number needs to be small to decrease noise;
parms.set_decomposition_bit_count(16); // this number needs to be small to decrease noise;
parms.validate(); // parms.validate() is the end of parms creation
performance_test(parms, path);
cout << endl;
// define the input
int width = 32;
int height = 24;
vector<vector<double>> input(height, vector<double>(width, 0));
// Filled with random number [0,1]
for (int i = 0; i < input.size(); i++) {
for (int j = 0; j < input[0].size(); j++) {
input[i][j] = ((double)rand() / RAND_MAX);
}
}
print_example_banner("Example: Timing of Mimic CNN");
// Output to the screen
cout << "Input size: W = " << width << " H = " << height << endl;
cout << "Encryption parameters:" << endl;
cout << "{ poly_modulus: " << parms.poly_modulus().to_string() << endl;
cout << "{ coeff_modulus: " << parms.coeff_modulus().to_string() << endl;
cout << "{ plain_modulus: " << parms.plain_modulus().to_dec_string() << endl;
cout << "{ decomposition_bit_count: " << parms.decomposition_bit_count() << endl;
cout << "{ noise_standard_deviation: " << parms.noise_standard_deviation() << endl;
cout << "{ noise_max_deviation: " << parms.noise_max_deviation() << endl << endl;
// save to a txt file
string file = path + "timing.txt";
ofstream outputFile(file);
outputFile << "Input size: W = " << width << " H = " << height << endl;
outputFile << "Encryption parameters:" << endl;
outputFile << "{ poly_modulus: " << parms.poly_modulus().to_string() << endl;
outputFile << "{ coeff_modulus: " << parms.coeff_modulus().to_string() << endl;
outputFile << "{ plain_modulus: " << parms.plain_modulus().to_dec_string() << endl;
outputFile << "{ decomposition_bit_count: " << parms.decomposition_bit_count() << endl;
outputFile << "{ noise_standard_deviation: " << parms.noise_standard_deviation() << endl;
outputFile << "{ noise_max_deviation: " << parms.noise_max_deviation() << endl << endl;
// Generate keys.
cout << "Generating keys ..." << endl;
KeyGenerator generator(parms);
generator.generate(4); // int parameter is the evaluation key count
cout << "... key generation complete" << endl;
Ciphertext public_key = generator.public_key();
Plaintext secret_key = generator.secret_key();
EvaluationKeys evaluation_keys = generator.evaluation_keys();
FractionalEncoder encoder(parms.plain_modulus(), parms.poly_modulus(), 16, 32, 2);
// Create encryptor, evaluator, decryptor
Encryptor encryptor(parms, public_key);
Evaluator evaluator(parms, evaluation_keys);
Decryptor decryptor(parms, secret_key);
// timing
chrono::milliseconds time_encode(0);
chrono::milliseconds time_encrypt(0);
chrono::milliseconds time_CNN(0);
chrono::milliseconds time_decrypt(0);
chrono::milliseconds time_decode(0);
// mimic CNN inference
// temporary variables
vector<Plaintext> plainText{};
vector<Ciphertext> cipherText{};
cout << "encoding input: " << endl;
auto time_encode_start = chrono::high_resolution_clock::now();
for (int i = 0; i < input.size(); i++)
for (int j = 0; j < input[0].size(); j++) {
plainText.emplace_back(encoder.encode(input[i][j]));
}
auto time_encode_end = chrono::high_resolution_clock::now();
time_encode = chrono::duration_cast<chrono::milliseconds>(time_encode_end - time_encode_start);
cout << time_encode.count() << " MilliSeconds" << endl;
outputFile << "Fractional encoding: " << time_encode.count() << " MilliSeconds" << endl;
cout << "encrypting input: " << endl;
auto time_encrypt_start = chrono::high_resolution_clock::now();
for (int i = 0; i < (input.size() * input[0].size() - 1); i++) {
cipherText.emplace_back(encryptor.encrypt(plainText[i]));
}
auto time_encrypt_end = chrono::high_resolution_clock::now();
time_encrypt = chrono::duration_cast<chrono::milliseconds>(time_encrypt_end - time_encrypt_start);
cout << time_encode.count() << " MilliSeconds" << endl;
outputFile << "encrypting: " << time_encode.count() << " MilliSeconds" << endl;
cout << "Simulating CNN: " << endl;
auto time_CNN_start = chrono::high_resolution_clock::now();
for (int i = 0; i < ( input.size() * input[0].size() -1 ) ; i++) {
cipherText[i] = evaluator.multiply_plain(cipherText[i], encoder.encode(0.5));
cipherText[i] = evaluator.square(cipherText[i]);
}
auto time_CNN_end = chrono::high_resolution_clock::now();
time_CNN = chrono::duration_cast<chrono::milliseconds>(time_CNN_end - time_CNN_start);
cout << time_CNN.count() << " MilliSeconds" << endl;
outputFile << "Mimic CNN: " << time_CNN.count() << " MilliSeconds" << endl;
cout << "decrypting output: " << endl;
auto time_decrypt_start = chrono::high_resolution_clock::now();
for (int i = 0; i < (input.size() * input[0].size() - 1); i++) {
plainText[i] = decryptor.decrypt(cipherText[i]);
}
auto time_decrypt_end = chrono::high_resolution_clock::now();
time_decrypt = chrono::duration_cast<chrono::milliseconds>(time_decrypt_end - time_decrypt_start);
cout << time_decrypt.count() << " MilliSeconds" << endl;
outputFile << "decrypting: " << time_decrypt.count() << " MilliSeconds" << endl;
cout << "decoding output: " << endl;
auto time_decode_start = chrono::high_resolution_clock::now();
for (int i = 0; i < (input.size() * input[0].size() - 1); i++) {
double output = encoder.decode(plainText[i]);
}
auto time_decode_end = chrono::high_resolution_clock::now();
time_decode = chrono::duration_cast<chrono::milliseconds>(time_decode_end - time_decode_start);
cout << time_decode.count() << " MilliSeconds" << endl;
outputFile << "decoding: " << time_decode.count() << " MilliSeconds" << endl;
}
void CNN_test(string path) {
string x0, w1, b1, w2, b2, fcW, fc_b;
x0.append(path);
x0.append("/9.csv");
vector<vector<double> > input = readCSV(x0); // read MNIST number
// Create encryption parameters
EncryptionParameters parms;
parms.set_poly_modulus("1x^8192 + 1");
parms.set_coeff_modulus(ChooserEvaluator::default_parameter_options().at(8192));
parms.set_plain_modulus(1 << 30);
//parms.set_decomposition_bit_count(16); // this number needs to be small to decrease noise;
parms.set_decomposition_bit_count(16); // this number needs to be small to decrease noise;
parms.validate(); // parms.validate() is the end of parms creation
// Generate keys.
cout << "Generating keys ..." << endl;
KeyGenerator generator(parms);
generator.generate(4); // int parameter is the evaluation key count
cout << "... key generation complete" << endl;
Ciphertext public_key = generator.public_key();
Plaintext secret_key = generator.secret_key();
EvaluationKeys evaluation_keys = generator.evaluation_keys();
/*
We will need a fractional encoder for dealing with the rational numbers. Here we reserve
64 coefficients of the polynomial for the integral part (low-degree terms) and expand the
fractional part to 32 terms of precision (base 3) (high-degree terms).
*/
FractionalEncoder encoder(parms.plain_modulus(), parms.poly_modulus(), 16, 32, 2);
// Create encryptor, evaluator, decryptor
Encryptor encryptor(parms, public_key);
Evaluator evaluator(parms, evaluation_keys);
Decryptor decryptor(parms, secret_key);
// timing
chrono::milliseconds time_encrypt(0);
chrono::milliseconds time_CNN(0);
chrono::milliseconds time_decrypt(0);
chrono::milliseconds time_decode(0);
cout << "encrypting x: " << endl;
auto time_encrypt_start = chrono::high_resolution_clock::now();
vector<vector<Ciphertext>> x = encrypt(input, encoder, encryptor); // encryption
auto time_encrypt_end = chrono::high_resolution_clock::now();
cout << "CNN calculation: " << endl;
auto time_CNN_start = chrono::high_resolution_clock::now();
vector<Ciphertext> cnnEncrptRes = cnnCalMnist(x, encoder, evaluator, decryptor, path); // calculation // !!!!!! remember to take out the decryptor after development is done!
auto time_CNN_end = chrono::high_resolution_clock::now();
// Print size and noise budget of result.
cout << "Size of weightedSum without relinearization: " << cnnEncrptRes[0].size() << endl;
cout << "Noise budget in weightedSum without relinearization: " << decryptor.invariant_noise_budget(cnnEncrptRes[0]) << " bits" << endl;
// decrypt
vector<Plaintext> decryptedCnnRes;
auto time_decrypt_start = chrono::high_resolution_clock::now();
for (int i = 0; i < cnnEncrptRes.size(); i++) {
decryptedCnnRes.emplace_back(decryptor.decrypt(cnnEncrptRes[i]));
}
auto time_decrypt_end = chrono::high_resolution_clock::now();
// decode
cout << "---------------- Final results: -------------------------" << endl;
auto time_decode_start = chrono::high_resolution_clock::now();
for (int i = 0; i < decryptedCnnRes.size(); i++) {
cout << encoder.decode(decryptedCnnRes[i]) << endl;
}
auto time_decode_end = chrono::high_resolution_clock::now();
time_encrypt = chrono::duration_cast<chrono::milliseconds>(time_encrypt_end - time_encrypt_start);
time_CNN = chrono::duration_cast<chrono::milliseconds>(time_CNN_end - time_CNN_start);
time_decrypt = chrono::duration_cast<chrono::milliseconds>(time_decrypt_end - time_decrypt_start);
time_decode = chrono::duration_cast<chrono::milliseconds>(time_decode_end - time_decode_start);
// save to a txt file
string file = "./CNN_cpp/test/CNNtiming.txt";
ofstream outputFile(file);
outputFile << "Encryption parameters:" << endl;
outputFile << "{ poly_modulus: " << parms.poly_modulus().to_string() << endl;
outputFile << "{ coeff_modulus: " << parms.coeff_modulus().to_string() << endl;
outputFile << "{ plain_modulus: " << parms.plain_modulus().to_dec_string() << endl;
outputFile << "{ decomposition_bit_count: " << parms.decomposition_bit_count() << endl;
outputFile << "{ noise_standard_deviation: " << parms.noise_standard_deviation() << endl;
outputFile << "{ noise_max_deviation: " << parms.noise_max_deviation() << endl << endl;
outputFile << "encode/encrypt: " << time_encrypt.count() << " milliseconds" << endl;
outputFile << "CNN: " << time_CNN.count() << " milliseconds" << endl;
outputFile << "decrypt: " << time_decrypt.count() << " milliseconds" << endl;
outputFile << "decode: " << time_decode.count() << " milliseconds" << endl;
}
void CNN_test_slim(string path, int digit) {
//
string x0, w1, b1, w2, b2, fcW, fc_b;
x0.append(path);
x0.append("/");
x0.append(to_string(digit));
x0.append(".csv");
if (digit < 0 || digit > 9) {
cout << "wrong input: do not input digit outside of 0 - 9" << endl;
}
vector<vector<double> > input = readCSV(x0); // read MNIST number
// Create encryption parameters
EncryptionParameters parms;
parms.set_poly_modulus("1x^8192 + 1"); // 8192 works
parms.set_coeff_modulus(ChooserEvaluator::default_parameter_options().at(8192)); //8192 works
parms.set_plain_modulus(1 << 28); // 1 << 30 works
//parms.set_decomposition_bit_count(16); // this number needs to be small to decrease noise;
parms.set_decomposition_bit_count(16); // this number needs to be small to decrease noise;
parms.validate(); // parms.validate() is the end of parms creation
// Generate keys.
cout << "Slim version _v1.0 _ Generating keys ..." << endl;
KeyGenerator generator(parms);
generator.generate(4); // int parameter is the evaluation key count
cout << "... key generation complete" << endl;
Ciphertext public_key = generator.public_key();
Plaintext secret_key = generator.secret_key();
EvaluationKeys evaluation_keys = generator.evaluation_keys();
/*
We will need a fractional encoder for dealing with the rational numbers. Here we reserve
64 coefficients of the polynomial for the integral part (low-degree terms) and expand the
fractional part to 32 terms of precision (base 3) (high-degree terms).
*/
FractionalEncoder encoder(parms.plain_modulus(), parms.poly_modulus(), 16, 32, 2);
// Create encryptor, evaluator, decryptor
Encryptor encryptor(parms, public_key);
Evaluator evaluator(parms, evaluation_keys);
Decryptor decryptor(parms, secret_key);
cout << "encrypting x: " << endl;
vector<vector<Ciphertext>> x = encrypt(input, encoder, encryptor); // encryption
cout << "CNN calculation: " << endl;
vector<Ciphertext> cnnEncrptRes = cnnCalMnist_slim(x, encoder, evaluator, decryptor, path); // calculation // !!!!!! remember to take out the decryptor after development is done!
// Print size and noise budget of result.
cout << "Size of weightedSum without relinearization: " << cnnEncrptRes[0].size() << endl;
cout << "Noise budget in weightedSum without relinearization: " << decryptor.invariant_noise_budget(cnnEncrptRes[0]) << " bits" << endl;
// decrypt
vector<Plaintext> decryptedCnnRes;
vector<double> finalResults;
for (int i = 0; i < cnnEncrptRes.size(); i++) {
decryptedCnnRes.emplace_back(decryptor.decrypt(cnnEncrptRes[i]));
}
// decode
cout << "------------------- Final results: -------------------------" << endl;
for (int i = 0; i < decryptedCnnRes.size(); i++) {
cout << encoder.decode(decryptedCnnRes[i]) << endl;
finalResults.emplace_back(encoder.decode(decryptedCnnRes[i]));
}
double max = finalResults.at(0);
int idx = 0;
for (int i = 1; i < finalResults.size(); i++) {
if (finalResults.at(i) > max) {
idx = i;
}
}
cout << "My prediction for this digit image is: " << to_string(idx) << endl;
}
void example_linear_regression()
{
print_example_banner("Example: linear regression");
// In this example we demonstrate computing linear regression for diabetes, label is consistive: a quantitative measure of disease progression;
// input X has 10 features ([-0.07816532, 0.05068012, 0.07786339, 0.05285819, 0.07823631, 0.0644473, 0.02655027, -0.00259226, 0.04067226, -0.00936191])
// below is X to be predict:
const vector<double> rational_numbers{ -0.07816532, 0.05068012, 0.07786339, 0.05285819, 0.07823631, 0.0644473, 0.02655027, -0.00259226, 0.04067226, -0.00936191 };
// The weights are fit through python->sklearn:
const vector<double> coefficients{ 0.3034995490664121, -237.63931533353392, 510.5306054362245, 327.7369804093466, -814.1317093725389, 492.81458798373245, 102.84845219168025, 184.60648905984064, 743.5196167505419, 76.095172216624 };
// Create encryption parameters
EncryptionParameters parms;
parms.set_poly_modulus("1x^2048 + 1");
parms.set_coeff_modulus(ChooserEvaluator::default_parameter_options().at(2048));
parms.set_plain_modulus(1 << 8);
parms.validate();
// Generate keys.
cout << "Generating keys ..." << endl;
KeyGenerator generator(parms);
generator.generate();
cout << "... key generation complete" << endl;
Ciphertext public_key = generator.public_key();
Plaintext secret_key = generator.secret_key();
/*
We will need a fractional encoder for dealing with the rational numbers. Here we reserve
32 coefficients of the polynomial for the integral part (low-degree terms) and expand the
fractional part to 64 terms of precision (base 3) (high-degree terms).
*/
//FractionalEncoder encoder(parms.plain_modulus(), parms.poly_modulus(), 64, 32, 3);
FractionalEncoder encoder(parms.plain_modulus(), parms.poly_modulus(), 32, 64, 3);
// Create the rest of the tools
Encryptor encryptor(parms, public_key);
Evaluator evaluator(parms);
Decryptor decryptor(parms, secret_key);
// First we encrypt the rational numbers
cout << "Encrypting ... ";
vector<Ciphertext> encrypted_rationals;
for (int i = 0; i < 10; i++)
{
Plaintext encoded_number = encoder.encode(rational_numbers[i]);
encrypted_rationals.emplace_back(encryptor.encrypt(encoded_number));
cout << to_string(rational_numbers[i]).substr(0, 6) << ((i < 9) ? ", " : ".\n");
}
// Next we encode the coefficients. There is no reason to encrypt these since they are not private data.
cout << "Encoding ... ";
vector<Plaintext> encoded_coefficients;
for (int i = 0; i < 10; i++)
{
encoded_coefficients.emplace_back(encoder.encode(coefficients[i]));
cout << to_string(coefficients[i]).substr(0, 6) << ((i < 9) ? ", " : ".\n");
}
// We also need to encode bias. We will add_plain the result by this to got bias.
//const double bias = 152.764306916;
Plaintext bias = encoder.encode(152.764306916);
// Now compute all the products of the encrypted rational numbers with the plaintext coefficients
cout << "Computing products ... ";
vector<Ciphertext> encrypted_products;
for (int i = 0; i < 10; i++)
{
/*
We use Evaluator::multiply_plain(...) instead of Evaluator::multiply(...) (which would
require also the coefficient to be encrypted). This has much better noise growth
behavior than multiplying two encrypted numbers does.
*/
Ciphertext enc_plain_product = evaluator.multiply_plain(encrypted_rationals[i], encoded_coefficients[i]);
encrypted_products.emplace_back(enc_plain_product);
}
cout << "done." << endl;
// Now we add together these products. The most convenient way to do that is
// to use the function Evaluator::add_many(...).
cout << "Add up all 10 ciphertexts ... ";
Ciphertext encrypted_dot_product = evaluator.add_many(encrypted_products);
cout << " done." << endl;
// Finally we add bias to obtain the result.
cout << "adding bias ... ";
Ciphertext encrypted_result = evaluator.add_plain(encrypted_dot_product, bias);
cout << "done." << endl;
// Decrypt
cout << "Decrypting ... ";
Plaintext plain_result = decryptor.decrypt(encrypted_result);
cout << "done." << endl;
// Print the result
double result = encoder.decode(plain_result);
cout << "predicted Y: " << result << endl;
cout << "Y label: " << result << endl;
// How much noise budget are we left with?
cout << "Noise budget in result: " << decryptor.invariant_noise_budget(encrypted_result) << " bits" << endl;
}
// orignial from SEAL project
void example_basics()
{
print_example_banner("Example: Basics");
/*
In this example we demonstrate using some of the basic arithmetic operations on integers.
SEAL uses the Fan-Vercauteren (FV) homomorphic encryption scheme. We refer to
https://eprint.iacr.org/2012/144 for full details on how the FV scheme works.
*/
// Create encryption parameters.
EncryptionParameters parms;
/*
We first choose the polynomial modulus. This must be a power-of-2 cyclotomic polynomial,
i.e. a polynomial of the form "1x^(power-of-2) + 1". We recommend using polynomials of
degree at least 1024.
*/
//parms.set_poly_modulus("1x^2048 + 1");
parms.set_poly_modulus("1x^1024 + 1");
/*
Next we choose the coefficient modulus. SEAL comes with default values for the coefficient
modulus for some of the most reasonable choices of poly_modulus. They are as follows:
/----------------------------------------------------------------------\
| poly_modulus | default coeff_modulus | security |
| -------------|--------------------------------------------|----------|
| 1x^2048 + 1 | 2^60 - 2^14 + 1 (60 bits) | 119 bit |
| 1x^4096 + 1 | 2^116 - 2^18 + 1 (116 bits) | 122 bit |
| 1x^8192 + 1 | 2^226 - 2^26 + 1 (226 bits) | 124 bit |
| 1x^16384 + 1 | 2^435 - 2^33 + 1 (435 bits) | 130 bit |
| 1x^32768 + 1 | 2^889 - 2^54 - 2^53 - 2^52 + 1 (889 bits) | 127 bit |
\----------------------------------------------------------------------/
These can be conveniently accessed using ChooserEvaluator::default_parameter_options(),
which returns the above list of options as an std::map, keyed by the degree of the polynomial
modulus. The security levels are estimated based on https://eprint.iacr.org/2015/046 and
https://eprint.iacr.org/2017/047. We strongly recommend that the user consult an expert in
the security of RLWE-based cryptography to estimate the security of a particular choice
of parameters.
The user can also easily choose their custom coefficient modulus. For best performance, it should
be a prime of the form 2^A - B, where B is congruent to 1 modulo 2*degree(poly_modulus), and as small
as possible. Roughly speaking, When the rest of the parameters are held fixed, increasing coeff_modulus
decreases the security level. Thus we would not recommend using a value for coeff_modulus much larger
than those listed above (the defaults). In general, we highly recommend the user to consult with an expert
in the security of RLWE-based cryptography when selecting their parameters to ensure an appropriate level
of security.
The size of coeff_modulus affects the total noise budget that a freshly encrypted ciphertext has. More
precisely, every ciphertext starts with a certain amount of noise budget, which is consumed in homomorphic
operations - in particular in multiplication. Once the noise budget reaches 0, the ciphertext becomes
impossible to decrypt. The total noise budget in a freshly encrypted ciphertext is very roughly given by
log2(coeff_modulus/plain_modulus), so increasing coeff_modulus will allow the user to perform more
homomorphic operations on the ciphertexts without corrupting them. However, we must again warn that
increasing coeff_modulus has a strong negative effect on the security level.
*/
parms.set_coeff_modulus(ChooserEvaluator::default_parameter_options().at(2048));
/*
Now we set the plaintext modulus. This can be any positive integer, even though here we take it to be a
power of two. A larger plaintext modulus causes the noise to grow faster in homomorphic multiplication,
and also lowers the maximum amount of noise in ciphertexts that the system can tolerate (see above).
On the other hand, a larger plaintext modulus typically allows for better homomorphic integer arithmetic,
although this depends strongly on which encoder is used to encode integers into plaintext polynomials.
*/
parms.set_plain_modulus(1 << 8);
/*
Once all parameters are set, we need to call EncryptionParameters::validate(), which evaluates the
properties of the parameters, their validity for homomorphic encryption, and performs some important
pre-computation.
*/
parms.validate();
/*
Plaintext elements in the FV scheme are polynomials (represented by the Plaintext class) with coefficients
integers modulo plain_modulus. To encrypt for example integers instead, one must use an "encoding scheme",
i.e. a specific way of representing integers as such polynomials. SEAL comes with a few basic encoders:
IntegerEncoder:
Given an integer base b, encodes integers as plaintext polynomials in the following way. First, a base-b
expansion of the integer is computed. This expansion uses a "balanced" set of representatives of integers
modulo b as the coefficients. Namely, when b is off the coefficients are integers between -(b-1)/2 and
(b-1)/2. When b is even, the integers are between -b/2 and (b-1)/2, except when b is two and the usual
binary expansion is used (coefficients 0 and 1). Decoding amounts to evaluating the polynomial at x=b.
For example, if b=2, the integer 26 = 2^4 + 2^3 + 2^1 is encoded as the polynomial 1x^4 + 1x^3 + 1x^1.
When b=3, 26 = 3^3 - 3^0 is encoded as the polynomial 1x^3 - 1. In reality, coefficients of polynomials
are always unsigned integers, and in this case are stored as their smallest non-negative representatives
modulo plain_modulus. To create an integer encoder with a base b, use IntegerEncoder(plain_modulus, b).
If no b is given to the constructor, the default value of b=2 is used.
FractionalEncoder:
Encodes fixed-precision rational numbers as follows. First expand the number in a given base b, possibly
truncating an infinite fractional part to finite precision, e.g. 26.75 = 2^4 + 2^3 + 2^1 + 2^(-1) + 2^(-2)
when b=2. For the sake of the example, suppose poly_modulus is 1x^1024 + 1. Next represent the integer part
of the number in the same way as in IntegerEncoder (with b=2 here). Finally, represent the fractional part
in the leading coefficients of the polynomial, but when doing so invert the signs of the coefficients. So
in this example we would represent 26.75 as the polynomial -1x^1023 - 1x^1022 + 1x^4 + 1x^3 + 1x^1. The
negative coefficients of the polynomial will again be represented as their negatives modulo plain_modulus.
PolyCRTBuilder:
If poly_modulus is 1x^N + 1, PolyCRTBuilder allows "batching" of N plaintext integers modulo plain_modulus
into one plaintext polynomial, where homomorphic operations can be carried out very efficiently in a SIMD
manner by operating on such a "composed" plaintext or ciphertext polynomials. For full details on this very
powerful technique we recommend https://eprint.iacr.org/2012/565.pdf and https://eprint.iacr.org/2011/133.
A crucial fact to understand is that when homomorphic operations are performed on ciphertexts, they will
carry over to the underlying plaintexts, and as a result of additions and multiplications the coefficients
in the plaintext polynomials will increase from what they originally were in freshly encoded polynomials.
This becomes a problem when the coefficients reach the size of plain_modulus, in which case they will get
automatically reduced modulo plain_modulus, and might render the underlying plaintext polynomial impossible
to be correctly decoded back into an integer or rational number. Therefore, it is typically crucial to
have a good sense of how large the coefficients will grow in the underlying plaintext polynomials when
homomorphic computations are carried out on the ciphertexts, and make sure that plain_modulus is chosen to
be at least as large as this number.
Here we choose to create an IntegerEncoder with base b=2.
*/
//IntegerEncoder encoder(parms.plain_modulus());
IntegerEncoder encoder(parms.plain_modulus());
// Encode two integers as polynomials.
const int value1 = 5;
const int value2 = -7;
Plaintext encoded1 = encoder.encode(value1);
Plaintext encoded2 = encoder.encode(value2);
cout << "Encoded " << value1 << " as polynomial " << encoded1.to_string() << endl;
cout << "Encoded " << value2 << " as polynomial " << encoded2.to_string() << endl;
// Generate keys.
cout << "Generating keys ..." << endl;
KeyGenerator generator(parms);
generator.generate();
cout << "... key generation complete" << endl;
Ciphertext public_key = generator.public_key();
Plaintext secret_key = generator.secret_key();
// Encrypt values.
cout << "Encrypting values..." << endl;
Encryptor encryptor(parms, public_key);
Ciphertext encrypted1 = encryptor.encrypt(encoded1);
Ciphertext encrypted2 = encryptor.encrypt(encoded2);
//save encrypted into binary file from Ciphertext
ofstream myfile;
myfile.open("firstTrial.txt", std::ofstream::out | std::ofstream::binary);
// save encrypted
encrypted1.save(myfile);
myfile.close();
// generate an empty Ciphertext to load the saved binary file