1use alloc::{vec, vec::Vec};
19use ark_ec::{
20 models::short_weierstrass::Affine as SWJAffine, short_weierstrass::SWCurveConfig,
21 AdditiveGroup, AffineRepr, CurveGroup,
22};
23use ark_ff::{BitIteratorBE, Field, One, PrimeField, Zero};
24use core::ops::AddAssign;
25use itertools::Itertools;
26use mina_poseidon::sponge::ScalarChallenge;
27#[cfg(feature = "parallel")]
28use rayon::prelude::*;
29
30fn add_pairs_in_place<P: SWCurveConfig>(pairs: &mut Vec<SWJAffine<P>>) {
31 let len = if pairs.len().is_multiple_of(2) {
32 pairs.len()
33 } else {
34 pairs.len() - 1
35 };
36 let mut denominators = pairs
37 .chunks_exact_mut(2)
38 .map(|p| {
39 if p[0].x == p[1].x {
40 if p[1].y.is_zero() {
41 P::BaseField::one()
42 } else {
43 p[1].y.double()
44 }
45 } else {
46 p[0].x - p[1].x
47 }
48 })
49 .collect::<Vec<_>>();
50
51 ark_ff::batch_inversion::<P::BaseField>(&mut denominators);
52
53 for (i, d) in (0..len).step_by(2).zip(denominators.iter()) {
54 let j = i / 2;
55 if pairs[i + 1].is_zero() {
56 pairs[j] = pairs[i];
57 } else if pairs[i].is_zero() {
58 pairs[j] = pairs[i + 1];
59 } else if pairs[i + 1].x == pairs[i].x
60 && (pairs[i + 1].y != pairs[i].y || pairs[i + 1].y.is_zero())
61 {
62 pairs[j] = SWJAffine::<P>::zero();
63 } else if pairs[i + 1].x == pairs[i].x && pairs[i + 1].y == pairs[i].y {
64 let sq = pairs[i].x.square();
65 let s = (sq.double() + sq + P::COEFF_A) * d;
66 let x = s.square() - pairs[i].x.double();
67 let y = -pairs[i].y - (s * (x - pairs[i].x));
68 pairs[j].x = x;
69 pairs[j].y = y;
70 } else {
71 let s = (pairs[i].y - pairs[i + 1].y) * d;
72 let x = s.square() - pairs[i].x - pairs[i + 1].x;
73 let y = -pairs[i].y - (s * (x - pairs[i].x));
74 pairs[j].x = x;
75 pairs[j].y = y;
76 }
77 }
78
79 let len = pairs.len();
80 if len % 2 == 1 {
81 pairs[len / 2] = pairs[len - 1];
82 pairs.truncate(len / 2 + 1);
83 } else {
84 pairs.truncate(len / 2);
85 }
86}
87
88fn batch_add_assign_no_branch<P: SWCurveConfig>(
93 denominators: &mut [P::BaseField],
94 v0: &mut [SWJAffine<P>],
95 v1: &[SWJAffine<P>],
96) {
97 o1_utils::cfg_iter_mut!(denominators)
98 .enumerate()
99 .for_each(|(i, denom)| {
100 let p0 = v0[i];
101 let p1 = v1[i];
102 let d = p0.x - p1.x;
103 *denom = d;
104 });
105
106 ark_ff::batch_inversion::<P::BaseField>(denominators);
107
108 o1_utils::cfg_iter!(denominators)
109 .zip(o1_utils::cfg_iter_mut!(v0))
110 .zip(o1_utils::cfg_iter!(v1))
111 .for_each(|((d, p0), p1)| {
112 let s = (p0.y - p1.y) * d;
113 let x = s.square() - p0.x - p1.x;
114 let y = -p0.y - (s * (x - p0.x));
115 p0.x = x;
116 p0.y = y;
117 });
118}
119
120pub fn batch_add_assign<P: SWCurveConfig>(
122 denominators: &mut [P::BaseField],
123 v0: &mut [SWJAffine<P>],
124 v1: &[SWJAffine<P>],
125) {
126 o1_utils::cfg_iter_mut!(denominators)
127 .zip(o1_utils::cfg_iter!(v0))
128 .zip(o1_utils::cfg_iter!(v1))
129 .for_each(|((denom, p0), p1)| {
130 let d = if p0.x == p1.x {
131 if p1.y.is_zero() {
132 P::BaseField::one()
133 } else {
134 p1.y.double()
135 }
136 } else {
137 p0.x - p1.x
138 };
139 *denom = d;
140 });
141
142 ark_ff::batch_inversion::<P::BaseField>(denominators);
143
144 o1_utils::cfg_iter!(denominators)
145 .zip(o1_utils::cfg_iter_mut!(v0))
146 .zip(o1_utils::cfg_iter!(v1))
147 .for_each(|((d, p0), p1)| {
148 if p1.is_zero() {
149 } else if p0.is_zero() {
150 *p0 = *p1;
151 } else if p1.x == p0.x && (p1.y != p0.y || p1.y == P::BaseField::zero()) {
152 *p0 = SWJAffine::<P>::zero();
153 } else if p1.x == p0.x && p1.y == p0.y {
154 let sq = p0.x.square();
155 let s = (sq.double() + sq + P::COEFF_A) * d;
156 let x = s.square() - p0.x.double();
157 let y = -p0.y - (s * (x - p0.x));
158 p0.x = x;
159 p0.y = y;
160 } else {
161 let s = (p0.y - p1.y) * d;
162 let x = s.square() - p0.x - p1.x;
163 let y = -p0.y - (s * (x - p0.x));
164 p0.x = x;
165 p0.y = y;
166 }
167 });
168}
169
170fn affine_window_combine_base<P: SWCurveConfig>(
171 g1: &[SWJAffine<P>],
172 g2: &[SWJAffine<P>],
173 x1: P::ScalarField,
174 x2: P::ScalarField,
175) -> Vec<SWJAffine<P>> {
176 let g1g2 = {
177 let mut v: Vec<_> = (0..2 * g1.len())
178 .map(|i| {
179 let j = i / 2;
180 if i % 2 == 0 {
181 g1[j]
182 } else {
183 g2[j]
184 }
185 })
186 .collect();
187 add_pairs_in_place(&mut v);
188 v
189 };
190 assert!(g1g2.len() == g1.len());
191
192 let windows1 = BitIteratorBE::new(x1.into_bigint()).tuples();
193 let windows2 = BitIteratorBE::new(x2.into_bigint()).tuples();
194
195 let mut points = vec![SWJAffine::<P>::zero(); g1.len()];
196
197 let mut denominators = vec![P::BaseField::zero(); g1.len()];
198
199 let [g01_00, g10_00, g11_00, g00_01, g01_01, g10_01, g11_01, g00_10, g01_10, g10_10, g11_10, g00_11, g01_11, g10_11, g11_11] =
200 affine_shamir_window_table(&mut denominators, g1, g2);
201
202 for ((hi_1, lo_1), (hi_2, lo_2)) in windows1.zip(windows2) {
203 for _ in 0..2 {
205 for i in 0..g1.len() {
206 denominators[i] = points[i].y.double();
207 }
208 ark_ff::batch_inversion::<P::BaseField>(&mut denominators);
209
210 for i in 0..g1.len() {
212 let d = denominators[i];
213 let sq = points[i].x.square();
214 let s = (sq.double() + sq + P::COEFF_A) * d;
215 let x = s.square() - points[i].x.double();
216 let y = -points[i].y - (s * (x - points[i].x));
217 points[i].x = x;
218 points[i].y = y;
219 }
220 }
221
222 match ((hi_1, lo_1), (hi_2, lo_2)) {
223 ((false, false), (false, false)) => (),
224 ((false, true), (false, false)) => {
225 batch_add_assign(&mut denominators, &mut points, &g01_00);
226 }
227 ((true, false), (false, false)) => {
228 batch_add_assign(&mut denominators, &mut points, &g10_00);
229 }
230 ((true, true), (false, false)) => {
231 batch_add_assign(&mut denominators, &mut points, &g11_00);
232 }
233
234 ((false, false), (false, true)) => {
235 batch_add_assign(&mut denominators, &mut points, &g00_01);
236 }
237 ((false, true), (false, true)) => {
238 batch_add_assign(&mut denominators, &mut points, &g01_01);
239 }
240 ((true, false), (false, true)) => {
241 batch_add_assign(&mut denominators, &mut points, &g10_01);
242 }
243 ((true, true), (false, true)) => {
244 batch_add_assign(&mut denominators, &mut points, &g11_01);
245 }
246
247 ((false, false), (true, false)) => {
248 batch_add_assign(&mut denominators, &mut points, &g00_10);
249 }
250 ((false, true), (true, false)) => {
251 batch_add_assign(&mut denominators, &mut points, &g01_10);
252 }
253 ((true, false), (true, false)) => {
254 batch_add_assign(&mut denominators, &mut points, &g10_10);
255 }
256 ((true, true), (true, false)) => {
257 batch_add_assign(&mut denominators, &mut points, &g11_10);
258 }
259
260 ((false, false), (true, true)) => {
261 batch_add_assign(&mut denominators, &mut points, &g00_11);
262 }
263 ((false, true), (true, true)) => {
264 batch_add_assign(&mut denominators, &mut points, &g01_11);
265 }
266 ((true, false), (true, true)) => {
267 batch_add_assign(&mut denominators, &mut points, &g10_11);
268 }
269 ((true, true), (true, true)) => {
270 batch_add_assign(&mut denominators, &mut points, &g11_11);
271 }
272 }
273 }
274 points
275}
276
277fn batch_endo_in_place<P: SWCurveConfig>(endo_coeff: P::BaseField, ps: &mut [SWJAffine<P>]) {
278 o1_utils::cfg_iter_mut!(ps).for_each(|p| p.x *= endo_coeff);
279}
280
281fn batch_negate_in_place<P: SWCurveConfig>(ps: &mut [SWJAffine<P>]) {
282 o1_utils::cfg_iter_mut!(ps).for_each(|p| {
283 p.y = -p.y;
284 });
285}
286
287fn affine_window_combine_one_endo_base<P: SWCurveConfig>(
291 endo_coeff: P::BaseField,
292 g1: &[SWJAffine<P>],
293 g2: &[SWJAffine<P>],
294 chal: &ScalarChallenge<P::ScalarField>,
295) -> Vec<SWJAffine<P>> {
296 fn assign<A: Copy>(dst: &mut [A], src: &[A]) {
297 let n = dst.len();
298 dst[..n].clone_from_slice(&src[..n]);
299 }
300
301 fn get_bit(limbs_lsb: &[u64], i: u64) -> u64 {
302 let limb = i / 64;
303 let j = i % 64;
304 (limbs_lsb[limb as usize] >> j) & 1
305 }
306
307 let rep = chal.inner().into_bigint();
308 let r = rep.as_ref();
309
310 let mut denominators = vec![P::BaseField::zero(); g1.len()];
311 let mut points = g2.to_vec();
313 batch_endo_in_place(endo_coeff, &mut points);
314 batch_add_assign_no_branch(&mut denominators, &mut points, g2);
315 batch_double_in_place(&mut denominators, &mut points);
316
317 let mut tmp_s = g2.to_vec();
318 let mut tmp_acc = g2.to_vec();
319 for i in (0..(128 / 2)).rev() {
320 assign(&mut tmp_s, g2);
322 assign(&mut tmp_acc, &points);
324
325 let r_2i = get_bit(r, 2 * i);
326 if r_2i == 0 {
327 batch_negate_in_place(&mut tmp_s);
328 }
329 if get_bit(r, 2 * i + 1) == 1 {
330 batch_endo_in_place(endo_coeff, &mut tmp_s);
331 }
332
333 batch_add_assign_no_branch(&mut denominators, &mut points, &tmp_s);
335 batch_add_assign_no_branch(&mut denominators, &mut points, &tmp_acc);
336 }
337 batch_add_assign(&mut denominators, &mut points, g1);
339 points
340}
341
342fn batch_double_in_place<P: SWCurveConfig>(
344 denominators: &mut [P::BaseField],
345 points: &mut [SWJAffine<P>],
346) {
347 o1_utils::cfg_iter_mut!(denominators)
348 .zip(o1_utils::cfg_iter!(points))
349 .for_each(|(d, p)| {
350 *d = p.y.double();
351 });
352 ark_ff::batch_inversion::<P::BaseField>(denominators);
353
354 o1_utils::cfg_iter!(denominators)
356 .zip(o1_utils::cfg_iter_mut!(points))
357 .for_each(|(d, p)| {
358 let sq = p.x.square();
359 let s = (sq.double() + sq + P::COEFF_A) * d;
360 let x = s.square() - p.x.double();
361 let y = -p.y - (s * (x - p.x));
362 p.x = x;
363 p.y = y;
364 });
365}
366
367fn affine_window_combine_one_base<P: SWCurveConfig>(
368 g1: &[SWJAffine<P>],
369 g2: &[SWJAffine<P>],
370 x2: P::ScalarField,
371) -> Vec<SWJAffine<P>> {
372 let windows2 = BitIteratorBE::new(x2.into_bigint()).tuples();
373
374 let mut points = vec![SWJAffine::<P>::zero(); g1.len()];
375
376 let mut denominators = vec![P::BaseField::zero(); g1.len()];
377
378 let [g01, g10, g11] = affine_shamir_window_table_one(&mut denominators, g2);
379
380 for (hi_2, lo_2) in windows2 {
381 for _ in 0..2 {
383 for i in 0..g1.len() {
384 denominators[i] = points[i].y.double();
385 }
386 ark_ff::batch_inversion::<P::BaseField>(&mut denominators);
387
388 for i in 0..g1.len() {
390 let d = denominators[i];
391 let sq = points[i].x.square();
392 let s = (sq.double() + sq + P::COEFF_A) * d;
393 let x = s.square() - points[i].x.double();
394 let y = -points[i].y - (s * (x - points[i].x));
395 points[i].x = x;
396 points[i].y = y;
397 }
398 }
399
400 match (hi_2, lo_2) {
401 (false, false) => (),
402 (false, true) => batch_add_assign(&mut denominators, &mut points, &g01),
403 (true, false) => batch_add_assign(&mut denominators, &mut points, &g10),
404 (true, true) => {
405 batch_add_assign(&mut denominators, &mut points, &g11);
406 }
407 }
408 }
409
410 batch_add_assign(&mut denominators, &mut points, g1);
411
412 points
413}
414
415pub fn affine_window_combine<P: SWCurveConfig>(
416 g1: &[SWJAffine<P>],
417 g2: &[SWJAffine<P>],
418 x1: P::ScalarField,
419 x2: P::ScalarField,
420) -> Vec<SWJAffine<P>> {
421 const CHUNK_SIZE: usize = 10_000;
422 let b: Vec<_> = g1.chunks(CHUNK_SIZE).zip(g2.chunks(CHUNK_SIZE)).collect();
423 let v: Vec<_> = o1_utils::cfg_into_iter!(b)
424 .map(|(v1, v2)| affine_window_combine_base(v1, v2, x1, x2))
425 .collect();
426 v.concat()
427}
428
429pub fn affine_window_combine_one_endo<P: SWCurveConfig>(
434 endo_coeff: P::BaseField,
435 g1: &[SWJAffine<P>],
436 g2: &[SWJAffine<P>],
437 chal: &ScalarChallenge<P::ScalarField>,
438) -> Vec<SWJAffine<P>> {
439 const CHUNK_SIZE: usize = 4096;
440 let b: Vec<_> = g1.chunks(CHUNK_SIZE).zip(g2.chunks(CHUNK_SIZE)).collect();
441 let v: Vec<_> = o1_utils::cfg_into_iter!(b)
442 .map(|(v1, v2)| affine_window_combine_one_endo_base(endo_coeff, v1, v2, chal))
443 .collect();
444 v.concat()
445}
446pub fn affine_window_combine_one<P: SWCurveConfig>(
447 g1: &[SWJAffine<P>],
448 g2: &[SWJAffine<P>],
449 x2: P::ScalarField,
450) -> Vec<SWJAffine<P>> {
451 const CHUNK_SIZE: usize = 10_000;
452 let b: Vec<_> = g1.chunks(CHUNK_SIZE).zip(g2.chunks(CHUNK_SIZE)).collect();
453 let v: Vec<_> = o1_utils::cfg_into_iter!(b)
454 .map(|(v1, v2)| affine_window_combine_one_base(v1, v2, x2))
455 .collect();
456 v.concat()
457}
458
459pub fn window_combine<G: AffineRepr>(
460 g_lo: &[G],
461 g_hi: &[G],
462 x_lo: G::ScalarField,
463 x_hi: G::ScalarField,
464) -> Vec<G> {
465 let mut g_proj: Vec<G::Group> = {
466 let pairs: Vec<_> = g_lo.iter().zip(g_hi).collect();
467 o1_utils::cfg_into_iter!(pairs)
468 .map(|(lo, hi)| window_shamir::<G>(x_lo, *lo, x_hi, *hi))
469 .collect()
470 };
471 G::Group::normalize_batch(g_proj.as_mut_slice())
472}
473
474pub fn affine_shamir_window_table<P: SWCurveConfig>(
475 denominators: &mut [P::BaseField],
476 g1: &[SWJAffine<P>],
477 g2: &[SWJAffine<P>],
478) -> [Vec<SWJAffine<P>>; 15] {
479 fn assign<A: Copy>(dst: &mut [A], src: &[A]) {
480 let n = dst.len();
481 dst[..n].clone_from_slice(&src[..n]);
482 }
483
484 let n = g1.len();
485
486 let mut res: [Vec<_>; 15] = [
487 vec![SWJAffine::<P>::zero(); n],
488 vec![SWJAffine::<P>::zero(); n],
489 vec![SWJAffine::<P>::zero(); n],
490 vec![SWJAffine::<P>::zero(); n],
491 vec![SWJAffine::<P>::zero(); n],
492 vec![SWJAffine::<P>::zero(); n],
493 vec![SWJAffine::<P>::zero(); n],
494 vec![SWJAffine::<P>::zero(); n],
495 vec![SWJAffine::<P>::zero(); n],
496 vec![SWJAffine::<P>::zero(); n],
497 vec![SWJAffine::<P>::zero(); n],
498 vec![SWJAffine::<P>::zero(); n],
499 vec![SWJAffine::<P>::zero(); n],
500 vec![SWJAffine::<P>::zero(); n],
501 vec![SWJAffine::<P>::zero(); n],
502 ];
503
504 let [g01_00, g10_00, g11_00, g00_01, g01_01, g10_01, g11_01, g00_10, g01_10, g10_10, g11_10, g00_11, g01_11, g10_11, g11_11] =
505 &mut res;
506
507 assign(g01_00, g1);
508
509 assign(g10_00, g1);
510 batch_add_assign(denominators, g10_00, g1);
511
512 assign(g11_00, g10_00);
513 batch_add_assign(denominators, g11_00, g1);
514
515 assign(g00_01, g2);
516
517 assign(g01_01, g00_01);
518 batch_add_assign(denominators, g01_01, g1);
519
520 assign(g10_01, g01_01);
521 batch_add_assign(denominators, g10_01, g1);
522
523 assign(g11_01, g10_01);
524 batch_add_assign(denominators, g11_01, g1);
525
526 assign(g00_10, g00_01);
527 batch_add_assign(denominators, g00_10, g2);
528
529 assign(g01_10, g00_10);
530 batch_add_assign(denominators, g01_10, g1);
531
532 assign(g10_10, g01_10);
533 batch_add_assign(denominators, g10_10, g1);
534
535 assign(g11_10, g10_10);
536 batch_add_assign(denominators, g11_10, g1);
537
538 assign(g00_11, g00_10);
539 batch_add_assign(denominators, g00_11, g2);
540
541 assign(g01_11, g00_11);
542 batch_add_assign(denominators, g01_11, g1);
543
544 assign(g10_11, g01_11);
545 batch_add_assign(denominators, g10_11, g1);
546
547 assign(g11_11, g10_11);
548 batch_add_assign(denominators, g11_11, g1);
549
550 res
551}
552
553pub fn affine_shamir_window_table_one<P: SWCurveConfig>(
554 denominators: &mut [P::BaseField],
555 g1: &[SWJAffine<P>],
556) -> [Vec<SWJAffine<P>>; 3] {
557 fn assign<A: Copy>(dst: &mut [A], src: &[A]) {
558 let n = dst.len();
559 dst[..n].clone_from_slice(&src[..n]);
560 }
561
562 let n = g1.len();
563
564 let mut res: [Vec<_>; 3] = [
565 vec![SWJAffine::<P>::zero(); n],
566 vec![SWJAffine::<P>::zero(); n],
567 vec![SWJAffine::<P>::zero(); n],
568 ];
569
570 let [g01, g10, g11] = &mut res;
571
572 assign(g01, g1);
573
574 assign(g10, g1);
575 batch_add_assign(denominators, g10, g1);
576
577 assign(g11, g10);
578 batch_add_assign(denominators, g11, g1);
579
580 res
581}
582
583fn window_shamir<G: AffineRepr>(x1: G::ScalarField, g1: G, x2: G::ScalarField, g2: G) -> G::Group {
584 let [_g00_00, g01_00, g10_00, g11_00, g00_01, g01_01, g10_01, g11_01, g00_10, g01_10, g10_10, g11_10, g00_11, g01_11, g10_11, g11_11] =
585 shamir_window_table(g1, g2);
586
587 let windows1 = BitIteratorBE::new(x1.into_bigint()).tuples();
588 let windows2 = BitIteratorBE::new(x2.into_bigint()).tuples();
589
590 let mut res = G::Group::zero();
591
592 for ((hi_1, lo_1), (hi_2, lo_2)) in windows1.zip(windows2) {
593 res.double_in_place();
594 res.double_in_place();
595 match ((hi_1, lo_1), (hi_2, lo_2)) {
596 ((false, false), (false, false)) => (),
597 ((false, true), (false, false)) => res.add_assign(&g01_00),
598 ((true, false), (false, false)) => res.add_assign(&g10_00),
599 ((true, true), (false, false)) => res.add_assign(&g11_00),
600
601 ((false, false), (false, true)) => res.add_assign(&g00_01),
602 ((false, true), (false, true)) => res.add_assign(&g01_01),
603 ((true, false), (false, true)) => res.add_assign(&g10_01),
604 ((true, true), (false, true)) => res.add_assign(&g11_01),
605
606 ((false, false), (true, false)) => res.add_assign(&g00_10),
607 ((false, true), (true, false)) => res.add_assign(&g01_10),
608 ((true, false), (true, false)) => res.add_assign(&g10_10),
609 ((true, true), (true, false)) => res.add_assign(&g11_10),
610
611 ((false, false), (true, true)) => res.add_assign(&g00_11),
612 ((false, true), (true, true)) => res.add_assign(&g01_11),
613 ((true, false), (true, true)) => res.add_assign(&g10_11),
614 ((true, true), (true, true)) => res.add_assign(&g11_11),
615 }
616 }
617
618 res
619}
620
621pub fn shamir_window_table<G: AffineRepr>(g1: G, g2: G) -> [G; 16] {
622 let g00_00 = G::generator().into_group();
623 let g01_00 = g1.into_group();
624 let g10_00 = {
625 let mut g = g01_00;
626 g.add_assign(&g1);
627 g
628 };
629 let g11_00 = {
630 let mut g = g10_00;
631 g.add_assign(&g1);
632 g
633 };
634
635 let g00_01 = g2.into_group();
636 let g01_01 = {
637 let mut g = g00_01;
638 g.add_assign(&g1);
639 g
640 };
641 let g10_01 = {
642 let mut g = g01_01;
643 g.add_assign(&g1);
644 g
645 };
646 let g11_01 = {
647 let mut g = g10_01;
648 g.add_assign(&g1);
649 g
650 };
651
652 let g00_10 = {
653 let mut g = g00_01;
654 g.add_assign(&g2);
655 g
656 };
657 let g01_10 = {
658 let mut g = g00_10;
659 g.add_assign(&g1);
660 g
661 };
662 let g10_10 = {
663 let mut g = g01_10;
664 g.add_assign(&g1);
665 g
666 };
667 let g11_10 = {
668 let mut g = g10_10;
669 g.add_assign(&g1);
670 g
671 };
672 let g00_11 = {
673 let mut g = g00_10;
674 g.add_assign(&g2);
675 g
676 };
677 let g01_11 = {
678 let mut g = g00_11;
679 g.add_assign(&g1);
680 g
681 };
682 let g10_11 = {
683 let mut g = g01_11;
684 g.add_assign(&g1);
685 g
686 };
687 let g11_11 = {
688 let mut g = g10_11;
689 g.add_assign(&g1);
690 g
691 };
692
693 let mut v = vec![
694 g00_00, g01_00, g10_00, g11_00, g00_01, g01_01, g10_01, g11_01, g00_10, g01_10, g10_10,
695 g11_10, g00_11, g01_11, g10_11, g11_11,
696 ];
697 let v: Vec<_> = G::Group::normalize_batch(v.as_mut_slice());
698 [
699 v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13],
700 v[14], v[15],
701 ]
702}