// Implements the scheme of https://eprint.iacr.org/2019/966

package tkn

import (
	"crypto/subtle"
	"encoding/binary"
	"fmt"
	"io"

	pairing "github.com/cloudflare/circl/ecc/bls12381"
)

type PublicParams struct {
	b2  *matrixG2
	wb1 *matrixG1
	btk *matrixGT
}

func (p *PublicParams) MarshalBinary() ([]byte, error) {
	b2Bytes, err := p.b2.marshalBinary()
	if err != nil {
		return nil, fmt.Errorf("PublicParams serializing failed: %w", err)
	}
	ret := appendLenPrefixed(nil, b2Bytes)

	wb1Bytes, err := p.wb1.marshalBinary()
	if err != nil {
		return nil, fmt.Errorf("PublicParams serializing failed: %w", err)
	}
	ret = appendLenPrefixed(ret, wb1Bytes)

	btkBytes, err := p.btk.marshalBinary()
	if err != nil {
		return nil, fmt.Errorf("PublicParams serializing failed: %w", err)
	}
	ret = appendLenPrefixed(ret, btkBytes)

	return ret, nil
}

func (p *PublicParams) UnmarshalBinary(data []byte) error {
	b2Bytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return fmt.Errorf("PublicParams deserialization failure: %w", err)
	}
	p.b2 = newMatrixG2(0, 0)
	err = p.b2.unmarshalBinary(b2Bytes)
	if err != nil {
		return fmt.Errorf("PublicParams deserialization failure: %w", err)
	}

	wb1Bytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return fmt.Errorf("PublicParams deserialization failure: %w", err)
	}
	p.wb1 = newMatrixG1(0, 0)
	err = p.wb1.unmarshalBinary(wb1Bytes)
	if err != nil {
		return fmt.Errorf("PublicParams deserialization failure: %w", err)
	}

	btkBytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return fmt.Errorf("PublicParams deserialization failure: %w", err)
	}
	p.btk = newMatrixGT(0, 0)
	err = p.btk.unmarshalBinary(btkBytes)
	if err != nil {
		return fmt.Errorf("PublicParams deserialization failure: %w", err)
	}

	if len(data) != 0 {
		return fmt.Errorf("PublicParams deserialization failed: excess bytes remain in data")
	}
	return nil
}

func (p *PublicParams) Equal(p2 *PublicParams) bool {
	return p.b2.Equal(p2.b2) && p.wb1.Equal(p2.wb1) && p.btk.Equal(p2.btk)
}

type SecretParams struct {
	a       *matrixZp
	wtA     *matrixZp
	bstar   *matrixZp
	bstar12 *matrixZp
	k       *matrixZp // vectors are represented as 1 x n matrices
	prfKey  []byte
}

func (s *SecretParams) MarshalBinary() ([]byte, error) {
	aBytes, err := s.a.marshalBinary()
	if err != nil {
		return nil, fmt.Errorf("SecretParams serializing failed: %w", err)
	}
	wtABytes, err := s.wtA.marshalBinary()
	if err != nil {
		return nil, fmt.Errorf("SecretParams serializing failed: %w", err)
	}
	bstarBytes, err := s.bstar.marshalBinary()
	if err != nil {
		return nil, fmt.Errorf("SecretParams serializing failed: %w", err)
	}
	bstar12Bytes, err := s.bstar12.marshalBinary()
	if err != nil {
		return nil, fmt.Errorf("SecretParams serializing failed: %w", err)
	}
	kBytes, err := s.k.marshalBinary()
	if err != nil {
		return nil, fmt.Errorf("SecretParams serializing failed: %w", err)
	}
	bufs := [][]byte{
		aBytes, wtABytes, bstarBytes, bstar12Bytes, kBytes, s.prfKey,
	}

	ret := appendLenPrefixed(nil, bufs[0])
	for _, buf := range bufs[1:] {
		ret = appendLenPrefixed(ret, buf)
	}
	return ret, nil
}

