func (cs *R1CS) CurveID() ecc.ID {
	return ecc.BLS12_377
}
CurveID returns curve ID as defined in gnark-crypto (ecc.BLS12-377)
package cs
import (
	"errors"
	"fmt"
	"io"
	"math/big"
	"strings"
	"github.com/fxamacker/cbor/v2"
	"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"
	"text/template"
	"github.com/consensys/gnark-crypto/ecc/bls12-377/fr"
)
type R1CS struct {
	compiled.R1CS
	Coefficients []fr.Element  
}
func NewR1CS(cs compiled.R1CS, coefficients []big.Int) *R1CS {
	r := R1CS{
		R1CS:         cs,
		Coefficients: make([]fr.Element, len(coefficients)),
	}
	for i := 0; i < len(coefficients); i++ {
		r.Coefficients[i].SetBigInt(&coefficients[i])
	}
	return &r
}
func (cs *R1CS) Solve(witness, a, b, c []fr.Element, opt backend.ProverOption) ([]fr.Element, error) {
	nbWires := cs.NbPublicVariables + cs.NbSecretVariables + cs.NbInternalVariables
	solution, err := newSolution(nbWires, opt.HintFunctions, cs.Coefficients)
	if err != nil {
		return make([]fr.Element, nbWires), err
	}
	if len(witness) != int(cs.NbPublicVariables-1+cs.NbSecretVariables) {  
		return solution.values, fmt.Errorf("invalid witness size, got %d, expected %d = %d (public - ONE_WIRE) + %d (secret)", len(witness), int(cs.NbPublicVariables-1+cs.NbSecretVariables), cs.NbPublicVariables-1, cs.NbSecretVariables)
	}
	if len(a) != len(cs.Constraints) || len(b) != len(cs.Constraints) || len(c) != len(cs.Constraints) {
		return solution.values, errors.New("invalid input size: len(a, b, c) == len(Constraints)")
	}
	solution.solved[0] = true  
	solution.values[0].SetOne()
	copy(solution.values[1:], witness)  
	for i := 0; i < len(witness); i++ {
		solution.solved[i+1] = true
	}
	solution.nbSolved += len(witness) + 1
	defer solution.printLogs(opt.LoggerOut, cs.Logs)
	var check fr.Element
	for i := 0; i < len(cs.Constraints); i++ {
		if err := cs.solveConstraint(cs.Constraints[i], &solution); err != nil {
			return solution.values, err
		}
		a[i], b[i], c[i] = cs.instantiateR1C(cs.Constraints[i], &solution)
		check.Mul(&a[i], &b[i])
		if !check.Equal(&c[i]) {
			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 *R1CS) IsSolved(witness []fr.Element, opt backend.ProverOption) error {
	a := make([]fr.Element, len(cs.Constraints))
	b := make([]fr.Element, len(cs.Constraints))
	c := make([]fr.Element, len(cs.Constraints))
	_, err := cs.Solve(witness, a, b, c, opt)
	return err
}
func (cs *R1CS) mulByCoeff(res *fr.Element, t compiled.Term) {
	cID := t.CoeffID()
	switch cID {
	case compiled.CoeffIdOne:
		return
	case compiled.CoeffIdMinusOne:
		res.Neg(res)
	case compiled.CoeffIdZero:
		res.SetZero()
	case compiled.CoeffIdTwo:
		res.Double(res)
	default:
		res.Mul(res, &cs.Coefficients[cID])
	}
}
func (cs *R1CS) instantiateR1C(r compiled.R1C, solution *solution) (a, b, c fr.Element) {
	var v fr.Element
	for _, t := range r.L {
		v = solution.computeTerm(t)
		a.Add(&a, &v)
	}
	for _, t := range r.R {
		v = solution.computeTerm(t)
		b.Add(&b, &v)
	}
	for _, t := range r.O {
		v = solution.computeTerm(t)
		c.Add(&c, &v)
	}
	return
}
func (cs *R1CS) solveConstraint(r compiled.R1C, solution *solution) error {
	var loc uint8
	var a, b, c fr.Element
	var termToCompute compiled.Term
	processTerm := func(t compiled.Term, val *fr.Element, locValue uint8) error {
		vID := t.VariableID()
		if solution.solved[vID] {
			v := solution.computeTerm(t)
			val.Add(val, &v)
			return nil
		}
		if hint, ok := cs.MHints[vID]; ok {
			return solution.solveWithHint(vID, hint)
		}
		if loc != 0 {
			panic("found more than one wire to instantiate")
		}
		termToCompute = t
		loc = locValue
		return nil
	}
	for _, t := range r.L {
		if err := processTerm(t, &a, 1); err != nil {
			return err
		}
	}
	for _, t := range r.R {
		if err := processTerm(t, &b, 2); err != nil {
			return err
		}
	}
	for _, t := range r.O {
		if err := processTerm(t, &c, 3); err != nil {
			return err
		}
	}
	if loc == 0 {
		return nil
	}
	vID := termToCompute.VariableID()
	var wire fr.Element
	switch loc {
	case 1:
		if !b.IsZero() {
			wire.Div(&c, &b).
				Sub(&wire, &a)
			cs.mulByCoeff(&wire, termToCompute)
		}
	case 2:
		if !a.IsZero() {
			wire.Div(&c, &a).
				Sub(&wire, &b)
			cs.mulByCoeff(&wire, termToCompute)
		}
	case 3:
		wire.Mul(&a, &b).
			Sub(&wire, &c)
		cs.mulByCoeff(&wire, termToCompute)
	}
	solution.set(vID, wire)
	return nil
}
func (cs *R1CS) ToHTML(w io.Writer) error {
	t, err := template.New("cs.html").Funcs(template.FuncMap{
		"toHTML": toHTML,
		"add":    add,
		"sub":    sub,
	}).Parse(compiled.R1CSTemplate)
	if err != nil {
		return err
	}
	return t.Execute(w, cs)
}
func add(a, b int) int {
	return a + b
}
func sub(a, b int) int {
	return a - b
}
func toHTML(l compiled.LinearExpression, coeffs []fr.Element, MHints map[int]int) string {
	var sbb strings.Builder
	for i := 0; i < len(l); i++ {
		termToHTML(l[i], &sbb, coeffs, MHints, false)
		if i+1 < len(l) {
			sbb.WriteString(" + ")
		}
	}
	return sbb.String()
}
func termToHTML(t compiled.Term, sbb *strings.Builder, coeffs []fr.Element, MHints map[int]int, offset bool) {
	tID := t.CoeffID()
	if tID == compiled.CoeffIdOne {
	} else if tID == compiled.CoeffIdMinusOne {
		sbb.WriteString("<span class=\"coefficient\">-</span>")
	} else if tID == compiled.CoeffIdZero {
		sbb.WriteString("<span class=\"coefficient\">0</span>")
		return
	} else {
		sbb.WriteString("<span class=\"coefficient\">")
		sbb.WriteString(coeffs[tID].String())
		sbb.WriteString("</span>*")
	}
	vID := t.VariableID()
	class := ""
	switch t.VariableVisibility() {
	case compiled.Internal:
		class = "internal"
		if _, ok := MHints[vID]; ok {
			class = "hint"
		}
	case compiled.Public:
		class = "public"
	case compiled.Secret:
		class = "secret"
	case compiled.Virtual:
		class = "virtual"
	case compiled.Unset:
		class = "unset"
	default:
		panic("not implemented")
	}
	if offset {
		vID++  
	}
	sbb.WriteString(fmt.Sprintf("<span class=\"%s\">v%d</span>", class, vID))
}
func (cs *R1CS) GetNbCoefficients() int {
	return len(cs.Coefficients)
}