Substring search — count and locate occurrences
Counts non-overlapping occurrences of a needle by repeatedly slicing past each match, and reports all start indices. Demonstrates index_of with a moving window via substring, and guarding against an empty needle. Run: soma run substring_search.cell --signal count_occurrences "ababab" "ab"
A Soma program from the text part of the verified corpus: 49 lines, cells SubstringSearch, SubstringSearchTest. It passes soma check with soma 3.2.4.
How it is built
The cell SubstringSearch declares 2 signals in its face: count_occurrences(haystack: String, needle: String) -> Int and first_index(haystack: String, needle: String) -> Int. Handlers: count_occurrences (line 17) and first_index (line 34).
The file also holds 1 test cell (SubstringSearchTest) with 6 assertions, run by soma test.
The source
substring_search.cell
// ============================================================================
// Substring search — count and locate occurrences
// ============================================================================
//
// Counts non-overlapping occurrences of a needle by repeatedly slicing past
// each match, and reports all start indices. Demonstrates index_of with a
// moving window via substring, and guarding against an empty needle.
//
// Run: soma run substring_search.cell --signal count_occurrences "ababab" "ab"
cell SubstringSearch {
face {
signal count_occurrences(haystack: String, needle: String) -> Int
signal first_index(haystack: String, needle: String) -> Int
}
on count_occurrences(haystack: String, needle: String) {
if len(needle) == 0 { return 0 }
let count = 0
let i = 0
let n = len(haystack)
let m = len(needle)
while i <= n - m {
if substring(haystack, i, i + m) == needle {
count = count + 1
i = i + m
} else {
i = i + 1
}
}
return count
}
on first_index(haystack: String, needle: String) {
return index_of(haystack, needle)
}
}
cell test SubstringSearchTest {
rules {
assert count_occurrences("ababab", "ab") == 3
assert count_occurrences("aaaa", "aa") == 2
assert count_occurrences("hello", "z") == 0
assert count_occurrences("mississippi", "ss") == 2
assert first_index("hello", "llo") == 2
assert first_index("hello", "z") == -1
}
}What the toolchain says
$ soma check substring_search.cell
✓ All checks passed.$ soma test substring_search.cell
test SubstringSearchTest ...
✓ assert count_occurrences("ababab", "ab") == 3
✓ assert count_occurrences("aaaa", "aa") == 2
✓ assert count_occurrences("hello", "z") == 0
✓ assert count_occurrences("mississippi", "ss") == 2
✓ assert first_index("hello", "llo") == 2
✓ assert first_index("hello", "z") == -1
6 tests: 6 passed, 0 failed$ soma verify substring_search.cell
Verifying substring_search.cell...
SubstringSearch/termination:
⚠ handler `count_occurrences`: while-loop without provable termination bound (write `while [loop_bound(N)] cond { … }`, or a for-loop over a bounded range)
0 passed, 1 warnings, 0 failures
Temporal: 0 passed, 0 failed
VERIFY OK