func (scs *sparseR1CS) r1cToSparseR1C(r1c compiled.R1C) {
	lro, idCS := findUnsolvedVariable(r1c, scs.solvedVariables)
	if lro == -1 {
		scs.splitR1C(r1c)
		return  
	}
	l := r1c.L
	r := r1c.R
	o := r1c.O
	if lro == 1 {
		l, r = r, l
		lro = 0
	}
	var (
		cK big.Int  
		cS big.Int  
	)
	var toSolve compiled.Term
	l, cL := scs.popConstantTerm(l)
	r, cR := scs.popConstantTerm(r)
	o, cO := scs.popConstantTerm(o)
	if lro == 0 {  
		l, toSolve = popInternalVariable(l, idCS)
	} else {  
		o, toSolve = popInternalVariable(o, idCS)
	}
	cS.Set(&scs.coeffs[toSolve.CoeffID()])
	f1 := func() {
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		scs.addConstraint(compiled.SparseR1C{
			K: scs.coeffID(&cK),
			O: scs.newTerm(cS.Neg(&cS), idCS),
		})
	}
	f2 := func() {
		rt := scs.split(0, r)
		cRT := scs.multiply(rt, &cL)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		scs.addConstraint(compiled.SparseR1C{
			R: cRT,
			K: scs.coeffID(&cK),
			O: scs.newTerm(cS.Neg(&cS), idCS),
		},
		)
	}
	f3 := func() {
		lt := scs.split(0, l)
		cRLT := scs.multiply(lt, &cR)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		scs.addConstraint(compiled.SparseR1C{
			L: cRLT,
			O: scs.newTerm(cS.Neg(&cS), idCS),
			K: scs.coeffID(&cK),
		})
	}
	f4 := func() {
		lt := scs.split(0, l)
		rt := scs.split(0, r)
		cRLT := scs.multiply(lt, &cR)
		cRT := scs.multiply(rt, &cL)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		scs.addConstraint(compiled.SparseR1C{
			L: cRLT,
			R: cRT,
			M: [2]compiled.Term{lt, rt},
			K: scs.coeffID(&cK),
			O: scs.newTerm(cS.Neg(&cS), idCS),
		})
	}
	f5 := func() {
		ot := scs.split(0, o)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		cK.Neg(&cK)
		scs.addConstraint(compiled.SparseR1C{
			L: ot,
			K: scs.coeffID(&cK),
			O: scs.newTerm(cS.Neg(&cS), idCS),
		})
	}
	f6 := func() {
		rt := scs.split(0, r)
		ot := scs.split(0, o)
		cRT := scs.multiply(rt, &cL)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		cK.Neg(&cK)
		scs.addConstraint(compiled.SparseR1C{
			L: scs.negate(ot),
			R: cRT,
			K: scs.coeffID(&cK),
			O: scs.newTerm(cS.Neg(&cS), idCS),
		})
	}
	f7 := func() {
		lt := scs.split(0, l)
		ot := scs.split(0, o)
		cRLT := scs.multiply(lt, &cR)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		cK.Neg(&cK)
		scs.addConstraint(compiled.SparseR1C{
			R: scs.negate(ot),
			L: cRLT,
			K: scs.coeffID(&cK),
			O: scs.newTerm(cS.Neg(&cS), idCS),
		})
	}
	f8 := func() {
		lt := scs.split(0, l)
		rt := scs.split(0, r)
		ot := scs.split(0, o)
		cRLT := scs.multiply(lt, &cR)
		cRT := scs.multiply(rt, &cL)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		cK.Neg(&cK)
		u := scs.newTerm(bOne)
		scs.addConstraint(compiled.SparseR1C{
			L: cRLT,
			R: cRT,
			M: [2]compiled.Term{lt, rt},
			K: scs.coeffID(&cK),
			O: u,
		})
		scs.addConstraint(compiled.SparseR1C{
			L: u,
			R: ot,
			O: scs.newTerm(&cS, idCS),
		})
	}
	f9 := func() {
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		cS.Mul(&cS, &cR)
		scs.addConstraint(compiled.SparseR1C{
			L: scs.newTerm(&cS, idCS),
			K: scs.coeffID(&cK),
		})
	}
	f10 := func() {
		res := scs.newTerm(&cS, idCS)
		rt := scs.split(0, r)
		cRT := scs.multiply(rt, &cL)
		cRes := scs.multiply(res, &cR)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		scs.addConstraint(compiled.SparseR1C{
			L: cRes,
			R: cRT,
			M: [2]compiled.Term{res, rt},
			K: scs.coeffID(&cK),
		})
	}
	f11 := func() {
		lt := scs.split(0, l)
		lt = scs.multiply(lt, &cR)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		cS.Mul(&cS, &cR)
		scs.addConstraint(compiled.SparseR1C{
			L: scs.newTerm(&cS, idCS),
			R: lt,
			K: scs.coeffID(&cK),
		})
	}
	f12 := func() {
		u := scs.newTerm(bOne)
		lt := scs.split(0, l)
		rt := scs.split(0, r)
		cRLT := scs.multiply(lt, &cR)
		cRT := scs.multiply(rt, &cL)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		scs.addConstraint(compiled.SparseR1C{
			L: cRLT,
			R: cRT,
			M: [2]compiled.Term{lt, rt},
			O: u,
			K: scs.coeffID(&cK),
		})
		res := scs.newTerm(&cS, idCS)
		cRes := scs.multiply(res, &cR)
		scs.addConstraint(compiled.SparseR1C{
			R: cRes,
			M: [2]compiled.Term{res, rt},
			O: scs.negate(u),
		})
	}
	f13 := func() {
		ot := scs.split(0, o)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		cS.Mul(&cS, &cR)
		scs.addConstraint(compiled.SparseR1C{
			L: scs.newTerm(&cS, idCS),
			O: scs.negate(ot),
			K: scs.coeffID(&cK),
		})
	}
	f14 := func() {
		ot := scs.split(0, o)
		res := scs.newTerm(&cS, idCS)
		rt := scs.split(0, r)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		scs.addConstraint(compiled.SparseR1C{
			L: scs.multiply(res, &cR),
			R: scs.multiply(rt, &cL),
			M: [2]compiled.Term{res, rt},
			O: scs.negate(ot),
			K: scs.coeffID(&cK),
		})
	}
	f15 := func() {
		ot := scs.split(0, o)
		lt := scs.split(0, l)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		cS.Mul(&cS, &cR)
		scs.addConstraint(compiled.SparseR1C{
			L: scs.newTerm(&cS, idCS),
			R: scs.multiply(lt, &cR),
			O: scs.negate(ot),
			K: scs.coeffID(&cK),
		})
	}
	f16 := func() {
		u := scs.newTerm(bOne)
		lt := scs.split(0, l)
		rt := scs.split(0, r)
		cRLT := scs.multiply(lt, &cR)
		cRT := scs.multiply(rt, &cL)
		cK.Mul(&cL, &cR)
		cK.Sub(&cK, &cO)
		scs.addConstraint(compiled.SparseR1C{
			L: cRLT,
			R: cRT,
			M: [2]compiled.Term{lt, rt},
			O: u,
			K: scs.coeffID(&cK),
		})
		v := scs.newTerm(bOne)
		ot := scs.split(0, o)
		scs.addConstraint(compiled.SparseR1C{
			L: u,
			R: ot,
			O: v,
		})
		res := scs.newTerm(&cS, idCS)
		cRes := scs.multiply(res, &cR)
		scs.addConstraint(compiled.SparseR1C{
			R: cRes,
			M: [2]compiled.Term{res, rt},
			O: v,
		})
	}
	var s uint8
	if lro != 0 {
		s |= 0b1000
	}
	if len(o) != 0 {
		s |= 0b0100
	}
	if len(l) != 0 {
		s |= 0b0010
	}
	if len(r) != 0 {
		s |= 0b0001
	}
	switch s {
	case 0b0000:
		f9()
	case 0b0001:
		f10()
	case 0b0010:
		f11()
	case 0b0011:
		f12()
	case 0b0100:
		f13()
	case 0b0101:
		f14()
	case 0b0110:
		f15()
	case 0b0111:
		f16()
	case 0b1000:
		f1()
	case 0b1001:
		f2()
	case 0b1010:
		f3()
	case 0b1011:
		f4()
	case 0b1100:
		f5()
	case 0b1101:
		f6()
	case 0b1110:
		f7()
	case 0b1111:
		f8()
	}
}
r1cToSparseR1C splits a r1c constraint
package frontend
import (
	"math/big"
	"sync"
	"github.com/consensys/gnark-crypto/ecc"
	"github.com/consensys/gnark/internal/backend/compiled"
	bls12377r1cs "github.com/consensys/gnark/internal/backend/bls12-377/cs"
	bls12381r1cs "github.com/consensys/gnark/internal/backend/bls12-381/cs"
	bls24315r1cs "github.com/consensys/gnark/internal/backend/bls24-315/cs"
	bn254r1cs "github.com/consensys/gnark/internal/backend/bn254/cs"
	bw6761r1cs "github.com/consensys/gnark/internal/backend/bw6-761/cs"
)
type sparseR1CS struct {
	*ConstraintSystem
	ccs compiled.SparseR1CS
	scsInternalVariables int
	solvedVariables []bool
	currentR1CDebugID int  
}
var bOne = new(big.Int).SetInt64(1)
func (cs *ConstraintSystem) toSparseR1CS(curveID ecc.ID) (CompiledConstraintSystem, error) {
	res := sparseR1CS{
		ConstraintSystem: cs,
		ccs: compiled.SparseR1CS{
			CS: compiled.CS{
				NbInternalVariables: len(cs.internal.variables),
				NbPublicVariables:   len(cs.public.variables) - 1,  
				NbSecretVariables:   len(cs.secret.variables),
				DebugInfo:           make([]compiled.LogEntry, len(cs.debugInfo)),
				Logs:                make([]compiled.LogEntry, len(cs.logs)),
				MDebug:              make(map[int]int),
				MHints:              make(map[int]compiled.Hint),
			},
			Constraints: make([]compiled.SparseR1C, 0, len(cs.constraints)),
		},
		solvedVariables:      make([]bool, len(cs.internal.variables), len(cs.internal.variables)*2),
		scsInternalVariables: len(cs.internal.variables),
		currentR1CDebugID:    -1,
	}
	for vID := range cs.mHints {
		res.solvedVariables[vID] = true
	}
	for i := 0; i < len(cs.constraints); i++ {
		if dID, ok := cs.mDebug[i]; ok {
			res.currentR1CDebugID = dID
		} else {
			res.currentR1CDebugID = -1
		}
		res.r1cToSparseR1C(cs.constraints[i])
	}
	shiftVID := func(oldID int, visibility compiled.Visibility) int {
		switch visibility {
		case compiled.Internal:
			return oldID + res.ccs.NbPublicVariables + res.ccs.NbSecretVariables
		case compiled.Public:
			return oldID - 1
		case compiled.Secret:
			return oldID + res.ccs.NbPublicVariables
		default:
			return oldID
		}
	}
	offsetTermID := func(t *compiled.Term) {
		if *t == 0 {
			return
		}
		_, vID, visibility := t.Unpack()
		if vID == 0 && visibility == compiled.Public {
			t.SetVariableVisibility(compiled.Virtual)
			return
		}
		t.SetVariableID(shiftVID(vID, visibility))
	}
	for i := 0; i < len(res.ccs.Constraints); i++ {
		r1c := &res.ccs.Constraints[i]
		offsetTermID(&r1c.L)
		offsetTermID(&r1c.R)
		offsetTermID(&r1c.O)
		offsetTermID(&r1c.M[0])
		offsetTermID(&r1c.M[1])
	}
	for i := 0; i < len(cs.logs); i++ {
		res.ccs.Logs[i] = compiled.LogEntry{
			Format:    cs.logs[i].Format,
			ToResolve: make([]compiled.Term, len(cs.logs[i].ToResolve)),
		}
		copy(res.ccs.Logs[i].ToResolve, cs.logs[i].ToResolve)
		for j := 0; j < len(res.ccs.Logs[i].ToResolve); j++ {
			offsetTermID(&res.ccs.Logs[i].ToResolve[j])
		}
	}
	for i := 0; i < len(cs.debugInfo); i++ {
		res.ccs.DebugInfo[i] = compiled.LogEntry{
			Format:    cs.debugInfo[i].Format,
			ToResolve: make([]compiled.Term, len(cs.debugInfo[i].ToResolve)),
		}
		copy(res.ccs.DebugInfo[i].ToResolve, cs.debugInfo[i].ToResolve)
		for j := 0; j < len(res.ccs.DebugInfo[i].ToResolve); j++ {
			offsetTermID(&res.ccs.DebugInfo[i].ToResolve[j])
		}
	}
	for vID, hint := range cs.mHints {
		k := shiftVID(vID, compiled.Internal)
		inputs := make([]compiled.LinearExpression, len(hint.Inputs))
		copy(inputs, hint.Inputs)
		for j := 0; j < len(inputs); j++ {
			for k := 0; k < len(inputs[j]); k++ {
				offsetTermID(&inputs[j][k])
			}
		}
		res.ccs.MHints[k] = compiled.Hint{ID: hint.ID, Inputs: inputs}
	}
	res.ccs.NbInternalVariables = res.scsInternalVariables
	switch curveID {
	case ecc.BLS12_377:
		return bls12377r1cs.NewSparseR1CS(res.ccs, cs.coeffs), nil
	case ecc.BLS12_381:
		return bls12381r1cs.NewSparseR1CS(res.ccs, cs.coeffs), nil
	case ecc.BN254:
		return bn254r1cs.NewSparseR1CS(res.ccs, cs.coeffs), nil
	case ecc.BW6_761:
		return bw6761r1cs.NewSparseR1CS(res.ccs, cs.coeffs), nil
	case ecc.BLS24_315:
		return bls24315r1cs.NewSparseR1CS(res.ccs, cs.coeffs), nil
	default:
		panic("unknown curveID")
	}
}
func findUnsolvedVariable(r1c compiled.R1C, solvedVariables []bool) (int, int) {
	for i := 0; i < len(r1c.L); i++ {
		_, vID, visibility := r1c.L[i].Unpack()
		if visibility == compiled.Internal && !solvedVariables[vID] {
			return 0, vID
		}
	}
	for i := 0; i < len(r1c.R); i++ {
		_, vID, visibility := r1c.R[i].Unpack()
		if visibility == compiled.Internal && !solvedVariables[vID] {
			return 1, vID
		}
	}
	for i := 0; i < len(r1c.O); i++ {
		_, vID, visibility := r1c.O[i].Unpack()
		if visibility == compiled.Internal && !solvedVariables[vID] {
			return 2, vID
		}
	}
	return -1, -1
}
func popInternalVariable(l compiled.LinearExpression, id int) (compiled.LinearExpression, compiled.Term) {
	var t compiled.Term
	_l := make([]compiled.Term, len(l)-1)
	c := 0
	for i := 0; i < len(l); i++ {
		v := l[i]
		if v.VariableVisibility() == compiled.Internal && v.VariableID() == id {
			t = v
			continue
		}
		_l[c] = v
		c++
	}
	return _l, t
}
func (scs *sparseR1CS) popConstantTerm(l compiled.LinearExpression) (compiled.LinearExpression, big.Int) {
	const idOneWire = 0
	for i := 0; i < len(l); i++ {
		if l[i].VariableID() == idOneWire && l[i].VariableVisibility() == compiled.Public {
			lCopy := make(compiled.LinearExpression, len(l)-1)
			copy(lCopy, l[:i])
			copy(lCopy[i:], l[i+1:])
			return lCopy, scs.coeffs[l[i].CoeffID()]
		}
	}
	return l, big.Int{}
}
func (scs *sparseR1CS) newTerm(coeff *big.Int, idCS ...int) compiled.Term {
	var vID int
	if len(idCS) > 0 {
		vID = idCS[0]
	} else {
		vID = scs.scsInternalVariables
		scs.scsInternalVariables++
	}
	if vID >= len(scs.solvedVariables) {
		if vID < cap(scs.solvedVariables) {
			scs.solvedVariables = scs.solvedVariables[:vID+1]
		} else {
			newSlice := make([]bool, vID+1)
			copy(newSlice, scs.solvedVariables)
			scs.solvedVariables = newSlice
		}
	}
	scs.solvedVariables[vID] = true
	return compiled.Pack(vID, scs.coeffID(coeff), compiled.Internal)
}
func (scs *sparseR1CS) addConstraint(c compiled.SparseR1C) {
	if c.L == 0 {
		c.L.SetVariableID(c.M[0].VariableID())
	}
	if c.R == 0 {
		c.R.SetVariableID(c.M[1].VariableID())
	}
	if c.M[0] == 0 {
		c.M[0].SetVariableID(c.L.VariableID())
	}
	if c.M[1] == 0 {
		c.M[1].SetVariableID(c.R.VariableID())
	}
	if scs.currentR1CDebugID != -1 {
		scs.ccs.MDebug[len(scs.ccs.Constraints)] = scs.currentR1CDebugID
	}
	scs.ccs.Constraints = append(scs.ccs.Constraints, c)
}
func (scs *sparseR1CS) negate(t compiled.Term) compiled.Term {
	if t == 0 {
		return t
	}
	cID := t.CoeffID()
	switch cID {
	case compiled.CoeffIdMinusOne:
		t.SetCoeffID(compiled.CoeffIdOne)
	case compiled.CoeffIdZero:
	case compiled.CoeffIdOne:
		t.SetCoeffID(compiled.CoeffIdMinusOne)
	default:
		coeff := bigIntPool.Get().(*big.Int)
		defer bigIntPool.Put(coeff)
		coeff.Neg(&scs.coeffs[t.CoeffID()])
		t.SetCoeffID(scs.coeffID(coeff))
	}
	return t
}
func (scs *sparseR1CS) multiply(t compiled.Term, c *big.Int) compiled.Term {
	if c.IsInt64() {
		v := c.Int64()
		switch v {
		case 0:
			t.SetCoeffID(compiled.CoeffIdZero)
			return t
		case 1:
			return t
		case -1:
			switch t.CoeffID() {
			case compiled.CoeffIdZero:
				return t
			case compiled.CoeffIdOne:
				t.SetCoeffID(compiled.CoeffIdMinusOne)
				return t
			case compiled.CoeffIdMinusOne:
				t.SetCoeffID(compiled.CoeffIdOne)
				return t
			}
		}
	}
	coeff := bigIntPool.Get().(*big.Int)
	coeff.Mul(&scs.coeffs[t.CoeffID()], c)
	t.SetCoeffID(scs.coeffID(coeff))
	bigIntPool.Put(coeff)
	return t
}
func (scs *sparseR1CS) split(a compiled.Term, l compiled.LinearExpression) compiled.Term {
	if len(l) == 0 {
		return a
	}
	if a == 0 {
		return scs.split(l[0], l[1:])
	}
	r := l[0]
	o := scs.newTerm(bOne)
	scs.addConstraint(compiled.SparseR1C{L: a, R: r, O: o})
	o = scs.negate(o)
	return scs.split(o, l[1:])
}