func (cs *SparseR1CS) checkConstraint(c compiled.SparseR1C, solution *solution) error {
	l := solution.computeTerm(c.L)
	r := solution.computeTerm(c.R)
	m0 := solution.computeTerm(c.M[0])
	m1 := solution.computeTerm(c.M[1])
	o := solution.computeTerm(c.O)
	var t fr.Element
	t.Mul(&m0, &m1).Add(&t, &l).Add(&t, &r).Add(&t, &o).Add(&t, &cs.Coefficients[c.K])
	if !t.IsZero() {
		return fmt.Errorf("%w\n%s + %s + (%s * %s) + %s + %s != 0", ErrUnsatisfiedConstraint,
			l.String(),
			r.String(),
			m0.String(),
			m1.String(),
			o.String(),
			cs.Coefficients[c.K].String(),
		)
	}
	return nil
}
checkConstraint verifies that the constraint holds
package cs
import (
	"fmt"
	"github.com/consensys/gnark-crypto/ecc"
	"github.com/fxamacker/cbor/v2"
	"io"
	"math/big"
	"os"
	"strings"
	"text/template"
	"github.com/consensys/gnark/backend"
	"github.com/consensys/gnark/internal/backend/compiled"
	"github.com/consensys/gnark/internal/backend/ioutils"
	"github.com/consensys/gnark-crypto/ecc/bn254/fr"
)
type SparseR1CS struct {
	compiled.SparseR1CS
	Coefficients []fr.Element  
	loggerOut    io.Writer
}
func NewSparseR1CS(ccs compiled.SparseR1CS, coefficients []big.Int) *SparseR1CS {
	cs := SparseR1CS{
		SparseR1CS:   ccs,
		Coefficients: make([]fr.Element, len(coefficients)),
		loggerOut:    os.Stdout,
	}
	for i := 0; i < len(coefficients); i++ {
		cs.Coefficients[i].SetBigInt(&coefficients[i])
	}
	return &cs
}
func (cs *SparseR1CS) Solve(witness []fr.Element, opt backend.ProverOption) ([]fr.Element, error) {
	nbVariables := cs.NbInternalVariables + cs.NbSecretVariables + cs.NbPublicVariables
	expectedWitnessSize := int(cs.NbPublicVariables + cs.NbSecretVariables)
	if len(witness) != expectedWitnessSize {
		return make([]fr.Element, nbVariables), fmt.Errorf(
			"invalid witness size, got %d, expected %d = %d (public) + %d (secret)",
			len(witness),
			expectedWitnessSize,
			cs.NbPublicVariables,
			cs.NbSecretVariables,
		)
	}
	solution, err := newSolution(nbVariables, opt.HintFunctions, cs.Coefficients)
	if err != nil {
		return solution.values, err
	}
	copy(solution.values, witness)
	for i := 0; i < len(witness); i++ {
		solution.solved[i] = true
	}
	solution.nbSolved += len(witness)
	defer solution.printLogs(opt.LoggerOut, cs.Logs)
	coefficientsNegInv := fr.BatchInvert(cs.Coefficients)
	for i := 0; i < len(coefficientsNegInv); i++ {
		coefficientsNegInv[i].Neg(&coefficientsNegInv[i])
	}
	for i := 0; i < len(cs.Constraints); i++ {
		if err := cs.solveConstraint(cs.Constraints[i], &solution, coefficientsNegInv); err != nil {
			return solution.values, fmt.Errorf("constraint %d: %w", i, err)
		}
		if err := cs.checkConstraint(cs.Constraints[i], &solution); err != nil {
			if dID, ok := cs.MDebug[i]; ok {
				debugInfoStr := solution.logValue(cs.DebugInfo[dID])
				return solution.values, fmt.Errorf("%w: %s", ErrUnsatisfiedConstraint, debugInfoStr)
			}
			return solution.values, ErrUnsatisfiedConstraint
		}
	}
	if !solution.isValid() {
		panic("solver didn't instantiate all wires")
	}
	return solution.values, nil
}
func (cs *SparseR1CS) computeHints(c compiled.SparseR1C, solution *solution) (int, error) {
	r := -1
	lID, rID, oID := c.L.VariableID(), c.R.VariableID(), c.O.VariableID()
	if (c.L.CoeffID() != 0 || c.M[0].CoeffID() != 0) && !solution.solved[lID] {
		if hint, ok := cs.MHints[lID]; ok {
			if err := solution.solveWithHint(lID, hint); err != nil {
				return -1, err
			}
		} else {
			r = 0
		}
	}
	if (c.R.CoeffID() != 0 || c.M[1].CoeffID() != 0) && !solution.solved[rID] {
		if hint, ok := cs.MHints[rID]; ok {
			if err := solution.solveWithHint(rID, hint); err != nil {
				return -1, err
			}
		} else {
			r = 1
		}
	}
	if (c.O.CoeffID() != 0) && !solution.solved[oID] {
		if hint, ok := cs.MHints[oID]; ok {
			if err := solution.solveWithHint(oID, hint); err != nil {
				return -1, err
			}
		} else {
			r = 2
		}
	}
	return r, nil
}
func (cs *SparseR1CS) solveConstraint(c compiled.SparseR1C, solution *solution, coefficientsNegInv []fr.Element) error {
	lro, err := cs.computeHints(c, solution)
	if err != nil {
		return err
	}
	if lro == -1 {
		return nil
	}
	if lro == 1 {
		panic("unsolved wire in R; shouldn't happen as frontend puts unsolved wire in L")
	}
	if lro == 0 {  
		if !solution.solved[c.R.VariableID()] {
			panic("R wire should be instantiated when we solve L")
		}
		var u1, u2, u3, den, num, v1, v2 fr.Element
		u3.Mul(&cs.Coefficients[c.M[0].CoeffID()], &cs.Coefficients[c.M[1].CoeffID()])
		u1.Set(&cs.Coefficients[c.L.CoeffID()])
		u2.Set(&cs.Coefficients[c.R.CoeffID()])
		den.Mul(&u3, &solution.values[c.R.VariableID()]).Add(&den, &u1)
		v1 = solution.computeTerm(c.R)
		v2 = solution.computeTerm(c.O)
		num.Add(&v1, &v2).Add(&num, &cs.Coefficients[c.K])
		num.Div(&num, &den).Neg(&num)
		solution.set(c.L.VariableID(), num)
		return nil
	}
	var o fr.Element
	cID, vID, _ := c.O.Unpack()
	l := solution.computeTerm(c.L)
	r := solution.computeTerm(c.R)
	m0 := solution.computeTerm(c.M[0])
	m1 := solution.computeTerm(c.M[1])
	o.Mul(&m0, &m1).Add(&o, &l).Add(&o, &r).Add(&o, &cs.Coefficients[c.K])
	o.Mul(&o, &coefficientsNegInv[cID])
	solution.set(vID, o)
	return nil
}
func (cs *SparseR1CS) IsSolved(witness []fr.Element, opt backend.ProverOption) error {
	_, err := cs.Solve(witness, opt)
	return err
}