func (s *SecretParams) UnmarshalBinary(data []byte) error {
	aBytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return fmt.Errorf("SecretParams deserialization failure: %w", err)
	}
	s.a = newMatrixZp(0, 0)
	err = s.a.unmarshalBinary(aBytes)
	if err != nil {
		return fmt.Errorf("SecretParams deserialization failure: %w", err)
	}
	wtABytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return fmt.Errorf("SecretParams deserialization failure: %w", err)
	}
	s.wtA = newMatrixZp(0, 0)
	err = s.wtA.unmarshalBinary(wtABytes)
	if err != nil {
		return fmt.Errorf("SecretParams deserialization failure: %w", err)
	}
	bstarBytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return fmt.Errorf("SecretParams deserialization failure: %w", err)
	}
	s.bstar = newMatrixZp(0, 0)
	err = s.bstar.unmarshalBinary(bstarBytes)
	if err != nil {
		return fmt.Errorf("SecretParams deserialization failure: %w", err)
	}
	bstar12Bytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return fmt.Errorf("SecretParams deserialization failure: %w", err)
	}
	s.bstar12 = newMatrixZp(0, 0)
	err = s.bstar12.unmarshalBinary(bstar12Bytes)
	if err != nil {
		return fmt.Errorf("SecretParams deserialization failure: %w", err)
	}
	kBytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return fmt.Errorf("SecretParams deserialization failure: %w", err)
	}
	s.k = newMatrixZp(0, 0)
	err = s.k.unmarshalBinary(kBytes)
	if err != nil {
		return fmt.Errorf("SecretParams deserialization failure: %w", err)
	}
	prfBytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return fmt.Errorf("SecretParams deserialization failure: %w", err)
	}
	s.prfKey = prfBytes

	if len(data) != 0 {
		return fmt.Errorf("SecretParams deserialization failed: excess bytes remain in data")
	}
	return nil
}

func (s *SecretParams) Equal(s2 *SecretParams) bool {
	return s.a.Equal(s2.a) && s.wtA.Equal(s2.wtA) && s.bstar.Equal(s2.bstar) &&
		s.bstar12.Equal(s2.bstar12) && s.k.Equal(s2.k) &&
		subtle.ConstantTimeCompare(s.prfKey, s2.prfKey) == 1
}

type AttributesKey struct {
	a      *Attributes
	k1     *matrixG2
	k2     *matrixG1
	k3     map[string]*matrixG1
	k3wild map[string]*matrixG1 // only contains wildcards
}

func (a *AttributesKey) MarshalBinary() ([]byte, error) {
	aBytes, err := a.a.marshalBinary()
	if err != nil {
		return nil, fmt.Errorf("AttributesKey serializing failed: %w", err)
	}
	ret := appendLenPrefixed(nil, aBytes)
	k1Bytes, err := a.k1.marshalBinary()
	if err != nil {
		return nil, fmt.Errorf("AttributesKey serializing failed: %w", err)
	}
	ret = appendLenPrefixed(ret, k1Bytes)
	k2Bytes, err := a.k2.marshalBinary()
	if err != nil {
		return nil, fmt.Errorf("AttributesKey serializing failed: %w", err)
	}
	ret = appendLenPrefixed(ret, k2Bytes)
	ret = append(ret, 0, 0)
	binary.LittleEndian.PutUint16(ret[len(ret)-2:], uint16(len(a.k3)))
	k3Bytes, err := marshalBinarySortedMapMatrixG1(a.k3)
	if err != nil {
		return nil, fmt.Errorf("AttributesKey serializing failed: %w", err)
	}
	ret = append(ret, k3Bytes...)

	ret = append(ret, 0, 0)
	binary.LittleEndian.PutUint16(ret[len(ret)-2:], uint16(len(a.k3wild)))
	k3wildBytes, err := marshalBinarySortedMapMatrixG1(a.k3wild)
	if err != nil {
		return nil, fmt.Errorf("AttributesKey serializing failed: %w", err)
	}
	ret = append(ret, k3wildBytes...)

	return ret, nil
}

