github.com/consensys/gnark-crypto@v0.14.0/ecc/bw6-761/multiexp_affine.go (about)

     1  // Copyright 2020 Consensys Software Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Code generated by consensys/gnark-crypto DO NOT EDIT
    16  
    17  package bw6761
    18  
    19  import (
    20  	"github.com/consensys/gnark-crypto/ecc/bw6-761/fp"
    21  )
    22  
    23  type batchOpG1Affine struct {
    24  	bucketID uint16
    25  	point    G1Affine
    26  }
    27  
    28  // processChunkG1BatchAffine process a chunk of the scalars during the msm
    29  // using affine coordinates for the buckets. To amortize the cost of the inverse in the affine addition
    30  // we use a batch affine addition.
    31  //
    32  // this is derived from a PR by 0x0ece : https://github.com/ConsenSys/gnark-crypto/pull/249
    33  // See Section 5.3: ia.cr/2022/1396
    34  func processChunkG1BatchAffine[BJE ibg1JacExtended, B ibG1Affine, BS bitSet, TP pG1Affine, TPP ppG1Affine, TQ qOpsG1Affine, TC cG1Affine](
    35  	chunk uint64,
    36  	chRes chan<- g1JacExtended,
    37  	c uint64,
    38  	points []G1Affine,
    39  	digits []uint16,
    40  	sem chan struct{}) {
    41  
    42  	if sem != nil {
    43  		// if we are limited, wait for a token in the semaphore
    44  		<-sem
    45  	}
    46  
    47  	// the batch affine addition needs independent points; in other words, for a window of batchSize
    48  	// we want to hit independent bucketIDs when processing the digit. if there is a conflict (we're trying
    49  	// to add 2 different points to the same bucket), then we push the conflicted point to a queue.
    50  	// each time the batch is full, we execute it, and tentatively put the points (if not conflict)
    51  	// from the top of the queue into the next batch.
    52  	// if the queue is full, we "flush it"; we sequentially add the points to the buckets in
    53  	// g1JacExtended coordinates.
    54  	// The reasoning behind this is the following; batchSize is chosen such as, for a uniformly random
    55  	// input, the number of conflicts is going to be low, and the element added to the queue should be immediately
    56  	// processed in the next batch. If it's not the case, then our inputs are not random; and we fallback to
    57  	// non-batch-affine version.
    58  
    59  	// note that we have 2 sets of buckets
    60  	// 1 in G1Affine used with the batch affine additions
    61  	// 1 in g1JacExtended used in case the queue of conflicting points
    62  	var buckets B // in G1Affine coordinates, infinity point is represented as (0,0), no need to init
    63  	var bucketsJE BJE
    64  	for i := 0; i < len(buckets); i++ {
    65  		bucketsJE[i].setInfinity()
    66  	}
    67  
    68  	// setup for the batch affine;
    69  	var (
    70  		bucketIds BS  // bitSet to signify presence of a bucket in current batch
    71  		cptAdd    int // count the number of bucket + point added to current batch
    72  		R         TPP // bucket references
    73  		P         TP  // points to be added to R (buckets); it is beneficial to store them on the stack (ie copy)
    74  		queue     TQ  // queue of points that conflict the current batch
    75  		qID       int // current position in queue
    76  	)
    77  
    78  	batchSize := len(P)
    79  
    80  	isFull := func() bool { return cptAdd == batchSize }
    81  
    82  	executeAndReset := func() {
    83  		batchAddG1Affine[TP, TPP, TC](&R, &P, cptAdd)
    84  		var tmp BS
    85  		bucketIds = tmp
    86  		cptAdd = 0
    87  	}
    88  
    89  	addFromQueue := func(op batchOpG1Affine) {
    90  		// @precondition: must ensures bucket is not "used" in current batch
    91  		// note that there is a bit of duplicate logic between add and addFromQueue
    92  		// the reason is that as of Go 1.19.3, if we pass a pointer to the queue item (see add signature)
    93  		// the compiler will put the queue on the heap.
    94  		BK := &buckets[op.bucketID]
    95  
    96  		// handle special cases with inf or -P / P
    97  		if BK.IsInfinity() {
    98  			BK.Set(&op.point)
    99  			return
   100  		}
   101  		if BK.X.Equal(&op.point.X) {
   102  			if BK.Y.Equal(&op.point.Y) {
   103  				// P + P: doubling, which should be quite rare --
   104  				// we use the other set of buckets
   105  				bucketsJE[op.bucketID].addMixed(&op.point)
   106  				return
   107  			}
   108  			BK.setInfinity()
   109  			return
   110  		}
   111  
   112  		bucketIds[op.bucketID] = true
   113  		R[cptAdd] = BK
   114  		P[cptAdd] = op.point
   115  		cptAdd++
   116  	}
   117  
   118  	add := func(bucketID uint16, PP *G1Affine, isAdd bool) {
   119  		// @precondition: ensures bucket is not "used" in current batch
   120  		BK := &buckets[bucketID]
   121  		// handle special cases with inf or -P / P
   122  		if BK.IsInfinity() {
   123  			if isAdd {
   124  				BK.Set(PP)
   125  			} else {
   126  				BK.Neg(PP)
   127  			}
   128  			return
   129  		}
   130  		if BK.X.Equal(&PP.X) {
   131  			if BK.Y.Equal(&PP.Y) {
   132  				// P + P: doubling, which should be quite rare --
   133  				if isAdd {
   134  					bucketsJE[bucketID].addMixed(PP)
   135  				} else {
   136  					BK.setInfinity()
   137  				}
   138  				return
   139  			}
   140  			if isAdd {
   141  				BK.setInfinity()
   142  			} else {
   143  				bucketsJE[bucketID].subMixed(PP)
   144  			}
   145  			return
   146  		}
   147  
   148  		bucketIds[bucketID] = true
   149  		R[cptAdd] = BK
   150  		if isAdd {
   151  			P[cptAdd].Set(PP)
   152  		} else {
   153  			P[cptAdd].Neg(PP)
   154  		}
   155  		cptAdd++
   156  	}
   157  
   158  	flushQueue := func() {
   159  		for i := 0; i < qID; i++ {
   160  			bucketsJE[queue[i].bucketID].addMixed(&queue[i].point)
   161  		}
   162  		qID = 0
   163  	}
   164  
   165  	processTopQueue := func() {
   166  		for i := qID - 1; i >= 0; i-- {
   167  			if bucketIds[queue[i].bucketID] {
   168  				return
   169  			}
   170  			addFromQueue(queue[i])
   171  			// len(queue) < batchSize so no need to check for full batch.
   172  			qID--
   173  		}
   174  	}
   175  
   176  	for i, digit := range digits {
   177  
   178  		if digit == 0 || points[i].IsInfinity() {
   179  			continue
   180  		}
   181  
   182  		bucketID := uint16((digit >> 1))
   183  		isAdd := digit&1 == 0
   184  		if isAdd {
   185  			// add
   186  			bucketID -= 1
   187  		}
   188  
   189  		if bucketIds[bucketID] {
   190  			// put it in queue
   191  			queue[qID].bucketID = bucketID
   192  			if isAdd {
   193  				queue[qID].point.Set(&points[i])
   194  			} else {
   195  				queue[qID].point.Neg(&points[i])
   196  			}
   197  			qID++
   198  
   199  			// queue is full, flush it.
   200  			if qID == len(queue)-1 {
   201  				flushQueue()
   202  			}
   203  			continue
   204  		}
   205  
   206  		// we add the point to the batch.
   207  		add(bucketID, &points[i], isAdd)
   208  		if isFull() {
   209  			executeAndReset()
   210  			processTopQueue()
   211  		}
   212  	}
   213  
   214  	// flush items in batch.
   215  	executeAndReset()
   216  
   217  	// empty the queue
   218  	flushQueue()
   219  
   220  	// reduce buckets into total
   221  	// total =  bucket[0] + 2*bucket[1] + 3*bucket[2] ... + n*bucket[n-1]
   222  	var runningSum, total g1JacExtended
   223  	runningSum.setInfinity()
   224  	total.setInfinity()
   225  	for k := len(buckets) - 1; k >= 0; k-- {
   226  		runningSum.addMixed(&buckets[k])
   227  		if !bucketsJE[k].IsInfinity() {
   228  			runningSum.add(&bucketsJE[k])
   229  		}
   230  		total.add(&runningSum)
   231  	}
   232  
   233  	if sem != nil {
   234  		// release a token to the semaphore
   235  		// before sending to chRes
   236  		sem <- struct{}{}
   237  	}
   238  
   239  	chRes <- total
   240  
   241  }
   242  
   243  // we declare the buckets as fixed-size array types
   244  // this allow us to allocate the buckets on the stack
   245  type bucketG1AffineC10 [512]G1Affine
   246  type bucketG1AffineC16 [32768]G1Affine
   247  
   248  // buckets: array of G1Affine points of size 1 << (c-1)
   249  type ibG1Affine interface {
   250  	bucketG1AffineC10 |
   251  		bucketG1AffineC16
   252  }
   253  
   254  // array of coordinates fp.Element
   255  type cG1Affine interface {
   256  	cG1AffineC10 |
   257  		cG1AffineC16
   258  }
   259  
   260  // buckets: array of G1Affine points (for the batch addition)
   261  type pG1Affine interface {
   262  	pG1AffineC10 |
   263  		pG1AffineC16
   264  }
   265  
   266  // buckets: array of *G1Affine points (for the batch addition)
   267  type ppG1Affine interface {
   268  	ppG1AffineC10 |
   269  		ppG1AffineC16
   270  }
   271  
   272  // buckets: array of G1Affine queue operations (for the batch addition)
   273  type qOpsG1Affine interface {
   274  	qG1AffineC10 |
   275  		qG1AffineC16
   276  }
   277  
   278  // batch size 80 when c = 10
   279  type cG1AffineC10 [80]fp.Element
   280  type pG1AffineC10 [80]G1Affine
   281  type ppG1AffineC10 [80]*G1Affine
   282  type qG1AffineC10 [80]batchOpG1Affine
   283  
   284  // batch size 640 when c = 16
   285  type cG1AffineC16 [640]fp.Element
   286  type pG1AffineC16 [640]G1Affine
   287  type ppG1AffineC16 [640]*G1Affine
   288  type qG1AffineC16 [640]batchOpG1Affine
   289  
   290  type batchOpG2Affine struct {
   291  	bucketID uint16
   292  	point    G2Affine
   293  }
   294  
   295  // processChunkG2BatchAffine process a chunk of the scalars during the msm
   296  // using affine coordinates for the buckets. To amortize the cost of the inverse in the affine addition
   297  // we use a batch affine addition.
   298  //
   299  // this is derived from a PR by 0x0ece : https://github.com/ConsenSys/gnark-crypto/pull/249
   300  // See Section 5.3: ia.cr/2022/1396
   301  func processChunkG2BatchAffine[BJE ibg2JacExtended, B ibG2Affine, BS bitSet, TP pG2Affine, TPP ppG2Affine, TQ qOpsG2Affine, TC cG2Affine](
   302  	chunk uint64,
   303  	chRes chan<- g2JacExtended,
   304  	c uint64,
   305  	points []G2Affine,
   306  	digits []uint16,
   307  	sem chan struct{}) {
   308  
   309  	if sem != nil {
   310  		// if we are limited, wait for a token in the semaphore
   311  		<-sem
   312  	}
   313  
   314  	// the batch affine addition needs independent points; in other words, for a window of batchSize
   315  	// we want to hit independent bucketIDs when processing the digit. if there is a conflict (we're trying
   316  	// to add 2 different points to the same bucket), then we push the conflicted point to a queue.
   317  	// each time the batch is full, we execute it, and tentatively put the points (if not conflict)
   318  	// from the top of the queue into the next batch.
   319  	// if the queue is full, we "flush it"; we sequentially add the points to the buckets in
   320  	// g2JacExtended coordinates.
   321  	// The reasoning behind this is the following; batchSize is chosen such as, for a uniformly random
   322  	// input, the number of conflicts is going to be low, and the element added to the queue should be immediately
   323  	// processed in the next batch. If it's not the case, then our inputs are not random; and we fallback to
   324  	// non-batch-affine version.
   325  
   326  	// note that we have 2 sets of buckets
   327  	// 1 in G2Affine used with the batch affine additions
   328  	// 1 in g2JacExtended used in case the queue of conflicting points
   329  	var buckets B // in G2Affine coordinates, infinity point is represented as (0,0), no need to init
   330  	var bucketsJE BJE
   331  	for i := 0; i < len(buckets); i++ {
   332  		bucketsJE[i].setInfinity()
   333  	}
   334  
   335  	// setup for the batch affine;
   336  	var (
   337  		bucketIds BS  // bitSet to signify presence of a bucket in current batch
   338  		cptAdd    int // count the number of bucket + point added to current batch
   339  		R         TPP // bucket references
   340  		P         TP  // points to be added to R (buckets); it is beneficial to store them on the stack (ie copy)
   341  		queue     TQ  // queue of points that conflict the current batch
   342  		qID       int // current position in queue
   343  	)
   344  
   345  	batchSize := len(P)
   346  
   347  	isFull := func() bool { return cptAdd == batchSize }
   348  
   349  	executeAndReset := func() {
   350  		batchAddG2Affine[TP, TPP, TC](&R, &P, cptAdd)
   351  		var tmp BS
   352  		bucketIds = tmp
   353  		cptAdd = 0
   354  	}
   355  
   356  	addFromQueue := func(op batchOpG2Affine) {
   357  		// @precondition: must ensures bucket is not "used" in current batch
   358  		// note that there is a bit of duplicate logic between add and addFromQueue
   359  		// the reason is that as of Go 1.19.3, if we pass a pointer to the queue item (see add signature)
   360  		// the compiler will put the queue on the heap.
   361  		BK := &buckets[op.bucketID]
   362  
   363  		// handle special cases with inf or -P / P
   364  		if BK.IsInfinity() {
   365  			BK.Set(&op.point)
   366  			return
   367  		}
   368  		if BK.X.Equal(&op.point.X) {
   369  			if BK.Y.Equal(&op.point.Y) {
   370  				// P + P: doubling, which should be quite rare --
   371  				// we use the other set of buckets
   372  				bucketsJE[op.bucketID].addMixed(&op.point)
   373  				return
   374  			}
   375  			BK.setInfinity()
   376  			return
   377  		}
   378  
   379  		bucketIds[op.bucketID] = true
   380  		R[cptAdd] = BK
   381  		P[cptAdd] = op.point
   382  		cptAdd++
   383  	}
   384  
   385  	add := func(bucketID uint16, PP *G2Affine, isAdd bool) {
   386  		// @precondition: ensures bucket is not "used" in current batch
   387  		BK := &buckets[bucketID]
   388  		// handle special cases with inf or -P / P
   389  		if BK.IsInfinity() {
   390  			if isAdd {
   391  				BK.Set(PP)
   392  			} else {
   393  				BK.Neg(PP)
   394  			}
   395  			return
   396  		}
   397  		if BK.X.Equal(&PP.X) {
   398  			if BK.Y.Equal(&PP.Y) {
   399  				// P + P: doubling, which should be quite rare --
   400  				if isAdd {
   401  					bucketsJE[bucketID].addMixed(PP)
   402  				} else {
   403  					BK.setInfinity()
   404  				}
   405  				return
   406  			}
   407  			if isAdd {
   408  				BK.setInfinity()
   409  			} else {
   410  				bucketsJE[bucketID].subMixed(PP)
   411  			}
   412  			return
   413  		}
   414  
   415  		bucketIds[bucketID] = true
   416  		R[cptAdd] = BK
   417  		if isAdd {
   418  			P[cptAdd].Set(PP)
   419  		} else {
   420  			P[cptAdd].Neg(PP)
   421  		}
   422  		cptAdd++
   423  	}
   424  
   425  	flushQueue := func() {
   426  		for i := 0; i < qID; i++ {
   427  			bucketsJE[queue[i].bucketID].addMixed(&queue[i].point)
   428  		}
   429  		qID = 0
   430  	}
   431  
   432  	processTopQueue := func() {
   433  		for i := qID - 1; i >= 0; i-- {
   434  			if bucketIds[queue[i].bucketID] {
   435  				return
   436  			}
   437  			addFromQueue(queue[i])
   438  			// len(queue) < batchSize so no need to check for full batch.
   439  			qID--
   440  		}
   441  	}
   442  
   443  	for i, digit := range digits {
   444  
   445  		if digit == 0 || points[i].IsInfinity() {
   446  			continue
   447  		}
   448  
   449  		bucketID := uint16((digit >> 1))
   450  		isAdd := digit&1 == 0
   451  		if isAdd {
   452  			// add
   453  			bucketID -= 1
   454  		}
   455  
   456  		if bucketIds[bucketID] {
   457  			// put it in queue
   458  			queue[qID].bucketID = bucketID
   459  			if isAdd {
   460  				queue[qID].point.Set(&points[i])
   461  			} else {
   462  				queue[qID].point.Neg(&points[i])
   463  			}
   464  			qID++
   465  
   466  			// queue is full, flush it.
   467  			if qID == len(queue)-1 {
   468  				flushQueue()
   469  			}
   470  			continue
   471  		}
   472  
   473  		// we add the point to the batch.
   474  		add(bucketID, &points[i], isAdd)
   475  		if isFull() {
   476  			executeAndReset()
   477  			processTopQueue()
   478  		}
   479  	}
   480  
   481  	// flush items in batch.
   482  	executeAndReset()
   483  
   484  	// empty the queue
   485  	flushQueue()
   486  
   487  	// reduce buckets into total
   488  	// total =  bucket[0] + 2*bucket[1] + 3*bucket[2] ... + n*bucket[n-1]
   489  	var runningSum, total g2JacExtended
   490  	runningSum.setInfinity()
   491  	total.setInfinity()
   492  	for k := len(buckets) - 1; k >= 0; k-- {
   493  		runningSum.addMixed(&buckets[k])
   494  		if !bucketsJE[k].IsInfinity() {
   495  			runningSum.add(&bucketsJE[k])
   496  		}
   497  		total.add(&runningSum)
   498  	}
   499  
   500  	if sem != nil {
   501  		// release a token to the semaphore
   502  		// before sending to chRes
   503  		sem <- struct{}{}
   504  	}
   505  
   506  	chRes <- total
   507  
   508  }
   509  
   510  // we declare the buckets as fixed-size array types
   511  // this allow us to allocate the buckets on the stack
   512  type bucketG2AffineC10 [512]G2Affine
   513  type bucketG2AffineC16 [32768]G2Affine
   514  
   515  // buckets: array of G2Affine points of size 1 << (c-1)
   516  type ibG2Affine interface {
   517  	bucketG2AffineC10 |
   518  		bucketG2AffineC16
   519  }
   520  
   521  // array of coordinates fp.Element
   522  type cG2Affine interface {
   523  	cG2AffineC10 |
   524  		cG2AffineC16
   525  }
   526  
   527  // buckets: array of G2Affine points (for the batch addition)
   528  type pG2Affine interface {
   529  	pG2AffineC10 |
   530  		pG2AffineC16
   531  }
   532  
   533  // buckets: array of *G2Affine points (for the batch addition)
   534  type ppG2Affine interface {
   535  	ppG2AffineC10 |
   536  		ppG2AffineC16
   537  }
   538  
   539  // buckets: array of G2Affine queue operations (for the batch addition)
   540  type qOpsG2Affine interface {
   541  	qG2AffineC10 |
   542  		qG2AffineC16
   543  }
   544  
   545  // batch size 80 when c = 10
   546  type cG2AffineC10 [80]fp.Element
   547  type pG2AffineC10 [80]G2Affine
   548  type ppG2AffineC10 [80]*G2Affine
   549  type qG2AffineC10 [80]batchOpG2Affine
   550  
   551  // batch size 640 when c = 16
   552  type cG2AffineC16 [640]fp.Element
   553  type pG2AffineC16 [640]G2Affine
   554  type ppG2AffineC16 [640]*G2Affine
   555  type qG2AffineC16 [640]batchOpG2Affine
   556  
   557  type bitSetC2 [2]bool
   558  type bitSetC3 [4]bool
   559  type bitSetC4 [8]bool
   560  type bitSetC5 [16]bool
   561  type bitSetC8 [128]bool
   562  type bitSetC10 [512]bool
   563  type bitSetC16 [32768]bool
   564  
   565  type bitSet interface {
   566  	bitSetC2 |
   567  		bitSetC3 |
   568  		bitSetC4 |
   569  		bitSetC5 |
   570  		bitSetC8 |
   571  		bitSetC10 |
   572  		bitSetC16
   573  }