Skip to content

Commit

Permalink
added insert functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
querolita committed Nov 4, 2024
1 parent 0b54106 commit 3ba8c4b
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 7 deletions.
36 changes: 29 additions & 7 deletions src/lib/provable/dynamic-array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ class DynamicArrayBase<T = any, V = any> {
* Increments the length of the current array by n elements, checking that the
* new length is within the capacity.
*/
increaseLength(n: Field): void {
increaseLengthBy(n: Field): void {
let newLength = this.length.add(n).seal();
newLength.lessThanOrEqual(new Field(this.capacity)).assertTrue();
this.length = newLength;
Expand All @@ -313,7 +313,7 @@ class DynamicArrayBase<T = any, V = any> {
* Decrements the length of the current array by n elements, checking that the
* n is less or equal than the current length.
*/
decreaseLength(n: Field): void {
decreaseLengthBy(n: Field): void {
let oldLength = this.length;
n.assertLessThanOrEqual(this.length);
this.length = oldLength.sub(n).seal();
Expand All @@ -333,7 +333,7 @@ class DynamicArrayBase<T = any, V = any> {
*/
push(value: T): void {
let oldLength = this.length;
this.increaseLength(new Field(1));
this.increaseLengthBy(new Field(1));
this.setOrDoNothing(oldLength, value);
}

Expand All @@ -346,7 +346,7 @@ class DynamicArrayBase<T = any, V = any> {
*/
pop(n?: Field): void {
let dec = n !== undefined ? n : new Field(1);
this.decreaseLength(dec);
this.decreaseLengthBy(dec);

let NULL: T = ProvableType.synthesize(this.innerType);
if (n !== undefined) {
Expand Down Expand Up @@ -391,7 +391,7 @@ class DynamicArrayBase<T = any, V = any> {
NULL
);
}
this.decreaseLength(n);
this.decreaseLengthBy(n);
}

/**
Expand All @@ -402,7 +402,7 @@ class DynamicArrayBase<T = any, V = any> {
* @param n
*/
shiftRight(n: Field): void {
this.increaseLength(n);
this.increaseLengthBy(n);
let NULL = ProvableType.synthesize(this.innerType);

for (let i = this.capacity - 1; i >= 0; i--) {
Expand Down Expand Up @@ -470,9 +470,31 @@ class DynamicArrayBase<T = any, V = any> {
return res;
}

/**
* Inserts a value at index i, shifting all elements after that position to
* the right by one. The length of the array is increased by one, which must
* result in less than or equal to the capacity.
*
* @param i
* @param value
*/
insert(index: Field, value: T): void {
const right = this.slice(index, this.length);
this.increaseLengthBy(new Field(1));
this.set(index, value);
for (let i = 0; i < this.capacity; i++) {
let offset = new Field(i).sub(index).sub(new Field(1));
this.array[i] = Provable.if(
new Field(i).lessThanOrEqual(index),
this.innerType,
this.getOrUnconstrained(new Field(i)),
right.getOrUnconstrained(offset)
);
}
}

// TODO:
// - includes
// - insert

// cached variables to not duplicate constraints if we do something like
// array.get(i), array.set(i, ..) on the same index
Expand Down
66 changes: 66 additions & 0 deletions src/lib/provable/test/dynamic-array.unit-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,22 @@ let List = ZkProgram({
.value.equals(bytes.get(new Field(2)).value)
);

// Cannot slice out-of-bounds positions
try {
bytes.slice(new Field(1), new Field(5));
} catch (error) {
console.log('Cannot slice out-of-bounds positions');
}

// Cannot slice with end position smaller than start position
try {
bytes.slice(new Field(2), new Field(1));
} catch (error) {
console.log(
'Cannot slice with end position smaller than start position'
);
}

// Concatenate two empty arrays gives an empty array
let emptyLeft = new Bytestring();
let emptyRight = new Bytestring();
Expand Down Expand Up @@ -283,6 +299,56 @@ let List = ZkProgram({
.value.equals(right.get(new Field(i)).value)
);
}

// Inserting elements at the beginning of the array
bytes = new Bytestring([
new UInt8(2),
new UInt8(3),
new UInt8(4),
new UInt8(6),
new UInt8(7),
]);
bytes.insert(new Field(0), new UInt8(1));
assert(bytes.length.equals(new Field(6)));
assert(bytes.get(new Field(0)).value.equals(new Field(1)));
assert(bytes.get(new Field(1)).value.equals(new Field(2)));
assert(bytes.get(new Field(2)).value.equals(new Field(3)));
assert(bytes.get(new Field(3)).value.equals(new Field(4)));
assert(bytes.get(new Field(4)).value.equals(new Field(6)));
assert(bytes.get(new Field(5)).value.equals(new Field(7)));

// Inserting elements at the end of the array
bytes.insert(bytes.length, new UInt8(8));
assert(bytes.length.equals(new Field(7)));
assert(bytes.get(new Field(0)).value.equals(new Field(1)));
assert(bytes.get(new Field(1)).value.equals(new Field(2)));
assert(bytes.get(new Field(2)).value.equals(new Field(3)));
assert(bytes.get(new Field(3)).value.equals(new Field(4)));
assert(bytes.get(new Field(4)).value.equals(new Field(6)));
assert(bytes.get(new Field(5)).value.equals(new Field(7)));
assert(bytes.get(new Field(6)).value.equals(new Field(8)));

// Inserting elements in the middle of the array
bytes.insert(new Field(4), new UInt8(5));
assert(bytes.length.equals(new Field(8)));
for (let i = 0; i < 8; i++) {
assert(bytes.get(new Field(i)).value.equals(new Field(i + 1)));
}

// Cannot insert elements exceeding capacity
try {
bytes.insert(new Field(1), new UInt8(0));
} catch (error) {
console.log('Cannot insert above capacity');
}

// Cannot insert elements out-of-bounds
bytes = new Bytestring([new UInt8(1), new UInt8(2), new UInt8(3)]);
try {
bytes.insert(new Field(4), new UInt8(5));
} catch (error) {
console.log('Cannot insert out-of-bounds');
}
},
},
},
Expand Down

0 comments on commit 3ba8c4b

Please sign in to comment.