Skip to content

Commit 830af37

Browse files
coinage-layer task #88: strengthen find_coin_entry_exact_cover to sharp None
The None branch postcondition gets the same accumulator-pattern forall-no-pair-works claim as find_two_coin_exact_cover. Outer accumulator covers all coin indices < i; inner accumulator covers all entry indices < k for current i. 259 verified, 0 errors. First try clean — same pattern as the multi-coin extensions in #87.
1 parent 997934c commit 830af37

1 file changed

Lines changed: 57 additions & 1 deletion

File tree

  • rust/crates/coinage-layer/src

rust/crates/coinage-layer/src/lib.rs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8896,7 +8896,22 @@ impl State {
88968896
&& coin_value(self.coins()[coin_key].exponent)
88978897
+ coin_value(self.entries()[entry_key].exponent)
88988898
== amount as nat,
8899-
None => true,
8899+
None =>
8900+
// Sharp: no (coin, entry) pair satisfies the cover.
8901+
forall|i: int, k: int|
8902+
0 <= i < self.coins@.len()
8903+
&& 0 <= k < self.entries@.len()
8904+
==> {
8905+
let c = #[trigger] self.coins@[i];
8906+
let e = #[trigger] self.entries@[k];
8907+
c.purse != p
8908+
|| c.state != CoinState::Available
8909+
|| e.purse != p
8910+
|| e.on_chain != EntryOnChain::Ready
8911+
|| e.local != EntryLocal::LocalAvailable
8912+
|| (coin_value(c.exponent) + coin_value(e.exponent)
8913+
!= amount as nat)
8914+
},
89008915
},
89018916
{
89028917
let nc = self.coins.len();
@@ -8908,6 +8923,21 @@ impl State {
89088923
nc == self.coins.len(),
89098924
ne == self.entries.len(),
89108925
self.invariant(),
8926+
// Outer accumulator: no (coin, entry) pair with coin index < i.
8927+
forall|i1: int, k: int|
8928+
0 <= i1 < i as int
8929+
&& 0 <= k < ne as int
8930+
==> {
8931+
let c = #[trigger] self.coins@[i1];
8932+
let e = #[trigger] self.entries@[k];
8933+
c.purse != p
8934+
|| c.state != CoinState::Available
8935+
|| e.purse != p
8936+
|| e.on_chain != EntryOnChain::Ready
8937+
|| e.local != EntryLocal::LocalAvailable
8938+
|| (coin_value(c.exponent) + coin_value(e.exponent)
8939+
!= amount as nat)
8940+
},
89118941
decreases nc - i,
89128942
{
89138943
let ci_avail = matches!(self.coins[i].state, CoinState::Available);
@@ -8933,6 +8963,32 @@ impl State {
89338963
vi as nat == coin_value(self.coins@[i as int].exponent),
89348964
vi <= 1073741824u64,
89358965
vi <= amount,
8966+
// Outer accumulator carried.
8967+
forall|i1: int, kk: int|
8968+
0 <= i1 < i as int
8969+
&& 0 <= kk < ne as int
8970+
==> {
8971+
let c = #[trigger] self.coins@[i1];
8972+
let e = #[trigger] self.entries@[kk];
8973+
c.purse != p
8974+
|| c.state != CoinState::Available
8975+
|| e.purse != p
8976+
|| e.on_chain != EntryOnChain::Ready
8977+
|| e.local != EntryLocal::LocalAvailable
8978+
|| (coin_value(c.exponent) + coin_value(e.exponent)
8979+
!= amount as nat)
8980+
},
8981+
// Inner accumulator: for all checked k2 < k,
8982+
// the pair (i, k2) doesn't satisfy.
8983+
forall|k2: int|
8984+
0 <= k2 < k as int
8985+
==>
8986+
(#[trigger] self.entries@[k2]).purse != p
8987+
|| self.entries@[k2].on_chain != EntryOnChain::Ready
8988+
|| self.entries@[k2].local != EntryLocal::LocalAvailable
8989+
|| (coin_value(self.coins@[i as int].exponent)
8990+
+ coin_value(self.entries@[k2].exponent)
8991+
!= amount as nat),
89368992
decreases ne - k,
89378993
{
89388994
let e = &self.entries[k];

0 commit comments

Comments
 (0)