Skip to content

Commit 3016be0

Browse files
authored
Merge pull request #2250 from o1-labs/api/dynamicarray/reverse
DynamicArray support reverse feature
2 parents b2c1745 + 160fb68 commit 3016be0

File tree

3 files changed

+43
-5
lines changed

3 files changed

+43
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
2020
### Added
2121

2222
- Lazy mode for prover index computation. https://github.com/o1-labs/o1js/pull/2143
23+
- Added reverse functionality to `DynamicArray` and indexed `forEach` and `forEachReverse` variants.
2324
- Added `ZkProgram.analyzeSingleMethod(methodName: string)` to analyze a single method of a ZkProgram. https://github.com/o1-labs/o1js/pull/2217
2425
- This is an addition to `ZkProgram.analyzeMethods()` which analyzes all methods of a ZkProgram by executing them.
2526
- Now only a single method is analyzed at a time.

src/lib/provable/dynamic-array.ts

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,26 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
274274
* Iterate over all elements of the array.
275275
*
276276
* The callback will be passed an element and a boolean `isDummy` indicating
277-
* whether the value is part of the actual array.
277+
* whether the value is part of the actual array. Optionally, an index can be
278+
* passed as a third argument (used in `forEachReversed`)
278279
*/
279-
forEach(f: (t: ProvableValue, isDummy: Bool) => void) {
280-
zip(this.array, this.#dummyMask()).forEach(([t, isDummy]) => {
281-
f(t, isDummy);
282-
});
280+
forEach(f: (t: ProvableValue, isDummy: Bool, i?: number) => void): void {
281+
zip(this.array, this.#dummyMask()).forEach(([t, isDummy], i) => f(t, isDummy, i));
282+
}
283+
284+
/**
285+
* Iterate over all elements of the array, in reverse order.
286+
*
287+
* The callback will be passed an element and a boolean `isDummy` indicating whether the value is part of the actual array.
288+
*
289+
* Note: the indices are also passed in reverse order, i.e. we always have `t = this.array[i]`.
290+
*/
291+
forEachReverse(f: (t: ProvableValue, isDummy: Bool, i: number) => void) {
292+
zip(this.array, this.#dummyMask())
293+
.reverse()
294+
.forEach(([t, isDummy], i) => {
295+
f(t, isDummy, this.capacity - 1 - i);
296+
});
283297
}
284298

285299
/**
@@ -519,6 +533,21 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
519533
return sliced;
520534
}
521535

536+
/**
537+
* Returns a new array with the elements reversed.
538+
*/
539+
reverse(): DynamicArray<ProvableValue, Value> {
540+
let Array = DynamicArray(this.innerType, { capacity: this.capacity });
541+
// first, copy the inner array of length capacity and reverse it
542+
let array = this.array.slice().reverse();
543+
544+
// now, slice off the padding that is now at the beginning of the array
545+
let capacity = new Field(this.capacity);
546+
return new Array(array, capacity).slice(
547+
capacity.sub(this.length).seal()
548+
);
549+
}
550+
522551
/**
523552
* Concatenates the current array with another dynamic array, returning a new
524553
* dynamic array with the values of both arrays. The capacity of the new array

src/lib/provable/test/dynamic-array.unit-test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,14 @@ import { Provable } from '../provable.js';
581581
// Checking inclusion of elements
582582
assert(bytes.includes(new UInt8(1)));
583583
assert(bytes.includes(new UInt8(20)).not());
584+
585+
// Reverse the array
586+
let reversed = bytes.reverse();
587+
for (let i = 0; i < 8; i++) {
588+
assert(reversed.get(new Field(i)).value.equals(new Field(8 - i)));
589+
// the original array is not modified
590+
assert(bytes.get(new Field(i)).value.equals(new Field(i + 1)));
591+
}
584592
},
585593
},
586594
},

0 commit comments

Comments
 (0)