func (a *AttributesKey) UnmarshalBinary(data []byte) error {
	aBytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return fmt.Errorf("AttributesKey deserialization failure: %w", err)
	}
	a.a = &Attributes{}
	err = a.a.unmarshalBinary(aBytes)
	if err != nil {
		return fmt.Errorf("AttributesKey deserialization failure: %w", err)
	}
	k1Bytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return fmt.Errorf("AttributesKey deserialization failure: %w", err)
	}
	a.k1 = newMatrixG2(0, 0)
	err = a.k1.unmarshalBinary(k1Bytes)
	if err != nil {
		return fmt.Errorf("AttributesKey deserialization failure: %w", err)
	}
	k2Bytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return fmt.Errorf("AttributesKey deserialization failure: %w", err)
	}
	a.k2 = newMatrixG1(0, 0)
	err = a.k2.unmarshalBinary(k2Bytes)
	if err != nil {
		return fmt.Errorf("AttributesKey deserialization failure: %w", err)
	}

	if len(data) < 2 {
		return fmt.Errorf("AttributesKey deserialization failure: data too short")
	}
	n := int(binary.LittleEndian.Uint16(data))
	data = data[2:]
	a.k3 = make(map[string]*matrixG1, n)
	for i := 0; i < n; i++ {
		sBytes, rem, err := removeLenPrefixed(data)
		if err != nil {
			return fmt.Errorf("AttributesKey deserialization failure: %w", err)
		}
		mBytes, rem, err := removeLenPrefixed(rem)
		if err != nil {
			return fmt.Errorf("AttributesKey deserialization failure: %w", err)
		}
		m := newMatrixG1(0, 0)
		err = m.unmarshalBinary(mBytes)
		if err != nil {
			return fmt.Errorf("AttributesKey deserialization failure: %w", err)
		}
		a.k3[string(sBytes)] = m
		data = rem
	}

	if len(data) < 2 {
		return fmt.Errorf("AttributesKey deserialization failure: data too short")
	}
	n = int(binary.LittleEndian.Uint16(data))
	data = data[2:]
	a.k3wild = make(map[string]*matrixG1, n)
	for i := 0; i < n; i++ {
		sBytes, rem, err := removeLenPrefixed(data)
		if err != nil {
			return fmt.Errorf("AttributesKey deserialization failure: %w", err)
		}
		mBytes, rem, err := removeLenPrefixed(rem)
		if err != nil {
			return fmt.Errorf("AttributesKey deserialization failure: %w", err)
		}
		m := newMatrixG1(0, 0)
		err = m.unmarshalBinary(mBytes)
		if err != nil {
			return fmt.Errorf("AttributesKey deserialization failure: %w", err)
		}
		a.k3wild[string(sBytes)] = m
		data = rem
	}
	if len(data) != 0 {
		return fmt.Errorf("AttributesKey deserialization failed: excess bytes remain in data")
	}
	return nil
}

func (a *AttributesKey) Equal(b *AttributesKey) bool {
	if !a.a.Equal(b.a) || !a.k1.Equal(b.k1) || !a.k2.Equal(b.k2) {
		return false
	}
	if len(a.k3) != len(b.k3) || len(a.k3wild) != len(b.k3wild) {
		return false
	}
	for k, v := range a.k3 {
		if !b.k3[k].Equal(v) {
			return false
		}
	}
	for k, v := range a.k3wild {
		if !b.k3wild[k].Equal(v) {
			return false
		}
	}
	return true
}

type ciphertextHeader struct {
	p     *Policy
	c1    *matrixG2
	c2    []*matrixG2
	c3    []*matrixG1
	c3neg []*matrixG1 // additional vector for negated attributes
}

func (hdr *ciphertextHeader) marshalBinary() ([]byte, error) {
	pBytes, err := hdr.p.MarshalBinary()
	if err != nil {
		return nil, err
	}
	ret := appendLenPrefixed(nil, pBytes)

	c1Bytes, err := hdr.c1.marshalBinary()
	if err != nil {
		return nil, fmt.Errorf("c1 serializing: %w", err)
	}
	ret = appendLenPrefixed(ret, c1Bytes)

	// Now we need to indicate how long c2, c3, c3neg are.
	// Each array will be the same size (or nil), so with more work we can specialize
	// but for now we will ignore that.

	c2Len := len(hdr.c2)

	ret = append(ret, 0, 0)
	binary.LittleEndian.PutUint16(ret[len(ret)-2:], uint16(c2Len))
	for i := 0; i < c2Len; i++ {
		c2dat, errM := hdr.c2[i].marshalBinary()
		if errM != nil {
			return nil, fmt.Errorf("c2 serializing %d: %w", i, errM)
		}
		ret = appendLenPrefixed(ret, c2dat)
	}
	c3Len := len(hdr.c3)
	ret = append(ret, 0, 0)
	binary.LittleEndian.PutUint16(ret[len(ret)-2:], uint16(c3Len))
	for i := 0; i < c3Len; i++ {
		c3dat, errM := hdr.c3[i].marshalBinary()
		if errM != nil {
			return nil, fmt.Errorf("c3 serializing %d: %w", i, errM)
		}
		ret = appendLenPrefixed(ret, c3dat)
	}
	for i := 0; i < c3Len; i++ {
		var c3negdat []byte
		if hdr.c3neg[i] != nil {
			c3negdat, err = hdr.c3neg[i].marshalBinary()
			if err != nil {
				return nil, fmt.Errorf("c3neg serializing %d: %w", i, err)
			}
		} else {
			c3negdat = nil
		}
		ret = appendLenPrefixed(ret, c3negdat)
	}
	return ret, nil
}

