package paillier

import (
	"math/big"

	"xdx.jelly/xgcl/gmath"
)

// HomomorphicAdd set c = Enc(m1+m2) where c1 = Enc(m1) and c2 = Enc(m2)
func (c *Cipher) HomomorphicAdd(c1 *Cipher, c2 *Cipher, pk *PublicKey) *Cipher {
	if c.D == nil {
		c.D = new(big.Int)
	}
	c.D.Mul(c1.D, c2.D)
	c.D.Mod(c.D, pk.N2)
	return c
}

// HomomorphicAddInt set c = Enc(m1 + m) where c1 = Enc(m1) and m < N.
//
// Becareful, if the adversary knows c1, c, and m(or m1), then he can computes m1(or m).
// So, using HomomorphicAddInt if you know what you're doing,
// otherwise, you should use like this:
// c2, err :=  pk.Encrypt(m, rnd)
// c.HomomorphicAdd(c1, c2, pk)
func (c *Cipher) HomomorphicAddInt(c1 *Cipher, m *big.Int, pk *PublicKey) *Cipher {
	d := new(big.Int)
	c.D = d.Mul(pk.N, m).Add(d, gmath.BigInt1).Mod(d, pk.N2).Mul(d, c1.D).Mod(d, pk.N2)
	return c
}

// HomomorphicScalarMul if c1 = Enc(a), then HomomorphicScalarMul computes c = Enc(ka)
// if ka < n^2.
func (c *Cipher) HomomorphicScalarMul(a *Cipher, k *big.Int, pk *PublicKey) *Cipher {
	if c.D == nil {
		c.D = new(big.Int)
	}
	c.D.Exp(a.D, k, pk.N2)
	return c
}

// Neg negates the plaintext mod n, i.e., if c = Enc(m), then c.Neg() = Enc(n-m).
func (c *Cipher) Neg(pk *PublicKey) *Cipher {
	c.D.ModInverse(c.D, pk.N2)
	return c
}
