Skip to content

Commit

Permalink
test: added CountSubstringOccurrences tests
Browse files Browse the repository at this point in the history
  • Loading branch information
shreyas-londhe committed Sep 12, 2024
1 parent 49cddd9 commit 1f02068
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 12 deletions.
14 changes: 7 additions & 7 deletions packages/circuits/tests/check-substring-match.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe("CheckSubstringMatch Circuit", () => {
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(1n); // isMatch should be 1
expect(witness[1]).toBe(1n);
});

it("should not match when substring is different", async () => {
Expand All @@ -35,7 +35,7 @@ describe("CheckSubstringMatch Circuit", () => {
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(0n); // isMatch should be 0
expect(witness[1]).toBe(0n);
});

it("should match with full length substring", async () => {
Expand All @@ -49,7 +49,7 @@ describe("CheckSubstringMatch Circuit", () => {
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(1n); // isMatch should be 1
expect(witness[1]).toBe(1n);
});

it("should fail when first element of substring is zero", async () => {
Expand All @@ -69,7 +69,7 @@ describe("CheckSubstringMatch Circuit", () => {
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(0n); // isMatch should be 0
expect(witness[1]).toBe(0n);
});

it("should match with single-element substring", async () => {
Expand All @@ -79,7 +79,7 @@ describe("CheckSubstringMatch Circuit", () => {
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(1n); // isMatch should be 1
expect(witness[1]).toBe(1n);
});

it("should not match when input is all zeros", async () => {
Expand All @@ -89,7 +89,7 @@ describe("CheckSubstringMatch Circuit", () => {
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(0n); // isMatch should be 0
expect(witness[1]).toBe(0n);
});

it("should not match when substring is longer than non-zero part of input", async () => {
Expand All @@ -99,6 +99,6 @@ describe("CheckSubstringMatch Circuit", () => {
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(0n); // isMatch should be 0
expect(witness[1]).toBe(0n);
});
});
106 changes: 106 additions & 0 deletions packages/circuits/tests/count-substring-occurrences.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { wasm as wasm_tester } from "circom_tester";
import path from "path";

describe("CountSubstringOccurrences Circuit", () => {
jest.setTimeout(10 * 60 * 1000); // 10 minutes

let circuit: any;

beforeAll(async () => {
circuit = await wasm_tester(
path.join(
__dirname,
"./test-circuits/count-substring-occurrences-test.circom"
),
{
recompile: true,
include: path.join(__dirname, "../../../node_modules"),
output: path.join(__dirname, "./compiled-test-circuits"),
}
);
});

it("should count single occurrence at the beginning", async () => {
const input = {
in: [1, 2, 3, 4, 5, ...Array(1019).fill(0)],
substring: [1, 2, 3, ...Array(125).fill(0)],
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(1n);
});

it("should count multiple occurrences", async () => {
const input = {
in: [1, 2, 3, 4, 1, 2, 3, 5, 1, 2, 3, ...Array(1013).fill(0)],
substring: [1, 2, 3, ...Array(125).fill(0)],
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(3n);
});

it("should return 0 for no occurrences", async () => {
const input = {
in: [1, 2, 4, 5, 6, ...Array(1019).fill(0)],
substring: [1, 2, 3, ...Array(125).fill(0)],
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(0n);
});

it("should count occurrences with overlap", async () => {
const input = {
in: [1, 1, 1, 2, 1, 1, ...Array(1018).fill(0)],
substring: [1, 1, ...Array(126).fill(0)],
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(3n);
});

it("should handle full match of input", async () => {
const repeatedPattern = [1, 2, 3, 4];
const input = {
in: Array(256)
.fill(repeatedPattern)
.flat()
.concat(Array(1024 - 256 * 4).fill(0)),
substring: [1, 2, 3, 4, ...Array(124).fill(0)],
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(256n);
});

it("should handle single character substring", async () => {
const input = {
in: [1, 2, 1, 3, 1, 4, 1, ...Array(1017).fill(0)],
substring: [1, ...Array(127).fill(0)],
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(4n);
});

it("should handle substring at the end of input", async () => {
const input = {
in: [...Array(1021).fill(0), 1, 2, 3],
substring: [1, 2, 3, ...Array(125).fill(0)],
};
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);
expect(witness[1]).toBe(1n);
});

it("should return 0 for empty substring", async () => {
const input = {
in: [1, 2, 3, 4, 5, ...Array(1019).fill(0)],
substring: Array(128).fill(0),
};
await expect(async () => {
await circuit.calculateWitness(input);
}).rejects.toThrow("Assert Failed");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pragma circom 2.1.6;

include "../../utils/array.circom";

component main = CountSubstringOccurrences(1024, 128);
14 changes: 9 additions & 5 deletions packages/circuits/utils/array.circom
Original file line number Diff line number Diff line change
Expand Up @@ -231,18 +231,22 @@ template CountSubstringOccurrences(maxLen, maxSubstringLen) {
signal output count;

// Check for matches at each possible starting position
component matches[maxLen - maxSubstringLen];
for (var i = 0; i < maxLen - maxSubstringLen; i++) {
component matches[maxLen];
for (var i = 0; i < maxLen; i++) {
matches[i] = CheckSubstringMatch(maxSubstringLen);
for (var j = 0; j < maxSubstringLen; j++) {
matches[i].in[j] <== in[i + j];
if (i + j < maxLen) {
matches[i].in[j] <== in[i + j];
} else {
matches[i].in[j] <== 0;
}
}
matches[i].substring <== substring;
}

// Sum up all matches to get the total count
component summer = CalculateTotal(maxLen - maxSubstringLen);
for (var i = 0; i <= maxLen - maxSubstringLen; i++) {
component summer = CalculateTotal(maxLen);
for (var i = 0; i < maxLen; i++) {
summer.nums[i] <== matches[i].isMatch;
}

Expand Down

0 comments on commit 1f02068

Please sign in to comment.