func (hdr *ciphertextHeader) unmarshalBinary(data []byte) error {
	pBytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return err
	}
	hdr.p = new(Policy)
	err = hdr.p.UnmarshalBinary(pBytes)
	if err != nil {
		return err
	}
	c1Bytes, data, err := removeLenPrefixed(data)
	if err != nil {
		return err
	}
	hdr.c1 = newMatrixG2(0, 0)
	err = hdr.c1.unmarshalBinary(c1Bytes)
	if err != nil {
		return err
	}
	c2Len := int(binary.LittleEndian.Uint16(data))
	hdr.c2 = make([]*matrixG2, c2Len)
	data = data[2:]
	var c2data []byte
	var c3data []byte
	var c3negdata []byte

	for i := 0; i < c2Len; i++ {
		c2data, data, err = removeLenPrefixed(data)
		if err != nil {
			return err
		}
		hdr.c2[i] = newMatrixG2(0, 0)
		err = hdr.c2[i].unmarshalBinary(c2data)
		if err != nil {
			return err
		}
	}

	c3Len := int(binary.LittleEndian.Uint16(data))
	hdr.c3 = make([]*matrixG1, c3Len)
	hdr.c3neg = make([]*matrixG1, c3Len)
	data = data[2:]

	for i := 0; i < c3Len; i++ {
		c3data, data, err = removeLenPrefixed(data)
		if err != nil {
			return err
		}
		hdr.c3[i] = newMatrixG1(0, 0)
		err = hdr.c3[i].unmarshalBinary(c3data)
		if err != nil {
			return err
		}
	}

	for i := 0; i < c3Len; i++ {
		c3negdata, data, err = removeLenPrefixed(data)
		if err != nil {
			return err
		}

		if len(c3negdata) != 0 {
			hdr.c3neg[i] = newMatrixG1(0, 0)
			err = hdr.c3neg[i].unmarshalBinary(c3negdata)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func GenerateParams(rand io.Reader) (*PublicParams, *SecretParams, error) {
	A, err := sampleDlin(rand)
	if err != nil {
		return nil, nil, err
	}
	Bbar, err := randomMatrixZp(rand, 4, 4)
	if err != nil {
		return nil, nil, err
	}
	W, err := randomMatrixZp(rand, 3, 4)
	if err != nil {
		return nil, nil, err
	}
	k, err := randomMatrixZp(rand, 4, 1)
	if err != nil {
		return nil, nil, err
	}
	prfKey := make([]byte, 16)
	_, err = io.ReadFull(rand, prfKey)
	if err != nil {
		return nil, nil, err
	}

	B := newMatrixZp(0, 0)
	B.colsel(Bbar, []int{0, 1})
	wb := newMatrixZp(0, 0)
	wb.mul(W, B)

	Bt := newMatrixZp(0, 0)
	Bt.transpose(B)
	BtKp := newMatrixZp(0, 0)
	BtKp.mul(Bt, k)

	pp := PublicParams{}
	pp.b2 = newMatrixG2(0, 0)
	pp.b2.exp(B)
	pp.wb1 = newMatrixG1(0, 0)
	pp.wb1.exp(wb)
	pp.btk = newMatrixGT(0, 0)
	pp.btk.exp(BtKp)

	sp := SecretParams{}
	sp.a = A
	sp.wtA = newMatrixZp(0, 0)
	wt := newMatrixZp(0, 0)
	wt.transpose(W)
	sp.wtA.mul(wt, A)

	BbarTinv := newMatrixZp(0, 0)
	BbarT := newMatrixZp(0, 0)
	BbarT.transpose(Bbar)
	err = BbarTinv.inverse(BbarT)
	if err != nil {
		return nil, nil, err
	}

	sp.bstar = newMatrixZp(0, 0)
	sp.bstar.colsel(BbarTinv, []int{0, 1})

	sp.bstar12 = newMatrixZp(0, 0)
	sp.bstar12.colsel(BbarTinv, []int{2, 3})

	sp.k = k
	sp.prfKey = prfKey

	return &pp, &sp, nil
}

func max(in []int) int {
	max := 0
	for i := 0; i < len(in); i++ {
		if in[i] > max {
			max = in[i]
		}
	}
	return max
}

// encapsulate creates a new ephemeral key and header that can be opened to it. This is
// the transformation of an Elgamal like scheme to a KEM.
func encapsulate(rand io.Reader, pp *PublicParams, policy *Policy) (*ciphertextHeader, *pairing.Gt, error) {
	pi := policy.pi()
	d := max(pi) + 1
	ri := make([]*matrixZp, d)
	r, err := randomMatrixZp(rand, 2, 1)
	if err != nil {
		return nil, nil, err
	}
	for i := 0; i < d; i++ {
		ri[i], err = randomMatrixZp(rand, 2, 1)
		if err != nil {
			return nil, nil, err
		}
	}
	rshares, err := policy.F.share(rand, r)
	if err != nil {
		return nil, nil, err
	}
	tmp := newMatrixZp(2, 1)
	wshares := make([]*matrixG1, len(rshares))
	for i := 0; i < len(rshares); i++ {
		wshares[i] = newMatrixG1(0, 0)
		wshares[i].rightMult(pp.wb1, rshares[i])
	}

	c1 := newMatrixG2(0, 0)
	c1.rightMult(pp.b2, r)

	c2 := make([]*matrixG2, d)
	for i := 0; i < d; i++ {
		c2[i] = newMatrixG2(0, 0)
		c2[i].rightMult(pp.b2, ri[i])
	}

	c4mat := newMatrixGT(1, 1)
	tmp.transpose(r)
	c4mat.leftMult(tmp, pp.btk)
	if c4mat.rows != 1 || c4mat.cols != 1 {
		panic("failure to get the encryption right")
	}

	c3 := make([]*matrixG1, len(policy.Inputs))
	c3neg := make([]*matrixG1, len(policy.Inputs))
	for i := 0; i < len(policy.Inputs); i++ {
		U0, U1 := oracle([]byte(policy.Inputs[i].Label))
		if policy.Inputs[i].Positive {
			c3[i] = newMatrixG1(0, 0)

			c3[i].scalarMult(policy.Inputs[i].Value, U0)
			c3[i].add(c3[i], U1)
			c3[i].rightMult(c3[i], ri[pi[i]])
			c3[i].add(c3[i], wshares[i])
			c3neg[i] = nil
		} else {
			c3[i] = newMatrixG1(0, 0)
			c3[i].rightMult(U0, ri[pi[i]])
			c3[i].sub(c3[i], wshares[i])

			c3neg[i] = newMatrixG1(0, 0)
			c3neg[i].rightMult(U1, ri[pi[i]])
			tmpmat := newMatrixG1(0, 0)
			tmpmat.scalarMult(policy.Inputs[i].Value, wshares[i])
			c3neg[i].add(c3neg[i], tmpmat)
		}
	}
	return &ciphertextHeader{
		p:     policy,
		c1:    c1,
		c2:    c2,
		c3:    c3,
		c3neg: c3neg,
	}, &c4mat.entries[0], nil
}

func deriveAttributeKeys(rand io.Reader, sp *SecretParams, attrs *Attributes) (*AttributesKey, error) {
	s, err := randomMatrixZp(rand, 2, 1)
	if err != nil {
		return nil, err
	}
	As := newMatrixZp(0, 0)

	k1 := newMatrixG2(0, 0)
	As.mul(sp.a, s)
	k1.exp(As)
	Ast := newMatrixZp(0, 0)
	Ast.transpose(As)

	k2 := newMatrixG1(0, 0)
	tmp := newMatrixZp(0, 0)
	tmp.mul(sp.wtA, s)
	tmp.add(tmp, sp.k)
	k2.exp(tmp)

	k3 := make(map[string]*matrixG1)
	k3wild := make(map[string]*matrixG1)
	for label, attr := range *attrs {
		if attr.wild {
			// For wild k3 is y term, k3wild is constant term
			U0, U1 := oracle([]byte(label))
			V0, V1, err := prf(sp.prfKey, []byte(label))
			if err != nil {
				return nil, err
			}
			k3[label] = newMatrixG1(0, 0)
			k3wild[label] = newMatrixG1(0, 0)
			left := newMatrixG1(0, 0)
			right := newMatrixG1(0, 0)

			left.transpose(U0)
			left.leftMult(sp.bstar, left)
			left.rightMult(left, As)

			tmp.transpose(V0)
			tmp.mul(sp.bstar12, tmp)
			tmp.mul(tmp, As)

			right.exp(tmp)

			k3[label].add(left, right)

			left.transpose(U1)
			left.leftMult(sp.bstar, left)
			left.rightMult(left, As)

			tmp.transpose(V1)
			tmp.mul(sp.bstar12, tmp)
			tmp.mul(tmp, As)

			right.exp(tmp)

			k3wild[label].add(left, right)
		} else {
			U0, U1 := oracle([]byte(label))
			V0, V1, err := prf(sp.prfKey, []byte(label))
			if err != nil {
				return nil, err
			}

			k3[label] = newMatrixG1(0, 0)
			left := newMatrixG1(0, 0)
			right := newMatrixG1(0, 0)

			left.scalarMult(attr.Value, U0)
			left.add(U1, left)
			left.transpose(left)
			left.leftMult(sp.bstar, left)
			left.rightMult(left, As)

			tmp.scalarmul(attr.Value, V0)
			tmp.add(tmp, V1)
			tmp.transpose(tmp)
			tmp.mul(sp.bstar12, tmp)
			tmp.mul(tmp, As)

			right.exp(tmp)

			k3[label].add(left, right)
		}
	}

	return &AttributesKey{
		a:      attrs,
		k1:     k1,
		k2:     k2,
		k3:     k3,
		k3wild: k3wild,
	}, nil
}

// Decapsulate decapsulates
func decapsulate(header *ciphertextHeader, key *AttributesKey) (*pairing.Gt, error) {
	// First we need to determine the satisfying assignment: which attributes in attr
	// are needed.

	// We use pi to determine which D to sum into
	pi := header.p.pi()
	d := max(pi) + 1
	// p1, p2 are the left halves of the pairings.
	p1 := make([]*matrixG1, d)
	p2 := make([]*matrixG1, d)

	sat, err := header.p.Satisfaction(key.a)
	if err != nil {
		return nil, err
	}
	for k := 0; k < len(sat.matches); k++ {
		match := sat.matches[k]
		j := pi[match.wire]

		if p1[j] == nil {
			p1[j] = newMatrixG1(header.c3[match.wire].rows, header.c3[match.wire].cols)
		}
		if p2[j] == nil {
			p2[j] = newMatrixG1(key.k3[match.label].rows, key.k3[match.label].cols)
		}
		if header.p.Inputs[match.wire].Positive {
			p1[j].add(p1[j], header.c3[match.wire])

			if (*key.a)[match.label].wild {
				if key.k3wild[match.label] == nil {
					return nil, fmt.Errorf("missing wildcard data for Label %s", match.label)
				}
				y := header.p.Inputs[match.wire].Value
				tmp1 := newMatrixG1(0, 0)
				tmp1.scalarMult(y, key.k3[match.label])
				tmp1.add(tmp1, key.k3wild[match.label])
				p2[j].add(p2[j], tmp1)
			} else {
				p2[j].add(p2[j], key.k3[match.label])
			}
		} else {
			keymat := newMatrixG1(0, 0)
			y := &pairing.Scalar{}

			if (*key.a)[match.label].wild {
				y.Add(header.p.Inputs[match.wire].Value, ToScalar(1))
				keymat.scalarMult(y, key.k3[match.label])
				keymat.add(keymat, key.k3wild[match.label])
			} else {
				y.Set((*(key.a))[match.label].Value)
				keymat.set(key.k3[match.label])
			}
			diff := &pairing.Scalar{}

			diff.Sub(header.p.Inputs[match.wire].Value, y)
			diff.Inv(diff)
			p1add := newMatrixG1(0, 0)
			p1add.scalarMult(y, header.c3[match.wire])
			p1add.add(p1add, header.c3neg[match.wire])
			p1add.scalarMult(diff, p1add)

			p2add := newMatrixG1(0, 0)
			p2add.scalarMult(diff, keymat)

			p1[j].add(p1[j], p1add)
			p2[j].add(p2[j], p2add)
		}
	}

	pairs := &pairAccum{}

	var pTot *matrixG1
	for i := 0; i < d; i++ {
		if p1[i] != nil {
			if pTot == nil {
				pTot = newMatrixG1(p1[i].rows, p1[i].cols)
			}
			pTot.add(pTot, p1[i])
			pairs.addDuals(p2[i], header.c2[i], 1)
		}
	}
	pairs.addDuals(pTot, key.k1, -1)
	pairs.addDuals(key.k2.copy(), header.c1, 1)

	return pairs.eval(), nil
}
