Basic Arithmetic Implementation

Hello!

I’m trying to understand how the integer arithmetic work in plaint text and encryption. I “know” the math but for some reason I get different results when I make a dummy example in paper.

I try to get in and I get lost trying to know witch type of integer are the coefficient of a DCRTPoly.
Some guideline for this?

Please provide an example of the code and your manual plaintext computation. I can tell you which one is wrong :slight_smile:

DCRTPoly works with NativeInteger’s

1 Like

@ypolyakov Thanks again Yuriy! I’m a little overwhelm and I’m doing post not to clear. For the future I will take more time to make them right.

For a study reason I’m changing one Coefficients in EVAL mode of a plain text, and then encrypt it, and see how it changes (don’t ask me why… I’m following orders of my adviser jeje).
When I change higher bits in comparison to the modulus (like the 61-th bit), the corresponding encrypted coefficient sometimes its gets bigger than the modulus.
So I try to that math, and I don’t know how is getting that result.

Inside de Encrypt function in rns-pke.cpp file, basically it use the EncryptZeroCore function to get c0 and c1, and then they add to c0 the plaintext.
If I print with stderr the zero coefficient and zero limb of c0 (c0[0][0]),

std::cerr << "c0[0][0]      = " <<  (*ba)[0].GetAllElements()[0][0] << std::endl;

and in cpp I add to the corresponding plaintext coefficient i don’t get the result… If I take modulus to that, I also don’t get it in that regime.

I show you a simple test I did, with a loop for the bit that I change.

uint32_t multDepth = 0;
uint32_t scaleModSize = 30;
uint32_t firstModSize = 60;
uint32_t batchSize = 1024;
uint32_t ringDim= 2048;
ScalingTechnique rescaleTech = FIXEDMANUAL;
CCParams<CryptoContextCKKSRNS> parameters;
parameters.SetMultiplicativeDepth(multDepth);
parameters.SetScalingModSize(scaleModSize);
parameters.SetFirstModSize(firstModSize);
parameters.SetBatchSize(batchSize);
parameters.SetRingDim(ringDim);
parameters.SetScalingTechnique(rescaleTech);
parameters.SetSecurityLevel(HEStd_NotSet);
CryptoContext<DCRTPoly> cc = GenCryptoContext(parameters);
cc->Enable(PKE);
cc->Enable(LEVELEDSHE);
...
size_t bit_to_change = 58;
NativeInteger original_coeff = ptxt1->GetElement<DCRTPoly>().GetAllElements()[0][0];
NativeInteger c0 = 525107833192280017;

for(; bit_change<64; bit_change++)
{
  ptxt1->GetElement<DCRTPoly>().GetAllElements()[0][0] = bit_flip(original_coeff, bit_change);
  NativeInteger coeff_ptxt = ptxt1->GetElement<DCRTPoly>().GetAllElements()[0][0];
  auto c2 = cc->Encrypt(keys.publicKey, ptxt1);
  encryptElems = c2->GetElements();
  NativeInteger encryptModulus = encryptElems[0].GetModulus();
  NativeInteger encryptElems_c0 = encryptElems[0].GetAllElements()[0][0];
  std::cout << "bitchange     = " << bit_change <<std::endl;
  std::cout << "Modulus       = " << encryptModulus << std::endl;
  std::cout << "ptxt[0][0]    = " << coeff_ptxt << std::endl;
  std::cout << "Encrypt[0][0] = " << encryptElems_c0 << std::endl;
  std::cout << "c0+ptxt       = " << c0+coeff_ptxt << std::endl;
  std::cout << "c0+ptxt mod   = " << (c0+coeff_ptxt)%(NativeInteger)encryptModulus  <<std::endl;
  std::cout << "c0+ptxt mod2  = " << c0.ModAddEq(coeff_ptxt, encryptModulus)  <<std::endl;

  std::cout << std::endl;
}

I expect that Encrypt[0][0] = c0+ptxt mod2 , but this is not always the case. Whats going on?
Output:

c0[0][0]      = 525107833192280017
bitchange     = 58
Modulus       = 1152921504606830593
ptxt[0][0]    = 60978842879516456
Encrypt[0][0] = 586086676071796473
c0+ptxt       = 586086676071796473
c0+ptxt mod   = 586086676071796473
c0+ptxt mod2  = 586086676071796473

c0[0][0]      = 525107833192280017
bitchange     = 59
Modulus       = 1152921504606830593
ptxt[0][0]    = 925669971334651688
Encrypt[0][0] = 297856299920101112
c0+ptxt       = 1511756647406448161
c0+ptxt mod   = 358835142799617568
c0+ptxt mod2  = 358835142799617568

c0[0][0]      = 525107833192280017
bitchange     = 60
Modulus       = 1152921504606830593
ptxt[0][0]    = 1502130723638075176
Encrypt[0][0] = 874317052223524600
c0+ptxt       = 1860965866437692744
c0+ptxt mod   = 708044361830862151
c0+ptxt mod2  = 708044361830862151

c0[0][0]      = 525107833192280017
bitchange     = 61
Modulus       = 1152921504606830593
ptxt[0][0]    = 2655052228244922152
Encrypt[0][0] = 2027238556830371576
c0+ptxt       = 3363096590075784303
c0+ptxt mod   = 1057253580862123117
c0+ptxt mod2  = 1057253580862123117

c0[0][0]      = 525107833192280017
bitchange     = 62
Modulus       = 1152921504606830593
ptxt[0][0]    = 4960895237458616104
Encrypt[0][0] = 4333081566044065528
c0+ptxt       = 6018148818320739221
c0+ptxt mod   = 253541295286586256
c0+ptxt mod2  = 253541295286586256

c0[0][0]      = 525107833192280017
bitchange     = 63
Modulus       = 1152921504606830593
ptxt[0][0]    = 9572581255886004008
Encrypt[0][0] = 8944767584471453432
c0+ptxt       = 9826122551172590264
c0+ptxt mod   = 602750514317945520
c0+ptxt mod2  = 602750514317945520

With this I get the correct result when I change the bit 58, 59 or 60

PD: My real problem is that in my machine I can’t use VS or other IDE, so I’m with vim and I can’t use the entire symbol table… so I can’t move around freely in the codebase, and with gdb I also have some trobules jeje. (I did compile in debug mode)

With trial and error I “discover” that in the sum of the plain text and C0, you are using ModAddFast, that assume that the operands are less than the modulus, correct?

So this is why I’m getting things larger than the modulus. when I change the plaintext It may be larger.

Yes, this makes sense. We use ModAddFast for best efficiency.